Before this commit, it was not possible to refund a payment from Odoo. Users had to go through the payment acquirer's backend and update the payment accordingly in Odoo. With this commit, refunds are made available in Odoo directly from the payment form, for acquirers that support them. Acquirer can either only support full refunds or also support partial refunds. As of now, the only acquirer allowing refunds is Adyen, with partial refund support. task-2527891 closes odoo/odoo#70881 Related: odoo/upgrade#2689 Related: odoo/enterprise#19829 Signed-off-by: Antoine Vandevenne (anv) <AntoineVDV@users.noreply.github.com>
67 lines
2.9 KiB
Python
67 lines
2.9 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class PaymentRefundWizard(models.TransientModel):
|
|
_name = 'payment.refund.wizard'
|
|
_description = "Payment Refund Wizard"
|
|
|
|
payment_id = fields.Many2one(
|
|
string="Payment",
|
|
comodel_name='account.payment',
|
|
readonly=True,
|
|
default=lambda self: self.env.context.get('active_id'),
|
|
)
|
|
transaction_id = fields.Many2one(
|
|
string="Payment Transaction", related='payment_id.payment_transaction_id'
|
|
)
|
|
payment_amount = fields.Monetary(string="Payment Amount", related='payment_id.amount')
|
|
refunded_amount = fields.Monetary(string="Refunded Amount", compute='_compute_refunded_amount')
|
|
amount_available_for_refund = fields.Monetary(
|
|
string="Maximum Refund Allowed", related='payment_id.amount_available_for_refund'
|
|
)
|
|
amount_to_refund = fields.Monetary(
|
|
string="Refund Amount", compute='_compute_amount_to_refund', store=True, readonly=False
|
|
)
|
|
currency_id = fields.Many2one(string="Currency", related='transaction_id.currency_id')
|
|
support_refund = fields.Selection(related='transaction_id.acquirer_id.support_refund')
|
|
has_pending_refund = fields.Boolean(
|
|
string="Has a pending refund", compute='_compute_has_pending_refund'
|
|
)
|
|
|
|
@api.constrains('amount_to_refund')
|
|
def _check_amount_to_refund_within_boundaries(self):
|
|
for wizard in self:
|
|
if not 0 < wizard.amount_to_refund <= wizard.amount_available_for_refund:
|
|
raise ValidationError(_(
|
|
"The amount to be refunded must be positive and cannot be superior to %s.",
|
|
wizard.amount_available_for_refund
|
|
))
|
|
|
|
@api.depends('amount_available_for_refund')
|
|
def _compute_refunded_amount(self):
|
|
for wizard in self:
|
|
wizard.refunded_amount = wizard.payment_amount - wizard.amount_available_for_refund
|
|
|
|
@api.depends('amount_available_for_refund')
|
|
def _compute_amount_to_refund(self):
|
|
""" Set the default amount to refund to the amount available for refund. """
|
|
for wizard in self:
|
|
wizard.amount_to_refund = wizard.amount_available_for_refund
|
|
|
|
@api.depends('payment_id') # To always trigger the compute
|
|
def _compute_has_pending_refund(self):
|
|
for wizard in self:
|
|
pending_refunds_count = self.env['payment.transaction'].search_count([
|
|
('source_transaction_id', '=', wizard.payment_id.payment_transaction_id.id),
|
|
('operation', '=', 'refund'),
|
|
('state', 'in', ['draft', 'pending', 'authorized']),
|
|
])
|
|
wizard.has_pending_refund = pending_refunds_count > 0
|
|
|
|
def action_refund(self):
|
|
for wizard in self:
|
|
wizard.transaction_id.action_refund(amount_to_refund=wizard.amount_to_refund)
|