forked from Mapan/odoo17e
162 lines
6.4 KiB
Python
162 lines
6.4 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
||
import logging
|
||
|
||
from odoo import _, fields, models
|
||
from odoo.exceptions import UserError, ValidationError
|
||
|
||
from odoo.addons.payment import utils as payment_utils
|
||
|
||
|
||
class PaymentTransaction(models.Model):
|
||
_inherit = 'payment.transaction'
|
||
|
||
mandate_id = fields.Many2one(comodel_name='sdd.mandate')
|
||
|
||
#=== BUSINESS METHODS ===#
|
||
|
||
def _get_specific_processing_values(self, processing_values):
|
||
""" Override of `payment` to return SEPA-specific processing values. """
|
||
res = super()._get_specific_processing_values(processing_values)
|
||
if self.provider_id.custom_mode != 'sepa_direct_debit' or self.operation == 'online_token':
|
||
return res
|
||
|
||
return {
|
||
'access_token': payment_utils.generate_access_token(self.reference),
|
||
}
|
||
|
||
def _send_payment_request(self):
|
||
""" Override of payment to create the related `account.payment` and notify the customer.
|
||
|
||
Note: self.ensure_one()
|
||
|
||
:return: None
|
||
:raise: UserError if the transaction is not linked to a token
|
||
:raise: UserError if the transaction is not linked to a valid mandate
|
||
"""
|
||
super()._send_payment_request()
|
||
if self.provider_id.custom_mode != 'sepa_direct_debit':
|
||
return
|
||
|
||
if not self.token_id:
|
||
raise UserError("SEPA: " + _("The transaction is not linked to a token."))
|
||
|
||
mandate = self.token_id.sdd_mandate_id
|
||
if not mandate:
|
||
raise UserError("SEPA: " + _("The token is not linked to a mandate."))
|
||
|
||
if (
|
||
mandate.state != 'active'
|
||
or (mandate.end_date and mandate.end_date > fields.Datetime.now())
|
||
):
|
||
raise UserError("SEPA: " + _("The mandate is invalid."))
|
||
|
||
# There is no provider to send a payment request to, but we handle empty notification data
|
||
# to let the payment engine call the generic processing methods.
|
||
self._handle_notification_data('sepa_direct_debit', {'reference': self.reference})
|
||
|
||
def _get_tx_from_notification_data(self, provider_code, notification_data):
|
||
""" Override of `payment` to find the transaction based on dummy data.
|
||
|
||
:param str provider_code: The provider_code of the provider that handled the transaction.
|
||
:param dict notification_data: The dummy notification data.
|
||
:return: The transaction if found.
|
||
:rtype: recordset of `payment.transaction`
|
||
:raise ValidationError: If the data match no transaction.
|
||
"""
|
||
tx = super()._get_tx_from_notification_data(provider_code, notification_data)
|
||
if provider_code != 'sepa_direct_debit' or len(tx) == 1:
|
||
return tx
|
||
|
||
reference = notification_data.get('reference')
|
||
tx = self.search([
|
||
('reference', '=', reference),
|
||
('provider_code', '=', 'custom'),
|
||
('provider_id.custom_mode', '=', 'sepa_direct_debit'),
|
||
])
|
||
if not tx:
|
||
raise ValidationError(
|
||
"SEPA: " + _("No transaction found matching reference %s.", reference)
|
||
)
|
||
return tx
|
||
|
||
def _process_notification_data(self, notification_data):
|
||
""" Override of `payment` to process the transaction based on dummy data.
|
||
|
||
Note: self.ensure_one()
|
||
|
||
:param dict notification_data: The dummy notification data.
|
||
:return: None
|
||
:raise ValidationError: If inconsistent data were received.
|
||
"""
|
||
super()._process_notification_data(notification_data)
|
||
if self.provider_id.custom_mode != 'sepa_direct_debit':
|
||
return
|
||
|
||
if self.operation in ('online_token', 'offline'):
|
||
self._set_done() # SEPA transactions are confirmed as soon as the mandate is valid.
|
||
self._sdd_notify_debit(self.token_id)
|
||
|
||
def _set_done(self, *args, **kwargs):
|
||
confirmed_txs = super()._set_done(*args, **kwargs)
|
||
sepa_txs = confirmed_txs.filtered(
|
||
lambda t: t.provider_code == 'custom'
|
||
and t.provider_id.custom_mode == 'sepa_direct_debit'
|
||
and t.mandate_id
|
||
)
|
||
for tx in sepa_txs:
|
||
tx.token_id = tx.provider_id._sdd_create_token_for_mandate(tx.partner_id, tx.mandate_id)
|
||
tx.mandate_id._confirm()
|
||
return confirmed_txs
|
||
|
||
def _sdd_notify_debit(self, token):
|
||
""" Notify the customer that a debit has been made from his account.
|
||
|
||
This is required as per the SEPA Direct Debit rulebook.
|
||
The notice must include:
|
||
- the last 4 digits of the debtor’s bank account
|
||
- the mandate reference
|
||
- the amount to be debited
|
||
- your SEPA creditor identifier
|
||
- your contact information
|
||
Notifications should be sent at least 14 calendar days before the payment is created unless
|
||
specified otherwise.
|
||
|
||
:param recordset token: The token linked to the mandate from which the debit has been made,
|
||
as a `payment.token` record
|
||
:return: None
|
||
"""
|
||
ctx = self.env.context.copy()
|
||
ctx.update({
|
||
'iban_last_4': token.payment_details[:4],
|
||
'mandate_ref': token.sdd_mandate_id.name,
|
||
'creditor_identifier': self.env.company.sdd_creditor_identifier,
|
||
})
|
||
template = self.env.ref('payment_sepa_direct_debit.mail_template_sepa_notify_debit')
|
||
template.with_context(ctx).send_mail(self.id)
|
||
|
||
def _create_payment(self, **extra_create_values):
|
||
""" Override of `payment` to pass the correct payment method line id and the SDD mandate id
|
||
to the extra create values.
|
||
|
||
Note: self.ensure_one()
|
||
|
||
:param dict extra_create_values: The optional extra create values.
|
||
:return: The created payment.
|
||
:rtype: recordset of `account.payment`
|
||
"""
|
||
if self.provider_id.custom_mode != 'sepa_direct_debit':
|
||
return super()._create_payment(**extra_create_values)
|
||
|
||
if self.operation in ('online_token', 'offline'):
|
||
mandate = self.token_id.sdd_mandate_id
|
||
else:
|
||
mandate = self.mandate_id
|
||
|
||
payment_method_line = self.provider_id.journal_id.inbound_payment_method_line_ids.filtered(
|
||
lambda l: l.payment_provider_id == self.provider_id
|
||
)
|
||
return super()._create_payment(
|
||
payment_method_line_id=payment_method_line.id, sdd_mandate_id=mandate.id
|
||
)
|