135 lines
5.4 KiB
Python
135 lines
5.4 KiB
Python
from textwrap import dedent
|
|
from uuid import uuid4
|
|
|
|
from odoo import models, fields, api, _
|
|
from odoo.exceptions import ValidationError
|
|
|
|
ISO20022_CHARGE_BEARER_SELECTION = [
|
|
('CRED', "Creditor"),
|
|
('DEBT', "Debtor"),
|
|
('SHAR', "Shared"),
|
|
('SLEV', "Service Level"),
|
|
]
|
|
ISO20022_PRIORITY_SELECTION = [
|
|
('NORM', 'NORM - Normal'),
|
|
('HIGH', 'HIGH - High priority'),
|
|
('URGP', 'URGP - Critical urgency'),
|
|
('SVDA', 'SVDA - Same value date'),
|
|
]
|
|
ISO20022_PRIORITY_HELP = dedent('''\
|
|
• NORM: Standard processing time.
|
|
• HIGH: High priority payment.
|
|
• URGP: Critical, requires immediate processing.
|
|
• SVDA: Payments must settle on same day as submission.'''
|
|
)
|
|
|
|
|
|
class AccountPayment(models.Model):
|
|
_inherit = "account.payment"
|
|
|
|
end_to_end_uuid = fields.Char(
|
|
string='End to End ID',
|
|
compute='_compute_end_to_end_uuid',
|
|
store=True,
|
|
index=True,
|
|
help='Unique end-to-end assigned by the initiating party',
|
|
)
|
|
sepa_pain_version = fields.Selection(related='journal_id.sepa_pain_version')
|
|
iso20022_uetr = fields.Char(
|
|
string='UETR',
|
|
compute='_compute_iso20022_uetr',
|
|
store=True,
|
|
help='Unique end-to-end transaction reference',
|
|
)
|
|
iso20022_priority = fields.Selection(
|
|
string='Priority',
|
|
selection=ISO20022_PRIORITY_SELECTION,
|
|
compute='_compute_payment_method_priority',
|
|
store=True, readonly=False,
|
|
help=ISO20022_PRIORITY_HELP,
|
|
)
|
|
payment_method_is_iso20022 = fields.Boolean(related='payment_method_line_id.payment_method_id.is_iso20022')
|
|
iso20022_charge_bearer = fields.Selection(
|
|
string="Charge Bearer",
|
|
selection=ISO20022_CHARGE_BEARER_SELECTION,
|
|
compute='_compute_iso20022_charge_bearer',
|
|
readonly=False,
|
|
store=True,
|
|
tracking=True,
|
|
help="Specifies which party/parties will bear the charges associated with the processing of the payment transaction."
|
|
)
|
|
|
|
@api.depends('payment_method_id')
|
|
def _compute_iso20022_charge_bearer(self):
|
|
for payment in self:
|
|
if payment.payment_method_id.code == 'sepa_ct':
|
|
payment.iso20022_charge_bearer = 'SLEV'
|
|
else:
|
|
payment.iso20022_charge_bearer = payment.journal_id.iso20022_charge_bearer
|
|
|
|
@api.model
|
|
def _get_method_codes_using_bank_account(self):
|
|
res = super()._get_method_codes_using_bank_account()
|
|
res += ['sepa_ct', 'iso20022', 'iso20022_se', 'iso20022_ch', 'iso20022_us']
|
|
return res
|
|
|
|
@api.model
|
|
def _get_method_codes_needing_bank_account(self):
|
|
res = super()._get_method_codes_needing_bank_account()
|
|
res += ['sepa_ct', 'iso20022', 'iso20022_se', 'iso20022_ch', 'iso20022_us']
|
|
return res
|
|
|
|
@api.constrains('payment_method_line_id', 'journal_id')
|
|
def _check_sepa_bank_account(self):
|
|
sepa_payment_method = self.env.ref('account_iso20022.account_payment_method_sepa_ct')
|
|
for rec in self:
|
|
if rec.payment_method_id == sepa_payment_method:
|
|
if not rec.journal_id.bank_account_id or not rec.journal_id.bank_account_id.acc_type == 'iban':
|
|
raise ValidationError(_("The journal '%s' requires a proper IBAN account to pay via SEPA. Please configure it first.", rec.journal_id.name))
|
|
|
|
@api.constrains('payment_method_line_id', 'currency_id')
|
|
def _check_sepa_currency(self):
|
|
for rec in self:
|
|
if rec.payment_method_id.code == 'sepa_ct' and rec.currency_id.name != 'EUR':
|
|
raise ValidationError(_("SEPA only accepts payments in EUR."))
|
|
|
|
def _get_payment_method_codes_to_exclude(self):
|
|
res = super()._get_payment_method_codes_to_exclude()
|
|
currency_codes = ['BGN', 'HRK', 'CZK', 'DKK', 'GIP', 'HUF', 'ISK', 'CHF', 'NOK', 'PLN', 'RON', 'SEK', 'GBP', 'EUR', 'XPF']
|
|
currency_ids = self.env['res.currency'].with_context(active_test=False).search([('name', 'in', currency_codes)])
|
|
sepa_ct = self.env.ref('account_iso20022.account_payment_method_sepa_ct', raise_if_not_found=False)
|
|
if sepa_ct and self.currency_id not in currency_ids:
|
|
res.append(sepa_ct.code)
|
|
return res
|
|
|
|
@api.depends('payment_method_id')
|
|
def _compute_end_to_end_uuid(self):
|
|
for payment in self:
|
|
if not payment.end_to_end_uuid and payment.payment_method_id.code in {'iso20022', 'iso20022_se', 'iso20022_ch', 'iso20022_us', 'sepa_ct'}:
|
|
payment.end_to_end_uuid = uuid4().hex
|
|
|
|
@api.depends('payment_method_id')
|
|
def _compute_iso20022_uetr(self):
|
|
payments = self.filtered(
|
|
lambda p: not p.iso20022_uetr and p.payment_method_id.code == 'iso20022'
|
|
)
|
|
for payment in payments:
|
|
payment.iso20022_uetr = uuid4()
|
|
|
|
@api.depends('journal_id', 'payment_method_is_iso20022')
|
|
def _compute_payment_method_priority(self):
|
|
for payment in self:
|
|
payment.iso20022_priority = (
|
|
payment.payment_method_is_iso20022
|
|
and payment.journal_id.iso20022_default_priority
|
|
)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
res = super().create(vals_list)
|
|
# If the import doesn't have an end_to_end_uuid, there's on need to compute one for it as it should only be
|
|
# created by the initiating party.
|
|
if self.env.context.get('import_file'):
|
|
res.env.remove_to_compute(self._fields['end_to_end_uuid'], res)
|
|
return res
|