# pyrefly: ignore [missing-import] from odoo import api, fields, models # pyrefly: ignore [missing-import] from odoo.exceptions import ValidationError class EducationStudentPayment(models.Model): _name = 'education.student.payment' _description = 'Student Payment' _order = 'payment_date desc, id desc' _rec_name = 'receipt_number' receipt_number = fields.Char(string='Receipt Number', required=True, copy=False, readonly=True, default='/', index=True) student_id = fields.Many2one('education.student', string='Student', required=True, index=True) fee_assignment_id = fields.Many2one('education.fee.assignment', string='Fee Invoice / Assignment', index=True) payment_date = fields.Date(string='Payment Date', required=True, default=fields.Date.context_today) payment_method = fields.Selection([ ('cash', 'Cash'), ('card', 'Card'), ('upi', 'UPI'), ('bank', 'Bank Transfer'), ('cheque', 'Cheque'), ('online', 'Online Payment') ], string='Payment Method', required=True, default='cash') currency_id = fields.Many2one( 'res.currency', string='Currency', required=True, default=lambda self: self.env.company.currency_id, ) amount = fields.Monetary(string='Amount Paid', required=True, currency_field='currency_id', default=0.0) reference_number = fields.Char(string='Reference Number') remarks = fields.Text(string='Remarks') status = fields.Selection([ ('draft', 'Draft'), ('confirmed', 'Confirmed'), ('cancelled', 'Cancelled') ], string='Status', default='draft', required=True, index=True) active = fields.Boolean(default=True) company_id = fields.Many2one( 'res.company', string='Company', required=True, default=lambda self: self.env.company, index=True, ) _sql_constraints = [ ('uniq_receipt_number', 'unique(receipt_number, company_id)', 'Receipt number must be unique per company.'), ] @api.constrains('amount') def _check_amount(self): for rec in self: if rec.amount <= 0: raise ValidationError('Payment amount must be greater than zero.') def action_confirm(self): for rec in self: if rec.status == 'confirmed': continue outstanding = rec.student_id.outstanding_amount if rec.amount > outstanding: raise ValidationError(f"Cannot accept payment of {rec.amount} because the student's outstanding balance is {outstanding}.") if not rec.receipt_number or rec.receipt_number == '/': rec.receipt_number = self.env['ir.sequence'].next_by_code('education.student.payment') or '/' self.env['education.student.ledger'].create({ 'student_id': rec.student_id.id, 'date': rec.payment_date, 'ref': rec.receipt_number, 'description': f"Payment Received - Method: {dict(rec._fields['payment_method'].selection).get(rec.payment_method)} (Ref: {rec.reference_number or 'N/A'})", 'debit': 0.0, 'credit': rec.amount, 'status': 'paid', 'company_id': rec.company_id.id, }) rec.status = 'confirmed' def action_cancel(self): for rec in self: if rec.status == 'confirmed': ledger_entries = self.env['education.student.ledger'].search([ ('student_id', '=', rec.student_id.id), ('ref', '=', rec.receipt_number), ]) ledger_entries.unlink() rec.status = 'cancelled' @api.model_create_multi def create(self, vals_list): for vals in vals_list: if not vals.get('receipt_number') or vals.get('receipt_number') == '/': vals['receipt_number'] = self.env['ir.sequence'].next_by_code('education.student.payment') or '/' return super().create(vals_list)