mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[IMP] account_banking_sepa_direct_debit: black, isort
This commit is contained in:
@@ -1,3 +1,2 @@
|
||||
|
||||
from . import models
|
||||
from .post_install import update_bank_journals
|
||||
|
||||
@@ -4,30 +4,25 @@
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
{
|
||||
'name': 'Account Banking SEPA Direct Debit',
|
||||
'summary': 'Create SEPA files for Direct Debit',
|
||||
'version': '12.0.1.2.0',
|
||||
'license': 'AGPL-3',
|
||||
'author': "Akretion, "
|
||||
"Tecnativa, "
|
||||
"Odoo Community Association (OCA)",
|
||||
'website': 'https://github.com/OCA/bank-payment',
|
||||
'category': 'Banking addons',
|
||||
'depends': [
|
||||
'account_banking_pain_base',
|
||||
'account_banking_mandate',
|
||||
"name": "Account Banking SEPA Direct Debit",
|
||||
"summary": "Create SEPA files for Direct Debit",
|
||||
"version": "12.0.1.2.0",
|
||||
"license": "AGPL-3",
|
||||
"author": "Akretion, " "Tecnativa, " "Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/bank-payment",
|
||||
"category": "Banking addons",
|
||||
"depends": ["account_banking_pain_base", "account_banking_mandate"],
|
||||
"data": [
|
||||
"views/account_banking_mandate_view.xml",
|
||||
"views/res_config.xml",
|
||||
"views/account_payment_mode.xml",
|
||||
"data/mandate_expire_cron.xml",
|
||||
"data/account_payment_method.xml",
|
||||
"data/report_paperformat.xml",
|
||||
"reports/sepa_direct_debit_mandate.xml",
|
||||
"views/report_sepa_direct_debit_mandate.xml",
|
||||
],
|
||||
'data': [
|
||||
'views/account_banking_mandate_view.xml',
|
||||
'views/res_config.xml',
|
||||
'views/account_payment_mode.xml',
|
||||
'data/mandate_expire_cron.xml',
|
||||
'data/account_payment_method.xml',
|
||||
'data/report_paperformat.xml',
|
||||
'reports/sepa_direct_debit_mandate.xml',
|
||||
'views/report_sepa_direct_debit_mandate.xml',
|
||||
],
|
||||
'demo': ['demo/sepa_direct_debit_demo.xml'],
|
||||
'post_init_hook': 'update_bank_journals',
|
||||
'installable': True,
|
||||
"demo": ["demo/sepa_direct_debit_demo.xml"],
|
||||
"post_init_hook": "update_bank_journals",
|
||||
"installable": True,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo noupdate="1">
|
||||
<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="bank_account_required" eval="True"/>
|
||||
<field name="mandate_required" eval="True"/>
|
||||
<field name="bank_account_required" eval="True" />
|
||||
<field name="mandate_required" eval="True" />
|
||||
<field name="pain_version">pain.008.001.02</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
© 2013-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
|
||||
<odoo noupdate="1">
|
||||
<record id="sdd_mandate_expire_cron" model="ir.cron">
|
||||
<field name="name">Set SEPA Direct Debit Mandates to Expired</field>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="model_id" ref="model_account_banking_mandate"/>
|
||||
<field name="active" eval="True" />
|
||||
<field name="model_id" ref="model_account_banking_mandate" />
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._sdd_mandate_set_state_to_expired()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -922,4 +922,4 @@
|
||||
<xs:simpleType name="TrueFalseIndicator">
|
||||
<xs:restriction base="xs:boolean"/>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
||||
</xs:schema>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="paperformat_euro_sepa_lowmargin" model="report.paperformat">
|
||||
<field name="name">European A4 low margin for SEPA</field>
|
||||
<field name="default" eval="True" />
|
||||
@@ -16,5 +15,4 @@
|
||||
<field name="header_spacing">0</field>
|
||||
<field name="dpi">80</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,49 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<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="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>
|
||||
|
||||
<record id="base.main_company" model="res.company">
|
||||
<field name="sepa_creditor_identifier">FR78ZZZ424242</field>
|
||||
</record>
|
||||
|
||||
<!-- Camptocamp -->
|
||||
<record id="res_partner_12_mandate" model="account.banking.mandate">
|
||||
<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>
|
||||
<field name="signature_date" eval="time.strftime('%Y-01-01')"/>
|
||||
<field name="state">valid</field>
|
||||
</record>
|
||||
|
||||
<record id="base.res_partner_12" model="res.partner">
|
||||
<field name="customer_payment_mode_id" ref="payment_mode_inbound_sepa_dd1"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Agrolait -->
|
||||
<record id="res_partner_2_mandate" model="account.banking.mandate">
|
||||
<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>
|
||||
<field name="signature_date" eval="time.strftime('%Y-%m-01')" />
|
||||
<field name="state">valid</field>
|
||||
</record>
|
||||
|
||||
<record id="base.res_partner_2" model="res.partner">
|
||||
<field name="customer_payment_mode_id" ref="payment_mode_inbound_sepa_dd1"/>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
<data noupdate="1">
|
||||
<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="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>
|
||||
<record id="base.main_company" model="res.company">
|
||||
<field name="sepa_creditor_identifier">FR78ZZZ424242</field>
|
||||
</record>
|
||||
<!-- Camptocamp -->
|
||||
<record id="res_partner_12_mandate" model="account.banking.mandate">
|
||||
<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>
|
||||
<field name="signature_date" eval="time.strftime('%Y-01-01')" />
|
||||
<field name="state">valid</field>
|
||||
</record>
|
||||
<record id="base.res_partner_12" model="res.partner">
|
||||
<field
|
||||
name="customer_payment_mode_id"
|
||||
ref="payment_mode_inbound_sepa_dd1"
|
||||
/>
|
||||
</record>
|
||||
<!-- Agrolait -->
|
||||
<record id="res_partner_2_mandate" model="account.banking.mandate">
|
||||
<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>
|
||||
<field name="signature_date" eval="time.strftime('%Y-%m-01')" />
|
||||
<field name="state">valid</field>
|
||||
</record>
|
||||
<record id="base.res_partner_2" model="res.partner">
|
||||
<field
|
||||
name="customer_payment_mode_id"
|
||||
ref="payment_mode_inbound_sepa_dd1"
|
||||
/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
from . import res_company
|
||||
from . import res_config
|
||||
from . import account_banking_mandate
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# Copyright 2013-2016 Akretion - Alexis de Lattre
|
||||
# Copyright 2014 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import models, fields, api, exceptions, _
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import _, api, exceptions, fields, models
|
||||
|
||||
NUMBER_OF_UNUSED_MONTHS_BEFORE_EXPIRY = 36
|
||||
|
||||
@@ -13,89 +15,97 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class AccountBankingMandate(models.Model):
|
||||
"""SEPA Direct Debit Mandate"""
|
||||
_inherit = 'account.banking.mandate'
|
||||
_rec_name = 'display_name'
|
||||
|
||||
format = fields.Selection(
|
||||
selection_add=[('sepa', 'Sepa Mandate')], default='sepa')
|
||||
_inherit = "account.banking.mandate"
|
||||
_rec_name = "display_name"
|
||||
|
||||
format = fields.Selection(selection_add=[("sepa", "Sepa Mandate")], default="sepa")
|
||||
type = fields.Selection(
|
||||
selection_add=[
|
||||
('recurrent', 'Recurrent'),
|
||||
('oneoff', 'One-Off')
|
||||
])
|
||||
selection_add=[("recurrent", "Recurrent"), ("oneoff", "One-Off")]
|
||||
)
|
||||
recurrent_sequence_type = fields.Selection(
|
||||
[('first', 'First'),
|
||||
('recurring', 'Recurring'),
|
||||
('final', 'Final')],
|
||||
string='Sequence Type for Next Debit', track_visibility='onchange',
|
||||
[("first", "First"), ("recurring", "Recurring"), ("final", "Final")],
|
||||
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")
|
||||
scheme = fields.Selection([
|
||||
('CORE', 'Basic (CORE)'),
|
||||
('B2B', 'Enterprise (B2B)')],
|
||||
string='Scheme', default="CORE", track_visibility='onchange')
|
||||
"One-Off mandates.",
|
||||
default="first",
|
||||
)
|
||||
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
|
||||
display_name = fields.Char(compute='_compute_display_name2', store=True)
|
||||
display_name = fields.Char(compute="_compute_display_name2", store=True)
|
||||
|
||||
@api.multi
|
||||
@api.constrains('type', 'recurrent_sequence_type')
|
||||
@api.constrains("type", "recurrent_sequence_type")
|
||||
def _check_recurring_type(self):
|
||||
for mandate in self:
|
||||
if (mandate.type == 'recurrent' and
|
||||
not mandate.recurrent_sequence_type):
|
||||
if mandate.type == "recurrent" and not mandate.recurrent_sequence_type:
|
||||
raise exceptions.Warning(
|
||||
_("The recurrent mandate '%s' must have a sequence type.")
|
||||
% mandate.unique_mandate_reference)
|
||||
% mandate.unique_mandate_reference
|
||||
)
|
||||
|
||||
@api.multi
|
||||
@api.depends('unique_mandate_reference', 'recurrent_sequence_type')
|
||||
@api.depends("unique_mandate_reference", "recurrent_sequence_type")
|
||||
def _compute_display_name2(self):
|
||||
for mandate in self:
|
||||
if mandate.format == 'sepa':
|
||||
mandate.display_name = '%s (%s)' % (
|
||||
mandate.unique_mandate_reference,
|
||||
mandate.recurrent_sequence_type)
|
||||
if mandate.format == "sepa":
|
||||
mandate.display_name = "{} ({})".format(
|
||||
mandate.unique_mandate_reference, mandate.recurrent_sequence_type,
|
||||
)
|
||||
else:
|
||||
mandate.display_name = mandate.unique_mandate_reference
|
||||
|
||||
@api.multi
|
||||
@api.onchange('partner_bank_id')
|
||||
@api.onchange("partner_bank_id")
|
||||
def mandate_partner_bank_change(self):
|
||||
for mandate in self:
|
||||
super(AccountBankingMandate, self).mandate_partner_bank_change()
|
||||
res = {}
|
||||
if (
|
||||
mandate.state == 'valid' and
|
||||
mandate.partner_bank_id and
|
||||
mandate.type == 'recurrent' and
|
||||
mandate.recurrent_sequence_type != 'first'
|
||||
mandate.state == "valid"
|
||||
and mandate.partner_bank_id
|
||||
and mandate.type == "recurrent"
|
||||
and mandate.recurrent_sequence_type != "first"
|
||||
):
|
||||
mandate.recurrent_sequence_type = 'first'
|
||||
res['warning'] = {
|
||||
'title': _('Mandate update'),
|
||||
'message': _("As you changed the bank account attached "
|
||||
"to this mandate, the 'Sequence Type' has "
|
||||
"been set back to 'First'."),
|
||||
mandate.recurrent_sequence_type = "first"
|
||||
res["warning"] = {
|
||||
"title": _("Mandate update"),
|
||||
"message": _(
|
||||
"As you changed the bank account attached "
|
||||
"to this mandate, the 'Sequence Type' has "
|
||||
"been set back to 'First'."
|
||||
),
|
||||
}
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _sdd_mandate_set_state_to_expired(self):
|
||||
logger.info('Searching for SDD Mandates that must be set to Expired')
|
||||
logger.info("Searching for SDD Mandates that must be set to Expired")
|
||||
expire_limit_date = datetime.today() + relativedelta(
|
||||
months=-NUMBER_OF_UNUSED_MONTHS_BEFORE_EXPIRY)
|
||||
expire_limit_date_str = expire_limit_date.strftime('%Y-%m-%d')
|
||||
months=-NUMBER_OF_UNUSED_MONTHS_BEFORE_EXPIRY
|
||||
)
|
||||
expire_limit_date_str = expire_limit_date.strftime("%Y-%m-%d")
|
||||
expired_mandates = self.search(
|
||||
['|',
|
||||
('last_debit_date', '=', False),
|
||||
('last_debit_date', '<=', expire_limit_date_str),
|
||||
('state', '=', 'valid'),
|
||||
('signature_date', '<=', expire_limit_date_str)])
|
||||
[
|
||||
"|",
|
||||
("last_debit_date", "=", False),
|
||||
("last_debit_date", "<=", expire_limit_date_str),
|
||||
("state", "=", "valid"),
|
||||
("signature_date", "<=", expire_limit_date_str),
|
||||
]
|
||||
)
|
||||
if expired_mandates:
|
||||
expired_mandates.write({'state': 'expired'})
|
||||
expired_mandates.write({"state": "expired"})
|
||||
logger.info(
|
||||
'The following SDD Mandate IDs has been set to expired: %s'
|
||||
% expired_mandates.ids)
|
||||
"The following SDD Mandate IDs has been set to expired: %s"
|
||||
% expired_mandates.ids
|
||||
)
|
||||
else:
|
||||
logger.info('0 SDD Mandates had to be set to Expired')
|
||||
logger.info("0 SDD Mandates had to be set to Expired")
|
||||
return True
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
# Copyright 2019 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models, _
|
||||
from odoo import _, api, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AccountPaymentLine(models.Model):
|
||||
_inherit = 'account.payment.line'
|
||||
_inherit = "account.payment.line"
|
||||
|
||||
@api.multi
|
||||
def draft2open_payment_line_check(self):
|
||||
res = super(AccountPaymentLine, self).draft2open_payment_line_check()
|
||||
sepa_dd_lines = self.filtered(
|
||||
lambda l: l.order_id.payment_method_id.code == 'sepa_direct_debit')
|
||||
lambda l: l.order_id.payment_method_id.code == "sepa_direct_debit"
|
||||
)
|
||||
sepa_dd_lines._check_sepa_direct_debit_ready()
|
||||
return res
|
||||
|
||||
@@ -25,24 +26,33 @@ class AccountPaymentLine(models.Model):
|
||||
"""
|
||||
for rec in self:
|
||||
if not rec.mandate_id:
|
||||
raise UserError(_(
|
||||
"Missing SEPA Direct Debit mandate on the line with "
|
||||
"partner {partner_name} (reference {reference}).").format(
|
||||
partner_name=rec.partner_id.name, reference=rec.name))
|
||||
if rec.mandate_id.state != 'valid':
|
||||
raise UserError(_(
|
||||
"The SEPA Direct Debit mandate with reference "
|
||||
"{mandate_ref} for partner {partner_name} has "
|
||||
"expired.").format(
|
||||
mandate_ref=rec.mandate_id.unique_mandate_reference,
|
||||
partner_name=rec.partner_id.name))
|
||||
if rec.mandate_id.type == 'oneoff' and \
|
||||
rec.mandate_id.last_debit_date:
|
||||
raise UserError(_(
|
||||
"The SEPA Direct Debit mandate with reference "
|
||||
"{mandate_ref} for partner {partner_name} has type set "
|
||||
"to 'One-Off' but has a last debit date set to "
|
||||
"{last_debit_date}. Therefore, it cannot be used.").format(
|
||||
mandate_ref=rec.mandate_id.unique_mandate_reference,
|
||||
partner_name=rec.partner_id.name,
|
||||
last_debit_date=rec.mandate_id.last_debit_date))
|
||||
raise UserError(
|
||||
_(
|
||||
"Missing SEPA Direct Debit mandate on the line with "
|
||||
"partner {partner_name} (reference {reference})."
|
||||
).format(partner_name=rec.partner_id.name, reference=rec.name)
|
||||
)
|
||||
if rec.mandate_id.state != "valid":
|
||||
raise UserError(
|
||||
_(
|
||||
"The SEPA Direct Debit mandate with reference "
|
||||
"{mandate_ref} for partner {partner_name} has "
|
||||
"expired."
|
||||
).format(
|
||||
mandate_ref=rec.mandate_id.unique_mandate_reference,
|
||||
partner_name=rec.partner_id.name,
|
||||
)
|
||||
)
|
||||
if rec.mandate_id.type == "oneoff" and rec.mandate_id.last_debit_date:
|
||||
raise UserError(
|
||||
_(
|
||||
"The SEPA Direct Debit mandate with reference "
|
||||
"{mandate_ref} for partner {partner_name} has type set "
|
||||
"to 'One-Off' but has a last debit date set to "
|
||||
"{last_debit_date}. Therefore, it cannot be used."
|
||||
).format(
|
||||
mandate_ref=rec.mandate_id.unique_mandate_reference,
|
||||
partner_name=rec.partner_id.name,
|
||||
last_debit_date=rec.mandate_id.last_debit_date,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
# Copyright 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
_inherit = "account.payment.method"
|
||||
|
||||
pain_version = fields.Selection(selection_add=[
|
||||
('pain.008.001.02', 'pain.008.001.02 (recommended for direct debit)'),
|
||||
('pain.008.001.03', 'pain.008.001.03'),
|
||||
('pain.008.001.04', 'pain.008.001.04'),
|
||||
('pain.008.003.02', 'pain.008.003.02 (direct debit in Germany)'),
|
||||
])
|
||||
pain_version = fields.Selection(
|
||||
selection_add=[
|
||||
("pain.008.001.02", "pain.008.001.02 (recommended for direct debit)"),
|
||||
("pain.008.001.03", "pain.008.001.03"),
|
||||
("pain.008.001.04", "pain.008.001.04"),
|
||||
("pain.008.003.02", "pain.008.003.02 (direct debit 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
|
||||
"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()
|
||||
|
||||
@@ -1,32 +1,37 @@
|
||||
# Copyright 2016 Tecnativa - Antonio Espinosa
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from .common import is_sepa_creditor_identifier_valid
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from .common import is_sepa_creditor_identifier_valid
|
||||
|
||||
|
||||
class AccountPaymentMode(models.Model):
|
||||
_inherit = 'account.payment.mode'
|
||||
_inherit = "account.payment.mode"
|
||||
|
||||
sepa_creditor_identifier = fields.Char(
|
||||
string='SEPA Creditor Identifier', size=35,
|
||||
string="SEPA Creditor Identifier",
|
||||
size=35,
|
||||
help="Enter the Creditor Identifier that has been attributed to your "
|
||||
"company to make SEPA Direct Debits. If not defined, "
|
||||
"SEPA Creditor Identifier from company will be used.\n"
|
||||
"This identifier is composed of :\n"
|
||||
"- your country ISO code (2 letters)\n"
|
||||
"- a 2-digits checkum\n"
|
||||
"- a 3-letters business code\n"
|
||||
"- a country-specific identifier")
|
||||
"company to make SEPA Direct Debits. If not defined, "
|
||||
"SEPA Creditor Identifier from company will be used.\n"
|
||||
"This identifier is composed of :\n"
|
||||
"- your country ISO code (2 letters)\n"
|
||||
"- a 2-digits checkum\n"
|
||||
"- a 3-letters business code\n"
|
||||
"- a country-specific identifier",
|
||||
)
|
||||
|
||||
@api.multi
|
||||
@api.constrains('sepa_creditor_identifier')
|
||||
@api.constrains("sepa_creditor_identifier")
|
||||
def _check_sepa_creditor_identifier(self):
|
||||
for payment_mode in self:
|
||||
if payment_mode.sepa_creditor_identifier:
|
||||
if not is_sepa_creditor_identifier_valid(
|
||||
payment_mode.sepa_creditor_identifier):
|
||||
payment_mode.sepa_creditor_identifier
|
||||
):
|
||||
raise ValidationError(
|
||||
_("The SEPA Creditor Identifier '%s' is invalid.")
|
||||
% payment_mode.sepa_creditor_identifier)
|
||||
% payment_mode.sepa_creditor_identifier
|
||||
)
|
||||
|
||||
@@ -2,64 +2,72 @@
|
||||
# Copyright 2018 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from odoo import _, api, exceptions, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from lxml import etree
|
||||
|
||||
|
||||
class AccountPaymentOrder(models.Model):
|
||||
_inherit = 'account.payment.order'
|
||||
_inherit = "account.payment.order"
|
||||
|
||||
@api.multi
|
||||
def generate_payment_file(self):
|
||||
"""Creates the SEPA Direct Debit file. That's the important code !"""
|
||||
self.ensure_one()
|
||||
if self.payment_method_id.code != 'sepa_direct_debit':
|
||||
if self.payment_method_id.code != "sepa_direct_debit":
|
||||
return super(AccountPaymentOrder, self).generate_payment_file()
|
||||
pain_flavor = self.payment_method_id.pain_version
|
||||
# We use pain_flavor.startswith('pain.008.001.xx')
|
||||
# to support country-specific extensions such as
|
||||
# pain.008.001.02.ch.01 (cf l10n_ch_sepa)
|
||||
if pain_flavor.startswith('pain.008.001.02'):
|
||||
bic_xml_tag = 'BIC'
|
||||
if pain_flavor.startswith("pain.008.001.02"):
|
||||
bic_xml_tag = "BIC"
|
||||
name_maxsize = 70
|
||||
root_xml_tag = 'CstmrDrctDbtInitn'
|
||||
elif pain_flavor.startswith('pain.008.003.02'):
|
||||
bic_xml_tag = 'BIC'
|
||||
root_xml_tag = "CstmrDrctDbtInitn"
|
||||
elif pain_flavor.startswith("pain.008.003.02"):
|
||||
bic_xml_tag = "BIC"
|
||||
name_maxsize = 70
|
||||
root_xml_tag = 'CstmrDrctDbtInitn'
|
||||
elif pain_flavor.startswith('pain.008.001.03'):
|
||||
bic_xml_tag = 'BICFI'
|
||||
root_xml_tag = "CstmrDrctDbtInitn"
|
||||
elif pain_flavor.startswith("pain.008.001.03"):
|
||||
bic_xml_tag = "BICFI"
|
||||
name_maxsize = 140
|
||||
root_xml_tag = 'CstmrDrctDbtInitn'
|
||||
elif pain_flavor.startswith('pain.008.001.04'):
|
||||
bic_xml_tag = 'BICFI'
|
||||
root_xml_tag = "CstmrDrctDbtInitn"
|
||||
elif pain_flavor.startswith("pain.008.001.04"):
|
||||
bic_xml_tag = "BICFI"
|
||||
name_maxsize = 140
|
||||
root_xml_tag = 'CstmrDrctDbtInitn'
|
||||
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)
|
||||
_(
|
||||
"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
|
||||
)
|
||||
pay_method = self.payment_mode_id.payment_method_id
|
||||
xsd_file = pay_method.get_xsd_file_path()
|
||||
gen_args = {
|
||||
'bic_xml_tag': bic_xml_tag,
|
||||
'name_maxsize': name_maxsize,
|
||||
'convert_to_ascii': pay_method.convert_to_ascii,
|
||||
'payment_method': 'DD',
|
||||
'file_prefix': 'sdd_',
|
||||
'pain_flavor': pain_flavor,
|
||||
'pain_xsd_file': xsd_file,
|
||||
"bic_xml_tag": bic_xml_tag,
|
||||
"name_maxsize": name_maxsize,
|
||||
"convert_to_ascii": pay_method.convert_to_ascii,
|
||||
"payment_method": "DD",
|
||||
"file_prefix": "sdd_",
|
||||
"pain_flavor": pain_flavor,
|
||||
"pain_xsd_file": xsd_file,
|
||||
}
|
||||
nsmap = self.generate_pain_nsmap()
|
||||
attrib = self.generate_pain_attrib()
|
||||
xml_root = etree.Element('Document', nsmap=nsmap, attrib=attrib)
|
||||
xml_root = etree.Element("Document", nsmap=nsmap, attrib=attrib)
|
||||
pain_root = etree.SubElement(xml_root, root_xml_tag)
|
||||
# A. Group header
|
||||
group_header, nb_of_transactions_a, control_sum_a = \
|
||||
self.generate_group_header_block(pain_root, gen_args)
|
||||
(
|
||||
group_header,
|
||||
nb_of_transactions_a,
|
||||
control_sum_a,
|
||||
) = self.generate_group_header_block(pain_root, gen_args)
|
||||
transactions_count_a = 0
|
||||
amount_control_sum_a = 0.0
|
||||
lines_per_group = {}
|
||||
@@ -70,22 +78,21 @@ class AccountPaymentOrder(models.Model):
|
||||
priority = line.priority
|
||||
categ_purpose = line.category_purpose
|
||||
scheme = line.mandate_id.scheme
|
||||
if line.mandate_id.type == 'oneoff':
|
||||
seq_type = 'OOFF'
|
||||
elif line.mandate_id.type == 'recurrent':
|
||||
seq_type_map = {
|
||||
'recurring': 'RCUR',
|
||||
'first': 'FRST',
|
||||
'final': 'FNAL',
|
||||
}
|
||||
if line.mandate_id.type == "oneoff":
|
||||
seq_type = "OOFF"
|
||||
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]
|
||||
else:
|
||||
raise exceptions.UserError(_(
|
||||
"Invalid mandate type in '%s'. Valid ones are 'Recurrent' "
|
||||
"or 'One-Off'"
|
||||
) % line.mandate_id.unique_mandate_reference)
|
||||
raise exceptions.UserError(
|
||||
_(
|
||||
"Invalid mandate type in '%s'. Valid ones are 'Recurrent' "
|
||||
"or 'One-Off'"
|
||||
)
|
||||
% line.mandate_id.unique_mandate_reference
|
||||
)
|
||||
# The field line.date is the requested payment date
|
||||
# taking into account the 'date_preferred' setting
|
||||
# cf account_banking_payment_export/models/account_payment.py
|
||||
@@ -96,128 +103,169 @@ class AccountPaymentOrder(models.Model):
|
||||
else:
|
||||
lines_per_group[key] = [line]
|
||||
|
||||
for (requested_date, priority, categ_purpose, sequence_type, scheme),\
|
||||
lines in list(lines_per_group.items()):
|
||||
for (
|
||||
(requested_date, priority, categ_purpose, sequence_type, scheme),
|
||||
lines,
|
||||
) in list(lines_per_group.items()):
|
||||
requested_date = fields.Date.to_string(requested_date)
|
||||
# B. Payment info
|
||||
payment_info, nb_of_transactions_b, control_sum_b = \
|
||||
self.generate_start_payment_info_block(
|
||||
pain_root,
|
||||
"self.name + '-' + "
|
||||
"sequence_type + '-' + requested_date.replace('-', '') "
|
||||
"+ '-' + priority + '-' + category_purpose",
|
||||
priority, scheme, categ_purpose,
|
||||
sequence_type, requested_date, {
|
||||
'self': self,
|
||||
'sequence_type': sequence_type,
|
||||
'priority': priority,
|
||||
'category_purpose': categ_purpose or 'NOcateg',
|
||||
'requested_date': requested_date,
|
||||
}, gen_args)
|
||||
(
|
||||
payment_info,
|
||||
nb_of_transactions_b,
|
||||
control_sum_b,
|
||||
) = self.generate_start_payment_info_block(
|
||||
pain_root,
|
||||
"self.name + '-' + "
|
||||
"sequence_type + '-' + requested_date.replace('-', '') "
|
||||
"+ '-' + priority + '-' + category_purpose",
|
||||
priority,
|
||||
scheme,
|
||||
categ_purpose,
|
||||
sequence_type,
|
||||
requested_date,
|
||||
{
|
||||
"self": self,
|
||||
"sequence_type": sequence_type,
|
||||
"priority": priority,
|
||||
"category_purpose": categ_purpose or "NOcateg",
|
||||
"requested_date": requested_date,
|
||||
},
|
||||
gen_args,
|
||||
)
|
||||
|
||||
self.generate_party_block(
|
||||
payment_info, 'Cdtr', 'B',
|
||||
self.company_partner_bank_id, gen_args)
|
||||
charge_bearer = etree.SubElement(payment_info, 'ChrgBr')
|
||||
payment_info, "Cdtr", "B", self.company_partner_bank_id, gen_args
|
||||
)
|
||||
charge_bearer = etree.SubElement(payment_info, "ChrgBr")
|
||||
if self.sepa:
|
||||
charge_bearer_text = 'SLEV'
|
||||
charge_bearer_text = "SLEV"
|
||||
else:
|
||||
charge_bearer_text = self.charge_bearer
|
||||
charge_bearer.text = charge_bearer_text
|
||||
creditor_scheme_identification = etree.SubElement(
|
||||
payment_info, 'CdtrSchmeId')
|
||||
payment_info, "CdtrSchmeId"
|
||||
)
|
||||
self.generate_creditor_scheme_identification(
|
||||
creditor_scheme_identification,
|
||||
'self.payment_mode_id.sepa_creditor_identifier or '
|
||||
'self.company_id.sepa_creditor_identifier',
|
||||
'SEPA Creditor Identifier', {'self': self}, 'SEPA', gen_args)
|
||||
"self.payment_mode_id.sepa_creditor_identifier or "
|
||||
"self.company_id.sepa_creditor_identifier",
|
||||
"SEPA Creditor Identifier",
|
||||
{"self": self},
|
||||
"SEPA",
|
||||
gen_args,
|
||||
)
|
||||
transactions_count_b = 0
|
||||
amount_control_sum_b = 0.0
|
||||
for line in lines:
|
||||
transactions_count_b += 1
|
||||
# C. Direct Debit Transaction Info
|
||||
dd_transaction_info = etree.SubElement(
|
||||
payment_info, 'DrctDbtTxInf')
|
||||
payment_identification = etree.SubElement(
|
||||
dd_transaction_info, 'PmtId')
|
||||
dd_transaction_info = etree.SubElement(payment_info, "DrctDbtTxInf")
|
||||
payment_identification = etree.SubElement(dd_transaction_info, "PmtId")
|
||||
instruction_identification = etree.SubElement(
|
||||
payment_identification, 'InstrId')
|
||||
payment_identification, "InstrId"
|
||||
)
|
||||
instruction_identification.text = self._prepare_field(
|
||||
'Instruction Identification', 'line.name',
|
||||
{'line': line}, 35, gen_args=gen_args)
|
||||
"Instruction Identification",
|
||||
"line.name",
|
||||
{"line": line},
|
||||
35,
|
||||
gen_args=gen_args,
|
||||
)
|
||||
end2end_identification = etree.SubElement(
|
||||
payment_identification, 'EndToEndId')
|
||||
payment_identification, "EndToEndId"
|
||||
)
|
||||
end2end_identification.text = self._prepare_field(
|
||||
'End to End Identification', 'line.name',
|
||||
{'line': line}, 35, gen_args=gen_args)
|
||||
"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)
|
||||
"Currency Code",
|
||||
"line.currency_id.name",
|
||||
{"line": line},
|
||||
3,
|
||||
gen_args=gen_args,
|
||||
)
|
||||
instructed_amount = etree.SubElement(
|
||||
dd_transaction_info, 'InstdAmt', Ccy=currency_name)
|
||||
instructed_amount.text = '%.2f' % line.amount_currency
|
||||
dd_transaction_info, "InstdAmt", Ccy=currency_name
|
||||
)
|
||||
instructed_amount.text = "%.2f" % line.amount_currency
|
||||
amount_control_sum_a += line.amount_currency
|
||||
amount_control_sum_b += line.amount_currency
|
||||
dd_transaction = etree.SubElement(
|
||||
dd_transaction_info, 'DrctDbtTx')
|
||||
mandate_related_info = etree.SubElement(
|
||||
dd_transaction, 'MndtRltdInf')
|
||||
dd_transaction = etree.SubElement(dd_transaction_info, "DrctDbtTx")
|
||||
mandate_related_info = etree.SubElement(dd_transaction, "MndtRltdInf")
|
||||
mandate_identification = etree.SubElement(
|
||||
mandate_related_info, 'MndtId')
|
||||
mandate_related_info, "MndtId"
|
||||
)
|
||||
mandate_identification.text = self._prepare_field(
|
||||
'Unique Mandate Reference',
|
||||
'line.mandate_id.unique_mandate_reference',
|
||||
{'line': line}, 35, gen_args=gen_args)
|
||||
"Unique Mandate Reference",
|
||||
"line.mandate_id.unique_mandate_reference",
|
||||
{"line": line},
|
||||
35,
|
||||
gen_args=gen_args,
|
||||
)
|
||||
mandate_signature_date = etree.SubElement(
|
||||
mandate_related_info, 'DtOfSgntr')
|
||||
mandate_related_info, "DtOfSgntr"
|
||||
)
|
||||
mandate_signature_date.text = self._prepare_field(
|
||||
'Mandate Signature Date',
|
||||
'signature_date',
|
||||
"Mandate Signature Date",
|
||||
"signature_date",
|
||||
{
|
||||
'line': line,
|
||||
'signature_date': fields.Date.to_string(
|
||||
line.mandate_id.signature_date,
|
||||
"line": line,
|
||||
"signature_date": fields.Date.to_string(
|
||||
line.mandate_id.signature_date
|
||||
),
|
||||
}, 10, gen_args=gen_args)
|
||||
if sequence_type == 'FRST' and line.mandate_id.last_debit_date:
|
||||
},
|
||||
10,
|
||||
gen_args=gen_args,
|
||||
)
|
||||
if sequence_type == "FRST" and line.mandate_id.last_debit_date:
|
||||
amendment_indicator = etree.SubElement(
|
||||
mandate_related_info, 'AmdmntInd')
|
||||
amendment_indicator.text = 'true'
|
||||
mandate_related_info, "AmdmntInd"
|
||||
)
|
||||
amendment_indicator.text = "true"
|
||||
amendment_info_details = etree.SubElement(
|
||||
mandate_related_info, 'AmdmntInfDtls')
|
||||
mandate_related_info, "AmdmntInfDtls"
|
||||
)
|
||||
ori_debtor_account = etree.SubElement(
|
||||
amendment_info_details, 'OrgnlDbtrAcct')
|
||||
ori_debtor_account_id = etree.SubElement(
|
||||
ori_debtor_account, 'Id')
|
||||
amendment_info_details, "OrgnlDbtrAcct"
|
||||
)
|
||||
ori_debtor_account_id = etree.SubElement(ori_debtor_account, "Id")
|
||||
ori_debtor_agent_other = etree.SubElement(
|
||||
ori_debtor_account_id, 'Othr')
|
||||
ori_debtor_account_id, "Othr"
|
||||
)
|
||||
ori_debtor_agent_other_id = etree.SubElement(
|
||||
ori_debtor_agent_other, 'Id')
|
||||
ori_debtor_agent_other_id.text = 'SMNDA'
|
||||
ori_debtor_agent_other, "Id"
|
||||
)
|
||||
ori_debtor_agent_other_id.text = "SMNDA"
|
||||
# Until 20/11/2016, SMNDA meant
|
||||
# "Same Mandate New Debtor Agent"
|
||||
# After 20/11/2016, SMNDA means
|
||||
# "Same Mandate New Debtor Account"
|
||||
|
||||
self.generate_party_block(
|
||||
dd_transaction_info, 'Dbtr', 'C',
|
||||
line.partner_bank_id, gen_args, line)
|
||||
dd_transaction_info,
|
||||
"Dbtr",
|
||||
"C",
|
||||
line.partner_bank_id,
|
||||
gen_args,
|
||||
line,
|
||||
)
|
||||
|
||||
if line.purpose:
|
||||
purpose = etree.SubElement(
|
||||
dd_transaction_info, 'Purp')
|
||||
etree.SubElement(purpose, 'Cd').text = line.purpose
|
||||
purpose = etree.SubElement(dd_transaction_info, "Purp")
|
||||
etree.SubElement(purpose, "Cd").text = line.purpose
|
||||
|
||||
self.generate_remittance_info_block(
|
||||
dd_transaction_info, line, gen_args)
|
||||
self.generate_remittance_info_block(dd_transaction_info, line, gen_args)
|
||||
|
||||
nb_of_transactions_b.text = str(transactions_count_b)
|
||||
control_sum_b.text = '%.2f' % amount_control_sum_b
|
||||
control_sum_b.text = "%.2f" % amount_control_sum_b
|
||||
nb_of_transactions_a.text = str(transactions_count_a)
|
||||
control_sum_a.text = '%.2f' % amount_control_sum_a
|
||||
control_sum_a.text = "%.2f" % amount_control_sum_a
|
||||
|
||||
return self.finalize_sepa_file_creation(
|
||||
xml_root, gen_args)
|
||||
return self.finalize_sepa_file_creation(xml_root, gen_args)
|
||||
|
||||
@api.multi
|
||||
def generated2uploaded(self):
|
||||
@@ -230,7 +278,7 @@ class AccountPaymentOrder(models.Model):
|
||||
# is generated BEFORE, which will allow the split
|
||||
# of the account move per sequence_type
|
||||
res = super(AccountPaymentOrder, self).generated2uploaded()
|
||||
abmo = self.env['account.banking.mandate']
|
||||
abmo = self.env["account.banking.mandate"]
|
||||
for order in self:
|
||||
to_expire_mandates = abmo.browse([])
|
||||
first_mandates = abmo.browse([])
|
||||
@@ -239,25 +287,25 @@ class AccountPaymentOrder(models.Model):
|
||||
if bline.mandate_id in all_mandates:
|
||||
continue
|
||||
all_mandates += bline.mandate_id
|
||||
if bline.mandate_id.type == 'oneoff':
|
||||
if bline.mandate_id.type == "oneoff":
|
||||
to_expire_mandates += bline.mandate_id
|
||||
elif bline.mandate_id.type == 'recurrent':
|
||||
elif bline.mandate_id.type == "recurrent":
|
||||
seq_type = bline.mandate_id.recurrent_sequence_type
|
||||
if seq_type == 'final':
|
||||
if seq_type == "final":
|
||||
to_expire_mandates += bline.mandate_id
|
||||
elif seq_type == 'first':
|
||||
elif seq_type == "first":
|
||||
first_mandates += bline.mandate_id
|
||||
all_mandates.write(
|
||||
{'last_debit_date': order.date_generated})
|
||||
to_expire_mandates.write({'state': 'expired'})
|
||||
first_mandates.write({
|
||||
'recurrent_sequence_type': 'recurring',
|
||||
})
|
||||
all_mandates.write({"last_debit_date": order.date_generated})
|
||||
to_expire_mandates.write({"state": "expired"})
|
||||
first_mandates.write({"recurrent_sequence_type": "recurring"})
|
||||
for first_mandate in first_mandates:
|
||||
first_mandate.message_post(body=_(
|
||||
"Automatically switched from <b>First</b> to "
|
||||
"<b>Recurring</b> when the debit order "
|
||||
"<a href=# data-oe-model=account.payment.order "
|
||||
"data-oe-id=%d>%s</a> has been marked as uploaded.")
|
||||
% (order.id, order.name))
|
||||
first_mandate.message_post(
|
||||
body=_(
|
||||
"Automatically switched from <b>First</b> to "
|
||||
"<b>Recurring</b> when the debit order "
|
||||
"<a href=# data-oe-model=account.payment.order "
|
||||
"data-oe-id=%d>%s</a> has been marked as uploaded."
|
||||
)
|
||||
% (order.id, order.name)
|
||||
)
|
||||
return res
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Copyright 2015-2016 Akretion - Alexis de Lattre
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models, api
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class BankPaymentLine(models.Model):
|
||||
_inherit = 'bank.payment.line'
|
||||
_inherit = "bank.payment.line"
|
||||
|
||||
@api.multi
|
||||
def move_line_offsetting_account_hashcode(self):
|
||||
@@ -16,7 +16,6 @@ class BankPaymentLine(models.Model):
|
||||
So we split the transfer move lines by mandate type, so easier
|
||||
reconciliation of the bank statement.
|
||||
"""
|
||||
hashcode = super(BankPaymentLine, self).\
|
||||
move_line_offsetting_account_hashcode()
|
||||
hashcode += '-' + str(self.mandate_id.recurrent_sequence_type)
|
||||
hashcode = super(BankPaymentLine, self).move_line_offsetting_account_hashcode()
|
||||
hashcode += "-" + str(self.mandate_id.recurrent_sequence_type)
|
||||
return hashcode
|
||||
|
||||
@@ -19,20 +19,17 @@ def is_sepa_creditor_identifier_valid(sepa_creditor_identifier):
|
||||
try:
|
||||
sci = str(sepa_creditor_identifier).lower()
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"SEPA Creditor ID should contain only ASCII characters.")
|
||||
logger.warning("SEPA Creditor ID should contain only ASCII characters.")
|
||||
return False
|
||||
if len(sci) < 9:
|
||||
return False
|
||||
before_replacement = sci[7:] + sci[0:2] + '00'
|
||||
logger.debug(
|
||||
"SEPA ID check before_replacement = %s" % before_replacement)
|
||||
after_replacement = ''
|
||||
before_replacement = sci[7:] + sci[0:2] + "00"
|
||||
logger.debug("SEPA ID check before_replacement = %s" % before_replacement)
|
||||
after_replacement = ""
|
||||
for char in before_replacement:
|
||||
if char.isalpha():
|
||||
after_replacement += str(ord(char) - 87)
|
||||
else:
|
||||
after_replacement += char
|
||||
logger.debug(
|
||||
"SEPA ID check after_replacement = %s" % after_replacement)
|
||||
logger.debug("SEPA ID check after_replacement = %s" % after_replacement)
|
||||
return int(sci[2:4]) == (98 - (int(after_replacement) % 97))
|
||||
|
||||
@@ -3,29 +3,34 @@
|
||||
# Copyright 2016 Tecnativa - Antonio Espinosa
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from .common import is_sepa_creditor_identifier_valid
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from .common import is_sepa_creditor_identifier_valid
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
_inherit = "res.company"
|
||||
|
||||
sepa_creditor_identifier = fields.Char(
|
||||
string='SEPA Creditor Identifier', size=35,
|
||||
string="SEPA Creditor Identifier",
|
||||
size=35,
|
||||
help="Enter the Creditor Identifier that has been attributed to your "
|
||||
"company to make SEPA Direct Debits. This identifier is composed "
|
||||
"of :\n- your country ISO code (2 letters)\n- a 2-digits "
|
||||
"checkum\n- a 3-letters business code\n- a country-specific "
|
||||
"identifier")
|
||||
"company to make SEPA Direct Debits. This identifier is composed "
|
||||
"of :\n- your country ISO code (2 letters)\n- a 2-digits "
|
||||
"checkum\n- a 3-letters business code\n- a country-specific "
|
||||
"identifier",
|
||||
)
|
||||
|
||||
@api.multi
|
||||
@api.constrains('sepa_creditor_identifier')
|
||||
@api.constrains("sepa_creditor_identifier")
|
||||
def _check_sepa_creditor_identifier(self):
|
||||
for company in self:
|
||||
if company.sepa_creditor_identifier:
|
||||
if not is_sepa_creditor_identifier_valid(
|
||||
company.sepa_creditor_identifier):
|
||||
company.sepa_creditor_identifier
|
||||
):
|
||||
raise ValidationError(
|
||||
_("The SEPA Creditor Identifier '%s' is invalid.")
|
||||
% company.sepa_creditor_identifier)
|
||||
% company.sepa_creditor_identifier
|
||||
)
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
# Copyright 2016 Akretion - Alexis de Lattre
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models, fields
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
sepa_creditor_identifier = fields.Char(
|
||||
related='company_id.sepa_creditor_identifier',
|
||||
readonly=False,
|
||||
related="company_id.sepa_creditor_identifier", readonly=False
|
||||
)
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
# Copyright 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, SUPERUSER_ID
|
||||
from odoo import SUPERUSER_ID, api
|
||||
|
||||
|
||||
def update_bank_journals(cr, registry):
|
||||
with api.Environment.manage():
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
ajo = env['account.journal']
|
||||
journals = ajo.search([('type', '=', 'bank')])
|
||||
sdd = env.ref(
|
||||
'account_banking_sepa_direct_debit.sepa_direct_debit')
|
||||
ajo = env["account.journal"]
|
||||
journals = ajo.search([("type", "=", "bank")])
|
||||
sdd = env.ref("account_banking_sepa_direct_debit.sepa_direct_debit")
|
||||
if sdd:
|
||||
journals.write({
|
||||
'inbound_payment_method_ids': [(4, sdd.id)],
|
||||
})
|
||||
journals.write({"inbound_payment_method_ids": [(4, sdd.id)]})
|
||||
return
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<template id="assets_common" name="account_banking_sepa_direct_debit pdf assets" inherit_id="web.report_assets_common">
|
||||
<template
|
||||
id="assets_common"
|
||||
name="account_banking_sepa_direct_debit pdf assets"
|
||||
inherit_id="web.report_assets_common"
|
||||
>
|
||||
<xpath expr="." position="inside">
|
||||
<link href="/account_banking_sepa_direct_debit/static/src/css/report.css" rel="stylesheet"/>
|
||||
<link
|
||||
href="/account_banking_sepa_direct_debit/static/src/css/report.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- QWeb Report -->
|
||||
<report
|
||||
id="report_sepa_direct_debit_mandate"
|
||||
@@ -15,11 +20,11 @@
|
||||
report_type="qweb-pdf"
|
||||
name="account_banking_sepa_direct_debit.sepa_direct_debit_mandate"
|
||||
file="account_banking_sepa_direct_debit.sepa_direct_debit_mandate"
|
||||
/>
|
||||
|
||||
/>
|
||||
<record id="report_sepa_direct_debit_mandate" model="ir.actions.report">
|
||||
<field name="paperformat_id"
|
||||
ref="account_banking_sepa_direct_debit.paperformat_euro_sepa_lowmargin"/>
|
||||
<field
|
||||
name="paperformat_id"
|
||||
ref="account_banking_sepa_direct_debit.paperformat_euro_sepa_lowmargin"
|
||||
/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -89,4 +89,4 @@
|
||||
y="109.17231"
|
||||
id="tspan2995"
|
||||
style="font-weight:bold;-inkscape-font-specification:Sans Bold">DEBIT</tspan></text>
|
||||
</svg>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@@ -1,27 +1,27 @@
|
||||
.sepa_direct_debit h4 {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.sepa_direct_debit .under-line{
|
||||
border-bottom:1px solid;
|
||||
.sepa_direct_debit .under-line {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.sepa_direct_debit input{
|
||||
.sepa_direct_debit input {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
.sepa_direct_debit .card{
|
||||
border:2px solid;
|
||||
.sepa_direct_debit .card {
|
||||
border: 2px solid;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.sepa_direct_debit .card-body{
|
||||
.sepa_direct_debit .card-body {
|
||||
padding: 2px 14px;
|
||||
}
|
||||
.sepa_direct_debit p{
|
||||
.sepa_direct_debit p {
|
||||
margin: 0 0 4px;
|
||||
font-size: 8px;
|
||||
}
|
||||
.sepa_direct_debit .native_lang{
|
||||
.sepa_direct_debit .native_lang {
|
||||
font-style: italic;
|
||||
}
|
||||
.sepa_direct_debit .native_lang_small{
|
||||
.sepa_direct_debit .native_lang_small {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
|
||||
from . import test_sdd
|
||||
from . import test_mandate
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
# Copyright 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import fields
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestMandate(TransactionCase):
|
||||
def test_contrains(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.mandate.recurrent_sequence_type = False
|
||||
self.mandate.type = 'recurrent'
|
||||
self.mandate.type = "recurrent"
|
||||
self.mandate._check_recurring_type()
|
||||
|
||||
def test_onchange_bank(self):
|
||||
self.mandate.write({
|
||||
'type': 'recurrent', 'recurrent_sequence_type': 'recurring'
|
||||
})
|
||||
self.mandate.write(
|
||||
{"type": "recurrent", "recurrent_sequence_type": "recurring"}
|
||||
)
|
||||
self.mandate.validate()
|
||||
self.mandate.partner_bank_id = self.env.ref(
|
||||
'account_payment_mode.res_partner_2_iban'
|
||||
"account_payment_mode.res_partner_2_iban"
|
||||
)
|
||||
self.mandate.mandate_partner_bank_change()
|
||||
self.assertEqual(self.mandate.recurrent_sequence_type, 'first')
|
||||
self.assertEqual(self.mandate.recurrent_sequence_type, "first")
|
||||
|
||||
def test_expire(self):
|
||||
self.mandate.signature_date = fields.Date.today() + relativedelta(
|
||||
months=-50)
|
||||
self.mandate.signature_date = fields.Date.today() + relativedelta(months=-50)
|
||||
self.mandate.validate()
|
||||
self.assertEqual(self.mandate.state, 'valid')
|
||||
self.env['account.banking.mandate']._sdd_mandate_set_state_to_expired()
|
||||
self.assertEqual(self.mandate.state, 'expired')
|
||||
self.assertEqual(self.mandate.state, "valid")
|
||||
self.env["account.banking.mandate"]._sdd_mandate_set_state_to_expired()
|
||||
self.assertEqual(self.mandate.state, "expired")
|
||||
|
||||
def setUp(self):
|
||||
res = super(TestMandate, self).setUp()
|
||||
self.partner = self.env.ref('base.res_partner_12')
|
||||
bank_account = self.env.ref('account_payment_mode.res_partner_12_iban')
|
||||
self.mandate = self.env['account.banking.mandate'].create({
|
||||
'partner_bank_id': bank_account.id,
|
||||
'format': 'sepa',
|
||||
'type': 'oneoff',
|
||||
'signature_date': '2015-01-01',
|
||||
})
|
||||
self.partner = self.env.ref("base.res_partner_12")
|
||||
bank_account = self.env.ref("account_payment_mode.res_partner_12_iban")
|
||||
self.mandate = self.env["account.banking.mandate"].create(
|
||||
{
|
||||
"partner_bank_id": bank_account.id,
|
||||
"format": "sepa",
|
||||
"type": "oneoff",
|
||||
"signature_date": "2015-01-01",
|
||||
}
|
||||
)
|
||||
return res
|
||||
|
||||
@@ -3,230 +3,253 @@
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
import base64
|
||||
import time
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from odoo.tests import common
|
||||
from odoo.tools import float_compare
|
||||
import time
|
||||
from lxml import etree
|
||||
|
||||
|
||||
class TestSDD(common.HttpCase):
|
||||
def setUp(self):
|
||||
super(TestSDD, self).setUp()
|
||||
self.company = self.env['res.company']
|
||||
self.account_model = self.env['account.account']
|
||||
self.move_model = self.env['account.move']
|
||||
self.journal_model = self.env['account.journal']
|
||||
self.payment_order_model = self.env['account.payment.order']
|
||||
self.payment_line_model = self.env['account.payment.line']
|
||||
self.mandate_model = self.env['account.banking.mandate']
|
||||
self.bank_line_model = self.env['bank.payment.line']
|
||||
self.partner_bank_model = self.env['res.partner.bank']
|
||||
self.attachment_model = self.env['ir.attachment']
|
||||
self.invoice_model = self.env['account.invoice']
|
||||
self.invoice_line_model = self.env['account.invoice.line']
|
||||
self.partner_agrolait = self.env.ref('base.res_partner_2')
|
||||
self.partner_c2c = self.env.ref('base.res_partner_12')
|
||||
self.eur_currency = self.env.ref('base.EUR')
|
||||
self.main_company = self.env['res.company'].create({
|
||||
'name': 'Test EUR company',
|
||||
'currency_id': self.eur_currency.id,
|
||||
'sepa_creditor_identifier': 'FR78ZZZ424242',
|
||||
})
|
||||
self.env.user.write({
|
||||
'company_ids': [(6, 0, self.main_company.ids)],
|
||||
'company_id': self.main_company.id,
|
||||
})
|
||||
self.company = self.env["res.company"]
|
||||
self.account_model = self.env["account.account"]
|
||||
self.move_model = self.env["account.move"]
|
||||
self.journal_model = self.env["account.journal"]
|
||||
self.payment_order_model = self.env["account.payment.order"]
|
||||
self.payment_line_model = self.env["account.payment.line"]
|
||||
self.mandate_model = self.env["account.banking.mandate"]
|
||||
self.bank_line_model = self.env["bank.payment.line"]
|
||||
self.partner_bank_model = self.env["res.partner.bank"]
|
||||
self.attachment_model = self.env["ir.attachment"]
|
||||
self.invoice_model = self.env["account.invoice"]
|
||||
self.invoice_line_model = self.env["account.invoice.line"]
|
||||
self.partner_agrolait = self.env.ref("base.res_partner_2")
|
||||
self.partner_c2c = self.env.ref("base.res_partner_12")
|
||||
self.eur_currency = self.env.ref("base.EUR")
|
||||
self.main_company = self.env["res.company"].create(
|
||||
{
|
||||
"name": "Test EUR company",
|
||||
"currency_id": self.eur_currency.id,
|
||||
"sepa_creditor_identifier": "FR78ZZZ424242",
|
||||
}
|
||||
)
|
||||
self.env.user.write(
|
||||
{
|
||||
"company_ids": [(6, 0, self.main_company.ids)],
|
||||
"company_id": self.main_company.id,
|
||||
}
|
||||
)
|
||||
self.partner_agrolait.company_id = self.main_company.id
|
||||
self.partner_c2c.company_id = self.main_company.id
|
||||
self.env.ref(
|
||||
'l10n_generic_coa.configurable_chart_template'
|
||||
"l10n_generic_coa.configurable_chart_template"
|
||||
).try_loading_for_current_company()
|
||||
self.account_revenue = self.account_model.search([
|
||||
('user_type_id', '=',
|
||||
self.env.ref(
|
||||
'account.data_account_type_revenue').id),
|
||||
('company_id', '=', self.main_company.id),
|
||||
], limit=1)
|
||||
self.account_receivable = self.account_model.search([
|
||||
('user_type_id', '=',
|
||||
self.env.ref('account.data_account_type_receivable').id),
|
||||
('company_id', '=', self.main_company.id),
|
||||
], limit=1)
|
||||
self.company_bank = self.env.ref(
|
||||
'account_payment_mode.main_company_iban'
|
||||
).copy({
|
||||
'company_id': self.main_company.id,
|
||||
'partner_id': self.main_company.partner_id.id,
|
||||
'bank_id': (
|
||||
self.env.ref('account_payment_mode.bank_la_banque_postale').id
|
||||
),
|
||||
})
|
||||
self.account_revenue = self.account_model.search(
|
||||
[
|
||||
(
|
||||
"user_type_id",
|
||||
"=",
|
||||
self.env.ref("account.data_account_type_revenue").id,
|
||||
),
|
||||
("company_id", "=", self.main_company.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
self.account_receivable = self.account_model.search(
|
||||
[
|
||||
(
|
||||
"user_type_id",
|
||||
"=",
|
||||
self.env.ref("account.data_account_type_receivable").id,
|
||||
),
|
||||
("company_id", "=", self.main_company.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
self.company_bank = self.env.ref("account_payment_mode.main_company_iban").copy(
|
||||
{
|
||||
"company_id": self.main_company.id,
|
||||
"partner_id": self.main_company.partner_id.id,
|
||||
"bank_id": (
|
||||
self.env.ref("account_payment_mode.bank_la_banque_postale").id
|
||||
),
|
||||
}
|
||||
)
|
||||
# create journal
|
||||
self.bank_journal = self.journal_model.create({
|
||||
'name': 'Company Bank journal',
|
||||
'type': 'bank',
|
||||
'code': 'BNKFC',
|
||||
'bank_account_id': self.company_bank.id,
|
||||
'bank_id': self.company_bank.bank_id.id,
|
||||
})
|
||||
self.bank_journal = self.journal_model.create(
|
||||
{
|
||||
"name": "Company Bank journal",
|
||||
"type": "bank",
|
||||
"code": "BNKFC",
|
||||
"bank_account_id": self.company_bank.id,
|
||||
"bank_id": self.company_bank.bank_id.id,
|
||||
}
|
||||
)
|
||||
# update payment mode
|
||||
self.payment_mode = self.env.ref(
|
||||
'account_banking_sepa_direct_debit.payment_mode_inbound_sepa_dd1'
|
||||
).copy({
|
||||
'company_id': self.main_company.id,
|
||||
})
|
||||
self.payment_mode.write({
|
||||
'bank_account_link': 'fixed',
|
||||
'fixed_journal_id': self.bank_journal.id,
|
||||
})
|
||||
"account_banking_sepa_direct_debit.payment_mode_inbound_sepa_dd1"
|
||||
).copy({"company_id": self.main_company.id})
|
||||
self.payment_mode.write(
|
||||
{"bank_account_link": "fixed", "fixed_journal_id": self.bank_journal.id}
|
||||
)
|
||||
# Copy partner bank accounts
|
||||
bank1 = self.env.ref('account_payment_mode.res_partner_12_iban').copy({
|
||||
'company_id': self.main_company.id,
|
||||
})
|
||||
bank1 = self.env.ref("account_payment_mode.res_partner_12_iban").copy(
|
||||
{"company_id": self.main_company.id}
|
||||
)
|
||||
self.mandate12 = self.env.ref(
|
||||
'account_banking_sepa_direct_debit.res_partner_12_mandate'
|
||||
).copy({
|
||||
'partner_bank_id': bank1.id,
|
||||
'company_id': self.main_company.id,
|
||||
'state': 'valid',
|
||||
'unique_mandate_reference': 'BMTEST12',
|
||||
})
|
||||
bank2 = self.env.ref('account_payment_mode.res_partner_2_iban').copy({
|
||||
'company_id': self.main_company.id,
|
||||
})
|
||||
"account_banking_sepa_direct_debit.res_partner_12_mandate"
|
||||
).copy(
|
||||
{
|
||||
"partner_bank_id": bank1.id,
|
||||
"company_id": self.main_company.id,
|
||||
"state": "valid",
|
||||
"unique_mandate_reference": "BMTEST12",
|
||||
}
|
||||
)
|
||||
bank2 = self.env.ref("account_payment_mode.res_partner_2_iban").copy(
|
||||
{"company_id": self.main_company.id}
|
||||
)
|
||||
self.mandate2 = self.env.ref(
|
||||
'account_banking_sepa_direct_debit.res_partner_2_mandate'
|
||||
).copy({
|
||||
'partner_bank_id': bank2.id,
|
||||
'company_id': self.main_company.id,
|
||||
'state': 'valid',
|
||||
'unique_mandate_reference': 'BMTEST2',
|
||||
})
|
||||
"account_banking_sepa_direct_debit.res_partner_2_mandate"
|
||||
).copy(
|
||||
{
|
||||
"partner_bank_id": bank2.id,
|
||||
"company_id": self.main_company.id,
|
||||
"state": "valid",
|
||||
"unique_mandate_reference": "BMTEST2",
|
||||
}
|
||||
)
|
||||
# Trigger the recompute of account type on res.partner.bank
|
||||
self.partner_bank_model.search([])._compute_acc_type()
|
||||
|
||||
def test_pain_001_02(self):
|
||||
self.payment_mode.payment_method_id.pain_version = 'pain.008.001.02'
|
||||
self.payment_mode.payment_method_id.pain_version = "pain.008.001.02"
|
||||
self.check_sdd()
|
||||
|
||||
def test_pain_003_02(self):
|
||||
self.payment_mode.payment_method_id.pain_version = 'pain.008.003.02'
|
||||
self.payment_mode.payment_method_id.pain_version = "pain.008.003.02"
|
||||
self.check_sdd()
|
||||
|
||||
def test_pain_001_03(self):
|
||||
self.payment_mode.payment_method_id.pain_version = 'pain.008.001.03'
|
||||
self.payment_mode.payment_method_id.pain_version = "pain.008.001.03"
|
||||
self.check_sdd()
|
||||
|
||||
def test_pain_001_04(self):
|
||||
self.payment_mode.payment_method_id.pain_version = 'pain.008.001.04'
|
||||
self.payment_mode.payment_method_id.pain_version = "pain.008.001.04"
|
||||
self.check_sdd()
|
||||
|
||||
def check_sdd(self):
|
||||
self.mandate2.recurrent_sequence_type = 'first'
|
||||
invoice1 = self.create_invoice(
|
||||
self.partner_agrolait.id, self.mandate2, 42.0,
|
||||
)
|
||||
self.mandate12.type = 'oneoff'
|
||||
invoice2 = self.create_invoice(
|
||||
self.partner_c2c.id, self.mandate12, 11.0,
|
||||
)
|
||||
self.mandate2.recurrent_sequence_type = "first"
|
||||
invoice1 = self.create_invoice(self.partner_agrolait.id, self.mandate2, 42.0)
|
||||
self.mandate12.type = "oneoff"
|
||||
invoice2 = self.create_invoice(self.partner_c2c.id, self.mandate12, 11.0)
|
||||
for inv in [invoice1, invoice2]:
|
||||
action = inv.create_account_payment_line()
|
||||
self.assertEqual(action['res_model'], 'account.payment.order')
|
||||
payment_order = self.payment_order_model.browse(action['res_id'])
|
||||
self.assertEqual(
|
||||
payment_order.payment_type, 'inbound')
|
||||
self.assertEqual(
|
||||
payment_order.payment_mode_id, self.payment_mode)
|
||||
self.assertEqual(
|
||||
payment_order.journal_id, self.bank_journal)
|
||||
self.assertEqual(action["res_model"], "account.payment.order")
|
||||
payment_order = self.payment_order_model.browse(action["res_id"])
|
||||
self.assertEqual(payment_order.payment_type, "inbound")
|
||||
self.assertEqual(payment_order.payment_mode_id, self.payment_mode)
|
||||
self.assertEqual(payment_order.journal_id, self.bank_journal)
|
||||
# Check payment line
|
||||
pay_lines = self.payment_line_model.search([
|
||||
('partner_id', '=', self.partner_agrolait.id),
|
||||
('order_id', '=', payment_order.id)])
|
||||
pay_lines = self.payment_line_model.search(
|
||||
[
|
||||
("partner_id", "=", self.partner_agrolait.id),
|
||||
("order_id", "=", payment_order.id),
|
||||
]
|
||||
)
|
||||
self.assertEqual(len(pay_lines), 1)
|
||||
agrolait_pay_line1 = pay_lines[0]
|
||||
accpre = self.env['decimal.precision'].precision_get('Account')
|
||||
accpre = self.env["decimal.precision"].precision_get("Account")
|
||||
self.assertEqual(agrolait_pay_line1.currency_id, self.eur_currency)
|
||||
self.assertEqual(agrolait_pay_line1.mandate_id, invoice1.mandate_id)
|
||||
self.assertEqual(
|
||||
agrolait_pay_line1.currency_id, self.eur_currency)
|
||||
agrolait_pay_line1.partner_bank_id, invoice1.mandate_id.partner_bank_id
|
||||
)
|
||||
self.assertEqual(
|
||||
agrolait_pay_line1.mandate_id, invoice1.mandate_id)
|
||||
self.assertEqual(
|
||||
agrolait_pay_line1.partner_bank_id,
|
||||
invoice1.mandate_id.partner_bank_id)
|
||||
self.assertEqual(float_compare(
|
||||
agrolait_pay_line1.amount_currency, 42, precision_digits=accpre),
|
||||
0)
|
||||
self.assertEqual(agrolait_pay_line1.communication_type, 'normal')
|
||||
float_compare(
|
||||
agrolait_pay_line1.amount_currency, 42, precision_digits=accpre
|
||||
),
|
||||
0,
|
||||
)
|
||||
self.assertEqual(agrolait_pay_line1.communication_type, "normal")
|
||||
self.assertEqual(agrolait_pay_line1.communication, invoice1.number)
|
||||
payment_order.draft2open()
|
||||
self.assertEqual(payment_order.state, 'open')
|
||||
self.assertEqual(payment_order.state, "open")
|
||||
self.assertEqual(payment_order.sepa, True)
|
||||
# Check bank payment line
|
||||
bank_lines = self.bank_line_model.search([
|
||||
('partner_id', '=', self.partner_agrolait.id)])
|
||||
bank_lines = self.bank_line_model.search(
|
||||
[("partner_id", "=", self.partner_agrolait.id)]
|
||||
)
|
||||
self.assertEqual(len(bank_lines), 1)
|
||||
agrolait_bank_line = bank_lines[0]
|
||||
self.assertEqual(agrolait_bank_line.currency_id, self.eur_currency)
|
||||
self.assertEqual(
|
||||
agrolait_bank_line.currency_id, self.eur_currency)
|
||||
self.assertEqual(float_compare(
|
||||
agrolait_bank_line.amount_currency, 42.0, precision_digits=accpre),
|
||||
0)
|
||||
self.assertEqual(agrolait_bank_line.communication_type, 'normal')
|
||||
float_compare(
|
||||
agrolait_bank_line.amount_currency, 42.0, precision_digits=accpre
|
||||
),
|
||||
0,
|
||||
)
|
||||
self.assertEqual(agrolait_bank_line.communication_type, "normal")
|
||||
self.assertEqual(agrolait_bank_line.communication, invoice1.number)
|
||||
self.assertEqual(agrolait_bank_line.mandate_id, invoice1.mandate_id)
|
||||
self.assertEqual(
|
||||
agrolait_bank_line.communication, invoice1.number)
|
||||
self.assertEqual(
|
||||
agrolait_bank_line.mandate_id, invoice1.mandate_id)
|
||||
self.assertEqual(
|
||||
agrolait_bank_line.partner_bank_id,
|
||||
invoice1.mandate_id.partner_bank_id)
|
||||
agrolait_bank_line.partner_bank_id, invoice1.mandate_id.partner_bank_id
|
||||
)
|
||||
action = payment_order.open2generated()
|
||||
self.assertEqual(payment_order.state, 'generated')
|
||||
self.assertEqual(action['res_model'], 'ir.attachment')
|
||||
attachment = self.attachment_model.browse(action['res_id'])
|
||||
self.assertEqual(attachment.datas_fname[-4:], '.xml')
|
||||
self.assertEqual(payment_order.state, "generated")
|
||||
self.assertEqual(action["res_model"], "ir.attachment")
|
||||
attachment = self.attachment_model.browse(action["res_id"])
|
||||
self.assertEqual(attachment.datas_fname[-4:], ".xml")
|
||||
xml_file = base64.b64decode(attachment.datas)
|
||||
xml_root = etree.fromstring(xml_file)
|
||||
namespaces = xml_root.nsmap
|
||||
namespaces['p'] = xml_root.nsmap[None]
|
||||
namespaces["p"] = xml_root.nsmap[None]
|
||||
namespaces.pop(None)
|
||||
pay_method_xpath = xml_root.xpath(
|
||||
'//p:PmtInf/p:PmtMtd', namespaces=namespaces)
|
||||
self.assertEqual(pay_method_xpath[0].text, 'DD')
|
||||
pay_method_xpath = xml_root.xpath("//p:PmtInf/p:PmtMtd", namespaces=namespaces)
|
||||
self.assertEqual(pay_method_xpath[0].text, "DD")
|
||||
sepa_xpath = xml_root.xpath(
|
||||
'//p:PmtInf/p:PmtTpInf/p:SvcLvl/p:Cd', namespaces=namespaces)
|
||||
self.assertEqual(sepa_xpath[0].text, 'SEPA')
|
||||
"//p:PmtInf/p:PmtTpInf/p:SvcLvl/p:Cd", namespaces=namespaces
|
||||
)
|
||||
self.assertEqual(sepa_xpath[0].text, "SEPA")
|
||||
debtor_acc_xpath = xml_root.xpath(
|
||||
'//p:PmtInf/p:CdtrAcct/p:Id/p:IBAN', namespaces=namespaces)
|
||||
"//p:PmtInf/p:CdtrAcct/p:Id/p:IBAN", namespaces=namespaces
|
||||
)
|
||||
self.assertEqual(
|
||||
debtor_acc_xpath[0].text,
|
||||
payment_order.company_partner_bank_id.sanitized_acc_number)
|
||||
payment_order.company_partner_bank_id.sanitized_acc_number,
|
||||
)
|
||||
payment_order.generated2uploaded()
|
||||
self.assertEqual(payment_order.state, 'uploaded')
|
||||
self.assertEqual(payment_order.state, "uploaded")
|
||||
for inv in [invoice1, invoice2]:
|
||||
self.assertEqual(inv.state, 'paid')
|
||||
self.assertEqual(self.mandate2.recurrent_sequence_type, 'recurring')
|
||||
self.assertEqual(inv.state, "paid")
|
||||
self.assertEqual(self.mandate2.recurrent_sequence_type, "recurring")
|
||||
return
|
||||
|
||||
def create_invoice(
|
||||
self, partner_id, mandate, price_unit, type='out_invoice'):
|
||||
invoice = self.invoice_model.create({
|
||||
'partner_id': partner_id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': self.env.ref('base.EUR').id,
|
||||
'name': 'test',
|
||||
'account_id': self.account_receivable.id,
|
||||
'type': type,
|
||||
'date_invoice': time.strftime('%Y-%m-%d'),
|
||||
'payment_mode_id': self.payment_mode.id,
|
||||
'mandate_id': mandate.id,
|
||||
})
|
||||
self.invoice_line_model.create({
|
||||
'invoice_id': invoice.id,
|
||||
'price_unit': price_unit,
|
||||
'quantity': 1,
|
||||
'name': 'Great service',
|
||||
'account_id': self.account_revenue.id,
|
||||
})
|
||||
def create_invoice(self, partner_id, mandate, price_unit, type="out_invoice"):
|
||||
invoice = self.invoice_model.create(
|
||||
{
|
||||
"partner_id": partner_id,
|
||||
"reference_type": "none",
|
||||
"currency_id": self.env.ref("base.EUR").id,
|
||||
"name": "test",
|
||||
"account_id": self.account_receivable.id,
|
||||
"type": type,
|
||||
"date_invoice": time.strftime("%Y-%m-%d"),
|
||||
"payment_mode_id": self.payment_mode.id,
|
||||
"mandate_id": mandate.id,
|
||||
}
|
||||
)
|
||||
self.invoice_line_model.create(
|
||||
{
|
||||
"invoice_id": invoice.id,
|
||||
"price_unit": price_unit,
|
||||
"quantity": 1,
|
||||
"name": "Great service",
|
||||
"account_id": self.account_revenue.id,
|
||||
}
|
||||
)
|
||||
invoice.action_invoice_open()
|
||||
return invoice
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
© 2013-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
@@ -7,58 +7,76 @@
|
||||
Customize all mandate views (and actions) to fit SEPA mandate style
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
|
||||
<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"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="after">
|
||||
<field name="scheme" attrs="{'invisible': [('format', '!=', 'sepa')],
|
||||
'required': [('format', '=', 'sepa')]}"/>
|
||||
<field name="type" attrs="{'invisible': [('format', '!=', 'sepa')],
|
||||
'required': [('format', '=', 'sepa')]}"/>
|
||||
<field name="recurrent_sequence_type"
|
||||
attrs="{'invisible': ['|', ('type', '=', 'oneoff'), ('format', '!=', 'sepa')],
|
||||
'required': [('type', '=', 'recurrent')]}"/>
|
||||
<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" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="after">
|
||||
<field
|
||||
name="scheme"
|
||||
attrs="{'invisible': [('format', '!=', 'sepa')],
|
||||
'required': [('format', '=', 'sepa')]}"
|
||||
/>
|
||||
<field
|
||||
name="type"
|
||||
attrs="{'invisible': [('format', '!=', 'sepa')],
|
||||
'required': [('format', '=', 'sepa')]}"
|
||||
/>
|
||||
<field
|
||||
name="recurrent_sequence_type"
|
||||
attrs="{'invisible': ['|', ('type', '=', 'oneoff'), ('format', '!=', 'sepa')],
|
||||
'required': [('type', '=', 'recurrent')]}"
|
||||
/>
|
||||
</field>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<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"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="unique_mandate_reference" position="after">
|
||||
<field name="scheme"/>
|
||||
<field name="type" string="Type"/>
|
||||
<field name="recurrent_sequence_type" string="Sequence Type"/>
|
||||
</record>
|
||||
<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" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="unique_mandate_reference" position="after">
|
||||
<field name="scheme" />
|
||||
<field name="type" string="Type" />
|
||||
<field name="recurrent_sequence_type" string="Sequence Type" />
|
||||
</field>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<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"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="expired" position="after">
|
||||
<filter name="oneoff" string="One-Off" domain="[('type', '=', 'oneoff')]" />
|
||||
<filter name="recurrent" string="Recurrent" domain="[('type', '=', 'recurrent')]" />
|
||||
</filter>
|
||||
<filter name="signature_date_groupby" position="before">
|
||||
<filter name="type_groupby" string="Type"
|
||||
context="{'group_by': 'type'}"/>
|
||||
<filter name="scheme_groupby" string="Scheme"
|
||||
context="{'group_by': 'scheme'}"/>
|
||||
<filter name="recurrent_sequence_type_groupby"
|
||||
string="Sequence Type"
|
||||
context="{'group_by': 'recurrent_sequence_type'}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</record>
|
||||
<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" />
|
||||
<field name="arch" type="xml">
|
||||
<filter name="expired" position="after">
|
||||
<filter
|
||||
name="oneoff"
|
||||
string="One-Off"
|
||||
domain="[('type', '=', 'oneoff')]"
|
||||
/>
|
||||
<filter
|
||||
name="recurrent"
|
||||
string="Recurrent"
|
||||
domain="[('type', '=', 'recurrent')]"
|
||||
/>
|
||||
</filter>
|
||||
<filter name="signature_date_groupby" position="before">
|
||||
<filter
|
||||
name="type_groupby"
|
||||
string="Type"
|
||||
context="{'group_by': 'type'}"
|
||||
/>
|
||||
<filter
|
||||
name="scheme_groupby"
|
||||
string="Scheme"
|
||||
context="{'group_by': 'scheme'}"
|
||||
/>
|
||||
<filter
|
||||
name="recurrent_sequence_type_groupby"
|
||||
string="Sequence Type"
|
||||
context="{'group_by': 'recurrent_sequence_type'}"
|
||||
/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2015-2017 Tecnativa
|
||||
Copyright 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -->
|
||||
<odoo>
|
||||
|
||||
|
||||
<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="main" position="inside">
|
||||
<field name="sepa_creditor_identifier" groups="account_banking_pain_base.group_pain_multiple_identifier"/>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<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="main" position="inside">
|
||||
<field
|
||||
name="sepa_creditor_identifier"
|
||||
groups="account_banking_pain_base.group_pain_multiple_identifier"
|
||||
/>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<template id="sepa_direct_debit_mandate_document">
|
||||
<t t-set="doc" t-value="doc.with_context({'lang':doc.partner_id.lang})" />
|
||||
<t t-set="is_not_english" t-value="doc.partner_id.lang and doc.partner_id.lang[:2] != 'en'" />
|
||||
<t
|
||||
t-set="is_not_english"
|
||||
t-value="doc.partner_id.lang and doc.partner_id.lang[:2] != 'en'"
|
||||
/>
|
||||
<div class="page sepa_direct_debit">
|
||||
<div class="row mt0">
|
||||
<div class="col-12 text-center">
|
||||
<img t-if="doc.company_id.logo" t-att-src="image_data_uri(doc.company_id.logo)" style="max-height: 45px;" class="float-left"/>
|
||||
<img
|
||||
t-if="doc.company_id.logo"
|
||||
t-att-src="image_data_uri(doc.company_id.logo)"
|
||||
style="max-height: 45px;"
|
||||
class="float-left"
|
||||
/>
|
||||
<h4 t-if="doc.scheme != 'B2B'">
|
||||
<strong>Sepa Direct Debit Mandate</strong>
|
||||
<t t-if="is_not_english">
|
||||
<br/><span class="native_lang native_lang_small" t-esc="'Sepa Direct Debit Mandate'"/>
|
||||
<br />
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'Sepa Direct Debit Mandate'"
|
||||
/>
|
||||
</t>
|
||||
</h4>
|
||||
<h4 t-if="doc.scheme == 'B2B'">
|
||||
<strong>Sepa Business-To-Business Direct debit Mandate</strong>
|
||||
<t t-if="is_not_english">
|
||||
<br/><span class="native_lang native_lang_small" t-esc="'Sepa Business-To-Business Direct debit Mandate'"/>
|
||||
<br />
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'Sepa Business-To-Business Direct debit Mandate'"
|
||||
/>
|
||||
</t>
|
||||
</h4>
|
||||
</div>
|
||||
@@ -28,71 +43,128 @@
|
||||
<div class="col-12 text-right" style="font-size:8px;">
|
||||
To be completed by the creditor
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang" t-esc="'(To be completed by the creditor)'"/>
|
||||
<span
|
||||
class="native_lang"
|
||||
t-esc="'(To be completed by the creditor)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="col-12">
|
||||
<em>Mandate Reference:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Mandate Reference)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Mandate Reference)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.unique_mandate_reference"/></div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.unique_mandate_reference" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Identifier:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Identifier)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Identifier)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.company_id.sepa_creditor_identifier"/></div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span
|
||||
t-field="doc.company_id.sepa_creditor_identifier"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Creditor's Name:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Creditor\'s Name)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Creditor\'s Name)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.company_id.partner_id.name"/></div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.company_id.partner_id.name" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Address:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Address)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Address)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.company_id.partner_id.street"/></div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.company_id.partner_id.street" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Postal Code - City - Town:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Postal Code - City - Town)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Postal Code - City - Town)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt8">
|
||||
<span t-field="doc.company_id.partner_id.zip"/> -
|
||||
<span t-field="doc.company_id.partner_id.city"/> -
|
||||
<span t-field="doc.company_id.partner_id.state_id"/>
|
||||
<span t-field="doc.company_id.partner_id.zip" /> -
|
||||
<span t-field="doc.company_id.partner_id.city" /> -
|
||||
<span t-field="doc.company_id.partner_id.state_id" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Country:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Country)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Country)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.company_id.partner_id.country_id"/></div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.company_id.partner_id.country_id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt0">
|
||||
<div class="col-12">
|
||||
<p>By signing this mandate form, you authorise (A) <strong t-if="doc.scheme == 'B2B'"> <span t-field="doc.company_id.name"/> </strong>
|
||||
<p>By signing this mandate form, you authorise (A) <strong
|
||||
t-if="doc.scheme == 'B2B'"
|
||||
>
|
||||
<span t-field="doc.company_id.name" />
|
||||
</strong>
|
||||
to send instructions to your bank to debit your account and (B) your bank to
|
||||
debit your account in accordance with the instructions from <strong t-if="doc.scheme == 'B2B'"> <span t-field="doc.company_id.name"/> </strong>.
|
||||
debit your account in accordance with the instructions from <strong
|
||||
t-if="doc.scheme == 'B2B'"
|
||||
>
|
||||
<span t-field="doc.company_id.name" />
|
||||
</strong>.
|
||||
<t t-if="is_not_english">
|
||||
<br/>
|
||||
<span class="native_lang" t-esc="'(By signing this mandate form, you authorise (A) '"/>
|
||||
<t t-if="doc.scheme == 'B2B'"> <span class="native_lang" t-field="doc.company_id.name"/> </t>
|
||||
<span class="native_lang" t-esc="' to send instructions to your bank to debit your account and (B) your bank to debit your account in accordance with the instructions from '"/>
|
||||
<t t-if="doc.scheme == 'B2B'"> <span class="native_lang" t-field="doc.company_id.name"/> </t>.)
|
||||
<br />
|
||||
<span
|
||||
class="native_lang"
|
||||
t-esc="'(By signing this mandate form, you authorise (A) '"
|
||||
/>
|
||||
<t t-if="doc.scheme == 'B2B'">
|
||||
<span
|
||||
class="native_lang"
|
||||
t-field="doc.company_id.name"
|
||||
/>
|
||||
</t>
|
||||
<span
|
||||
class="native_lang"
|
||||
t-esc="' to send instructions to your bank to debit your account and (B) your bank to debit your account in accordance with the instructions from '"
|
||||
/>
|
||||
<t t-if="doc.scheme == 'B2B'">
|
||||
<span
|
||||
class="native_lang"
|
||||
t-field="doc.company_id.name"
|
||||
/>
|
||||
</t>.)
|
||||
</t>
|
||||
</p>
|
||||
</div>
|
||||
@@ -105,7 +177,11 @@
|
||||
been debited, but you are entitled to request your bank
|
||||
not to debit your account up until the day on which the payment is due.
|
||||
<t t-if="is_not_english">
|
||||
<br/><span class="native_lang" t-esc="'(This mandate is only intended for business-to-business transactions. You are not entitled to a refund from your bank after your account has been debited, but you are entitled to request your bank not to debit your account up until the day on which the payment is due.)'"/>
|
||||
<br />
|
||||
<span
|
||||
class="native_lang"
|
||||
t-esc="'(This mandate is only intended for business-to-business transactions. You are not entitled to a refund from your bank after your account has been debited, but you are entitled to request your bank not to debit your account up until the day on which the payment is due.)'"
|
||||
/>
|
||||
</t>
|
||||
</p>
|
||||
<p t-if="doc.scheme != 'B2B'">
|
||||
@@ -114,7 +190,11 @@
|
||||
with your bank.
|
||||
A refund must be claimed within 8 weeks starting from the date on which your account was debited.
|
||||
<t t-if="is_not_english">
|
||||
<br/><span class="native_lang" t-esc="'(As part of your rights, you are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited.)'"/>
|
||||
<br />
|
||||
<span
|
||||
class="native_lang"
|
||||
t-esc="'(As part of your rights, you are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited.)'"
|
||||
/>
|
||||
</t>
|
||||
</p>
|
||||
</div>
|
||||
@@ -125,91 +205,143 @@
|
||||
<div class="col-12 text-right" style="font-size:8px;">
|
||||
To be completed by the debtor
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang" t-esc="'(To be completed by the debtor)'"/>
|
||||
<span
|
||||
class="native_lang"
|
||||
t-esc="'(To be completed by the debtor)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="col-12">
|
||||
<em>Debtor's Name:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Debtor\'s Name)'"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.partner_id"/></div>
|
||||
<div class="col-12">
|
||||
<em>Address of the Debtor:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Address of the Debtor)'"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.partner_id.street"/></div>
|
||||
<div class="col-12">
|
||||
<em>Postal Code - City - Town:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Postal Code - City - Town)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Debtor\'s Name)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.partner_id.zip"/> -
|
||||
<span t-field="doc.partner_id.city"/> -
|
||||
<span t-field="doc.partner_id.state_id"/>
|
||||
<span t-field="doc.partner_id" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Address of the Debtor:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Address of the Debtor)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.partner_id.street" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Postal Code - City - Town:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Postal Code - City - Town)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.partner_id.zip" /> -
|
||||
<span t-field="doc.partner_id.city" /> -
|
||||
<span t-field="doc.partner_id.state_id" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Country of the debtor:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Country of the debtor)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Country of the debtor)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.partner_id.country_id"/></div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.partner_id.country_id" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Swift BIC (up to 8 or 11 characteres):</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Swift BIC, up to 8 or 11 characteres)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Swift BIC, up to 8 or 11 characteres)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.partner_bank_id.bank_bic"/></div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.partner_bank_id.bank_bic" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Account Number - IBAN:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Account Number - IBAN)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Account Number - IBAN)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt4"><span t-field="doc.partner_bank_id.acc_number"/></div>
|
||||
<div class="col-10 offset-2 under-line mt4">
|
||||
<span t-field="doc.partner_bank_id.acc_number" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Type of payment:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Type of payment)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Type of payment)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 mt4">
|
||||
<input type="checkbox" t-att-checked="doc.type=='recurrent' or None">
|
||||
<input
|
||||
type="checkbox"
|
||||
t-att-checked="doc.type=='recurrent' or None"
|
||||
>
|
||||
Recurrent
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Recurrent)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Recurrent)'"
|
||||
/>
|
||||
</t>
|
||||
</input>
|
||||
-
|
||||
<input type="checkbox" t-att-checked="doc.type=='oneoff' or None">
|
||||
<input
|
||||
type="checkbox"
|
||||
t-att-checked="doc.type=='oneoff' or None"
|
||||
>
|
||||
One-Off
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(One-Off)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(One-Off)'"
|
||||
/>
|
||||
</t>
|
||||
</input>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<em>Date - Location:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Date - Location)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Date - Location)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt16"/>
|
||||
<div class="col-10 offset-2 under-line mt16" />
|
||||
<div class="col-12">
|
||||
<em>Signature of the debtor:</em>
|
||||
<t t-if="is_not_english">
|
||||
<span class="native_lang native_lang_small" t-esc="'(Signature of the debtor)'"/>
|
||||
<span
|
||||
class="native_lang native_lang_small"
|
||||
t-esc="'(Signature of the debtor)'"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-10 offset-2 under-line mt16"/>
|
||||
<div class="col-10 offset-2 under-line mt16" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -219,7 +351,11 @@
|
||||
<p t-if="doc.scheme != 'B2B'">
|
||||
ALL GAPS ARE MANDATORY. ONCE THIS MANDATE HAS BEEN SIGNED MUST BE SENT TO CREDITOR FOR STORAGE.
|
||||
<t t-if="is_not_english">
|
||||
<br/><span class="native_lang" t-esc="'(ALL GAPS ARE MANDATORY. ONCE THIS MANDATE HAS BEEN SIGNED MUST BE SENT TO CREDITOR FOR STORAGE.)'"/>
|
||||
<br />
|
||||
<span
|
||||
class="native_lang"
|
||||
t-esc="'(ALL GAPS ARE MANDATORY. ONCE THIS MANDATE HAS BEEN SIGNED MUST BE SENT TO CREDITOR FOR STORAGE.)'"
|
||||
/>
|
||||
</t>
|
||||
</p>
|
||||
<p t-if="doc.scheme == 'B2B'">
|
||||
@@ -227,22 +363,27 @@
|
||||
NEVERTHELESS, THE BANK OF DEBTOR REQUIRES DEBTOR’S AUTHORIZATION BEFORE DEBITING B2B DIRECT DEBITS IN THE ACCOUNT.
|
||||
THE DEBTOR WILL BE ABLE TO MANAGE THE MENTIONED AUTHORIZATION THROUGH THE MEANS PROVIDED BY HIS BANK.
|
||||
<t t-if="is_not_english">
|
||||
<br/><span class="native_lang" t-esc="'(ALL GAPS ARE MANDATORY. ONCE THIS MANDATE HAS BEEN SIGNED MUST BE SENT TO CREDITOR FOR STORAGE. NEVERTHELESS, THE BANK OF DEBTOR REQUIRES DEBTOR’S AUTHORIZATION BEFORE DEBITING B2B DIRECT DEBITS IN THE ACCOUNT. THE DEBTOR WILL BE ABLE TO MANAGE THE MENTIONED AUTHORIZATION THROUGH THE MEANS PROVIDED BY HIS BANK.)'"/>
|
||||
<br />
|
||||
<span
|
||||
class="native_lang"
|
||||
t-esc="'(ALL GAPS ARE MANDATORY. ONCE THIS MANDATE HAS BEEN SIGNED MUST BE SENT TO CREDITOR FOR STORAGE. NEVERTHELESS, THE BANK OF DEBTOR REQUIRES DEBTOR’S AUTHORIZATION BEFORE DEBITING B2B DIRECT DEBITS IN THE ACCOUNT. THE DEBTOR WILL BE ABLE TO MANAGE THE MENTIONED AUTHORIZATION THROUGH THE MEANS PROVIDED BY HIS BANK.)'"
|
||||
/>
|
||||
</t>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="sepa_direct_debit_mandate">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
<t t-call="web.basic_layout">
|
||||
<t t-call="account_banking_sepa_direct_debit.sepa_direct_debit_mandate_document" t-lang="doc.partner_id.lang"/>
|
||||
<t
|
||||
t-call="account_banking_sepa_direct_debit.sepa_direct_debit_mandate_document"
|
||||
t-lang="doc.partner_id.lang"
|
||||
/>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
|
||||
<record id="view_account_config_settings" model="ir.ui.view">
|
||||
<field name="name">sepa_direct_debit.account_config_settings.form
|
||||
</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id"
|
||||
ref="account_banking_pain_base.view_account_config_settings"/>
|
||||
<field
|
||||
name="inherit_id"
|
||||
ref="account_banking_pain_base.view_account_config_settings"
|
||||
/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@id='pain']/div/div/div" position="inside">
|
||||
<div class="row mt16">
|
||||
<label for="sepa_creditor_identifier"
|
||||
class="col-md-3 o_light_label"/>
|
||||
<field name="sepa_creditor_identifier"
|
||||
placeholder="Write the ICS of your company"/>
|
||||
<label
|
||||
for="sepa_creditor_identifier"
|
||||
class="col-md-3 o_light_label"
|
||||
/>
|
||||
<field
|
||||
name="sepa_creditor_identifier"
|
||||
placeholder="Write the ICS of your company"
|
||||
/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user