1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/account_sepa/models/account_batch_payment.py
2024-12-10 09:04:09 +07:00

154 lines
6.6 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models, fields, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_round, float_repr, DEFAULT_SERVER_DATE_FORMAT
import base64
import re
from datetime import datetime
class AccountBatchPayment(models.Model):
_inherit = 'account.batch.payment'
sct_batch_booking = fields.Boolean(string="SCT Batch Booking", default=True, help="Request batch booking from the bank for the related bank statements.")
sct_generic = fields.Boolean(compute='_compute_sct_generic',
help=u"Technical feature used during the file creation. A SEPA message is said to be 'generic' if it cannot be considered as "
u"a standard european credit transfer. That is if the bank journal is not in €, a transaction is not in € or a payee is "
u"not identified by an IBAN account number.")
@api.depends('payment_ids', 'journal_id')
def _compute_sct_generic(self):
switch_to_generic_warnings = {'no_iban', 'no_eur'}
for record in self:
sct_warnings = record._get_sct_genericity_warnings()
record.sct_generic = any(warning.get('code') in switch_to_generic_warnings for warning in sct_warnings) or record.journal_id.sepa_pain_version == 'iso_20022'
def _get_methods_generating_files(self):
rslt = super(AccountBatchPayment, self)._get_methods_generating_files()
rslt.append('sepa_ct')
return rslt
def validate_batch(self):
for batch in self.filtered(lambda x: x.payment_method_code == 'sepa_ct'):
if batch.journal_id.bank_account_id.acc_type != 'iban':
raise UserError(_("The account %s, of journal '%s', is not of type IBAN.\nA valid IBAN account is required to use SEPA features.", batch.journal_id.bank_account_id.acc_number, batch.journal_id.name))
return super(AccountBatchPayment, self).validate_batch()
def _get_sct_genericity_warnings(self):
self.ensure_one()
if self.journal_id.sepa_pain_version == 'iso_20022':
# Forcing generic SEPA; so no genericity warning
return []
rslt = []
no_iban_payments = self.env['account.payment']
no_eur_payments = self.env['account.payment']
invalid_address_payments = self.env['account.payment']
invalid_ref_payments = self.env['account.payment']
for payment in self.mapped('payment_ids'):
if payment.partner_bank_id.acc_type != 'iban':
no_iban_payments += payment
if payment.currency_id.name != 'EUR':
no_eur_payments += payment
if no_iban_payments:
rslt.append({
'code': 'no_iban',
'title': _("Some payments are not made on an IBAN recipient account. This batch might not be accepted by certain banks because of that."),
'records': no_iban_payments,
})
if no_eur_payments:
rslt.append({
'code': 'no_eur',
'title': _("Some payments were instructed in another currency than Euro. This batch might not be accepted by certain banks because of that."),
'records': no_eur_payments,
})
return rslt
def check_payments_for_warnings(self):
rslt = super(AccountBatchPayment, self).check_payments_for_warnings()
if self.payment_method_code == 'sepa_ct':
sct_warnings = self._get_sct_genericity_warnings()
if (self.journal_id.currency_id or self.journal_id.company_id.currency_id).name != 'EUR':
sct_warnings = [warning for warning in sct_warnings if warning.get('code') != 'no_eur']
rslt += sct_warnings
return rslt
def check_payments_for_errors(self):
rslt = super(AccountBatchPayment, self).check_payments_for_errors()
if self.payment_method_code != 'sepa_ct':
return rslt
no_bank_acc_payments = self.env['account.payment']
too_big_payments = self.env['account.payment']
for payment in self.payment_ids.filtered(lambda x: x.state == 'posted'):
if not payment.partner_bank_id:
no_bank_acc_payments += payment
max_digits = payment.currency_id.name == 'EUR' and 11 or 15
if len(float_repr(payment.amount, 2)) >= max_digits: # The dot counts in this limit
too_big_payments += payment
if no_bank_acc_payments:
rslt.append({'title': _("Some payments have no recipient bank account set."), 'records': no_bank_acc_payments})
if too_big_payments:
rslt.append({
'title': _("Some payments are above the maximum amount allowed."),
'records': too_big_payments,
'help': _("Maximum amount is %s for payments in Euros, %s for other currencies.", 8 * '9' + ".99", 12 * '9' + ".99")
})
return rslt
def _generate_export_file(self):
if self.payment_method_code == 'sepa_ct':
payments = self.payment_ids.sorted(key=lambda r: r.id)
payment_dicts = self._generate_payment_template(payments)
xml_doc = self.journal_id.create_iso20022_credit_transfer(payment_dicts, self.sct_batch_booking, self.sct_generic)
return {
'file': base64.encodebytes(xml_doc),
'filename': "SCT-" + self.journal_id.code + "-" + datetime.now().strftime('%Y%m%d%H%M%S') + ".xml",
}
return super(AccountBatchPayment, self)._generate_export_file()
def _get_payment_vals(self, payment):
return {
'id' : payment.id,
'name': str(payment.id) + '-' + (payment.ref or 'SCT-' + self.journal_id.code + '-' + str(fields.Date.today())),
'payment_date' : payment.date,
'amount' : payment.amount,
'journal_id' : self.journal_id.id,
'currency_id' : payment.currency_id.id,
'payment_type' : payment.payment_type,
'ref' : payment.ref,
'partner_id' : payment.partner_id.id,
'partner_bank_id': payment.partner_bank_id.id,
'partner_country_code': payment.partner_id.country_id.code,
'sepa_uetr': payment.sepa_uetr,
}
def _generate_payment_template(self, payments):
payment_dicts = []
for payment in payments:
if not payment.partner_bank_id:
raise UserError(_('A bank account is not defined.'))
payment_dicts.append(self._get_payment_vals(payment))
return payment_dicts