Start to port bank-payment to v9 (with a lot of improvements) during the Sorrento Code sprint 2016

Improvements include:
- full re-organisation of modules and big re-organisation of the code
- simplification of the code related to the fact that support for direct debit is now in t
he base module, not added by an optional module account_direct_debit (module was removed)
- new design of the wizard to select move lines to pay
- support for non-SEPA file transfer-
- support for German direct debit SEPA files (fixes bug #129)
- remove workflow of payment.order

This port to v9 is not finished... there is still a lot of work:
- finish the code of account_payment_order/wizard/account_payment_line_create.py
- port account_banking_payment_transfer and integrate it inside account_payment_order
- fix bugs
- clean-up code, remove dead code
- test in several complex scenarios
This commit is contained in:
Alexis de Lattre
2016-04-30 01:46:34 +02:00
parent 29cfd14a38
commit bee3093841
167 changed files with 3375 additions and 3359 deletions

View File

@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
# © 2014 Compassion CH - Cyril Sester <csester@compassion.ch>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2015-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Account Banking Mandate',
'summary': 'Banking mandates',
'version': '8.0.0.2.0',
'version': '9.0.0.2.0',
'license': 'AGPL-3',
'author': "Compassion CH, "
"Serv. Tecnol. Avanzados - Pedro M. Baeza, "
@@ -16,19 +16,20 @@
'website': 'https://github.com/OCA/bank-payment',
'category': 'Banking addons',
'depends': [
'account_banking_payment_export',
'account_payment_order',
],
'data': [
'views/account_banking_mandate_view.xml',
'views/account_invoice_view.xml',
'views/account_payment_view.xml',
'views/account_payment_line.xml',
'views/res_partner_bank_view.xml',
'views/bank_payment_line_view.xml',
'views/account_move_line.xml',
'data/mandate_reference_sequence.xml',
'security/mandate_security.xml',
'security/ir.model.access.csv',
],
'demo': [],
'test': ['test/banking_mandate.yml'],
'installable': False,
'installable': True,
}

View File

@@ -3,11 +3,6 @@
<data noupdate="1">
<record id="dd_mandate_seq_type" model="ir.sequence.type">
<field name="name">DD Mandate Reference</field>
<field name="code">account.banking.mandate</field>
</record>
<record id="dd_mandate_seq" model="ir.sequence">
<field name="name">DD Mandate Reference</field>
<field name="code">account.banking.mandate</field>

View File

@@ -6,5 +6,6 @@
from . import account_banking_mandate
from . import account_invoice
from . import res_partner_bank
from . import payment_line
from . import account_payment_line
from . import bank_payment_line
from . import account_move_line

View File

@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# © 2014 Compassion CH - Cyril Sester <csester@compassion.ch>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2015-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, exceptions, api, _
from openerp import models, fields, api, _
from openerp.exceptions import UserError, ValidationError
class AccountBankingMandate(models.Model):
@@ -17,30 +18,10 @@ class AccountBankingMandate(models.Model):
_rec_name = 'unique_mandate_reference'
_inherit = ['mail.thread']
_order = 'signature_date desc'
_track = {
'state': {
'account_banking_mandate.mandate_valid': (
lambda self, cr, uid, obj, ctx=None: obj['state'] == 'valid'),
'account_banking_mandate.mandate_expired': (
lambda self, cr, uid, obj, ctx=None:
obj['state'] == 'expired'),
'account_banking_mandate.mandate_cancel': (
lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel'),
},
}
def _get_states(self):
return [('draft', 'Draft'),
('valid', 'Valid'),
('expired', 'Expired'),
('cancel', 'Cancelled')]
format = fields.Selection(
[('basic', _('Basic Mandate'))],
default='basic',
required=True,
string='Mandate Format',
)
[('basic', 'Basic Mandate')], default='basic', required=True,
string='Mandate Format', track_visibility='onchange')
partner_bank_id = fields.Many2one(
comodel_name='res.partner.bank', string='Bank Account',
track_visibility='onchange')
@@ -52,19 +33,22 @@ class AccountBankingMandate(models.Model):
default=lambda self: self.env['res.company']._company_default_get(
'account.banking.mandate'))
unique_mandate_reference = fields.Char(
string='Unique Mandate Reference', track_visibility='always',
default='/')
string='Unique Mandate Reference', track_visibility='onchange')
signature_date = fields.Date(string='Date of Signature of the Mandate',
track_visibility='onchange')
scan = fields.Binary(string='Scan of the Mandate')
last_debit_date = fields.Date(string='Date of the Last Debit',
readonly=True)
state = fields.Selection(
_get_states, string='Status', default='draft',
state = fields.Selection([
('draft', 'Draft'),
('valid', 'Valid'),
('expired', 'Expired'),
('cancel', 'Cancelled'),
], string='Status', default='draft', track_visibility='onchange',
help="Only valid mandates can be used in a payment line. A cancelled "
"mandate is a mandate that has been cancelled by the customer.")
"mandate is a mandate that has been cancelled by the customer.")
payment_line_ids = fields.One2many(
comodel_name='payment.line', inverse_name='mandate_id',
comodel_name='account.payment.line', inverse_name='mandate_id',
string="Related Payment Lines")
_sql_constraints = [(
@@ -79,13 +63,13 @@ class AccountBankingMandate(models.Model):
if (mandate.signature_date and
mandate.signature_date > fields.Date.context_today(
mandate)):
raise exceptions.Warning(
raise ValidationError(
_("The date of signature of mandate '%s' "
"is in the future !")
% mandate.unique_mandate_reference)
if (mandate.signature_date and mandate.last_debit_date and
mandate.signature_date > mandate.last_debit_date):
raise exceptions.Warning(
raise ValidationError(
_("The mandate '%s' can't have a date of last debit "
"before the date of signature."
) % mandate.unique_mandate_reference)
@@ -96,20 +80,21 @@ class AccountBankingMandate(models.Model):
for mandate in self:
if mandate.state == 'valid':
if not mandate.signature_date:
raise exceptions.Warning(
raise ValidationError(
_("Cannot validate the mandate '%s' without a date of "
"signature.") % mandate.unique_mandate_reference)
if not mandate.partner_bank_id:
raise exceptions.Warning(
raise ValidationError(
_("Cannot validate the mandate '%s' because it is not "
"attached to a bank account.") %
mandate.unique_mandate_reference)
@api.model
def create(self, vals=None):
if vals.get('unique_mandate_reference', '/') == '/':
if vals.get('unique_mandate_reference', 'New') == 'New':
vals['unique_mandate_reference'] = \
self.env['ir.sequence'].next_by_code('account.banking.mandate')
self.env['ir.sequence'].next_by_code('account.banking.mandate')\
or 'New'
return super(AccountBankingMandate, self).create(vals)
@api.multi
@@ -122,7 +107,7 @@ class AccountBankingMandate(models.Model):
def validate(self):
for mandate in self:
if mandate.state != 'draft':
raise exceptions.Warning(
raise UserError(
_('Mandate should be in draft state'))
self.write({'state': 'valid'})
return True
@@ -131,7 +116,7 @@ class AccountBankingMandate(models.Model):
def cancel(self):
for mandate in self:
if mandate.state not in ('draft', 'valid'):
raise exceptions.Warning(
raise UserError(
_('Mandate should be in draft or valid state'))
self.write({'state': 'cancel'})
return True
@@ -143,7 +128,7 @@ class AccountBankingMandate(models.Model):
"""
for mandate in self:
if mandate.state != 'cancel':
raise exceptions.Warning(
raise UserError(
_('Mandate should be in cancel state'))
self.write({'state': 'draft'})
return True

View File

@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# © 2014 Compassion CH - Cyril Sester <csester@compassion.ch>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
from openerp import models, fields, api
class AccountInvoice(models.Model):
@@ -12,5 +13,15 @@ class AccountInvoice(models.Model):
mandate_id = fields.Many2one(
'account.banking.mandate', string='Direct Debit Mandate',
domain=[('state', '=', 'valid')], readonly=True,
states={'draft': [('readonly', False)]})
ondelete='restrict',
readonly=True, states={'draft': [('readonly', False)]})
@api.model
def line_get_convert(self, line, part):
"""Copy mandate from invoice to account move line"""
res = super(AccountInvoice, self).line_get_convert(line, part)
if line.get('type') == 'dest' and line.get('invoice_id'):
invoice = self.browse(line['invoice_id'])
if invoice.type in ('out_invoice', 'out_refund'):
res['mandate_id'] = invoice.mandate_id.id or False
return res

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (http://www.akretion.com/)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class AccountMoveLine(models.Model):
_inherit = 'account.move.line'
mandate_id = fields.Many2one(
'account.banking.mandate', string='Direct Debit Mandate',
ondelete='restrict')
@api.multi
def _prepare_payment_line_vals(self, payment_order):
vals = super(AccountMoveLine, self)._prepare_payment_line_vals(
payment_order)
# TODO : test on the view field "mandate required ?"
if payment_order.payment_type == 'inbound' and self.mandate_id:
vals['mandate_id'] = self.mandate_id.id or False
vals['partner_bank_id'] = self.mandate_id.partner_bank_id.id or False
return vals

View File

@@ -1,19 +1,21 @@
# -*- coding: utf-8 -*-
# © 2014 Compassion CH - Cyril Sester <csester@compassion.ch>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2015-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, exceptions, _
from openerp import models, fields, api, _
from openerp.exceptions import ValidationError
class PaymentLine(models.Model):
_inherit = 'payment.line'
class AccountPaymentLine(models.Model):
_inherit = 'account.payment.line'
mandate_id = fields.Many2one(
comodel_name='account.banking.mandate', string='Direct Debit Mandate',
domain=[('state', '=', 'valid')])
# TODO : remove this
@api.model
def create(self, vals=None):
"""If the customer invoice has a mandate, take it
@@ -21,7 +23,7 @@ class PaymentLine(models.Model):
"""
if vals is None:
vals = {}
partner_bank_id = vals.get('bank_id')
partner_bank_id = vals.get('partner_bank_id')
move_line_id = vals.get('move_line_id')
if (self.env.context.get('search_payment_order_type') == 'debit' and
'mandate_id' not in vals):
@@ -31,7 +33,7 @@ class PaymentLine(models.Model):
line.invoice.mandate_id):
vals.update({
'mandate_id': line.invoice.mandate_id.id,
'bank_id': line.invoice.mandate_id.partner_bank_id.id,
'partner_bank_id': line.invoice.mandate_id.partner_bank_id.id,
})
if partner_bank_id and 'mandate_id' not in vals:
mandates = self.env['account.banking.mandate'].search(
@@ -39,21 +41,23 @@ class PaymentLine(models.Model):
('state', '=', 'valid')])
if mandates:
vals['mandate_id'] = mandates[0].id
return super(PaymentLine, self).create(vals)
return super(AccountPaymentLine, self).create(vals)
@api.one
@api.constrains('mandate_id', 'bank_id')
@api.constrains('mandate_id', 'partner_bank_id')
def _check_mandate_bank_link(self):
if (self.mandate_id and self.bank_id and
if (self.mandate_id and self.partner_bank_id and
self.mandate_id.partner_bank_id.id !=
self.bank_id.id):
raise exceptions.Warning(
self.partner_bank_id.id):
raise ValidationError(
_("The payment line with reference '%s' has the bank account "
"'%s' which is not attached to the mandate '%s' (this "
"mandate is attached to the bank account '%s').") %
(self.name,
self.env['res.partner.bank'].name_get(
[self.bank_id.id])[0][1],
self.partner_bank_id.name_get()[0][1],
self.mandate_id.unique_mandate_reference,
self.env['res.partner.bank'].name_get(
[self.mandate_id.partner_bank_id.id])[0][1]))
self.mandate_id.partner_bank_id.name_get()[0][1]))
# @api.multi
# def check_payment_line(self):
# TODO : i would like to block here is mandate is missing... but how do you know it's required ? => create option on payment order ?

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# © 2014 Compassion CH - Cyril Sester <csester@compassion.ch>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2015-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api

View File

@@ -1,3 +1,3 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_account_banking_mandate","Full access on account.banking.mandate","model_account_banking_mandate","account_payment.group_account_payment",1,1,1,1
"access_account_banking_mandate_read","Read access on account.banking.mandate","model_account_banking_mandate","base.group_user",1,0,0,0
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_banking_mandate,Full access on account.banking.mandate,model_account_banking_mandate,account_payment_order.group_account_payment,1,1,1,1
access_account_banking_mandate_read,Read access on account.banking.mandate,model_account_banking_mandate,base.group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_banking_mandate Full access on account.banking.mandate model_account_banking_mandate account_payment.group_account_payment account_payment_order.group_account_payment 1 1 1 1
3 access_account_banking_mandate_read Read access on account.banking.mandate model_account_banking_mandate base.group_user 1 0 0 0

View File

@@ -101,33 +101,11 @@
</record>
<menuitem id="mandate_menu"
parent="account_payment.menu_main_payment"
parent="account_payment_order.payment_root"
action="mandate_action"
sequence="20"
sequence="30"
/>
<!-- notifications in the chatter -->
<record id="mandate_valid" model="mail.message.subtype">
<field name="name">Mandate Validated</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Banking Mandate Validated</field>
</record>
<record id="mandate_expired" model="mail.message.subtype">
<field name="name">Mandate Expired</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Banking Mandate has Expired</field>
</record>
<record id="mandate_cancel" model="mail.message.subtype">
<field name="name">Mandate Cancelled</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Banking Mandate Cancelled</field>
</record>
</data>
</openerp>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
Copyright (C) 2013-2016 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
@@ -10,10 +10,10 @@
<record id="invoice_form" model="ir.ui.view">
<field name="name">add.mandate.on.customer.invoice.form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field name="inherit_id" ref="account_payment_partner.invoice_form"/>
<field name="arch" type="xml">
<field name="partner_bank_id" position="after">
<field name="mandate_id" domain="[('partner_id', '=', partner_id), ('state', '=', 'valid')]" attrs="{'invisible': [('type', '=', 'out_refund')]}"/>
<field name="mandate_id" domain="[('partner_id', '=', commercial_partner_id), ('state', '=', 'valid')]" attrs="{'invisible': [('type', '=', 'out_refund')]}"/>
</field>
</field>
</record>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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_move_line_form" model="ir.ui.view">
<field name="name">account_banking_mandate.move_line_form</field>
<field name="model">account.move.line</field>
<field name="inherit_id" ref="account_payment_order.view_move_line_form" />
<field name="arch" type="xml">
<field name="partner_bank_id" position="after">
<field name="mandate_id"
domain="[('partner_id', '=', partner_id), ('state', '=', 'valid')]"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013-2016 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="account_payment_line_form" model="ir.ui.view">
<field name="name">account_banking_mandate.account.payment.line.form</field>
<field name="model">account.payment.line</field>
<field name="inherit_id" ref="account_payment_order.account_payment_line_form"/>
<field name="arch" type="xml">
<field name="partner_bank_id" position="after">
<field name="mandate_id"
domain="[('partner_bank_id', '=', partner_bank_id), ('state', '=', 'valid')]"
invisible="context.get('default_payment_type') != 'inbound'"
context="{'default_partner_bank_id': partner_bank_id}"/>
</field>
</field>
</record>
<record id="account_payment_line_tree" model="ir.ui.view">
<field name="name">account_banking_mandate.account.payment.line.tree</field>
<field name="model">account.payment.line</field>
<field name="inherit_id" ref="account_payment_order.account_payment_line_tree"/>
<field name="arch" type="xml">
<field name="partner_bank_id" position="after">
<field name="mandate_id"
invisible="context.get('default_payment_type') != 'inbound'"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -1,30 +0,0 @@
<?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_mandate_payment_order_form" model="ir.ui.view">
<field name="name">mandate.payment.order.form</field>
<field name="model">payment.order</field>
<field name="inherit_id" ref="account_banking_payment_export.view_payment_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='line_ids']/form//field[@name='bank_id']" position="after">
<field name="mandate_id"
domain="[('partner_bank_id', '=', bank_id), ('state', '=', 'valid')]"
invisible="context.get('search_payment_order_type')!='debit'"
context="{'default_partner_bank_id': bank_id}"/>
<newline />
</xpath>
<xpath expr="//field[@name='line_ids']/tree/field[@name='bank_id']" position="after">
<field name="mandate_id" string="Mandate"
invisible="context.get('search_payment_order_type')!='debit'"/>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 Akretion (http://www.akretion.com)
Copyright (C) 2015-2016 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
@@ -10,11 +10,11 @@
<record id="bank_payment_line_form" model="ir.ui.view">
<field name="name">banking.mandate.bank.payment.line.form</field>
<field name="model">bank.payment.line</field>
<field name="inherit_id" ref="account_banking_payment_export.bank_payment_line_form"/>
<field name="inherit_id" ref="account_payment_order.bank_payment_line_form"/>
<field name="arch" type="xml">
<field name="bank_id" position="after">
<field name="partner_bank_id" position="after">
<field name="mandate_id"
invisible="context.get('search_payment_order_type')!='debit'"/>
invisible="context.get('default_payment_type')!='inbound'"/>
</field>
</field>
</record>
@@ -22,11 +22,11 @@
<record id="bank_payment_line_tree" model="ir.ui.view">
<field name="name">banking.mandate.bank.payment.line.tree</field>
<field name="model">bank.payment.line</field>
<field name="inherit_id" ref="account_banking_payment_export.bank_payment_line_tree"/>
<field name="inherit_id" ref="account_payment_order.bank_payment_line_tree"/>
<field name="arch" type="xml">
<field name="bank_id" position="after">
<field name="partner_bank_id" position="after">
<field name="mandate_id" string="Mandate"
invisible="context.get('search_payment_order_type')!='debit'"/>
invisible="context.get('default_payment_type')!='inbound'"/>
</field>
</field>
</record>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
Copyright (C) 2013-2016 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
@@ -12,8 +12,8 @@
<field name="model">res.partner.bank</field>
<field name="inherit_id" ref="base.view_partner_bank_form"/>
<field name="arch" type="xml">
<group name="bank" position="after">
<group name="mandates" string="Direct Debit Mandates" colspan="4">
<group col="4" position="after">
<group name="mandates" string="Direct Debit Mandates">
<field name="mandate_ids" context="{'default_partner_bank_id': active_id, 'mandate_bank_partner_view': True}" nolabel="1"/>
</group>
</group>
@@ -26,23 +26,10 @@
<field name="inherit_id" ref="base.view_partner_bank_tree"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="mandate_ids" string="DD Mandates"/>
<field name="mandate_ids" string="Mandates"/>
</field>
</field>
</record>
<!-- add number of mandates in this list of bank accounts
on the partner form -->
<record id="mandate_partner_form" model="ir.ui.view">
<field name="name">mandate.partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="account.view_partner_property_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='bank_ids']/tree/field[@name='owner_name']" position="after">
<field name="mandate_ids" string="DD Mandates"/>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013-2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@@ -7,7 +7,7 @@
{
'name': 'Account Banking PAIN Base Module',
'summary': 'Base module for PAIN file generation',
'version': '8.0.0.4.0',
'version': '9.0.1.0.0',
'license': 'AGPL-3',
'author': "Akretion, "
"Noviat, "
@@ -17,16 +17,18 @@
'website': 'https://github.com/OCA/bank-payment',
'contributors': ['Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>'],
'category': 'Hidden',
'depends': ['account_banking_payment_export'],
'depends': ['account_payment_order'],
'external_dependencies': {
'python': ['unidecode', 'lxml'],
},
'data': [
'views/payment_line_view.xml',
'views/account_payment_line.xml',
'views/account_payment_order.xml',
'views/bank_payment_line_view.xml',
'views/payment_mode_view.xml',
'views/account_payment_mode.xml',
'views/res_company_view.xml',
'views/account_payment_method.xml',
],
'post_init_hook': 'set_default_initiating_party',
'installable': False,
'installable': True,
}

View File

@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
# © 2013 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import payment_line
from . import account_payment_line
from . import account_payment_order
from . import bank_payment_line
from . import payment_mode
from . import account_payment_mode
from . import res_company
from . import banking_export_pain
from . import res_partner_bank
from . import account_payment_method

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# © 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
class AccountPaymentLine(models.Model):
_inherit = 'account.payment.line'
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.")
# PAIN allows 140 caracters
communication = fields.Char(size=140)
# The field struct_communication_type has been dropped in v9
# We now use communication_type ; you should add an option
# in communication_type with selection_add=[]
communication_type = fields.Selection(selection_add=[('ISO', 'ISO')])

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, _
from openerp.exceptions import UserError
class AccountPaymentMethod(models.Model):
_inherit = 'account.payment.method'
pain_version = fields.Selection([], string='PAIN Version')
@api.multi
def get_xsd_file_path(self):
"""This method is designed to be inherited in the SEPA modules"""
self.ensure_one()
raise UserError(_(
"No XSD file path found for payment method '%s'") % self.name)

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013-2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@@ -7,8 +7,8 @@
from openerp import models, fields, api
class PaymentMode(models.Model):
_inherit = 'payment.mode'
class AccountPaymentMode(models.Model):
_inherit = 'account.payment.mode'
convert_to_ascii = fields.Boolean(
string='Convert to ASCII', default=True,
@@ -33,17 +33,18 @@ class PaymentMode(models.Model):
"- Country code (2, optional)\n"
"- Company idenfier (N, VAT)\n"
"- Service suffix (N, issued by bank)")
sepa_type = fields.Char(compute="_compute_sepa_type")
# I plan to change this -- Alexis
#sepa_type = fields.Char(compute="_compute_sepa_type")
def _sepa_type_get(self):
"""Defined to be inherited by child addons, for instance:
- account_banking_sepa_credit_transfer
- account_banking_sepa_direct_debit
"""
return False
#def _sepa_type_get(self):
# """Defined to be inherited by child addons, for instance:
# - account_banking_sepa_credit_transfer
# - account_banking_sepa_direct_debit
# """
# return False
@api.multi
@api.depends('type')
def _compute_sepa_type(self):
for mode in self:
mode.sepa_type = mode._sepa_type_get()
#@api.multi
#@api.depends('type')
#def _compute_sepa_type(self):
# for mode in self:
# mode.sepa_type = mode._sepa_type_get()

View File

@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# © 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class AccountPaymentOrder(models.Model):
_inherit = 'account.payment.order'
sepa = fields.Boolean(
compute='compute_sepa', readonly=True,
string="SEPA Payment")
charge_bearer = fields.Selection([
('SLEV', 'Following Service Level'),
('SHAR', 'Shared'),
('CRED', 'Borne by Creditor'),
('DEBT', 'Borne by Debtor')], string='Charge Bearer',
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 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.")
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.")
@api.multi
@api.depends(
'company_partner_bank_id.acc_type',
'payment_line_ids.currency_id',
'payment_line_ids.partner_bank_id.acc_type')
def compute_sepa(self):
eur = self.env.ref('base.EUR')
for order in self:
sepa = True
if order.company_partner_bank_id.acc_type != 'iban':
sepa = False
for pline in order.payment_line_ids:
if pline.currency_id != eur:
sepa = False
break
if pline.partner_bank_id.acc_type != 'iban':
sepa = False
break
self.sepa = sepa

View File

@@ -10,13 +10,10 @@ class BankPaymentLine(models.Model):
priority = fields.Selection(
related='payment_line_ids.priority', string='Priority')
struct_communication_type = fields.Selection(
related='payment_line_ids.struct_communication_type',
string='Structured Communication Type')
@api.model
def same_fields_payment_line_and_bank_payment_line(self):
res = super(BankPaymentLine, self).\
same_fields_payment_line_and_bank_payment_line()
res += ['priority', 'struct_communication_type']
res += ['priority']
return res

View File

@@ -1,17 +1,16 @@
# -*- coding: utf-8 -*-
# © 2013-2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, api, _
from openerp.exceptions import Warning
from openerp.exceptions import UserError
from openerp.tools.safe_eval import safe_eval
from datetime import datetime
from lxml import etree
from openerp import tools
import logging
import base64
try:
@@ -22,17 +21,8 @@ except ImportError:
logger = logging.getLogger(__name__)
class BankingExportPain(models.AbstractModel):
_name = 'banking.export.pain'
@api.model
def _validate_iban(self, iban):
"""if IBAN is valid, returns IBAN
if IBAN is NOT valid, raises an error message"""
if self.env['res.partner.bank'].is_iban_valid(iban):
return iban.replace(' ', '')
else:
raise Warning(_("This IBAN is not valid : %s") % iban)
class AccountPaymentOrder(models.Model):
_inherit = 'account.payment.order'
@api.model
def _prepare_field(self, field_name, field_value, eval_ctx,
@@ -58,20 +48,20 @@ class BankingExportPain(models.AbstractModel):
except:
line = eval_ctx.get('line')
if line:
raise Warning(
raise UserError(
_("Cannot compute the '%s' of the Payment Line with "
"reference '%s'.")
% (field_name, line.name))
else:
raise Warning(
raise UserError(
_("Cannot compute the '%s'.") % field_name)
if not isinstance(value, (str, unicode)):
raise Warning(
raise UserError(
_("The type of the field '%s' is %s. It should be a string "
"or unicode.")
% (field_name, type(value)))
if not value:
raise Warning(
raise UserError(
_("The '%s' is empty or 0. It should have a non-null value.")
% field_name)
if max_size and len(value) > max_size:
@@ -92,7 +82,7 @@ class BankingExportPain(models.AbstractModel):
"The XML file is invalid against the XML Schema Definition")
logger.warning(xml_string)
logger.warning(e)
raise Warning(
raise UserError(
_("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 "
@@ -102,8 +92,7 @@ class BankingExportPain(models.AbstractModel):
return True
@api.multi
def finalize_sepa_file_creation(
self, xml_root, total_amount, transactions_count, gen_args):
def finalize_sepa_file_creation(self, xml_root, gen_args):
xml_string = etree.tostring(
xml_root, pretty_print=True, encoding='UTF-8',
xml_declaration=True)
@@ -113,30 +102,8 @@ class BankingExportPain(models.AbstractModel):
logger.debug(xml_string)
self._validate_xml(xml_string, gen_args)
order_ref = []
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({
'nb_transactions': transactions_count,
'total_amount': total_amount,
'filename': filename,
'file': base64.encodestring(xml_string),
'state': 'finish',
})
action = {
'name': _('SEPA File'),
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form,tree',
'res_model': self._name,
'res_id': self.ids[0],
'target': 'new',
}
return action
filename = '%s%s.xml' % (gen_args['file_prefix'], self.name)
return (xml_string, filename)
@api.model
def generate_group_header_block(self, parent_node, gen_args):
@@ -145,7 +112,7 @@ class BankingExportPain(models.AbstractModel):
group_header_1_0, 'MsgId')
message_identification_1_1.text = self._prepare_field(
'Message Identification',
'self.payment_order_ids[0].reference',
'self.name',
{'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(
@@ -196,10 +163,11 @@ class BankingExportPain(models.AbstractModel):
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 self.sepa:
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')
@@ -230,18 +198,17 @@ class BankingExportPain(models.AbstractModel):
def generate_initiating_party_block(self, parent_node, gen_args):
my_company_name = self._prepare_field(
'Company Name',
'self.payment_order_ids[0].mode.bank_id.partner_id.name',
'self.company_partner_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
payment = self.payment_order_ids[0]
initiating_party_identifier = (
payment.mode.initiating_party_identifier or
payment.company_id.initiating_party_identifier)
self.payment_mode_id.initiating_party_identifier or
self.payment_mode_id.company_id.initiating_party_identifier)
initiating_party_issuer = (
payment.mode.initiating_party_issuer or
payment.company_id.initiating_party_issuer)
self.payment_mode_id.initiating_party_issuer or
self.payment_mode_id.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')
@@ -252,11 +219,11 @@ class BankingExportPain(models.AbstractModel):
iniparty_org_other, 'Issr')
iniparty_org_other_issuer.text = initiating_party_issuer
elif self._must_have_initiating_party(gen_args):
raise Warning(
raise UserError(
_("Missing 'Initiating Party Issuer' and/or "
"'Initiating Party Identifier' for the company '%s'. "
"Both fields must have a value.")
% payment.company_id.name)
% self.company_id.name)
return True
@api.model
@@ -275,11 +242,10 @@ class BankingExportPain(models.AbstractModel):
party_agent_bic = etree.SubElement(
party_agent_institution, gen_args.get('bic_xml_tag'))
party_agent_bic.text = bic
except Warning:
except UserError:
if order == 'C':
if iban[0:2] != gen_args['initiating_party_country_code']:
raise Warning(
_('Error:'),
raise UserError(
_("The bank account with IBAN '%s' of partner '%s' "
"must have an associated BIC because it is a "
"cross-border SEPA operation.")
@@ -314,9 +280,10 @@ class BankingExportPain(models.AbstractModel):
party_name = self._prepare_field(
'%s Name' % party_type_label, name, eval_ctx,
gen_args.get('name_maxsize'), gen_args=gen_args)
piban = self._prepare_field(
viban = self._prepare_field(
'%s IBAN' % party_type_label, iban, eval_ctx, gen_args=gen_args)
viban = self._validate_iban(piban)
# TODO : add support for bank accounts other than IBAN
#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':
@@ -353,11 +320,6 @@ class BankingExportPain(models.AbstractModel):
'line.communication', {'line': line}, 140,
gen_args=gen_args)
else:
if not line.struct_communication_type:
raise Warning(
_("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(
@@ -385,7 +347,7 @@ class BankingExportPain(models.AbstractModel):
creditor_ref_info_type_code_2_123.text = 'SCOR'
creditor_ref_info_type_issuer_2_125.text = \
line.struct_communication_type
line.communication_type
creditor_reference_2_126.text = \
self._prepare_field(
'Creditor Structured Reference',

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
# © 2013-2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class PaymentLine(models.Model):
_inherit = 'payment.line'
@api.model
def _get_struct_communication_types(self):
return [('ISO', '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')

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2015-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013-2016 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="account_payment_line_form" model="ir.ui.view">
<field name="name">pain.base.account.payment.line</field>
<field name="model">account.payment.line</field>
<field name="inherit_id" ref="account_payment_order.account_payment_line_form"/>
<field name="arch" type="xml">
<field name="communication_type" position="before">
<field name="priority"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="account_payment_method_form" model="ir.ui.view">
<field name="name">pain_base.account_payment_method.form</field>
<field name="model">account.payment.method</field>
<field name="inherit_id" ref="account_payment_mode.account_payment_method_form"/>
<field name="arch" type="xml">
<field name="payment_type" position="after">
<field name="pain_version"/>
</field>
</field>
</record>
<record id="account_payment_method_tree" model="ir.ui.view">
<field name="name">pain_base.account_payment_method.tree</field>
<field name="model">account.payment.method</field>
<field name="inherit_id" ref="account_payment_mode.account_payment_method_tree"/>
<field name="arch" type="xml">
<field name="payment_type" position="after">
<field name="pain_version"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013-2016 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="account_payment_mode_form" model="ir.ui.view">
<field name="name">pain_base.account.payment.mode.form</field>
<field name="model">account.payment.mode</field>
<field name="inherit_id" ref="account_payment_order.account_payment_mode_form"/>
<field name="arch" type="xml">
<group name="payment_order_options" position="after">
<group name="pain_options" string="Options for PAIN">
<field name="convert_to_ascii"/>
<!-- To be set visible in the localisation modules that need it -->
<field name="initiating_party_identifier" invisible="1"/>
<field name="initiating_party_issuer" invisible="1"/>
</group>
</group>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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="account_payment_order_form" model="ir.ui.view">
<field name="name">pain.base.account.payment.order.form</field>
<field name="model">account.payment.order</field>
<field name="inherit_id" ref="account_payment_order.account_payment_order_form"/>
<field name="arch" type="xml">
<field name="company_partner_bank_id" position="after">
<field name="sepa"/>
<field name="batch_booking"/>
<field name="charge_bearer" attrs="{'invisible': [('sepa', '=', True)]}"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 Akretion (http://www.akretion.com)
Copyright (C) 2015-2016 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
@@ -10,14 +10,11 @@
<record id="bank_payment_line_form" model="ir.ui.view">
<field name="name">pain.base.bank.payment.line.form</field>
<field name="model">bank.payment.line</field>
<field name="inherit_id" ref="account_banking_payment_export.bank_payment_line_form"/>
<field name="inherit_id" ref="account_payment_order.bank_payment_line_form"/>
<field name="arch" type="xml">
<field name="bank_id" position="after">
<field name="partner_bank_id" position="after">
<field name="priority"/>
</field>
<field name="state" position="after">
<field name="struct_communication_type" attrs="{'invisible': [('state', '!=', 'structured')]}"/>
</field>
</field>
</record>

View File

@@ -1,26 +0,0 @@
<?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_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_banking_payment_export.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

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
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>
<xpath expr="//form/group" position="after">
<group name="sepa_identifiers" string="SEPA identifiers"
attrs="{'invisible': [('sepa_type', '=', False)]}">
<field name="sepa_type" invisible="1"/>
<group>
<field name="initiating_party_identifier" />
<field name="initiating_party_issuer"/>
</group>
</group>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 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="bank_payment_line_seq_type" model="ir.sequence.type">
<field name="name">Bank Payment Line</field>
<field name="code">bank.payment.line</field>
</record>
<record id="bank_payment_line_seq" model="ir.sequence">
<field name="name">Bank Payment Line</field>
<field name="code">bank.payment.line</field>
<field name="prefix">L</field>
<field name="padding">5</field>
<field name="number_next">1</field>
<field name="company_id" eval="False"/>
</record>
</data>
</openerp>

View File

@@ -1,11 +0,0 @@
# -*- coding: utf-8 -*-
from . import account_payment
# important: import payment_mode_type before payment_mode
# to let the _auto_init work properly
from . import payment_mode_type
from . import payment_mode
from . import account_move_line
from . import account_invoice
from . import bank_payment_line
from . import payment_line
from . import res_partner_bank

View File

@@ -1,37 +0,0 @@
# -*- coding: utf-8 -*-
# © 2004-2014 OpenERP S.A. (http://www.openerp.com/)
# © 2014 Akretion (http://www.akretion.com/)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class AccountMoveLine(models.Model):
_inherit = 'account.move.line'
@api.one
def _get_journal_entry_ref(self):
if self.move_id.state == 'draft':
if self.invoice.id:
self.journal_entry_ref = self.invoice.number
else:
self.journal_entry_ref = '*' + str(self.move_id.id)
else:
self.journal_entry_ref = self.move_id.name
journal_entry_ref = fields.Char(compute=_get_journal_entry_ref,
string='Journal Entry Ref')
@api.multi
def get_balance(self):
"""
Return the balance of any set of move lines.
Not to be confused with the 'balance' field on this model, which
returns the account balance that the move line applies to.
"""
total = 0.0
for line in self:
total += (line.debit or 0.0) - (line.credit or 0.0)
return total

View File

@@ -1,159 +0,0 @@
# -*- coding: utf-8 -*-
# © 2009 EduSense BV (<http://www.edusense.nl>)
# © 2011-2013 Therp BV (<http://therp.nl>)
# © 2016 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, exceptions, workflow, _
try:
# This is to avoid the drop of the column total each time you update
# the module account_payment, because the store attribute is set later
# and Odoo doesn't defer this removal
from openerp.addons.account_payment.account_payment import payment_order
payment_order._columns['total'].nodrop = True
except ImportError:
pass
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')
bank_line_ids = fields.One2many(
'bank.payment.line', 'order_id', string="Bank Payment Lines",
readonly=True)
total = fields.Float(compute='_compute_total', store=True)
bank_line_count = fields.Integer(
compute='_bank_line_count', string='Number of Bank Lines')
@api.depends('line_ids', 'line_ids.amount')
@api.one
def _compute_total(self):
self.total = sum(self.mapped('line_ids.amount') or [0.0])
@api.multi
@api.depends('bank_line_ids')
def _bank_line_count(self):
for order in self:
order.bank_line_count = len(order.bank_line_ids)
@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
for order_id in self.ids:
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
@api.multi
def action_cancel(self):
for order in self:
order.write({'state': 'cancel'})
order.bank_line_ids.unlink()
return True
@api.model
def _prepare_bank_payment_line(self, paylines):
return {
'order_id': paylines[0].order_id.id,
'payment_line_ids': [(6, 0, paylines.ids)],
'communication': '-'.join(
[line.communication for line in paylines]),
}
@api.multi
def action_open(self):
"""
Called when you click on the 'Confirm' button
Set the 'date' on payment line depending on the 'date_prefered'
setting of the payment.order
Re-generate the bank payment lines
"""
res = super(PaymentOrder, self).action_open()
bplo = self.env['bank.payment.line']
today = fields.Date.context_today(self)
for order in self:
# Delete existing bank payment lines
order.bank_line_ids.unlink()
# Create the bank payment lines from the payment lines
group_paylines = {} # key = hashcode
for payline in order.line_ids:
# Compute requested payment date
if order.date_prefered == 'due':
requested_date = payline.ml_maturity_date or today
elif order.date_prefered == 'fixed':
requested_date = order.date_scheduled or today
else:
requested_date = today
# Write requested_date on 'date' field of payment line
payline.date = requested_date
# Group options
if order.mode.group_lines:
hashcode = payline.payment_line_hashcode()
else:
# Use line ID as hascode, which actually means no grouping
hashcode = payline.id
if hashcode in group_paylines:
group_paylines[hashcode]['paylines'] += payline
group_paylines[hashcode]['total'] +=\
payline.amount_currency
else:
group_paylines[hashcode] = {
'paylines': payline,
'total': payline.amount_currency,
}
# Create bank payment lines
for paydict in group_paylines.values():
# Block if a bank payment line is <= 0
if paydict['total'] <= 0:
raise exceptions.Warning(_(
"The amount for Partner '%s' is negative "
"or null (%.2f) !")
% (paydict['paylines'][0].partner_id.name,
paydict['total']))
vals = self._prepare_bank_payment_line(paydict['paylines'])
bplo.create(vals)
return res

View File

@@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
import openerp.addons.decimal_precision as dp
class BankPaymentLine(models.Model):
_name = 'bank.payment.line'
_description = 'Bank Payment Lines'
name = fields.Char(string='Bank Payment Line Ref', required=True)
order_id = fields.Many2one(
'payment.order', string='Order', ondelete='cascade', select=True)
payment_line_ids = fields.One2many(
'payment.line', 'bank_line_id', string='Payment Lines')
partner_id = fields.Many2one(
'res.partner', string='Partner', related='payment_line_ids.partner_id')
# Function Float fields are sometimes badly displayed in tree view,
# see bug report https://github.com/odoo/odoo/issues/8632
amount_currency = fields.Float(
string='Amount', digits=dp.get_precision('Account'),
compute='_compute_amount', store=True)
# I would have preferred currency_id, but I need to keep the field names
# similar to the field names of payment.line
currency = fields.Many2one(
'res.currency', string='Currency', required=True,
related='payment_line_ids.currency')
bank_id = fields.Many2one(
'res.partner.bank', string='Bank Account',
related='payment_line_ids.bank_id')
date = fields.Date(
string='Payment Date', related='payment_line_ids.date')
state = fields.Selection(
related='payment_line_ids.state', string='Communication Type')
communication = fields.Char(string='Communication', required=True)
company_id = fields.Many2one(
'res.company', string='Company', readonly=True,
related='order_id.company_id', store=True)
@api.model
def same_fields_payment_line_and_bank_payment_line(self):
"""
This list of fields is used both to compute the grouping
hashcode and to copy the values from payment line
to bank payment line
The fields must have the same name on the 2 objects
"""
same_fields = [
'currency', 'partner_id',
'bank_id', 'date', 'state']
return same_fields
@api.multi
@api.depends('payment_line_ids', 'payment_line_ids.amount_currency')
def _compute_amount(self):
for line in self:
line.amount_currency = sum(
line.mapped('payment_line_ids.amount_currency'))
@api.model
@api.returns('self')
def create(self, vals):
if vals.get('name', '/') == '/':
vals['name'] = self.env['ir.sequence'].next_by_code(
'bank.payment.line')
return super(BankPaymentLine, self).create(vals)

View File

@@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class PaymentLine(models.Model):
_inherit = 'payment.line'
bank_line_id = fields.Many2one(
'bank.payment.line', string='Bank Payment Line')
@api.multi
def payment_line_hashcode(self):
self.ensure_one()
bplo = self.env['bank.payment.line']
values = []
for field in bplo.same_fields_payment_line_and_bank_payment_line():
values.append(unicode(self[field]))
hashcode = '-'.join(values)
return hashcode

View File

@@ -1,119 +0,0 @@
# -*- coding: utf-8 -*-
# © 2009 EduSense BV (<http://www.edusense.nl>)
# © 2011-2013 Therp BV (<http://therp.nl>)
# © 2014-2016 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, SUPERUSER_ID
class PaymentMode(models.Model):
"""Restoring the payment type from version 5,
used to select the export wizard (if any)
"""
_inherit = "payment.mode"
def _get_manual_bank_transfer(self, cr, uid, context=None):
""" hack: pre-create the manual bank transfer that is also
defined in the data directory, so we have an id in to use
in _auto_init """
model_data = self.pool['ir.model.data']
try:
_, res = model_data.get_object_reference(
cr, uid,
'account_banking_payment_export',
'manual_bank_tranfer')
except ValueError:
payment_mode_type = self.pool['payment.mode.type']
res = payment_mode_type.create(
cr, uid,
{'name': 'Manual Bank Transfer',
'code': 'BANKMAN'})
model_data.create(
cr, uid,
{'module': 'account_banking_payment_export',
'model': 'payment.mode.type',
'name': 'manual_bank_tranfer',
'res_id': res,
'noupdate': False})
return res
def _auto_init(self, cr, context=None):
""" hack: pre-create and initialize the type column so that the
constraint setting will not fail, this is a hack, made necessary
because Odoo tries to set the not-null constraint before
applying default values """
self._field_create(cr, context=context)
column_data = self._select_column_data(cr)
if 'type' not in column_data:
default_type = self._get_manual_bank_transfer(
cr, SUPERUSER_ID, context=context)
if default_type:
cr.execute('ALTER TABLE "{table}" ADD COLUMN "type" INTEGER'.
format(table=self._table))
cr.execute('UPDATE "{table}" SET type=%s'.
format(table=self._table),
(default_type,))
return super(PaymentMode, self)._auto_init(cr, context=context)
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",
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)
sale_ok = fields.Boolean(string='Selectable on sale operations',
default=True)
purchase_ok = fields.Boolean(string='Selectable on purchase operations',
default=True)
note = fields.Text(string="Note", translate=True)
# Default options for the "payment.order.create" wizard
default_journal_ids = fields.Many2many(
'account.journal', string="Journals Filter")
default_invoice = fields.Boolean(
string='Linked to an Invoice or Refund', default=False)
default_date_type = fields.Selection([
('due', 'Due'),
('move', 'Move'),
], default='due', string="Type of Date Filter")
default_populate_results = fields.Boolean(
string='Populate Results Directly')
group_lines = fields.Boolean(
string="Group lines in payment orders", default=True,
help="If this mark is checked, the payment order lines will be "
"grouped when validating the payment order before exporting the "
"bank file. The grouping will be done only if the following "
"fields matches:\n"
"* Partner\n"
"* Currency\n"
"* Destination Bank Account\n"
"* Communication Type (structured, free)\n"
"* Payment Date\n"
"(other modules can set additional fields to restrict the "
"grouping.)")
@api.onchange('type')
def type_on_change(self):
if self.type:
ajo = self.env['account.journal']
aj_ids = []
if self.type.payment_order_type == 'payment':
aj_ids = ajo.search([
('type', 'in', ('purchase_refund', 'purchase'))]).ids
elif self.type.payment_order_type == 'debit':
aj_ids = ajo.search([
('type', 'in', ('sale_refund', 'sale'))]).ids
self.default_journal_ids = [(6, 0, aj_ids)]

View File

@@ -1,44 +0,0 @@
# -*- coding: utf-8 -*-
# © 2009 EduSense BV (<http://www.edusense.nl>)
# © 2011-2013 Therp BV (<http://therp.nl>)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
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

@@ -1,3 +0,0 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_payment_mode_type","payment.mode.type","model_payment_mode_type","account_payment.group_account_payment",1,1,1,1
"access_bank_payment_line","Full access on bank.payment.line to Payment Manager","model_bank_payment_line","account_payment.group_account_payment",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_payment_mode_type payment.mode.type model_payment_mode_type account_payment.group_account_payment 1 1 1 1
3 access_bank_payment_line Full access on bank.payment.line to Payment Manager model_bank_payment_line account_payment.group_account_payment 1 1 1 1

View File

@@ -1,107 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- restore wizard functionality when making payments -->
<record id="view_payment_order_form" model="ir.ui.view">
<field name="name">account.payment.order.form.banking-1</field>
<field name="inherit_id" ref="account_payment.view_payment_order_form" />
<field name="model">payment.order</field>
<field name="arch" type="xml">
<xpath expr="//button[@string='Make Payments']" position="attributes">
<attribute name="name">launch_wizard</attribute>
</xpath>
<field name="mode" position="after">
<field name="mode_type" invisible="1"/>
<field name="payment_order_type" invisible="1"/>
<field name="bank_line_count" attrs="{'invisible': [('state', 'in', ('draft', 'cancel'))]}"/>
</field>
<xpath expr="//button[@string='Invoices']" position="attributes">
<attribute name="attrs">{
'invisible': [('state', '!=', 'draft')]}</attribute>
</xpath>
<!-- sorry, I have no choice but to replace the line_ids field
in order to introduce a 'notebook' in this view -->
<field name="line_ids" position="replace">
<notebook>
<page string="Payment Lines" name="payment-lines">
<!-- copy/paste from the view that we inherit -->
<field name="line_ids" context="{'order_id': active_id or False}" >
<form string="Payment Line">
<notebook>
<page string="Payment">
<group col="4">
<field name="move_line_id" on_change="onchange_move_line(move_line_id,parent.mode,parent.date_prefered,parent.date_scheduled,currency,company_currency)" domain="[('reconcile_id','=', False), ('account_id.reconcile', '=', True)] "/> <!-- we removed the filter on amount_to_pay, because we want to be able to select refunds -->
<separator colspan="4" string="Transaction Information"/>
<field name="date"/>
<label for="amount_currency" string="Amount"/>
<div>
<field name="amount_currency" on_change="onchange_amount(amount_currency,currency,company_currency)" class="oe_inline"/>
<field name="currency" nolabel="1" class="oe_inline"/>
</div>
<field name="partner_id" on_change="onchange_partner(partner_id,parent.mode)"/>
<field domain="[('partner_id','=',partner_id)]" name="bank_id"/>
<separator colspan="2" string="Owner Account"/>
<separator colspan="2" string="Partner Bank Account"/>
<field colspan="2" name="info_owner" nolabel="1"/>
<field colspan="2" name="info_partner" nolabel="1"/>
<field colspan="4" name="communication"/>
<field colspan="4" name="communication2"/>
<field name="name"/>
<field name="state"/>
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
</group>
</page>
<page string="Information">
<group col="4" string="General Information">
<label for="amount" groups="base.group_multi_currency"/>
<div groups="base.group_multi_currency">
<field name="amount" class="oe_inline"/>
<field name="company_currency" class="oe_inline"/>
</div>
<separator colspan="4" string="Entry Information"/>
<field name="create_date" readonly="1"/>
<field name="ml_maturity_date"/>
<field name="ml_inv_ref"/>
<field name="bank_line_id" readonly="1"/>
</group>
</page>
</notebook>
</form>
<tree string="Payment Line">
<field name="ml_inv_ref" />
<field name="partner_id"/>
<field name="communication"/>
<field name="bank_id" domain="[('partner_id', '=', partner_id)]"/>
<field name="ml_maturity_date"/>
<field name="date"/>
<field name="amount_currency" string="Amount"/>
<field name="currency"/>
<field name="name"/>
<field name="amount" sum="Total in Company Currency" invisible="1"/>
</tree>
</field>
<!-- end of the copy/paste -->
</page>
<page string="Bank Payment Lines" name="bank-payment-lines">
<field name="bank_line_ids" context="{'search_payment_order_type': context.get('search_payment_order_type')}"/>
</page>
</notebook>
</field>
</field>
</record>
<record id="view_payment_order_tree" model="ir.ui.view">
<field name="name">account_banking_payment_export.payment.order.tree</field>
<field name="inherit_id" ref="account_payment.view_payment_order_tree" />
<field name="model">payment.order</field>
<field name="arch" type="xml">
<field name="date_done" position="after">
<field name="bank_line_count"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -1,18 +0,0 @@
<?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

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!--
Add the payment mode type settings
-->
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
<field name="name">payment.mode.form.inherit</field>
<field name="model">payment.mode</field>
<field name="inherit_id" ref="account_payment.view_payment_mode_form"/>
<field name="arch" type="xml">
<field name="company_id" position="after">
<field name="active"/>
<field name="type"/>
<field name="purchase_ok"/>
<field name="sale_ok"/>
</field>
<form position="inside">
<group name="payment_order_create_defaults" string="Select Move Lines to Pay - Default Values">
<field name="default_populate_results"/>
<field name="default_journal_ids" widget="many2many_tags"/>
<field name="default_invoice"/>
<field name="default_date_type"/>
</group>
<group name="group_grouping" string="Line grouping">
<field name="group_lines"/>
</group>
<group string="Note" col="4">
<field name="note" nolabel="1"/>
</group>
</form>
</field>
</record>
</data>
</openerp>

View File

@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_payment_mode_tree_inherit" model="ir.ui.view">
<field name="name">payment.mode.tree.inherit</field>
<field name="model">payment.mode</field>
<field name="inherit_id" ref="account_payment.view_payment_mode_tree"/>
<field name="arch" type="xml">
<field name="company_id" position="after">
<field name="type"/>
</field>
</field>
</record>
<!-- basic view for payment mode type -->
<record model="ir.ui.view" id="view_payment_mode_type_form">
<field name="name">view.payment.mode.type.form</field>
<field name="model">payment.mode.type</field>
<field name="arch" type="xml">
<form string="Payment Type" version="7.0">
<group name="main">
<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>
</record>
<record id="view_payment_mode_type_tree" model="ir.ui.view">
<field name="name">view.payment.mode.type.tree</field>
<field name="model">payment.mode.type</field>
<field name="arch" type="xml">
<tree string="Payment Types">
<field name="name"/>
<field name="code"/>
<field name="active"/>
<field name="ir_model_id"/>
</tree>
</field>
</record>
<record id="action_payment_mode_type" model="ir.actions.act_window">
<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>
<field name="context">{'active_test': False}</field>
</record>
<menuitem id="menu_payment_mode_type"
action="action_payment_mode_type"
parent="account.menu_configuration_misc" />
</data>
</openerp>

View File

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

View File

@@ -1,42 +0,0 @@
# -*- 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, api, workflow
class PaymentManual(models.TransientModel):
_name = 'payment.manual'
_description = 'Send payment order(s) manually'
@api.multi
def button_ok(self):
for order_id in self.env.context.get('active_ids', []):
workflow.trg_validate(self.env.uid, 'payment.order', order_id,
'done', self.env.cr)
return {'type': 'ir.actions.act_window_close'}

View File

@@ -1,18 +0,0 @@
<?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

@@ -1,70 +0,0 @@
<?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="journal_ids"
widget="many2many_tags"
placeholder="Keep empty for using all journals"
/>
<field name="invoice"/>
<field name="populate_results"/>
</field>
<field name="duedate" position="before">
<field name="date_type"/>
<field name="move_date" attrs="{'required': [('date_type', '=', 'move')], 'invisible': [('date_type', '!=', 'move')]}"/>
</field>
<field name="duedate" position="attributes">
<attribute name="attrs">{'required': [('date_type', '=', 'due')], 'invisible': [('date_type', '!=', 'due')]}</attribute>
</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">{'display_credit': context.get('display_credit', False),'display_debit': context.get('display_debit', False),'journal_type': 'sale', 'tree_view_ref' : 'account_banking_payment_export.payment_order_populate_view_move_line_tree'}</attribute>
<attribute name="nolabel">1</attribute>
</field>
</field>
</record>
<record id="payment_order_populate_view_move_line_tree" model="ir.ui.view">
<field name="name">payment.order.populate.account.move.line.tree</field>
<field name="model">account.move.line</field>
<field name="arch" type="xml">
<tree string="Journal Items">
<field name="journal_id" />
<field name="date"/>
<field name="name"/>
<field name="ref"/>
<field name="partner_id" />
<field name="account_id" />
<field name="journal_entry_ref" string="Journal Entry" />
<field name="debit" sum="Total Debit" invisible="not context.get('display_debit', False)"/>
<field name="credit" sum="Total Credit" invisible="not context.get('display_credit', False)"/>
<field name="amount_residual" />
<field name="date_maturity" invisible="context.get('journal_type', False) not in ['sale','sale_refund','purchase','purchase_refund']" />
<field name="reconcile_ref"/>
<field name="amount_currency" invisible="not context.get('currency',False)"/>
<field name="currency_id" invisible="not context.get('currency',False)" />
</tree>
</field>
</record>
</data>
</openerp>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 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="account_payment.act_done" model="workflow.activity">
<field name="action">action_done()</field>
</record>
<record id="account_payment.act_cancel" model="workflow.activity">
<field name="action">action_cancel()</field>
</record>
</data>
</openerp>

View File

@@ -2,4 +2,3 @@
# © 2010-2013 Akretion (www.akretion.com)
from . import models
from . import wizard

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2010-2015 Akretion (www.akretion.com)
# © 2010-2016 Akretion (www.akretion.com)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@@ -7,7 +7,7 @@
{
'name': 'Account Banking SEPA Credit Transfer',
'summary': 'Create SEPA XML files for Credit Transfers',
'version': '8.0.0.5.0',
'version': '9.0.1.0.0',
'license': 'AGPL-3',
'author': "Akretion, "
"Serv. Tecnol. Avanzados - Pedro M. Baeza, "
@@ -15,13 +15,13 @@
"Odoo Community Association (OCA)",
'website': 'https://github.com/OCA/bank-payment',
'category': 'Banking addons',
'conflicts': ['account_sepa'],
'depends': ['account_banking_pain_base'],
'data': [
'wizard/export_sepa_view.xml',
'data/payment_type_sepa_sct.xml',
'data/account_payment_method.xml',
],
'demo': [
'demo/sepa_credit_transfer_demo.xml'
],
'installable': False,
'installable': True,
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="sepa_credit_transfer" model="account.payment.method">
<field name="name">SEPA Credit Transfer to suppliers</field>
<field name="code">sepa_credit_transfer</field>
<field name="payment_type">outbound</field>
<field name="pain_version">pain.001.001.03</field>
</record>
</data>
</openerp>

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<!-- TODO In the suitable_bank_types field, we should restrict to IBAN type and Rib+IBAN of l10n_fr_rib ? -->
<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"/>
</record>
<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"/>
</record>
<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"/>
</record>
<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"/>
</record>
<record id="export_sepa_sct_001_003_03" model="payment.mode.type">
<field name="name">SEPA Credit Transfer pain 001.003.03 (used in Germany)</field>
<field name="code">pain.001.003.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"/>
</record>
</data>
</openerp>

View File

@@ -3,13 +3,12 @@
<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"/>
<record id="payment_mode_outbound_sepa_ct1" model="account.payment.mode">
<field name="name">SEPA Credit Transfer to suppliers</field>
<field name="company_id" ref="base.main_company"/>
<field name="type" ref="export_sepa_sct_001_001_03"/>
<field name="sale_ok" eval="False"/>
<field name="bank_account_link">variable</field>
<field name="payment_method_id" ref="sepa_credit_transfer"/>
<field name="default_journal_ids" search="[('type', 'in', ('purchase', 'purchase_refund'))]"/>
</record>
</data>

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import payment_mode
from . import account_payment_method
from . import account_payment_order

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class AccountPaymentMethod(models.Model):
_inherit = 'account.payment.method'
pain_version = fields.Selection(selection_add=[
('pain.001.001.02', 'pain.001.001.02'),
('pain.001.001.03', 'pain.001.001.03 (recommended)'),
('pain.001.001.04', 'pain.001.001.04'),
('pain.001.001.05', 'pain.001.001.05'),
('pain.001.003.03', 'pain.001.003.03 (used in Germany)'),
])
@api.multi
def get_xsd_file_path(self):
self.ensure_one()
if self.pain_version in [
'pain.001.001.02', 'pain.001.001.03', 'pain.001.001.04',
'pain.001.001.05', 'pain.001.003.03']:
path = 'account_banking_sepa_credit_transfer/data/%s.xsd'\
% self.pain_version
return path
return super(AccountPaymentMethod, self).get_xsd_file_path()

View File

@@ -1,67 +1,28 @@
# -*- coding: utf-8 -*-
# © 2010-2015 Akretion (www.akretion.com)
# © 2010-2016 Akretion (www.akretion.com)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, _
from openerp.exceptions import Warning
from openerp import workflow
from openerp import models, api, _
from openerp.exceptions import UserError
from lxml import etree
class BankingExportSepaWizard(models.TransientModel):
_name = 'banking.export.sepa.wizard'
_inherit = ['banking.export.pain']
_description = 'Export SEPA Credit Transfer File'
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)
@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(vals)
class AccountPaymentOrder(models.Model):
_inherit = 'account.payment.order'
@api.multi
def create_sepa(self):
def generate_payment_file(self):
"""Creates the SEPA Credit Transfer file. That's the important code!"""
pain_flavor = self.payment_order_ids[0].mode.type.code
convert_to_ascii = \
self.payment_order_ids[0].mode.convert_to_ascii
self.ensure_one()
if (
self.payment_mode_id.payment_method_id.code !=
'sepa_credit_transfer'):
return super(AccountPaymentOrder, self).generate_payment_file()
pain_flavor = self.payment_mode_id.payment_method_id.pain_version
if not pain_flavor:
pain_flavor = 'pain.001.001.03'
if pain_flavor == 'pain.001.001.02':
bic_xml_tag = 'BIC'
name_maxsize = 70
@@ -94,24 +55,19 @@ class BankingExportSepaWizard(models.TransientModel):
name_maxsize = 70
root_xml_tag = 'CstmrCdtTrfInitn'
else:
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', "
"'pain.001.001.04', 'pain.001.001.05'"
" and 'pain.001.003.03'.") %
pain_flavor)
raise UserError(
_("PAIN version '%s' is not supported.") % pain_flavor)
xsd_file = self.payment_mode_id.payment_method_id.get_xsd_file_path()
gen_args = {
'bic_xml_tag': bic_xml_tag,
'name_maxsize': name_maxsize,
'convert_to_ascii': convert_to_ascii,
'convert_to_ascii': self.payment_mode_id.convert_to_ascii,
'payment_method': 'TRF',
'file_prefix': 'sct_',
'pain_flavor': pain_flavor,
'pain_xsd_file':
'account_banking_sepa_credit_transfer/data/%s.xsd'
% pain_flavor,
'pain_xsd_file': xsd_file,
}
# TODO: make it inheritable
pain_ns = {
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
None: 'urn:iso:std:iso:20022:tech:xsd:%s' % pain_flavor,
@@ -128,30 +84,27 @@ class BankingExportSepaWizard(models.TransientModel):
group_header_1_0, nb_of_transactions_1_6, control_sum_1_7 = \
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
for payment_order in self.payment_order_ids:
total_amount = total_amount + payment_order.total
for line in payment_order.bank_line_ids:
priority = line.priority
# The field line.date is the requested payment date
# taking into account the 'date_prefered' setting
# cf account_banking_payment_export/models/account_payment.py
# in the inherit of action_open()
key = (line.date, priority)
if key in lines_per_group:
lines_per_group[key].append(line)
else:
lines_per_group[key] = [line]
for line in self.bank_line_ids:
priority = line.priority
# The field line.date is the requested payment date
# taking into account the 'date_prefered' setting
# cf account_banking_payment_export/models/account_payment.py
# in the inherit of action_open()
key = (line.date, priority)
if key in lines_per_group:
lines_per_group[key].append(line)
else:
lines_per_group[key] = [line]
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(
pain_root,
"self.payment_order_ids[0].reference + '-' "
"self.name + '-' "
"+ requested_date.replace('-', '') + '-' + priority",
priority, False, False, requested_date, {
'self': self,
@@ -160,15 +113,17 @@ class BankingExportSepaWizard(models.TransientModel):
}, gen_args)
self.generate_party_block(
payment_info_2_0, 'Dbtr', 'B',
'self.payment_order_ids[0].mode.bank_id.partner_id.'
'name',
'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.company_partner_bank_id.partner_id.name',
'self.company_partner_bank_id.sanitized_acc_number',
'self.company_partner_bank_id.bank_bic',
{'self': self},
gen_args)
charge_bearer_2_24 = etree.SubElement(payment_info_2_0, 'ChrgBr')
charge_bearer_2_24.text = self.charge_bearer
if self.sepa:
charge_bearer = 'SLEV'
else:
charge_bearer = self.charge_bearer
charge_bearer_2_24.text = charge_bearer
transactions_count_2_4 = 0
amount_control_sum_2_5 = 0.0
for line in lines:
@@ -185,7 +140,7 @@ class BankingExportSepaWizard(models.TransientModel):
'End to End Identification', 'line.name',
{'line': line}, 35, gen_args=gen_args)
currency_name = self._prepare_field(
'Currency Code', 'line.currency.name',
'Currency Code', 'line.currency_id.name',
{'line': line}, 3, gen_args=gen_args)
amount_2_42 = etree.SubElement(
credit_transfer_transaction_info_2_27, 'Amt')
@@ -194,16 +149,16 @@ class BankingExportSepaWizard(models.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 Warning(
if not line.partner_bank_id:
raise UserError(
_("Bank account is missing on the bank payment line "
"of partner '%s' (reference '%s').")
% (line.partner_id.name, line.name))
self.generate_party_block(
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)
'C', 'line.partner_id.name',
'line.partner_bank_id.sanitized_acc_number',
'line.partner_bank_id.bank_bic', {'line': line}, gen_args)
self.generate_remittance_info_block(
credit_transfer_transaction_info_2_27, line, gen_args)
if pain_flavor in pain_03_to_05:
@@ -215,24 +170,4 @@ class BankingExportSepaWizard(models.TransientModel):
else:
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(
xml_root, total_amount, transactions_count_1_6, gen_args)
@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.
"""
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
return self.finalize_sepa_file_creation(xml_root, gen_args)

View File

@@ -1,16 +0,0 @@
# -*- coding: utf-8 -*-
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models
class PaymentMode(models.Model):
_inherit = 'payment.mode'
def _sepa_type_get(self):
res = super(PaymentMode, self)._sepa_type_get()
if not res:
if self.type.code and self.type.code.startswith('pain.001'):
res = 'sepa_credit_transfer'
return res

View File

@@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
# © 2010-2013 Akretion (www.akretion.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import export_sepa

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2010-2015 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="banking_export_sepa_wizard_view" model="ir.ui.view">
<field name="name">banking.export.sepa.wizard.view</field>
<field name="model">banking.export.sepa.wizard</field>
<field name="arch" type="xml">
<form string="SEPA File Generation">
<field name="state" invisible="True"/>
<group states="create">
<field name="batch_booking" />
<field name="charge_bearer" />
</group>
<group states="finish">
<field name="total_amount" />
<field name="nb_transactions" />
<field name="file" filename="filename" />
<field name="filename" invisible="True"/>
</group>
<footer>
<button string="Generate" name="create_sepa" type="object" class="oe_highlight" states="create"/>
<button string="Validate" name="save_sepa" type="object" class="oe_highlight" states="finish"/>
<button string="Cancel" special="cancel" class="oe_link"/>
</footer>
</form>
</field>
</record>
</data>
</openerp>

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
# © 2013 Akretion (www.akretion.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models
from . import wizard

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013-2015 Akretion (www.akretion.com)
# © 2013-2016 Akretion (www.akretion.com)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@@ -7,7 +7,7 @@
{
'name': 'Account Banking SEPA Direct Debit',
'summary': 'Create SEPA files for Direct Debit',
'version': '8.0.0.5.0',
'version': '9.0.1.0.0',
'license': 'AGPL-3',
'author': "Akretion, "
"Serv. Tecnol. Avanzados - Pedro M. Baeza, "
@@ -16,22 +16,19 @@
'website': 'https://github.com/OCA/bank-payment',
'category': 'Banking addons',
'depends': [
'account_direct_debit',
'account_banking_pain_base',
'account_banking_mandate',
],
'data': [
'views/account_banking_mandate_view.xml',
'views/res_company_view.xml',
'views/payment_mode_view.xml',
'wizard/export_sdd_view.xml',
'views/account_payment_mode.xml',
'data/mandate_expire_cron.xml',
'data/payment_type_sdd.xml',
'data/account_payment_method.xml',
'data/report_paperformat.xml',
'reports/sepa_direct_debit_mandate.xml',
'views/report_sepa_direct_debit_mandate.xml',
'security/original_mandate_required_security.xml',
],
'demo': ['demo/sepa_direct_debit_demo.xml'],
'installable': False,
'installable': True,
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="sepa_direct_debit" model="account.payment.method">
<field name="name">SEPA Direct Debit for customers</field>
<field name="code">sepa_direct_debit</field>
<field name="payment_type">inbound</field>
<field name="pain_version">pain.008.001.02</field>
</record>
</data>
</openerp>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com/)
Copyright (C) 2013-2016 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->

View File

@@ -0,0 +1,614 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Mit XMLSpy v2008 rel. 2 (http://www.altova.com) von Wenzel (SIZ Bonn) bearbeitet -->
<!-- Version gemäß DFÜ-Abkommen Anlage 3, Version 2.7, gültig ab November 2013 mit Umsetzung von IBAN Only gemäß EPC SDD Core IG 7.0 bzw. SDD B2B IG 5.0 , zudem Einbau der COR1-Option durch Erweiterung Local Instrument und Erweiterung Service Level auf Externe Codeliste-->
<!-- Mit XMLSpy v2008 rel. 2 sp2 (http://www.altova.com) am 29.11.2012 von der SIZ GmbH bearbeitet -->
<xs:schema xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.003.02" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:iso:std:iso:20022:tech:xsd:pain.008.003.02" elementFormDefault="qualified">
<xs:element name="Document" type="Document"/>
<xs:complexType name="AccountIdentificationSEPA">
<xs:sequence>
<xs:element name="IBAN" type="IBAN2007Identifier"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="ActiveOrHistoricCurrencyAndAmount_SimpleTypeSEPA">
<xs:restriction base="xs:decimal">
<xs:minInclusive value="0.01"/>
<xs:maxInclusive value="999999999.99"/>
<xs:fractionDigits value="2"/>
<xs:totalDigits value="11"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ActiveOrHistoricCurrencyAndAmountSEPA">
<xs:simpleContent>
<xs:extension base="ActiveOrHistoricCurrencyAndAmount_SimpleTypeSEPA">
<xs:attribute name="Ccy" type="ActiveOrHistoricCurrencyCodeEUR" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="ActiveOrHistoricCurrencyCodeEUR">
<xs:restriction base="xs:string">
<xs:enumeration value="EUR"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ActiveOrHistoricCurrencyCode">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{3,3}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="AmendmentInformationDetailsSDD">
<xs:sequence>
<xs:element name="OrgnlMndtId" type="RestrictedIdentificationSEPA2" minOccurs="0">
<xs:annotation>
<xs:documentation>Mandatory if changes occur in Mandate Identification, otherwise not to be used.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="OrgnlCdtrSchmeId" type="PartyIdentificationSEPA4" minOccurs="0">
<xs:annotation>
<xs:documentation>Mandatory if changes occur in 'Creditor Scheme Identification', otherwise not to be used.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="OrgnlDbtrAcct" type="CashAccountSEPA2" minOccurs="0">
<xs:annotation>
<xs:documentation>To be used only for changes of accounts within the same bank.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="OrgnlDbtrAgt" type="BranchAndFinancialInstitutionIdentificationSEPA2" minOccurs="0">
<xs:annotation>
<xs:documentation>To use 'Identification under 'Other' under 'Financial Institution Identifier with code SMNDA to indicate same mandate with new Debtor Agent. To be used with the FRST indicator in the Sequence Type.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="AnyBICIdentifier">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="BICIdentifier">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="BatchBookingIndicator">
<xs:restriction base="xs:boolean"/>
</xs:simpleType>
<xs:complexType name="BranchAndFinancialInstitutionIdentificationSEPA3">
<xs:sequence>
<xs:element name="FinInstnId" type="FinancialInstitutionIdentificationSEPA3"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="BranchAndFinancialInstitutionIdentificationSEPA2">
<xs:sequence>
<xs:element name="FinInstnId" type="FinancialInstitutionIdentificationSEPA2"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CashAccountSEPA1">
<xs:sequence>
<xs:element name="Id" type="AccountIdentificationSEPA"/>
<xs:element name="Ccy" type="ActiveOrHistoricCurrencyCode" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CashAccountSEPA2">
<xs:sequence>
<xs:element name="Id" type="AccountIdentificationSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CategoryPurposeSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalCategoryPurpose1Code"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="ChargeBearerTypeSEPACode">
<xs:restriction base="xs:string">
<xs:enumeration value="SLEV"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="CountryCode">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{2,2}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="CreditorReferenceInformationSEPA1">
<xs:sequence>
<xs:element name="Tp" type="CreditorReferenceTypeSEPA"/>
<xs:element name="Ref" type="Max35Text">
<xs:annotation>
<xs:documentation>If a Creditor Reference contains a check digit, the receiving bank is not required to validate this.
If the receiving bank validates the check digit and if this validation fails, the bank may continue its processing and send the transaction to the next party in the chain.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreditorReferenceTypeSEPA">
<xs:sequence>
<xs:element name="CdOrPrtry" type="CreditorReferenceTypeCodeSEPA"/>
<xs:element name="Issr" type="Max35Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreditorReferenceTypeCodeSEPA">
<xs:sequence>
<xs:element name="Cd" type="DocumentType3CodeSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CustomerDirectDebitInitiationV02">
<xs:sequence>
<xs:element name="GrpHdr" type="GroupHeaderSDD"/>
<xs:element name="PmtInf" type="PaymentInstructionInformationSDD" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DateAndPlaceOfBirth">
<xs:sequence>
<xs:element name="BirthDt" type="ISODate"/>
<xs:element name="PrvcOfBirth" type="Max35Text" minOccurs="0"/>
<xs:element name="CityOfBirth" type="Max35Text"/>
<xs:element name="CtryOfBirth" type="CountryCode"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DecimalNumber">
<xs:restriction base="xs:decimal">
<xs:fractionDigits value="17"/>
<xs:totalDigits value="18"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="DirectDebitTransactionSDD">
<xs:sequence>
<xs:element name="MndtRltdInf" type="MandateRelatedInformationSDD"/>
<xs:element name="CdtrSchmeId" type="PartyIdentificationSEPA3" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that all transactions within the same Payment Information block have the same Creditor Scheme Identification.
This data element must be present at either Payment Information or Direct Debit
Transaction level.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DirectDebitTransactionInformationSDD">
<xs:sequence>
<xs:element name="PmtId" type="PaymentIdentificationSEPA"/>
<xs:element name="InstdAmt" type="ActiveOrHistoricCurrencyAndAmountSEPA"/>
<xs:element name="ChrgBr" type="ChargeBearerTypeSEPACode" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that this element be specified at Payment Information level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DrctDbtTx" type="DirectDebitTransactionSDD"/>
<xs:element name="UltmtCdtr" type="PartyIdentificationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>This data element may be present either at Payment Information or at Direct Debit Transaction Information level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DbtrAgt" type="BranchAndFinancialInstitutionIdentificationSEPA3"/>
<xs:element name="Dbtr" type="PartyIdentificationSEPA2"/>
<xs:element name="DbtrAcct" type="CashAccountSEPA2"/>
<xs:element name="UltmtDbtr" type="PartyIdentificationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>Mandatory if provided by the debtor in the mandate.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Purp" type="PurposeSEPA" minOccurs="0"/>
<xs:element name="RmtInf" type="RemittanceInformationSEPA1Choice" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Document">
<xs:sequence>
<xs:element name="CstmrDrctDbtInitn" type="CustomerDirectDebitInitiationV02"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DocumentType3CodeSEPA">
<xs:restriction base="xs:string">
<xs:enumeration value="SCOR"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalCategoryPurpose1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalLocalInstrument1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="35"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalOrganisationIdentification1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalPersonIdentification1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalPurpose1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalServiceLevel1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="FinancialInstitutionIdentificationSEPA3">
<xs:sequence>
<xs:choice>
<xs:element name="BIC" type="BICIdentifier"/>
<xs:element name="Othr" type="OthrIdentification"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OthrIdentification">
<xs:sequence>
<xs:element name="Id" type="OthrIdentificationCode"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="OthrIdentificationCode">
<xs:restriction base="xs:string">
<xs:enumeration value="NOTPROVIDED"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="FinancialInstitutionIdentificationSEPA2">
<xs:sequence>
<xs:element name="Othr" type="RestrictedFinancialIdentificationSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RestrictedFinancialIdentificationSEPA">
<xs:sequence>
<xs:element name="Id" type="RestrictedSMNDACode"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="RestrictedSMNDACode">
<xs:restriction base="xs:string">
<xs:enumeration value="SMNDA"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="GenericOrganisationIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max35Text"/>
<xs:element name="SchmeNm" type="OrganisationIdentificationSchemeName1Choice" minOccurs="0"/>
<xs:element name="Issr" type="Max35Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GenericPersonIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max35Text"/>
<xs:element name="SchmeNm" type="PersonIdentificationSchemeName1Choice" minOccurs="0"/>
<xs:element name="Issr" type="Max35Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RestrictedPersonIdentificationSEPA">
<xs:sequence>
<xs:element name="Id" type="RestrictedPersonIdentifierSEPA"/>
<xs:element name="SchmeNm" type="RestrictedPersonIdentificationSchemeNameSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="RestrictedPersonIdentifierSEPA">
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-Z]{2,2}[0-9]{2,2}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){3,3}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,28}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="GroupHeaderSDD">
<xs:sequence>
<xs:element name="MsgId" type="RestrictedIdentificationSEPA1"/>
<xs:element name="CreDtTm" type="ISODateTime"/>
<xs:element name="NbOfTxs" type="Max15NumericText"/>
<xs:element name="CtrlSum" type="DecimalNumber" minOccurs="0"/>
<xs:element name="InitgPty" type="PartyIdentificationSEPA1"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="IBAN2007Identifier">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ISODate">
<xs:restriction base="xs:date"/>
</xs:simpleType>
<xs:simpleType name="ISODateTime">
<xs:restriction base="xs:dateTime"/>
</xs:simpleType>
<xs:complexType name="LocalInstrumentSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalLocalInstrument1Code"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="MandateRelatedInformationSDD">
<xs:sequence>
<xs:element name="MndtId" type="RestrictedIdentificationSEPA2"/>
<xs:element name="DtOfSgntr" type="ISODate"/>
<xs:element name="AmdmntInd" type="TrueFalseIndicator" minOccurs="0"/>
<xs:element name="AmdmntInfDtls" type="AmendmentInformationDetailsSDD" minOccurs="0">
<xs:annotation>
<xs:documentation>Mandatory if 'Amendment Indicator' is 'TRUE'
The reason code from the Rulebook is indicated using one of the following message subelements.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ElctrncSgntr" type="Max1025Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="Max1025Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="1025"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max140Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="140"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max15NumericText">
<xs:restriction base="xs:string">
<xs:pattern value="[0-9]{1,15}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max35Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="35"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max70Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="70"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="OrganisationIdentificationSEPAChoice">
<xs:sequence>
<xs:choice>
<xs:element name="BICOrBEI" type="AnyBICIdentifier"/>
<xs:element name="Othr" type="GenericOrganisationIdentification1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OrganisationIdentificationSchemeName1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Cd" type="ExternalOrganisationIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartySEPAChoice">
<xs:sequence>
<xs:choice>
<xs:element name="OrgId" type="OrganisationIdentificationSEPAChoice">
<xs:annotation>
<xs:documentation>Either BIC or BEI or one
occurrence of Other is allowed.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PrvtId" type="PersonIdentificationSEPA1Choice">
<xs:annotation>
<xs:documentation>Either Date and Place of Birth or one occurrence of Other is allowed</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartySEPA2">
<xs:sequence>
<xs:element name="PrvtId" type="PersonIdentificationSEPA2">
<xs:annotation>
<xs:documentation>Private Identification is used to identify either an organisation or a private
person.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA1">
<xs:sequence>
<xs:element name="Nm" type="Max70Text" minOccurs="0">
<xs:annotation>
<xs:documentation>Name is limited to 70 characters in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Id" type="PartySEPAChoice" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA2">
<xs:sequence>
<xs:element name="Nm" type="Max70Text">
<xs:annotation>
<xs:documentation>Name is limited to 70 characters in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PstlAdr" type="PostalAddressSEPA" minOccurs="0"/>
<xs:element name="Id" type="PartySEPAChoice" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA3">
<xs:sequence>
<xs:element name="Id" type="PartySEPA2"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA4">
<xs:sequence>
<xs:element name="Nm" type="Max70Text" minOccurs="0">
<xs:annotation>
<xs:documentation>If present the new Name must be specified under Creditor. Name is limited to 70 characters in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Id" type="PartySEPA2" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA5">
<xs:sequence>
<xs:element name="Nm" type="Max70Text">
<xs:annotation>
<xs:documentation>Name is limited to 70 characters in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PstlAdr" type="PostalAddressSEPA" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PaymentIdentificationSEPA">
<xs:sequence>
<xs:element name="InstrId" type="RestrictedIdentificationSEPA1" minOccurs="0"/>
<xs:element name="EndToEndId" type="RestrictedIdentificationSEPA1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PaymentInstructionInformationSDD">
<xs:sequence>
<xs:element name="PmtInfId" type="RestrictedIdentificationSEPA1"/>
<xs:element name="PmtMtd" type="PaymentMethod2Code"/>
<xs:element name="BtchBookg" type="BatchBookingIndicator" minOccurs="0">
<xs:annotation>
<xs:documentation>If present and contains true, batch booking is requested. If present and contains false, booking per transaction is requested. If element is not present, pre-agreed customer-to-bank conditions apply.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="NbOfTxs" type="Max15NumericText" minOccurs="0"/>
<xs:element name="CtrlSum" type="DecimalNumber" minOccurs="0"/>
<xs:element name="PmtTpInf" type="PaymentTypeInformationSDD"/>
<xs:element name="ReqdColltnDt" type="ISODate"/>
<xs:element name="Cdtr" type="PartyIdentificationSEPA5"/>
<xs:element name="CdtrAcct" type="CashAccountSEPA1"/>
<xs:element name="CdtrAgt" type="BranchAndFinancialInstitutionIdentificationSEPA3"/>
<xs:element name="UltmtCdtr" type="PartyIdentificationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>This data element may be present either at Payment Information or at Direct Debit Transaction Information level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ChrgBr" type="ChargeBearerTypeSEPACode" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that this element be specified at Payment Information level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="CdtrSchmeId" type="PartyIdentificationSEPA3" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that all transactions within the same Payment Information block have the same Creditor Scheme Identification.
This data element must be present at either Payment Information or Direct Debit
Transaction level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DrctDbtTxInf" type="DirectDebitTransactionInformationSDD" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="PaymentMethod2Code">
<xs:restriction base="xs:string">
<xs:enumeration value="DD"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="PaymentTypeInformationSDD">
<xs:sequence>
<xs:element name="SvcLvl" type="ServiceLevelSEPA"/>
<xs:element name="LclInstrm" type="LocalInstrumentSEPA">
<xs:annotation>
<xs:documentation>Only B2B, 'CORE' or 'COR1' is allowed. The mixing of different Local Instrument values is not allowed in the same message.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SeqTp" type="SequenceType1Code">
<xs:annotation>
<xs:documentation>If 'Amendment Indicator' is 'true' and 'Original Debtor Agent' is set to 'SMNDA' this message element must indicate 'FRST'</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="CtgyPurp" type="CategoryPurposeSEPA" minOccurs="0">
<xs:annotation>
<xs:documentation>Depending on the agreement between the Creditor and the Creditor Bank, Category Purpose may be forwarded to the Debtor Bank.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PersonIdentificationSEPA1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="DtAndPlcOfBirth" type="DateAndPlaceOfBirth"/>
<xs:element name="Othr" type="GenericPersonIdentification1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PersonIdentificationSEPA2">
<xs:sequence>
<xs:element name="Othr" type="RestrictedPersonIdentificationSEPA">
<xs:annotation>
<xs:documentation>Only one occurrence of Other is allowed, and no other sub-elements are allowed.
Identification must be used with an identifier described in General Message Element Specifications, Chapter 1.5.2 of the Implementation Guide.
Scheme Name under Other must specify SEPA under Proprietary</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PersonIdentificationSchemeName1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Cd" type="ExternalPersonIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RestrictedPersonIdentificationSchemeNameSEPA">
<xs:sequence>
<xs:element name="Prtry" type="IdentificationSchemeNameSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="IdentificationSchemeNameSEPA">
<xs:restriction base="xs:string">
<xs:enumeration value="SEPA"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="PostalAddressSEPA">
<xs:sequence>
<xs:element name="Ctry" type="CountryCode" minOccurs="0"/>
<xs:element name="AdrLine" type="Max70Text" minOccurs="0" maxOccurs="2"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PurposeSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalPurpose1Code">
<xs:annotation>
<xs:documentation>Only codes from the ISO 20022 ExternalPurposeCode list are allowed.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RemittanceInformationSEPA1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Ustrd" type="Max140Text"/>
<xs:element name="Strd" type="StructuredRemittanceInformationSEPA1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="SequenceType1Code">
<xs:restriction base="xs:string">
<xs:enumeration value="FRST"/>
<xs:enumeration value="RCUR"/>
<xs:enumeration value="FNAL"/>
<xs:enumeration value="OOFF"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ServiceLevelSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalServiceLevel1Code"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="StructuredRemittanceInformationSEPA1">
<xs:sequence>
<xs:element name="CdtrRefInf" type="CreditorReferenceInformationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>When present, the receiving bank is not obliged to validate the reference information.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="TrueFalseIndicator">
<xs:restriction base="xs:boolean"/>
</xs:simpleType>
<xs:simpleType name="RestrictedIdentificationSEPA1">
<xs:restriction base="xs:string">
<xs:pattern value="([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|'| ]){1,35}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="RestrictedIdentificationSEPA2">
<xs:restriction base="xs:string">
<xs:pattern value="([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,35}"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="export_sdd_008_001_02" model="payment.mode.type">
<field name="name">SEPA Direct Debit v02 (recommended)</field>
<field name="code">pain.008.001.02</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sdd_wizard"/>
<field name="payment_order_type">debit</field>
</record>
<record id="export_sdd_008_001_03" model="payment.mode.type">
<field name="name">SEPA Direct Debit v03</field>
<field name="code">pain.008.001.03</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sdd_wizard"/>
<field name="payment_order_type">debit</field>
</record>
<record id="export_sdd_008_001_04" model="payment.mode.type">
<field name="name">SEPA Direct Debit v04</field>
<field name="code">pain.008.001.04</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
<field name="ir_model_id" ref="model_banking_export_sdd_wizard"/>
<field name="payment_order_type">debit</field>
</record>
</data>
</openerp>

View File

@@ -3,13 +3,11 @@
<openerp>
<data noupdate="1">
<record id="sepa_direct_debit_mode" model="payment.mode">
<field name="name">SEPA Direct Debit La Banque Postale</field>
<field name="journal" ref="account.bank_journal"/>
<field name="bank_id" ref="account_banking_payment_export.main_company_iban"/>
<record id="payment_mode_inbound_sepa_dd1" model="account.payment.mode">
<field name="name">SEPA Direct Debit of customers</field>
<field name="company_id" ref="base.main_company"/>
<field name="type" ref="export_sdd_008_001_02"/>
<field name="purchase_ok" eval="False"/>
<field name="bank_account_link">variable</field>
<field name="payment_method_id" ref="sepa_direct_debit"/>
<field name="default_journal_ids" search="[('type', 'in', ('sale', 'sale_refund'))]"/>
</record>
@@ -18,7 +16,7 @@
</record>
<record id="res_partner_12_mandate" model="account.banking.mandate">
<field name="partner_bank_id" ref="account_banking_payment_export.res_partner_12_iban"/>
<field name="partner_bank_id" ref="account_payment_mode.res_partner_12_iban"/>
<field name="format">sepa</field>
<field name="type">recurrent</field>
<field name="recurrent_sequence_type">first</field>
@@ -27,7 +25,7 @@
</record>
<record id="res_partner_2_mandate" model="account.banking.mandate">
<field name="partner_bank_id" ref="account_banking_payment_export.res_partner_2_iban"/>
<field name="partner_bank_id" ref="account_payment_mode.res_partner_2_iban"/>
<field name="format">sepa</field>
<field name="type">recurrent</field>
<field name="recurrent_sequence_type">first</field>

View File

@@ -3,4 +3,6 @@
from . import res_company
from . import account_banking_mandate
from . import bank_payment_line
from . import payment_mode
from . import account_payment_mode
from . import account_payment_method
from . import account_payment_order

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2013-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@@ -16,29 +16,13 @@ logger = logging.getLogger(__name__)
class AccountBankingMandate(models.Model):
"""SEPA Direct Debit Mandate"""
_inherit = 'account.banking.mandate'
_track = {
'recurrent_sequence_type': {
'account_banking_sepa_direct_debit.recurrent_sequence_type_first':
lambda self, cr, uid, obj, ctx=None:
obj['recurrent_sequence_type'] == 'first',
'account_banking_sepa_direct_debit.'
'recurrent_sequence_type_recurring':
lambda self, cr, uid, obj, ctx=None:
obj['recurrent_sequence_type'] == 'recurring',
'account_banking_sepa_direct_debit.recurrent_sequence_type_final':
lambda self, cr, uid, obj, ctx=None:
obj['recurrent_sequence_type'] == 'final',
}
}
format = fields.Selection(
selection_add=[('sepa', _('Sepa Mandate'))],
default='sepa',
)
selection_add=[('sepa', 'Sepa Mandate')], default='sepa')
type = fields.Selection([('recurrent', 'Recurrent'),
('oneoff', 'One-Off')],
string='Type of Mandate',
track_visibility='always')
track_visibility='onchange')
recurrent_sequence_type = fields.Selection(
[('first', 'First'),
('recurring', 'Recurring'),
@@ -46,24 +30,10 @@ class AccountBankingMandate(models.Model):
string='Sequence Type for Next Debit', track_visibility='onchange',
help="This field is only used for Recurrent mandates, not for "
"One-Off mandates.", default="first")
sepa_migrated = fields.Boolean(
string='Migrated to SEPA', track_visibility='onchange',
help="If this field is not active, the mandate section of the next "
"direct debit file that include this mandate will contain the "
"'Original Mandate Identification' and the 'Original Creditor "
"Scheme Identification'. This is required in a few countries "
"(Belgium for instance), but not in all countries. If this is "
"not required in your country, you should keep this field always "
"active.", default=True)
original_mandate_identification = fields.Char(
string='Original Mandate Identification', track_visibility='onchange',
size=35,
help="When the field 'Migrated to SEPA' is not active, this field "
"will be used as the Original Mandate Identification in the "
"Direct Debit file.")
scheme = fields.Selection([('CORE', 'Basic (CORE)'),
('B2B', 'Enterprise (B2B)')],
string='Scheme', default="CORE")
scheme = fields.Selection([
('CORE', 'Basic (CORE)'),
('B2B', 'Enterprise (B2B)')],
string='Scheme', default="CORE", track_visibility='onchange')
unique_mandate_reference = fields.Char(size=35) # cf ISO 20022
@api.multi
@@ -76,30 +46,6 @@ class AccountBankingMandate(models.Model):
_("The recurrent mandate '%s' must have a sequence type.")
% mandate.unique_mandate_reference)
@api.multi
@api.constrains('type', 'recurrent_sequence_type', 'sepa_migrated')
def _check_migrated_to_sepa(self):
for mandate in self:
if (mandate.type == 'recurrent' and not mandate.sepa_migrated and
mandate.recurrent_sequence_type != 'first'):
raise exceptions.Warning(
_("The recurrent mandate '%s' which is not marked as "
"'Migrated to SEPA' must have its recurrent sequence "
"type set to 'First'.")
% mandate.unique_mandate_reference)
@api.multi
@api.constrains('type', 'original_mandate_identification', 'sepa_migrated')
def _check_original_mandate_identification(self):
for mandate in self:
if (mandate.type == 'recurrent' and not mandate.sepa_migrated and
not mandate.original_mandate_identification):
raise exceptions.Warning(
_("You must set the 'Original Mandate Identification' on "
"the recurrent mandate '%s' which is not marked as "
"'Migrated to SEPA'.")
% mandate.unique_mandate_reference)
@api.multi
@api.onchange('partner_bank_id')
def mandate_partner_bank_change(self):
@@ -137,5 +83,5 @@ class AccountBankingMandate(models.Model):
'The following SDD Mandate IDs has been set to expired: %s'
% expired_mandates.ids)
else:
logger.info('0 SDD Mandates must be set to Expired')
logger.info('0 SDD Mandates had to be set to Expired')
return True

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class AccountPaymentMethod(models.Model):
_inherit = 'account.payment.method'
pain_version = fields.Selection(selection_add=[
('pain.008.001.02', 'pain.008.001.02 (recommended)'),
('pain.008.001.03', 'pain.008.001.03'),
('pain.008.001.04', 'pain.008.001.04'),
('pain.008.003.02', 'pain.008.003.02 (used in Germany)'),
])
@api.multi
def get_xsd_file_path(self):
self.ensure_one()
if self.pain_version in [
'pain.008.001.02', 'pain.008.001.03', 'pain.008.001.04',
'pain.008.003.02']:
path = 'account_banking_sepa_direct_debit/data/%s.xsd'\
% self.pain_version
return path
return super(AccountPaymentMethod, self).get_xsd_file_path()

View File

@@ -2,12 +2,13 @@
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, exceptions, _
from openerp import models, fields, api, _
from .common import is_sepa_creditor_identifier_valid
from openerp.exceptions import ValidationError
class PaymentMode(models.Model):
_inherit = 'payment.mode'
class AccountPaymentMode(models.Model):
_inherit = 'account.payment.mode'
sepa_creditor_identifier = fields.Char(
string='SEPA Creditor Identifier', size=35,
@@ -19,13 +20,9 @@ class PaymentMode(models.Model):
"- a 2-digits checkum\n"
"- a 3-letters business code\n"
"- a country-specific identifier")
original_creditor_identifier = fields.Char(
string='Original Creditor Identifier', size=70,
help="If not defined, Original Creditor Identifier from company "
"will be used.")
def _sepa_type_get(self):
res = super(PaymentMode, self)._sepa_type_get()
res = super(AccountPaymentMode, self)._sepa_type_get()
if not res:
if self.type.code and self.type.code.startswith('pain.008'):
res = 'sepa_direct_debit'
@@ -38,6 +35,6 @@ class PaymentMode(models.Model):
if payment_mode.sepa_creditor_identifier:
if not is_sepa_creditor_identifier_valid(
payment_mode.sepa_creditor_identifier):
raise exceptions.Warning(
_('Error'),
_("Invalid SEPA Creditor Identifier."))
raise ValidationError(
_("The SEPA Creditor Identifier '%s' is invalid.")
% payment_mode.sepa_creditor_identifier)

View File

@@ -0,0 +1,298 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, fields, api, _
from openerp.exceptions import UserError
from lxml import etree
class AccountPaymentOrder(models.Model):
_inherit = 'account.payment.order'
def _get_previous_bank(self, payline):
previous_bank = False
older_lines = self.env['account.payment.line'].search([
('mandate_id', '=', payline.mandate_id.id),
('partner_bank_id', '!=', payline.partner_bank_id.id)])
if older_lines:
previous_date = False
previous_payline = False
for older_line in older_lines:
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.partner_bank_id
return previous_bank
@api.multi
def generate_payment_file(self):
"""Creates the SEPA Direct Debit file. That's the important code !"""
self.ensure_one()
if (
self.payment_mode_id.payment_method_id.code !=
'sepa_direct_debit'):
return super(AccountPaymentOrder, self).generate_payment_file()
pain_flavor = self.payment_mode_id.payment_method_id.pain_version
if pain_flavor == 'pain.008.001.02':
bic_xml_tag = 'BIC'
name_maxsize = 70
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor == 'pain.008.003.02':
bic_xml_tag = 'BIC'
name_maxsize = 70
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor == 'pain.008.001.03':
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor == 'pain.008.001.04':
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrDrctDbtInitn'
else:
raise UserError(
_("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 "
"'pain.008.001.04'.") % pain_flavor)
xsd_file = self.payment_mode_id.payment_method_id.get_xsd_file_path()
gen_args = {
'bic_xml_tag': bic_xml_tag,
'name_maxsize': name_maxsize,
'convert_to_ascii': self.payment_mode_id.convert_to_ascii,
'payment_method': 'DD',
'file_prefix': 'sdd_',
'pain_flavor': pain_flavor,
'pain_xsd_file': xsd_file,
}
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)
# A. Group header
group_header_1_0, nb_of_transactions_1_6, control_sum_1_7 = \
self.generate_group_header_block(pain_root, gen_args)
transactions_count_1_6 = 0
amount_control_sum_1_7 = 0.0
lines_per_group = {}
# key = (requested_date, priority, sequence type)
# value = list of lines as objects
for line in self.bank_line_ids:
transactions_count_1_6 += 1
priority = line.priority
# The field line.date is the requested payment date
# taking into account the 'date_prefered' setting
# cf account_banking_payment_export/models/account_payment.py
# in the inherit of action_open()
if not line.mandate_id:
raise UserError(
_("Missing SEPA Direct Debit mandate on the "
"bank payment line with partner '%s' "
"(reference '%s').")
% (line.partner_id.name, line.name))
scheme = line.mandate_id.scheme
if line.mandate_id.state != 'valid':
raise Warning(
_("The SEPA Direct Debit mandate with reference '%s' "
"for partner '%s' has expired.")
% (line.mandate_id.unique_mandate_reference,
line.mandate_id.partner_id.name))
if line.mandate_id.type == 'oneoff':
seq_type = 'OOFF'
if line.mandate_id.last_debit_date:
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 "
"it.")
% (line.mandate_id.unique_mandate_reference,
line.mandate_id.partner_id.name,
line.mandate_id.last_debit_date))
elif line.mandate_id.type == 'recurrent':
seq_type_map = {
'recurring': 'RCUR',
'first': 'FRST',
'final': 'FNAL',
}
seq_type_label = \
line.mandate_id.recurrent_sequence_type
assert seq_type_label is not False
seq_type = seq_type_map[seq_type_label]
key = (line.date, priority, seq_type, scheme)
if key in lines_per_group:
lines_per_group[key].append(line)
else:
lines_per_group[key] = [line]
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(
pain_root,
"self.name + '-' + "
"sequence_type + '-' + requested_date.replace('-', '') "
"+ '-' + priority",
priority, scheme, sequence_type, requested_date, {
'self': self,
'sequence_type': sequence_type,
'priority': priority,
'requested_date': requested_date,
}, gen_args)
self.generate_party_block(
payment_info_2_0, 'Cdtr', 'B',
'self.company_partner_bank_id.partner_id.name',
'self.company_partner_bank_id.sanitized_acc_number',
'self.company_partner_bank_id.bank_bic',
{'self': self}, gen_args)
charge_bearer_2_24 = etree.SubElement(payment_info_2_0, 'ChrgBr')
if self.sepa:
charge_bearer = 'SLEV'
else:
charge_bearer = self.charge_bearer
charge_bearer_2_24.text = charge_bearer
creditor_scheme_identification_2_27 = etree.SubElement(
payment_info_2_0, 'CdtrSchmeId')
self.generate_creditor_scheme_identification(
creditor_scheme_identification_2_27,
'self.payment_mode_id.sepa_creditor_identifier or '
'self.company_id.sepa_creditor_identifier',
'SEPA Creditor Identifier', {'self': self}, 'SEPA', gen_args)
transactions_count_2_4 = 0
amount_control_sum_2_5 = 0.0
for line in lines:
transactions_count_2_4 += 1
# C. Direct Debit Transaction Info
dd_transaction_info_2_28 = etree.SubElement(
payment_info_2_0, 'DrctDbtTxInf')
payment_identification_2_29 = etree.SubElement(
dd_transaction_info_2_28, 'PmtId')
end2end_identification_2_31 = etree.SubElement(
payment_identification_2_29, 'EndToEndId')
end2end_identification_2_31.text = self._prepare_field(
'End to End Identification', 'line.name',
{'line': line}, 35, gen_args=gen_args)
currency_name = self._prepare_field(
'Currency Code', 'line.currency_id.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
amount_control_sum_1_7 += line.amount_currency
amount_control_sum_2_5 += line.amount_currency
dd_transaction_2_46 = etree.SubElement(
dd_transaction_info_2_28, 'DrctDbtTx')
mandate_related_info_2_47 = etree.SubElement(
dd_transaction_2_46, 'MndtRltdInf')
mandate_identification_2_48 = etree.SubElement(
mandate_related_info_2_47, 'MndtId')
mandate_identification_2_48.text = self._prepare_field(
'Unique Mandate Reference',
'line.mandate_id.unique_mandate_reference',
{'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(
'Mandate Signature Date',
'line.mandate_id.signature_date',
{'line': line}, 10, gen_args=gen_args)
if sequence_type == 'FRST' and line.mandate_id.last_debit_date:
previous_bank = self._get_previous_bank(line)
if previous_bank:
amendment_indicator_2_50 = etree.SubElement(
mandate_related_info_2_47, 'AmdmntInd')
amendment_indicator_2_50.text = 'true'
amendment_info_details_2_51 = etree.SubElement(
mandate_related_info_2_47, 'AmdmntInfDtls')
if (
previous_bank.bank_bic ==
line.partner_bank_id.bank_bic):
ori_debtor_account_2_57 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlDbtrAcct')
ori_debtor_account_id = etree.SubElement(
ori_debtor_account_2_57, 'Id')
ori_debtor_account_iban = etree.SubElement(
ori_debtor_account_id, 'IBAN')
ori_debtor_account_iban.text = self._validate_iban(
self._prepare_field(
'Original Debtor Account',
'previous_bank.sanitized_acc_number',
{'previous_bank': previous_bank},
gen_args=gen_args))
else:
ori_debtor_agent_2_58 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlDbtrAgt')
ori_debtor_agent_institution = etree.SubElement(
ori_debtor_agent_2_58, 'FinInstnId')
ori_debtor_agent_bic = etree.SubElement(
ori_debtor_agent_institution, bic_xml_tag)
ori_debtor_agent_bic.text = self._prepare_field(
'Original Debtor Agent',
'previous_bank.bank_bic',
{'previous_bank': previous_bank},
gen_args=gen_args)
ori_debtor_agent_other = etree.SubElement(
ori_debtor_agent_institution, 'Othr')
ori_debtor_agent_other_id = etree.SubElement(
ori_debtor_agent_other, 'Id')
ori_debtor_agent_other_id.text = 'SMNDA'
# SMNDA = Same Mandate New Debtor Agent
self.generate_party_block(
dd_transaction_info_2_28, 'Dbtr', 'C',
'line.partner_id.name',
'line.partner_bank_id.sanitized_acc_number',
'line.partner_bank_id.bank_bic',
{'line': line}, gen_args)
self.generate_remittance_info_block(
dd_transaction_info_2_28, line, gen_args)
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 = unicode(transactions_count_1_6)
control_sum_1_7.text = '%.2f' % amount_control_sum_1_7
return self.finalize_sepa_file_creation(
xml_root, gen_args)
@api.multi
def finalize_sepa_file_creation(self, xml_root, gen_args):
"""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.
"""
abmo = self.env['account.banking.mandate']
to_expire_mandates = abmo.browse([])
first_mandates = abmo.browse([])
all_mandates = abmo.browse([])
for bline in self.bank_line_ids:
if bline.mandate_id in all_mandates:
continue
all_mandates += bline.mandate_id
if bline.mandate_id.type == 'oneoff':
to_expire_mandates += bline.mandate_id
elif bline.mandate_id.type == 'recurrent':
seq_type = bline.mandate_id.recurrent_sequence_type
if seq_type == 'final':
to_expire_mandates += bline.mandate_id
elif seq_type == 'first':
first_mandates += bline.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',
})
return super(AccountPaymentOrder, self).finalize_sepa_file_creation(
xml_root, gen_args)

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# © 2015-2016 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, api

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013 Akretion (www.akretion.com)
# © 2013-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

View File

@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
# © 2013 Akretion (www.akretion.com)
# © 2013-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, exceptions, _
from openerp import models, fields, api, _
from .common import is_sepa_creditor_identifier_valid
from openerp.exceptions import ValidationError
class ResCompany(models.Model):
@@ -28,6 +29,6 @@ class ResCompany(models.Model):
if company.sepa_creditor_identifier:
if not is_sepa_creditor_identifier_valid(
company.sepa_creditor_identifier):
raise exceptions.Warning(
_('Error'),
_("Invalid SEPA Creditor Identifier."))
raise ValidationError(
_("The SEPA Creditor Identifier '%s' is invalid.")
% company.sepa_creditor_identifier)

View File

@@ -1,17 +0,0 @@
<?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">
<record id="group_original_mandate_required" model="res.groups">
<field name="name">Original Mandate Required (SEPA)</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
</data>
</openerp>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
Copyright (C) 2013-2016 Akretion (http://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __openerp__.py
-->
@@ -11,7 +11,7 @@
<data>
<record id="sdd_mandate_form" model="ir.ui.view">
<record id="view_mandate_form" model="ir.ui.view">
<field name="name">sdd.mandate.form</field>
<field name="model">account.banking.mandate</field>
<field name="inherit_id" ref="account_banking_mandate.view_mandate_form"/>
@@ -25,14 +25,10 @@
<field name="scheme" attrs="{'invisible': [('format', '!=', 'sepa')],
'required': [('format', '=', 'sepa')]}"/>
</field>
<field name="last_debit_date" position="after">
<field name="sepa_migrated" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
<field name="original_mandate_identification" attrs="{'invisible': [('sepa_migrated', '=', True)], 'required': [('sepa_migrated', '=', False)]}" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
</field>
</field>
</record>
<record id="sdd_mandate_tree" model="ir.ui.view">
<record id="view_mandate_tree" model="ir.ui.view">
<field name="name">sdd.mandate.tree</field>
<field name="model">account.banking.mandate</field>
<field name="inherit_id" ref="account_banking_mandate.view_mandate_tree"/>
@@ -45,7 +41,7 @@
</field>
</record>
<record id="sdd_mandate_search" model="ir.ui.view">
<record id="view_mandate_search" model="ir.ui.view">
<field name="name">sdd.mandate.search</field>
<field name="model">account.banking.mandate</field>
<field name="inherit_id" ref="account_banking_mandate.view_mandate_search"/>
@@ -66,67 +62,5 @@
</field>
</record>
<record id="mandate_action" model="ir.actions.act_window">
<field name="name">SEPA Direct Debit Mandates</field>
<field name="res_model">account.banking.mandate</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new SEPA Direct Debit Mandate.
</p><p>
A SEPA Direct Debit Mandate is a document signed by your customer that gives you the autorization to do one or several direct debits on his bank account.
</p>
</field>
</record>
<menuitem id="account_banking_mandate.mandate_menu"
parent="account_payment.menu_main_payment"
action="mandate_action"
sequence="20"
/>
<record id="sdd_mandate_partner_bank_tree" model="ir.ui.view">
<field name="name">sdd.mandate.res.partner.bank.tree</field>
<field name="model">res.partner.bank</field>
<field name="inherit_id" ref="account_banking_mandate.mandate_partner_bank_tree"/>
<field name="arch" type="xml">
<field name="mandate_ids" position="attributes">
<attribute name="string">SDD Mandates</attribute>
</field>
</field>
</record>
<record id="sdd_mandate_partner_form" model="ir.ui.view">
<field name="name">sdd.mandate.partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="account_banking_mandate.mandate_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='bank_ids']/tree/field[@name='mandate_ids']" position="attributes">
<attribute name="string">SDD Mandates</attribute>
</xpath>
</field>
</record>
<record id="recurrent_sequence_type_first" model="mail.message.subtype">
<field name="name">Sequence Type set to First</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to First</field>
</record>
<record id="recurrent_sequence_type_recurring" model="mail.message.subtype">
<field name="name">Sequence Type set to Recurring</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to Recurring</field>
</record>
<record id="recurrent_sequence_type_final" model="mail.message.subtype">
<field name="name">Sequence Type set to Final</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to Final</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
© 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<data>
<record id="account_payment_mode_form" model="ir.ui.view">
<field name="name">Add SEPA identifiers on payment mode form</field>
<field name="model">account.payment.mode</field>
<field name="inherit_id" ref="account_banking_pain_base.account_payment_mode_form"/>
<field name="arch" type="xml">
<group name="pain_options" position="inside">
<field name="sepa_creditor_identifier" invisible="1"/>
<!-- This field should be set visible by localization modules
for countries that may have several sepa_creditor_identifier for
the same company (I guess only Spain) -->
</group>
</field>
</record>
</data>
</openerp>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<data>
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
<field name="name">Add SEPA identifiers</field>
<field name="model">payment.mode</field>
<field name="inherit_id" ref="account_banking_pain_base.view_payment_mode_form_inherit"/>
<field name="arch" type="xml">
<group name="sepa_identifiers" position="inside">
<group>
<field name="sepa_creditor_identifier"
attrs="{'invisible': [('sepa_type', '!=', 'sepa_direct_debit')]}"/>
<field name="original_creditor_identifier"
attrs="{'invisible': [('sepa_type', '!=', 'sepa_direct_debit')]}"/>
</group>
</group>
</field>
</record>
</data>
</openerp>

View File

@@ -1,20 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Akretion (http://www.akretion.com)
Copyright (C) 2013-2016 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="sdd_res_company_form" model="ir.ui.view">
<record id="view_company_form" model="ir.ui.view">
<field name="name">sepa_direct_debit.res.company.form</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="account_banking_pain_base.view_company_form"/>
<field name="arch" type="xml">
<group name="pain" position="inside">
<field name="sepa_creditor_identifier"/>
<field name="original_creditor_identifier" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
</group>
</field>
</record>

View File

@@ -1,23 +0,0 @@
# -*- 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 export_sdd

View File

@@ -1,394 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Direct Debit module for Odoo
# Copyright (C) 2013-2015 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.exceptions import Warning
from openerp import workflow
from lxml import etree
class BankingExportSddWizard(models.TransientModel):
_name = 'banking.export.sdd.wizard'
_inherit = ['banking.export.pain']
_description = 'Export SEPA Direct Debit File'
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)
@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(vals)
def _get_previous_bank(self, payline):
previous_bank = False
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 = False
for older_line in older_lines:
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
@api.multi
def create_sepa(self):
"""Creates the SEPA Direct Debit file. That's the important code !"""
pain_flavor = self.payment_order_ids[0].mode.type.code
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
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor == 'pain.008.001.03':
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrDrctDbtInitn'
elif pain_flavor == 'pain.008.001.04':
bic_xml_tag = 'BICFI'
name_maxsize = 140
root_xml_tag = 'CstmrDrctDbtInitn'
else:
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 "
"'pain.008.001.04'.") % pain_flavor)
gen_args = {
'bic_xml_tag': bic_xml_tag,
'name_maxsize': name_maxsize,
'convert_to_ascii': convert_to_ascii,
'payment_method': 'DD',
'file_prefix': 'sdd_',
'pain_flavor': pain_flavor,
'pain_xsd_file':
'account_banking_sepa_direct_debit/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)
# A. Group header
group_header_1_0, nb_of_transactions_1_6, control_sum_1_7 = \
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, sequence type)
# value = list of lines as objects
# Iterate on payment orders
for payment_order in self.payment_order_ids:
total_amount = total_amount + payment_order.total
# Iterate each payment lines
for line in payment_order.bank_line_ids:
transactions_count_1_6 += 1
priority = line.priority
# The field line.date is the requested payment date
# taking into account the 'date_prefered' setting
# cf account_banking_payment_export/models/account_payment.py
# in the inherit of action_open()
if not line.mandate_id:
raise Warning(
_("Missing SEPA Direct Debit mandate on the "
"bank payment line with partner '%s' "
"(reference '%s').")
% (line.partner_id.name, line.name))
scheme = line.mandate_id.scheme
if line.mandate_id.state != 'valid':
raise Warning(
_("The SEPA Direct Debit mandate with reference '%s' "
"for partner '%s' has expired.")
% (line.mandate_id.unique_mandate_reference,
line.mandate_id.partner_id.name))
if line.mandate_id.type == 'oneoff':
seq_type = 'OOFF'
if line.mandate_id.last_debit_date:
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 "
"it.")
% (line.mandate_id.unique_mandate_reference,
line.mandate_id.partner_id.name,
line.mandate_id.last_debit_date))
elif line.mandate_id.type == 'recurrent':
seq_type_map = {
'recurring': 'RCUR',
'first': 'FRST',
'final': 'FNAL',
}
seq_type_label = \
line.mandate_id.recurrent_sequence_type
assert seq_type_label is not False
seq_type = seq_type_map[seq_type_label]
key = (line.date, priority, seq_type, scheme)
if key in lines_per_group:
lines_per_group[key].append(line)
else:
lines_per_group[key] = [line]
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(
pain_root,
"self.payment_order_ids[0].reference + '-' + "
"sequence_type + '-' + requested_date.replace('-', '') "
"+ '-' + priority",
priority, scheme, sequence_type, requested_date, {
'self': self,
'sequence_type': sequence_type,
'priority': priority,
'requested_date': requested_date,
}, gen_args)
self.generate_party_block(
payment_info_2_0, 'Cdtr', 'B',
'self.payment_order_ids[0].mode.bank_id.partner_id.'
'name',
'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 = self.charge_bearer
creditor_scheme_identification_2_27 = etree.SubElement(
payment_info_2_0, 'CdtrSchmeId')
self.generate_creditor_scheme_identification(
creditor_scheme_identification_2_27,
'self.payment_order_ids[0].mode.'
'sepa_creditor_identifier or '
'self.payment_order_ids[0].company_id.'
'sepa_creditor_identifier',
'SEPA Creditor Identifier', {'self': self}, 'SEPA', gen_args)
transactions_count_2_4 = 0
amount_control_sum_2_5 = 0.0
for line in lines:
transactions_count_2_4 += 1
# C. Direct Debit Transaction Info
dd_transaction_info_2_28 = etree.SubElement(
payment_info_2_0, 'DrctDbtTxInf')
payment_identification_2_29 = etree.SubElement(
dd_transaction_info_2_28, 'PmtId')
end2end_identification_2_31 = etree.SubElement(
payment_identification_2_29, 'EndToEndId')
end2end_identification_2_31.text = self._prepare_field(
'End to End Identification', 'line.name',
{'line': line}, 35, gen_args=gen_args)
currency_name = self._prepare_field(
'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
amount_control_sum_1_7 += line.amount_currency
amount_control_sum_2_5 += line.amount_currency
dd_transaction_2_46 = etree.SubElement(
dd_transaction_info_2_28, 'DrctDbtTx')
mandate_related_info_2_47 = etree.SubElement(
dd_transaction_2_46, 'MndtRltdInf')
mandate_identification_2_48 = etree.SubElement(
mandate_related_info_2_47, 'MndtId')
mandate_identification_2_48.text = self._prepare_field(
'Unique Mandate Reference',
'line.mandate_id.unique_mandate_reference',
{'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(
'Mandate Signature Date',
'line.mandate_id.signature_date',
{'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(line)
if previous_bank or not line.mandate_id.sepa_migrated:
amendment_indicator_2_50 = etree.SubElement(
mandate_related_info_2_47, 'AmdmntInd')
amendment_indicator_2_50.text = 'true'
amendment_info_details_2_51 = etree.SubElement(
mandate_related_info_2_47, 'AmdmntInfDtls')
if previous_bank:
if (previous_bank.bank.bic or
previous_bank.bank_bic) == \
(line.bank_id.bank.bic or
line.bank_id.bank_bic):
ori_debtor_account_2_57 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlDbtrAcct')
ori_debtor_account_id = etree.SubElement(
ori_debtor_account_2_57, 'Id')
ori_debtor_account_iban = etree.SubElement(
ori_debtor_account_id, 'IBAN')
ori_debtor_account_iban.text = self._validate_iban(
self._prepare_field(
'Original Debtor Account',
'previous_bank.acc_number',
{'previous_bank': previous_bank},
gen_args=gen_args))
else:
ori_debtor_agent_2_58 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlDbtrAgt')
ori_debtor_agent_institution = etree.SubElement(
ori_debtor_agent_2_58, 'FinInstnId')
ori_debtor_agent_bic = etree.SubElement(
ori_debtor_agent_institution, bic_xml_tag)
ori_debtor_agent_bic.text = self._prepare_field(
'Original Debtor Agent',
'previous_bank.bank.bic or '
'previous_bank.bank_bic',
{'previous_bank': previous_bank},
gen_args=gen_args)
ori_debtor_agent_other = etree.SubElement(
ori_debtor_agent_institution, 'Othr')
ori_debtor_agent_other_id = etree.SubElement(
ori_debtor_agent_other, 'Id')
ori_debtor_agent_other_id.text = 'SMNDA'
# SMNDA = Same Mandate New Debtor Agent
elif not line.mandate_id.sepa_migrated:
ori_mandate_identification_2_52 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlMndtId')
ori_mandate_identification_2_52.text = \
self._prepare_field(
'Original Mandate Identification',
'line.mandate_id.'
'original_mandate_identification',
{'line': line},
gen_args=gen_args)
ori_creditor_scheme_id_2_53 = etree.SubElement(
amendment_info_details_2_51, 'OrgnlCdtrSchmeId')
self.generate_creditor_scheme_identification(
ori_creditor_scheme_id_2_53,
'self.payment_order_ids[0].mode.'
'original_creditor_identifier or '
'self.payment_order_ids[0].company_id.'
'original_creditor_identifier',
'Original Creditor Identifier',
{'self': self}, 'SEPA', gen_args)
self.generate_party_block(
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)
self.generate_remittance_info_block(
dd_transaction_info_2_28, line, gen_args)
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 = unicode(transactions_count_1_6)
control_sum_1_7.text = '%.2f' % amount_control_sum_1_7
return self.finalize_sepa_file_creation(
xml_root, total_amount, transactions_count_1_6, gen_args)
@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.
"""
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 bline in order.bank_line_ids:
if bline.mandate_id in all_mandates:
continue
all_mandates += bline.mandate_id
if bline.mandate_id.type == 'oneoff':
to_expire_mandates += bline.mandate_id
elif bline.mandate_id.type == 'recurrent':
seq_type = bline.mandate_id.recurrent_sequence_type
if seq_type == 'final':
to_expire_mandates += bline.mandate_id
elif seq_type == 'first':
first_mandates += bline.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

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2010-2012 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="banking_export_sdd_wizard_view" model="ir.ui.view">
<field name="name">banking.export.sdd.wizard.view</field>
<field name="model">banking.export.sdd.wizard</field>
<field name="arch" type="xml">
<form string="SEPA Direct Debit XML file generation">
<field name="state" invisible="True"/>
<group states="create">
<field name="batch_booking" />
<field name="charge_bearer" />
</group>
<group states="finish">
<field name="total_amount" />
<field name="nb_transactions" />
<field name="file" filename="filename" />
<field name="filename" invisible="True"/>
</group>
<footer>
<button string="Generate" name="create_sepa" type="object" class="oe_highlight" states="create"/>
<button string="Validate" name="save_sepa" type="object" class="oe_highlight" states="finish"/>
<button string="Cancel" special="cancel" class="oe_link"/>
</footer>
</form>
</field>
</record>
</data>
</openerp>

View File

@@ -1,77 +0,0 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:alt: License: AGPL-3
Direct Debit
============
This module adds support for direct debit orders, analogous to payment orders.
A new entry in the Accounting/Payment menu allow you to create a direct debit
order that helps you to select any customer invoices for you to collect.
Debit orders are advanced in total by the bank. Amounts that cannot be
debited or are canceled by account owners are credited afterwards.
Installation
============
This module depends on :
* account_banking_payment_export
This module is part of the OCA/bank-payment suite.
Configuration
=============
Please refer to module *Account Banking SEPA Direct Debit*
Usage
=====
Please refer to module *Account Banking SEPA Direct Debit*
For further information, please visit:
* https://www.odoo.com/forum/help-1
Known issues / Roadmap
======================
* No known issues
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/bank-payment/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/bank-payment/issues/new?body=module:%20account_direct_debit%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Credits
=======
Contributors
------------
* Stefan Rijnhart
* Pedro M. Baeza
* Alexis de Lattre <alexis.delattre@akretion.com>
* Danimar Ribeiro
* Stéphane Bidoul <stephane.bidoul@acsone.eu>
* Alexandre Fayolle
* Sandy Carter
* Holger Brunn
Maintainer
----------
.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.

View File

@@ -1,2 +0,0 @@
from . import models
from . import wizard

View File

@@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# © 2011 Smile (<http://smile.fr>)
# © 2011-2013 Therp BV (<http://therp.nl>)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Direct Debit',
'version': '8.0.2.1.0',
'license': 'AGPL-3',
'author': 'Therp BV, '
'Smile, '
'Odoo Community Association (OCA)',
'website': 'https://github.com/OCA/bank-payment',
'category': 'Banking addons',
'depends': ['account_banking_payment_export'],
'data': [
'views/account_payment.xml',
'views/payment_mode.xml',
'views/payment_mode_type.xml',
'data/account_payment_term.xml',
'data/payment_mode_type.xml'
],
'installable': False,
}

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="payment_term_direct_debit" model="account.payment.term">
<field name="name">Direct debit</field>
<field name="note">Direct debit in 14 days</field>
</record>
<record id="payment_term_line_direct_debit" model="account.payment.term.line">
<field name="value">balance</field>
<field eval="14" name="days"/>
<field eval="0" name="days2"/>
<field eval="payment_term_direct_debit" name="payment_id"/>
</record>
</data>
</openerp>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Add direct debit as payment option -->
<record model="payment.mode.type" id="manual_direct_debit">
<field name="name">Manual Direct Debit</field>
<field name="code">MANDDEB</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base.bank_normal'),ref('base_iban.bank_iban'),])]" />
<field name="ir_model_id"
ref="account_banking_payment_export.model_payment_manual"/>
<field name="payment_order_type">debit</field>
</record>
</data>
</openerp>

View File

@@ -1,127 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_direct_debit
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-10-31 23:05+0000\n"
"PO-Revision-Date: 2014-10-31 23:05+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_direct_debit
#: model:ir.actions.act_window,help:account_direct_debit.action_debit_order_tree
msgid "A debit order is a debit request from your company to collect customer invoices. Here you can register all debit orders that should be done, keep track of all debit orders and mention the invoice reference and the partner the withdrawal should be done for."
msgstr ""
#. module: account_direct_debit
#: field:account.move.line,amount_to_receive:0
msgid "Amount to receive"
msgstr ""
#. module: account_direct_debit
#: code:addons/account_direct_debit/models/payment_line.py:133
#, python-format
msgid "Can not reconcile"
msgstr ""
#. module: account_direct_debit
#: code:addons/account_direct_debit/models/payment_line.py:134
#, python-format
msgid "Cancelation of payment line '%s' has already been processed"
msgstr ""
#. module: account_direct_debit
#: view:account.invoice:account_direct_debit.invoice_form
msgid "Debit Denied"
msgstr ""
#. module: account_direct_debit
#: view:account.invoice:account_direct_debit.view_account_invoice_filter
msgid "Debit denied"
msgstr ""
#. module: account_direct_debit
#: model:ir.actions.act_window,name:account_direct_debit.action_debit_order_tree
#: model:ir.ui.menu,name:account_direct_debit.menu_action_debit_order_form
msgid "Direct Debit Orders"
msgstr ""
#. module: account_direct_debit
#: model:account.payment.term,name:account_direct_debit.payment_term_direct_debit
msgid "Direct debit"
msgstr ""
#. module: account_direct_debit
#: model:account.payment.term,note:account_direct_debit.payment_term_direct_debit
msgid "Direct debit in 14 days"
msgstr ""
#. module: account_direct_debit
#: code:addons/account_direct_debit/models/account_invoice.py:149
#, python-format
msgid "Error !"
msgstr ""
#. module: account_direct_debit
#: help:payment.line,storno:0
msgid "If this is true, the debit order has been canceled by the bank or by the customer"
msgstr ""
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_account_invoice
msgid "Invoice"
msgstr ""
#. module: account_direct_debit
#: code:addons/account_direct_debit/models/account_invoice.py:154
#, python-format
msgid "Invoice '%s': direct debit is denied."
msgstr ""
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Invoices"
msgstr ""
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_account_move_line
msgid "Journal Items"
msgstr ""
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_payment_line
msgid "Payment Line"
msgstr ""
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_payment_order
msgid "Payment Order"
msgstr ""
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Select invoices to collect"
msgstr ""
#. module: account_direct_debit
#: view:account.invoice:account_direct_debit.view_account_invoice_filter
msgid "Show only invoices with state Debit denied"
msgstr ""
#. module: account_direct_debit
#: field:payment.line,storno:0
msgid "Storno"
msgstr ""
#. module: account_direct_debit
#: code:addons/account_direct_debit/models/account_invoice.py:150
#, python-format
msgid "You cannot set invoice '%s' to state 'debit denied', as it is still reconciled."
msgstr ""

View File

@@ -1,68 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_direct_debit
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-02-16 07:59+0000\n"
"PO-Revision-Date: 2016-02-16 07:59+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_direct_debit
#: model:ir.actions.act_window,help:account_direct_debit.action_debit_order_tree
msgid "A debit order is a debit request from your company to collect customer invoices. Here you can register all debit orders that should be done, keep track of all debit orders and mention the invoice reference and the partner the withdrawal should be done for."
msgstr "Una orden de cobro es una petición de dinero de su compañía para saldar las facturas de cliente. Aquí puede registrar todas las órdenes de cobro que se deban realizar, seguirles el rastro, y apuntar la referencia de factura y de la empresa para la que se debe hacer el cargo."
#. module: account_direct_debit
#: model:ir.actions.act_window,name:account_direct_debit.action_debit_order_tree
#: model:ir.ui.menu,name:account_direct_debit.menu_action_debit_order_form
msgid "Direct Debit Orders"
msgstr "Órdenes de cobro"
#. module: account_direct_debit
#: model:account.payment.term,name:account_direct_debit.payment_term_direct_debit
msgid "Direct debit"
msgstr "Cobro"
#. module: account_direct_debit
#: model:account.payment.term,note:account_direct_debit.payment_term_direct_debit
msgid "Direct debit in 14 days"
msgstr "Adeudo directo en 14 días"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Invoices"
msgstr "Facturas"
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_account_move_line
msgid "Journal Items"
msgstr "Apuntes contables"
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_payment_line
msgid "Payment Line"
msgstr "Línea de pago"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Select invoices to collect"
msgstr "Seleccione facturas"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "[('payment_order_type', '=', payment_order_type)]"
msgstr "[('payment_order_type', '=', payment_order_type)]"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "{'invisible': ['|', ('state', '!=', 'draft'), ('payment_order_type', '!=', 'payment')]}"
msgstr "{'invisible': ['|', ('state', '!=', 'draft'), ('payment_order_type', '!=', 'payment')]}"

View File

@@ -1,75 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_direct_debit
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: bank-payment (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-04-08 00:46+0000\n"
"PO-Revision-Date: 2016-04-06 00:15+0000\n"
"Last-Translator: <>\n"
"Language-Team: French (http://www.transifex.com/oca/OCA-bank-payment-8-0/language/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: account_direct_debit
#: model:ir.actions.act_window,help:account_direct_debit.action_debit_order_tree
msgid ""
"A debit order is a debit request from your company to collect customer "
"invoices. Here you can register all debit orders that should be done, keep "
"track of all debit orders and mention the invoice reference and the partner "
"the withdrawal should be done for."
msgstr ""
#. module: account_direct_debit
#: model:ir.actions.act_window,name:account_direct_debit.action_debit_order_tree
#: model:ir.ui.menu,name:account_direct_debit.menu_action_debit_order_form
msgid "Direct Debit Orders"
msgstr ""
#. module: account_direct_debit
#: model:account.payment.term,name:account_direct_debit.payment_term_direct_debit
msgid "Direct debit"
msgstr ""
#. module: account_direct_debit
#: model:account.payment.term,note:account_direct_debit.payment_term_direct_debit
msgid "Direct debit in 14 days"
msgstr ""
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Invoices"
msgstr ""
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_account_move_line
msgid "Journal Items"
msgstr ""
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_payment_line
msgid "Payment Line"
msgstr "Ligne de paiement"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Select invoices to collect"
msgstr ""
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "[('payment_order_type', '=', payment_order_type)]"
msgstr ""
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid ""
"{'invisible': ['|', ('state', '!=', 'draft'), ('payment_order_type', '!=', "
"'payment')]}"
msgstr ""

View File

@@ -1,76 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_direct_debit
#
# Translators:
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013
msgid ""
msgstr ""
"Project-Id-Version: bank-payment (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-04-08 00:46+0000\n"
"PO-Revision-Date: 2016-04-06 00:15+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>\n"
"Language-Team: Dutch (http://www.transifex.com/oca/OCA-bank-payment-8-0/language/nl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: nl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: account_direct_debit
#: model:ir.actions.act_window,help:account_direct_debit.action_debit_order_tree
msgid ""
"A debit order is a debit request from your company to collect customer "
"invoices. Here you can register all debit orders that should be done, keep "
"track of all debit orders and mention the invoice reference and the partner "
"the withdrawal should be done for."
msgstr "Een incasso opdracht is een opdracht van uw bedrijf aan uw klant voor het incasseren van openstaande posten. Hier kunt u alle incasso opdrachten invoeren die moeten worden uitgevoerd en overzicht houden op alle incasso opdrachten. Tevens voert u hier de factuurreferentie en klant in waar de afschrijving moet plaatsvinden."
#. module: account_direct_debit
#: model:ir.actions.act_window,name:account_direct_debit.action_debit_order_tree
#: model:ir.ui.menu,name:account_direct_debit.menu_action_debit_order_form
msgid "Direct Debit Orders"
msgstr "Incasso opdracht"
#. module: account_direct_debit
#: model:account.payment.term,name:account_direct_debit.payment_term_direct_debit
msgid "Direct debit"
msgstr "Incasso opdracht"
#. module: account_direct_debit
#: model:account.payment.term,note:account_direct_debit.payment_term_direct_debit
msgid "Direct debit in 14 days"
msgstr "Incasso opdracht in 14 dagen"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Invoices"
msgstr ""
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_account_move_line
msgid "Journal Items"
msgstr "Boekingen"
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_payment_line
msgid "Payment Line"
msgstr "Betaalregel"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Select invoices to collect"
msgstr ""
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "[('payment_order_type', '=', payment_order_type)]"
msgstr ""
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid ""
"{'invisible': ['|', ('state', '!=', 'draft'), ('payment_order_type', '!=', "
"'payment')]}"
msgstr ""

View File

@@ -1,76 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_direct_debit
#
# Translators:
# danimaribeiro <danimaribeiro@gmail.com>, 2016
msgid ""
msgstr ""
"Project-Id-Version: bank-payment (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-04-08 00:46+0000\n"
"PO-Revision-Date: 2016-04-06 01:04+0000\n"
"Last-Translator: danimaribeiro <danimaribeiro@gmail.com>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/oca/OCA-bank-payment-8-0/language/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: account_direct_debit
#: model:ir.actions.act_window,help:account_direct_debit.action_debit_order_tree
msgid ""
"A debit order is a debit request from your company to collect customer "
"invoices. Here you can register all debit orders that should be done, keep "
"track of all debit orders and mention the invoice reference and the partner "
"the withdrawal should be done for."
msgstr "A ordem de débito é um pedido de débito de sua empresa para coletar faturas de clientes. Aqui você pode registrar todas as ordens de débito que devem ser feitas, manter o controle de todas as ordens de débito e mencionar a referência da fatura e o parceiro que a retirada deve ser feita."
#. module: account_direct_debit
#: model:ir.actions.act_window,name:account_direct_debit.action_debit_order_tree
#: model:ir.ui.menu,name:account_direct_debit.menu_action_debit_order_form
msgid "Direct Debit Orders"
msgstr "Débito direto autorizado"
#. module: account_direct_debit
#: model:account.payment.term,name:account_direct_debit.payment_term_direct_debit
msgid "Direct debit"
msgstr "Débito direto"
#. module: account_direct_debit
#: model:account.payment.term,note:account_direct_debit.payment_term_direct_debit
msgid "Direct debit in 14 days"
msgstr "Débito direto em 14 dias"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Invoices"
msgstr "Faturas"
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_account_move_line
msgid "Journal Items"
msgstr "Itens de diário"
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_payment_line
msgid "Payment Line"
msgstr "Linha de pagamento"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Select invoices to collect"
msgstr "Selecione as faturas"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "[('payment_order_type', '=', payment_order_type)]"
msgstr "[('payment_order_type', '=', payment_order_type)]"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid ""
"{'invisible': ['|', ('state', '!=', 'draft'), ('payment_order_type', '!=', "
"'payment')]}"
msgstr "{'invisible': ['|', ('state', '!=', 'draft'), ('payment_order_type', '!=', 'payment')]}"

View File

@@ -1,76 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_direct_debit
#
# Translators:
# Matjaž Mozetič <m.mozetic@matmoz.si>, 2016
msgid ""
msgstr ""
"Project-Id-Version: bank-payment (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-04-08 00:46+0000\n"
"PO-Revision-Date: 2016-04-08 05:55+0000\n"
"Last-Translator: Matjaž Mozetič <m.mozetic@matmoz.si>\n"
"Language-Team: Slovenian (http://www.transifex.com/oca/OCA-bank-payment-8-0/language/sl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: sl\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
#. module: account_direct_debit
#: model:ir.actions.act_window,help:account_direct_debit.action_debit_order_tree
msgid ""
"A debit order is a debit request from your company to collect customer "
"invoices. Here you can register all debit orders that should be done, keep "
"track of all debit orders and mention the invoice reference and the partner "
"the withdrawal should be done for."
msgstr "Bremenilni nalog je zahtevek vaše družbe po obremenitvi za zbir prejetih računov. Registrirate lahko vse bremenilne naloge, ki naj bi se izvedli, sledite bremenilnim nalogom in navedete sklic računa in partnerja, za katerega se izvaja nakazilo."
#. module: account_direct_debit
#: model:ir.actions.act_window,name:account_direct_debit.action_debit_order_tree
#: model:ir.ui.menu,name:account_direct_debit.menu_action_debit_order_form
msgid "Direct Debit Orders"
msgstr "Nalogi za direktne obremenitve"
#. module: account_direct_debit
#: model:account.payment.term,name:account_direct_debit.payment_term_direct_debit
msgid "Direct debit"
msgstr "Direktna obremenitev"
#. module: account_direct_debit
#: model:account.payment.term,note:account_direct_debit.payment_term_direct_debit
msgid "Direct debit in 14 days"
msgstr "Direktna obremenitev v 14 dneh"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Invoices"
msgstr "Računi"
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_account_move_line
msgid "Journal Items"
msgstr "Dnevniške postavke"
#. module: account_direct_debit
#: model:ir.model,name:account_direct_debit.model_payment_line
msgid "Payment Line"
msgstr "Plačilna postavka"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "Select invoices to collect"
msgstr "Izbira zbira računov"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid "[('payment_order_type', '=', payment_order_type)]"
msgstr "[('payment_order_type', '=', payment_order_type)]"
#. module: account_direct_debit
#: view:payment.order:account_direct_debit.view_payment_order_form
msgid ""
"{'invisible': ['|', ('state', '!=', 'draft'), ('payment_order_type', '!=', "
"'payment')]}"
msgstr "{'invisible': ['|', ('state', '!=', 'draft'), ('payment_order_type', '!=', 'payment')]}"

View File

@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
from . import payment_line
from . import account_move_line

View File

@@ -1,38 +0,0 @@
# -*- coding: utf-8 -*-
# © 2011 Smile (<http://smile.fr>)
# © 2011-2013 Therp BV (<http://therp.nl>)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
def line2bank(self, cr, uid, ids, payment_type=None, context=None):
"""I have to inherit this function for direct debits to fix the
following issue : if the customer invoice has a value for
'partner_bank_id', then it will take this partner_bank_id
in the payment line... but, on a customer invoice,
the partner_bank_id is the bank account of the company,
not the bank account of the customer !
"""
pay_mode_obj = self.pool['payment.mode']
if payment_type:
pay_mode = pay_mode_obj.browse(
cr, uid, payment_type, context=context)
if pay_mode.type.payment_order_type == 'debit':
line2bank = {}
bank_type = pay_mode_obj.suitable_bank_types(
cr, uid, pay_mode.id, context=context)
for line in self.browse(cr, uid, ids, context=context):
line2bank[line.id] = False
if line.partner_id:
for bank in line.partner_id.bank_ids:
if bank.state in bank_type:
line2bank[line.id] = bank.id
break
return line2bank
return super(AccountMoveLine, self).line2bank(
cr, uid, ids, payment_type=pay_mode.id, context=context)

View File

@@ -1,14 +0,0 @@
# -*- coding: utf-8 -*-
# © 2011 Smile (<http://smile.fr>)
# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
class PaymentLine(models.Model):
_inherit = 'payment.line'
# The original string is "Destination Bank Account"...
# but in direct debit this field is the *Source* Bank Account !
bank_id = fields.Many2one(string='Partner Bank Account')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

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