126 lines
4.7 KiB
Python
126 lines
4.7 KiB
Python
import json
|
|
import logging
|
|
# pyrefly: ignore [missing-import]
|
|
from odoo import api, fields, models, _
|
|
# pyrefly: ignore [missing-import]
|
|
from odoo.exceptions import UserError, ValidationError
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class EducationPaymentTransaction(models.Model):
|
|
_name = 'education.payment.transaction'
|
|
_description = 'Education Payment Transaction'
|
|
_order = 'create_date desc, id desc'
|
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
|
|
|
name = fields.Char(
|
|
string='Transaction Reference',
|
|
required=True,
|
|
copy=False,
|
|
readonly=True,
|
|
default=lambda self: _('New'),
|
|
index=True
|
|
)
|
|
student_id = fields.Many2one(
|
|
'education.student',
|
|
string='Student',
|
|
required=True,
|
|
ondelete='cascade',
|
|
index=True,
|
|
tracking=True
|
|
)
|
|
company_id = fields.Many2one(
|
|
'res.company',
|
|
string='Company',
|
|
required=True,
|
|
default=lambda self: self.env.company,
|
|
index=True
|
|
)
|
|
currency_id = fields.Many2one(
|
|
'res.currency',
|
|
string='Currency',
|
|
required=True,
|
|
default=lambda self: self.env.company.currency_id
|
|
)
|
|
amount = fields.Monetary(
|
|
string='Amount',
|
|
currency_field='currency_id',
|
|
required=True,
|
|
tracking=True
|
|
)
|
|
provider = fields.Selection([
|
|
('razorpay', 'Razorpay'),
|
|
('stripe', 'Stripe'),
|
|
('cashfree', 'Cashfree'),
|
|
('manual', 'Manual'),
|
|
('mock', 'Sandbox Simulator'),
|
|
], string='Payment Gateway', default='razorpay', required=True, tracking=True)
|
|
|
|
razorpay_order_id = fields.Char(string='Razorpay Order ID', index=True, copy=False)
|
|
razorpay_payment_id = fields.Char(string='Razorpay Payment ID', index=True, copy=False)
|
|
razorpay_signature = fields.Char(string='Razorpay Signature', copy=False)
|
|
|
|
status = fields.Selection([
|
|
('pending', 'Pending'),
|
|
('authorized', 'Authorized'),
|
|
('success', 'Success'),
|
|
('failed', 'Failed'),
|
|
('cancelled', 'Cancelled'),
|
|
('refunded', 'Refunded'),
|
|
], string='Status', default='pending', required=True, index=True, tracking=True)
|
|
|
|
payment_date = fields.Datetime(string='Payment Date', default=fields.Datetime.now, tracking=True)
|
|
verified_on = fields.Datetime(string='Verified On', copy=False)
|
|
receipt_number = fields.Char(string='Receipt Number', copy=False, tracking=True)
|
|
|
|
fee_assignment_ids = fields.Many2many(
|
|
'education.fee.assignment',
|
|
'education_payment_tx_fee_rel',
|
|
'tx_id',
|
|
'fee_id',
|
|
string='Paid Dues'
|
|
)
|
|
payment_id = fields.Many2one('education.student.payment', string='Student Payment Record', copy=False)
|
|
gateway_response = fields.Text(string='Gateway Response JSON', copy=False)
|
|
error_message = fields.Text(string='Error / Failure Reason', copy=False, tracking=True)
|
|
retry_count = fields.Integer(string='Retry Count', default=0, copy=False)
|
|
audit_log = fields.Text(string='Audit Log', copy=False)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
for vals in vals_list:
|
|
if vals.get('name', _('New')) == _('New'):
|
|
vals['name'] = self.env['ir.sequence'].next_by_code('education.payment.transaction') or _('New')
|
|
if not vals.get('audit_log'):
|
|
vals['audit_log'] = f"[{fields.Datetime.now()}] Transaction created in Pending state.\n"
|
|
return super().create(vals_list)
|
|
|
|
def log_audit(self, message):
|
|
"""Append audit entry with timestamp"""
|
|
for rec in self:
|
|
entry = f"[{fields.Datetime.now()}] {message}\n"
|
|
rec.audit_log = (rec.audit_log or "") + entry
|
|
rec.message_post(body=message)
|
|
|
|
def action_mark_failed(self, error_reason="Payment verification failed or dismissed."):
|
|
"""Mark transaction failed"""
|
|
for rec in self:
|
|
if rec.status in ('success', 'refunded'):
|
|
raise ValidationError(_("Cannot mark a successful or refunded transaction as failed."))
|
|
rec.write({
|
|
'status': 'failed',
|
|
'error_message': error_reason,
|
|
})
|
|
rec.log_audit(f"Transaction marked FAILED. Reason: {error_reason}")
|
|
|
|
def action_process_refund(self):
|
|
"""Finance Office Action: Process refund and reverse ledger entry"""
|
|
# pyrefly: ignore [missing-import]
|
|
from odoo.addons.education_portal.services.payment_service import PaymentService
|
|
for rec in self:
|
|
if rec.status != 'success':
|
|
raise UserError(_("Only successful transactions can be refunded."))
|
|
PaymentService.process_refund(rec.env, rec)
|
|
return True
|