# pyrefly: ignore [missing-import] from odoo import api, fields, models class EducationStudent(models.Model): _inherit = 'education.student' ledger_line_ids = fields.One2many('education.student.ledger', 'student_id', string='Ledger Lines') payment_ids = fields.One2many('education.student.payment', 'student_id', string='Payments') fee_assignment_ids = fields.One2many('education.fee.assignment', 'student_id', string='Fee Invoices') currency_id = fields.Many2one('res.currency', string='Currency', related='company_id.currency_id', readonly=True, store=True) outstanding_amount = fields.Monetary(string='Outstanding Dues', compute='_compute_finance_metrics', currency_field='currency_id', store=True, index=True) total_fees = fields.Monetary(string='Total Fees', compute='_compute_finance_metrics', currency_field='currency_id', store=True, index=True) total_paid = fields.Monetary(string='Total Paid', compute='_compute_finance_metrics', currency_field='currency_id', store=True, index=True) overdue_amount = fields.Monetary(string='Overdue Amount', compute='_compute_finance_metrics', currency_field='currency_id', store=True, index=True) next_due_date = fields.Date(string='Next Due Date', compute='_compute_finance_metrics', store=True, index=True) last_payment_date = fields.Date(string='Last Payment Date', compute='_compute_finance_metrics', store=True, index=True) last_receipt_number = fields.Char(string='Last Receipt Number', compute='_compute_finance_metrics', store=True, index=True) payment_status = fields.Selection([ ('no_dues', 'No Dues'), ('partial', 'Partial'), ('overdue', 'Overdue'), ('fully_paid', 'Fully Paid') ], string='Payment Status', compute='_compute_finance_metrics', default='no_dues', store=True, index=True) @api.depends( 'ledger_line_ids.debit', 'ledger_line_ids.credit', 'ledger_line_ids.status', 'payment_ids.amount', 'payment_ids.status', 'payment_ids.payment_date', 'payment_ids.receipt_number', 'fee_assignment_ids.state', 'fee_assignment_ids.amount_due', 'fee_assignment_ids.due_date' ) def _compute_finance_metrics(self): today = fields.Date.today() for rec in self: active_ledgers = rec.ledger_line_ids.filtered(lambda l: l.status in ('outstanding', 'paid')) total_debits = sum(line.debit for line in active_ledgers) total_credits = sum(line.credit for line in active_ledgers) rec.total_fees = total_debits confirmed_payments = rec.payment_ids.filtered(lambda p: p.status == 'confirmed').sorted(key=lambda p: (p.payment_date or fields.Date.today(), p.id), reverse=True) rec.total_paid = sum(p.amount for p in confirmed_payments) rec.outstanding_amount = max(0.0, total_debits - total_credits) last_payment = confirmed_payments[0] if confirmed_payments else False rec.last_payment_date = last_payment.payment_date if last_payment else False rec.last_receipt_number = last_payment.receipt_number if last_payment else False overdue = 0.0 upcoming_due_dates = [] for a in rec.fee_assignment_ids.filtered(lambda x: x.state == 'confirmed' and x.amount_due > 0): if a.due_date and a.due_date < today: overdue += a.amount_due elif a.due_date and a.due_date >= today: upcoming_due_dates.append(a.due_date) rec.overdue_amount = overdue rec.next_due_date = min(upcoming_due_dates) if upcoming_due_dates else False if total_debits == 0: rec.payment_status = 'no_dues' elif rec.outstanding_amount <= 0: rec.payment_status = 'fully_paid' elif overdue > 0: rec.payment_status = 'overdue' elif rec.total_paid > 0: rec.payment_status = 'partial' else: rec.payment_status = 'overdue'