forked from Mapan/odoo17e
154 lines
6.6 KiB
Python
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
|