# pyrefly: ignore [missing-import] from odoo.tests.common import TransactionCase # pyrefly: ignore [missing-import] from odoo.exceptions import ValidationError class TestHostelModels(TransactionCase): """Phase K — Hostel model tests. Tests 1-13: Model creation and business logic. """ @classmethod def setUpClass(cls): super().setUpClass() # Campus cls.campus = cls.env['education.campus'].create({ 'name': 'Main Campus', 'code': 'MC', 'company_id': cls.env.company.id, }) # Academic Year cls.academic_year = cls.env['education.academic.year'].create({ 'name': '2025-26', 'code': 'AY2526', 'start_date': '2025-06-01', 'end_date': '2026-05-31', 'company_id': cls.env.company.id, }) # Academic Term cls.academic_term = cls.env['education.academic.term'].create({ 'name': 'Term 1', 'code': 'T1', 'academic_year_id': cls.academic_year.id, 'start_date': '2025-06-01', 'end_date': '2025-11-30', 'sequence': 1, }) # Department cls.department = cls.env['education.department'].create({ 'name': 'Hostel Engineering', 'code': 'H-ENG', 'campus_id': cls.campus.id, 'company_id': cls.env.company.id, }) # Level cls.level = cls.env['education.academic.level'].create({ 'name': 'Hostel Undergraduate', 'code': 'H-UG', 'company_id': cls.env.company.id, }) # Program cls.program = cls.env['education.program'].create({ 'name': 'Hostel B.Tech', 'code': 'H-BTECH', 'department_id': cls.department.id, 'level_id': cls.level.id, 'company_id': cls.env.company.id, }) # Batch/Semester/Section cls.batch = cls.env['education.batch'].create({'name': 'Hostel B1', 'code': 'H-B1', 'company_id': cls.env.company.id}) cls.semester = cls.env['education.semester'].create({'name': 'Hostel S1', 'code': 'H-S1', 'company_id': cls.env.company.id}) cls.section = cls.env['education.section'].create({'name': 'Hostel A', 'code': 'H-A', 'company_id': cls.env.company.id}) # Hostel cls.hostel = cls.env['education.hostel'].create({ 'name': 'Alpha Hostel', 'code': 'AH', 'campus_id': cls.campus.id, 'hostel_type': 'boys', 'company_id': cls.env.company.id, }) # Floor cls.floor = cls.env['education.hostel.floor'].create({ 'hostel_id': cls.hostel.id, 'name': 'Ground Floor', 'code': 'GF', 'sequence': 1, }) # Room cls.room = cls.env['education.hostel.room'].create({ 'hostel_id': cls.hostel.id, 'floor_id': cls.floor.id, 'name': 'Room 101', 'room_number': '101', 'room_type': 'double', 'capacity': 2, 'gender': 'male', }) # Bed cls.bed1 = cls.env['education.hostel.bed'].create({ 'room_id': cls.room.id, 'bed_number': '1', 'code': 'AH-101-1', 'status': 'available', }) cls.bed2 = cls.env['education.hostel.bed'].create({ 'room_id': cls.room.id, 'bed_number': '2', 'code': 'AH-101-2', 'status': 'available', }) def _make_student(first, last, gender='male'): return cls.env['education.student'].create({ 'first_name': first, 'last_name': last, 'gender': gender, 'campus_id': cls.campus.id, 'department_id': cls.department.id, 'program_id': cls.program.id, 'level_id': cls.level.id, 'academic_year_id': cls.academic_year.id, 'academic_term_id': cls.academic_term.id, 'batch_id': cls.batch.id, 'semester_id': cls.semester.id, 'section_id': cls.section.id, 'company_id': cls.env.company.id, 'email': f'{first.lower()}.{last.lower()}@test.edu', }) cls.student_a = _make_student('Arjun', 'Kumar', 'male') cls.student_b = _make_student('Bharath', 'Raj', 'male') cls.student_female = _make_student('Priya', 'Sharma', 'female') # 1. Hostel Creation def test_01_hostel_creation(self): self.assertEqual(self.hostel.name, 'Alpha Hostel') self.assertEqual(self.hostel.code, 'AH') self.assertEqual(self.hostel.hostel_type, 'boys') self.assertTrue(self.hostel.active) # 2. Hostel Hierarchy def test_02_hostel_hierarchy(self): self.assertEqual(self.floor.hostel_id, self.hostel) self.assertEqual(self.room.hostel_id, self.hostel) self.assertEqual(self.room.floor_id, self.floor) self.assertEqual(self.bed1.room_id, self.room) self.assertEqual(self.bed1.hostel_id, self.hostel) # 3. Room Capacity Constraint def test_03_room_capacity_constraint(self): with self.assertRaises(ValidationError): self.env['education.hostel.room'].create({ 'hostel_id': self.hostel.id, 'floor_id': self.floor.id, 'name': 'Bad Room', 'room_number': '999', 'room_type': 'single', 'capacity': 0, # invalid 'gender': 'male', }) # 4. Bed Uniqueness def test_04_bed_code_uniqueness(self): with self.assertRaises(ValidationError): self.env['education.hostel.bed'].create({ 'room_id': self.room.id, 'bed_number': '3', 'code': 'AH-101-1', # duplicate code 'status': 'available', }) # 5. Student Allocation def test_05_student_allocation(self): member = self.env['education.hostel.member'].create({ 'student_id': self.student_a.id, 'hostel_id': self.hostel.id, 'room_id': self.room.id, 'bed_id': self.bed1.id, 'allocation_date': '2025-06-01', }) self.assertEqual(member.status, 'pending') member.action_activate() self.assertEqual(member.status, 'active') self.assertEqual(self.bed1.status, 'occupied') return member # 6. Duplicate Active Allocation Blocked def test_06_duplicate_active_allocation_blocked(self): # Activate first allocation m1 = self.env['education.hostel.member'].create({ 'student_id': self.student_a.id, 'hostel_id': self.hostel.id, 'room_id': self.room.id, 'bed_id': self.bed1.id, 'allocation_date': '2025-06-01', }) m1.action_activate() # Second active allocation for same student must fail with self.assertRaises(ValidationError): m2 = self.env['education.hostel.member'].create({ 'student_id': self.student_a.id, 'hostel_id': self.hostel.id, 'room_id': self.room.id, 'bed_id': self.bed2.id, 'allocation_date': '2025-06-01', 'status': 'active', }) # Cleanup m1.action_exit() # 7. Bed Conflict Blocked def test_07_bed_conflict_blocked(self): m1 = self.env['education.hostel.member'].create({ 'student_id': self.student_a.id, 'hostel_id': self.hostel.id, 'room_id': self.room.id, 'bed_id': self.bed1.id, 'allocation_date': '2025-06-01', }) m1.action_activate() with self.assertRaises(ValidationError): m2 = self.env['education.hostel.member'].create({ 'student_id': self.student_b.id, 'hostel_id': self.hostel.id, 'room_id': self.room.id, 'bed_id': self.bed1.id, # same bed! 'allocation_date': '2025-06-01', 'status': 'active', }) m1.action_exit() # 8. Gender Restriction def test_08_gender_restriction(self): # room.gender = 'male'; female student should be blocked with self.assertRaises(ValidationError): self.env['education.hostel.member'].create({ 'student_id': self.student_female.id, 'hostel_id': self.hostel.id, 'room_id': self.room.id, 'bed_id': self.bed1.id, 'allocation_date': '2025-06-01', 'status': 'active', }) # 9. Exit Workflow def test_09_exit_workflow(self): member = self.env['education.hostel.member'].create({ 'student_id': self.student_a.id, 'hostel_id': self.hostel.id, 'room_id': self.room.id, 'bed_id': self.bed1.id, 'allocation_date': '2025-06-01', }) member.action_activate() self.assertEqual(self.bed1.status, 'occupied') member.action_exit() self.assertEqual(member.status, 'exited') self.assertEqual(self.bed1.status, 'available') self.assertTrue(member.actual_exit_date) # 10. Complaint Workflow def test_10_complaint_workflow(self): complaint = self.env['education.hostel.complaint'].create({ 'student_id': self.student_a.id, 'hostel_id': self.hostel.id, 'category': 'maintenance', 'title': 'Fan not working', }) self.assertEqual(complaint.state, 'draft') complaint.action_submit() self.assertEqual(complaint.state, 'submitted') complaint.assigned_to = self.env.user complaint.action_start_progress() self.assertEqual(complaint.state, 'in_progress') complaint.resolution = 'Fan replaced.' complaint.action_resolve() self.assertEqual(complaint.state, 'resolved') complaint.action_close() self.assertEqual(complaint.state, 'closed') # 11. Visitor Workflow def test_11_visitor_workflow(self): visitor = self.env['education.hostel.visitor'].create({ 'student_id': self.student_a.id, 'hostel_id': self.hostel.id, 'visitor_name': 'Parent', 'relationship': 'Father', 'visit_date': '2025-07-01', }) self.assertEqual(visitor.state, 'pending') visitor.action_approve() self.assertEqual(visitor.state, 'approved') visitor.action_check_in() self.assertEqual(visitor.state, 'checked_in') self.assertTrue(visitor.check_in) visitor.action_check_out() self.assertEqual(visitor.state, 'checked_out') # 12. Fee Structure Creation def test_12_fee_structure_creation(self): fs = self.env['education.hostel.fee.structure'].create({ 'hostel_id': self.hostel.id, 'room_type': 'double', 'amount': 5000.0, 'billing_period': 'monthly', 'academic_year_id': self.academic_year.id, }) self.assertEqual(fs.amount, 5000.0) self.assertEqual(fs.billing_period, 'monthly') # 13. Fee Validation def test_13_fee_validation(self): with self.assertRaises(ValidationError): self.env['education.hostel.fee.structure'].create({ 'hostel_id': self.hostel.id, 'room_type': 'single', 'amount': -100.0, # invalid 'billing_period': 'monthly', }) # Valid fee creation fee = self.env['education.hostel.fee'].create({ 'student_id': self.student_a.id, 'hostel_id': self.hostel.id, 'amount': 5000.0, 'due_date': '2025-07-31', }) self.assertEqual(fee.state, 'draft') fee.action_set_due() self.assertEqual(fee.state, 'due') fee.action_mark_paid() self.assertEqual(fee.state, 'paid')