2026-09-25 18:00:58 +05:30

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