diff --git a/student_organization/__init__.py b/student_organization/__init__.py
new file mode 100644
index 0000000..9a7e03e
--- /dev/null
+++ b/student_organization/__init__.py
@@ -0,0 +1 @@
+from . import models
\ No newline at end of file
diff --git a/student_organization/__manifest__.py b/student_organization/__manifest__.py
new file mode 100644
index 0000000..bb1e444
--- /dev/null
+++ b/student_organization/__manifest__.py
@@ -0,0 +1,19 @@
+{
+ 'name': 'student_organization',
+ 'version': '1.3',
+ 'summary': '',
+ 'sequence': 11,
+ 'depends': ['base','hr'],
+ 'data':[
+ 'security/ir.model.access.csv',
+ 'views/view_student_organization.xml',
+ 'views/view_student_info.xml',
+ 'data/basic_grade.xml',
+ 'data/student_org.xml',
+ ],
+
+ 'installable': True,
+ 'application': True,
+
+ 'license': 'LGPL-3',
+}
diff --git a/student_organization/__pycache__/__init__.cpython-312.pyc b/student_organization/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..af2bdea
Binary files /dev/null and b/student_organization/__pycache__/__init__.cpython-312.pyc differ
diff --git a/student_organization/data/basic_grade.xml b/student_organization/data/basic_grade.xml
new file mode 100644
index 0000000..65ef685
--- /dev/null
+++ b/student_organization/data/basic_grade.xml
@@ -0,0 +1,105 @@
+
+
+
+ 大一
+ 1
+
+
+ 大二
+ 2
+
+
+ 大三
+ 3
+
+
+ 大四
+ 4
+
+
+
+
+ 2010
+ 1
+
+
+ 2011
+ 2
+
+
+ 2012
+ 3
+
+
+ 2013
+ 4
+
+
+ 2014
+ 5
+
+
+ 2015
+ 6
+
+
+ 2016
+ 7
+
+
+ 2017
+ 8
+
+
+ 2018
+ 9
+
+
+ 2019
+ 10
+
+
+ 2020
+ 11
+
+
+ 2021
+ 12
+
+
+ 2022
+ 13
+
+
+ 2023
+ 14
+
+
+ 2024
+ 15
+
+
+ 2025
+ 16
+
+
+ 2026
+ 17
+
+
+ 2027
+ 18
+
+
+ 2028
+ 19
+
+
+ 2029
+ 20
+
+
+ 2030
+ 21
+
+
\ No newline at end of file
diff --git a/student_organization/data/student_org.xml b/student_organization/data/student_org.xml
new file mode 100644
index 0000000..9f25991
--- /dev/null
+++ b/student_organization/data/student_org.xml
@@ -0,0 +1,249 @@
+
+
+
+
+
+ 信息工程学院
+ 01
+ college
+ 10
+
+
+
+ 经济管理学院
+ 02
+ college
+ 20
+
+
+
+
+ 计算机科学与技术
+ 080901
+ major
+
+ 10
+ 4
+ bachelor
+
+
+
+ 软件工程
+ 080902
+ major
+
+ 20
+ 4
+ bachelor
+
+
+
+ 数据科学与大数据技术
+ 080910T
+ major
+
+ 30
+ 4
+ bachelor
+
+
+
+
+ 工商管理
+ 120201K
+ major
+
+ 10
+ 4
+ bachelor
+
+
+
+ 市场营销
+ 120202
+ major
+
+ 20
+ 4
+ bachelor
+
+
+
+ 会计学
+ 120203K
+ major
+
+ 30
+ 4
+ bachelor
+
+
+
+
+ 计算机1班
+ 08090101
+ class
+
+ 10
+ 2024级
+
+
+
+ 计算机2班
+ 08090102
+ class
+
+ 20
+ 2024级
+
+
+
+ 计算机3班
+ 08090103
+ class
+
+ 30
+ 2024级
+
+
+
+
+ 软件1班
+ 08090201
+ class
+
+ 10
+ 2024级
+
+
+
+ 软件2班
+ 08090202
+ class
+
+ 20
+ 2024级
+
+
+
+ 软件3班
+ 08090203
+ class
+
+ 30
+ 2024级
+
+
+
+
+ 大数据1班
+ 080910T01
+ class
+
+ 10
+ 2024级
+
+
+
+ 大数据2班
+ 080910T02
+ class
+
+ 20
+ 2024级
+
+
+
+ 大数据3班
+ 080910T03
+ class
+
+ 30
+ 2024级
+
+
+
+
+ 工商1班
+ 120201K01
+ class
+
+ 10
+ 2024级
+
+
+
+ 工商2班
+ 120201K02
+ class
+
+ 20
+ 2024级
+
+
+
+ 工商3班
+ 120201K03
+ class
+
+ 30
+ 2024级
+
+
+
+
+ 营销1班
+ 12020201
+ class
+
+ 10
+ 2024级
+
+
+
+ 营销2班
+ 12020202
+ class
+
+ 20
+ 2024级
+
+
+
+ 营销3班
+ 12020203
+ class
+
+ 30
+ 2024级
+
+
+
+
+ 会计1班
+ 120203K01
+ class
+
+ 10
+ 2024级
+
+
+
+ 会计2班
+ 120203K02
+ class
+
+ 20
+ 2024级
+
+
+
+ 会计3班
+ 120203K03
+ class
+
+ 30
+ 2024级
+
+
+
\ No newline at end of file
diff --git a/student_organization/models/__init__.py b/student_organization/models/__init__.py
new file mode 100644
index 0000000..936e066
--- /dev/null
+++ b/student_organization/models/__init__.py
@@ -0,0 +1,3 @@
+from . import student_organization
+from . import basic_grade
+from . import student_info
diff --git a/student_organization/models/__pycache__/__init__.cpython-312.pyc b/student_organization/models/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..67717f4
Binary files /dev/null and b/student_organization/models/__pycache__/__init__.cpython-312.pyc differ
diff --git a/student_organization/models/__pycache__/basic_grade.cpython-312.pyc b/student_organization/models/__pycache__/basic_grade.cpython-312.pyc
new file mode 100644
index 0000000..13976b6
Binary files /dev/null and b/student_organization/models/__pycache__/basic_grade.cpython-312.pyc differ
diff --git a/student_organization/models/__pycache__/student_info.cpython-312.pyc b/student_organization/models/__pycache__/student_info.cpython-312.pyc
new file mode 100644
index 0000000..e7fb7d1
Binary files /dev/null and b/student_organization/models/__pycache__/student_info.cpython-312.pyc differ
diff --git a/student_organization/models/__pycache__/student_organization.cpython-312.pyc b/student_organization/models/__pycache__/student_organization.cpython-312.pyc
new file mode 100644
index 0000000..c218c9e
Binary files /dev/null and b/student_organization/models/__pycache__/student_organization.cpython-312.pyc differ
diff --git a/student_organization/models/basic_grade.py b/student_organization/models/basic_grade.py
new file mode 100644
index 0000000..f745475
--- /dev/null
+++ b/student_organization/models/basic_grade.py
@@ -0,0 +1,20 @@
+from odoo import api,fields,models
+from datetime import datetime
+class BasicGradeXx(models.Model):
+ _name = 'basic.grade.xx' #xx 详细
+ _rec_name = 'grade'
+ grade=fields.Char(string='年级')
+ sequence=fields.Integer(string='序号')
+ @api.model
+ def _get_current_year_range_domain(self):
+ year = datetime.now().year
+ return [('name', '>=', str(year - 4)), ('name', '<=',
+ str(year + 1))] # 别的直接调用domain = lambda self: self.env['basic.grade.xx']._get_current_year_range_domain()
+
+
+class BasicGradeBx(models.Model):
+ _name = 'basic.grade.bx' #xx 不详细
+ _rec_name = 'grade'
+ grade=fields.Char(string='年级')
+ sequence = fields.Integer(string='序号')
+
diff --git a/student_organization/models/student_info.py b/student_organization/models/student_info.py
new file mode 100644
index 0000000..ca54486
--- /dev/null
+++ b/student_organization/models/student_info.py
@@ -0,0 +1,54 @@
+from odoo import api,models,fields
+
+class StudentInfo(models.Model):
+ _name="student.info"
+
+ stu_name=fields.Char(string="姓名",tracking=True)
+ stu_num=fields.Char(string="学号",tracking=True)
+ stu_sex = fields.Selection([('male', '男'),('female', '女')], string="性别", default='male', required=True)
+ stu_birthday = fields.Date(string="出生日期", tracking=True)
+ stu_id_card = fields.Char(string="身份证号", index=True, tracking=True)
+ stu_nation = fields.Char(string="民族", default="汉族")
+ stu_shenfen = fields.Selection([
+ ('mass', '群众'),
+ ('league', '共青团员'),
+ ('party', '中共党员'),
+ ('other', '其他')
+ ], string="政治面貌", default='mass')
+ stu_phone = fields.Char(string="手机号")
+ stu_email = fields.Char(string="邮箱")
+ stu_address = fields.Text(string="家庭地址")
+ stu_emergency_contact = fields.Char(string="紧急联系人")
+ stu_emergency_phone = fields.Char(string="紧急联系电话")
+ stu_photo = fields.Binary(string="照片", attachment=True)
+ stu_begin_date = fields.Date(string="入学日期", required=True, tracking=True)
+ stu_end_date = fields.Date(string="毕业日期")
+ stu_grade_xx = fields.Many2one('basic.grade.xx',string="年级", tracking=True,domain = lambda self: self.env['basic.grade.xx']._get_current_year_range_domain())
+ stu_grade_bx=fields.Many2one('basic.grade.xx',string='年级',readonle=1)
+ stu_status = fields.Selection([
+ ('studying', '在读'),
+ ('graduated', '已毕业'),
+ ('suspended', '休学'),
+ ('expelled', '退学'),
+ ('transferred', '转出')
+ ], string="学籍状态", default='studying', tracking=True)
+ org_path = fields.Char(string='组织路径', compute='_compute_org_path',
+ store=True, index=True)
+
+ class_id = fields.Many2one('student.organization', string="行政班", tracking=True,domain="[('org_type', '=', 'class')]")
+ parent_path = fields.Char(related='class_id.parent_path',store=True)
+ class_name = fields.Char(string="班级名称", related='class_id.name', store=True)
+ major_id = fields.Many2one('student.organization', string="专业", tracking=True,domain="[('org_type', '=', 'major')]")
+ major_name = fields.Char(string="专业名称", related='major_id.name', store=True)
+ department_id = fields.Many2one('student.organization', string="院系",store=True,domain="[('org_type', '=', 'college')]")
+
+
+ # user_id = fields.Many2one('res.users', string="系统用户", ondelete='restrict')
+ @api.depends('parent_path')
+ def _compute_org_path(self):
+ for record in self:
+ if record.class_id:
+ # 直接使用班级的 parent_path + 班级ID
+ record.org_path = f"{record.class_id.parent_path}{record.class_id.id}/"
+ else:
+ record.org_path = False
\ No newline at end of file
diff --git a/student_organization/models/student_organization.py b/student_organization/models/student_organization.py
new file mode 100644
index 0000000..5e9eccd
--- /dev/null
+++ b/student_organization/models/student_organization.py
@@ -0,0 +1,75 @@
+from odoo import api, fields, models
+
+
+class StudentOrganization(models.Model):
+ _name = 'student.organization'
+ _description = '学生架构'
+ _rec_name = 'full_name'
+ _order = 'parent_id, sequence, id'
+ _parent_store = True
+
+ _parent_order = 'sequence, name'
+
+ # 基本信息
+ name = fields.Char(string='名称', required=True, translate=True)
+ code = fields.Char(string='代码')
+ sequence = fields.Integer(string='序号', default=10)
+
+ org_type = fields.Selection([
+ ('college', '学院'),
+ ('major', '专业'),
+ ('class', '班级'), #用法 domain="[('org_type', '=', 'class')]"
+ ], string='类型', required=True)
+
+ # 层级关系
+ parent_id = fields.Many2one('student.organization', string='上级组织', ondelete='cascade')
+ child_ids = fields.One2many('student.organization', 'parent_id', string='下级组织')
+ parent_path = fields.Char(index=True)
+
+ # 完整名称(显示层级)
+ full_name = fields.Char(string='完整名称', compute='_compute_full_name', store=True)
+
+ # 负责人信息(学院用)
+ manager_id = fields.Many2one('res.users', string='负责人')
+
+ # 联系方式(学院用)
+ phone = fields.Char(string='电话')
+ mobile = fields.Char(string='手机')
+ email = fields.Char(string='邮箱')
+ address = fields.Text(string='办公地址')
+
+ # 专业专用字段
+ duration = fields.Integer(string='修业年限(年)', default=4)
+ degree = fields.Selection([
+ ('bachelor', '学士'),
+ ('master', '硕士'),
+ ('doctor', '博士'),
+ ], string='学位')
+
+ # 班级专用字段
+ grade = fields.Char(string='年级')
+
+ homeroom_teacher = fields.Many2one('hr.employee', string='导员')
+
+ # 统计字段
+ student_count = fields.Integer(string='学生数', compute='_compute_student_count')
+
+ @api.depends('parent_id.full_name', 'name')
+ def _compute_full_name(self):
+ for record in self:
+ if record.parent_id:
+ record.full_name = f'{record.parent_id.full_name} / {record.name}'
+ else:
+ record.full_name = record.name
+
+ def _compute_student_count(self):
+ for record in self:
+ if record.org_type == 'class':
+ # 班级的学生数
+ record.student_count = self.env['student.info'].search_count([('class_id', '=', record.id)])
+ else:
+ # 学院/专业的学生数(统计下级)
+ students = self.env['student.info'].search([
+ ('org_path', 'like', f'{record.parent_path}%')
+ ]) if record.parent_path else self.env['student.info'].search([])
+ record.student_count = len(students)
\ No newline at end of file
diff --git a/student_organization/security/ir.model.access.csv b/student_organization/security/ir.model.access.csv
new file mode 100644
index 0000000..bf78021
--- /dev/null
+++ b/student_organization/security/ir.model.access.csv
@@ -0,0 +1,5 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_student_organization_sys,student.organization.access.sys,model_student_organization,base.group_system,1,1,1,1
+access_student_info_sys,student.info.access.sys,model_student_info,base.group_system,1,1,1,1
+access_basic_grade_xx_sys,basic.grade.access.sys,model_basic_grade_xx,base.group_system,1,1,1,1
+access_basic_grade_bx_sys,basic.grade.access.sys,model_basic_grade_bx,base.group_system,1,1,1,1
\ No newline at end of file
diff --git a/student_organization/views/view_student_info.xml b/student_organization/views/view_student_info.xml
new file mode 100644
index 0000000..4cfd0ec
--- /dev/null
+++ b/student_organization/views/view_student_info.xml
@@ -0,0 +1,95 @@
+
+
+ student.info.list
+ student.info
+
+
+
+
+
+
+
+
+
+
+
+
+
+ student.info.form
+ student.info
+
+
+
+
+
+
+ 学生信息
+ student.info
+ list,form
+
+
+
+ student.info.search
+ student.info
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/student_organization/views/view_student_organization.xml b/student_organization/views/view_student_organization.xml
new file mode 100644
index 0000000..fbf46be
--- /dev/null
+++ b/student_organization/views/view_student_organization.xml
@@ -0,0 +1,105 @@
+
+
+
+ student.organization.list
+ student.organization
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ student.organization.form
+ student.organization
+
+
+
+
+
+
+
+
+
+ student.organization.sub.list
+ student.organization
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 组织架构
+ student.organization
+ list,form,search
+
+
+
+
+
+
+
+
+
\ No newline at end of file