Compare commits

..

1 Commits

Author SHA1 Message Date
hrrr
a9793e3320 学生组织架构 2026-06-10 16:05:38 +08:00
16 changed files with 731 additions and 0 deletions

View File

@ -0,0 +1 @@
from . import models

View File

@ -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',
}

View File

@ -0,0 +1,105 @@
<odoo>
<!-- ==================== 年级数据(大一到大四) ==================== -->
<record id="grade_dy" model="basic.grade.bx">
<field name="grade">大一</field>
<field name="sequence">1</field>
</record>
<record id="grade_de" model="basic.grade.bx">
<field name="grade">大二</field>
<field name="sequence">2</field>
</record>
<record id="grade_ds" model="basic.grade.bx">
<field name="grade">大三</field>
<field name="sequence">3</field>
</record>
<record id="grade_dsi" model="basic.grade.bx">
<field name="grade">大四</field>
<field name="sequence">4</field>
</record>
<!-- ==================== 年级数据2010-2030 ==================== -->
<record id="grade_2010" model="basic.grade.xx">
<field name="grade">2010</field>
<field name="sequence">1</field>
</record>
<record id="grade_2011" model="basic.grade.xx">
<field name="grade">2011</field>
<field name="sequence">2</field>
</record>
<record id="grade_2012" model="basic.grade.xx">
<field name="grade">2012</field>
<field name="sequence">3</field>
</record>
<record id="grade_2013" model="basic.grade.xx">
<field name="grade">2013</field>
<field name="sequence">4</field>
</record>
<record id="grade_2014" model="basic.grade.xx">
<field name="grade">2014</field>
<field name="sequence">5</field>
</record>
<record id="grade_2015" model="basic.grade.xx">
<field name="grade">2015</field>
<field name="sequence">6</field>
</record>
<record id="grade_2016" model="basic.grade.xx">
<field name="grade">2016</field>
<field name="sequence">7</field>
</record>
<record id="grade_2017" model="basic.grade.xx">
<field name="grade">2017</field>
<field name="sequence">8</field>
</record>
<record id="grade_2018" model="basic.grade.xx">
<field name="grade">2018</field>
<field name="sequence">9</field>
</record>
<record id="grade_2019" model="basic.grade.xx">
<field name="grade">2019</field>
<field name="sequence">10</field>
</record>
<record id="grade_2020" model="basic.grade.xx">
<field name="grade">2020</field>
<field name="sequence">11</field>
</record>
<record id="grade_2021" model="basic.grade.xx">
<field name="grade">2021</field>
<field name="sequence">12</field>
</record>
<record id="grade_2022" model="basic.grade.xx">
<field name="grade">2022</field>
<field name="sequence">13</field>
</record>
<record id="grade_2023" model="basic.grade.xx">
<field name="grade">2023</field>
<field name="sequence">14</field>
</record>
<record id="grade_2024" model="basic.grade.xx">
<field name="grade">2024</field>
<field name="sequence">15</field>
</record>
<record id="grade_2025" model="basic.grade.xx">
<field name="grade">2025</field>
<field name="sequence">16</field>
</record>
<record id="grade_2026" model="basic.grade.xx">
<field name="grade">2026</field>
<field name="sequence">17</field>
</record>
<record id="grade_2027" model="basic.grade.xx">
<field name="grade">2027</field>
<field name="sequence">18</field>
</record>
<record id="grade_2028" model="basic.grade.xx">
<field name="grade">2028</field>
<field name="sequence">19</field>
</record>
<record id="grade_2029" model="basic.grade.xx">
<field name="grade">2029</field>
<field name="sequence">20</field>
</record>
<record id="grade_2030" model="basic.grade.xx">
<field name="grade">2030</field>
<field name="sequence">21</field>
</record>
</odoo>

View File

@ -0,0 +1,249 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ==================== 学院数据 ==================== -->
<record id="org_college_1" model="student.organization">
<field name="name">信息工程学院</field>
<field name="code">01</field>
<field name="org_type">college</field>
<field name="sequence">10</field>
</record>
<record id="org_college_2" model="student.organization">
<field name="name">经济管理学院</field>
<field name="code">02</field>
<field name="org_type">college</field>
<field name="sequence">20</field>
</record>
<!-- ==================== 信息工程学院专业 ==================== -->
<record id="org_major_1_1" model="student.organization">
<field name="name">计算机科学与技术</field>
<field name="code">080901</field>
<field name="org_type">major</field>
<field name="parent_id" ref="org_college_1"/>
<field name="sequence">10</field>
<field name="duration">4</field>
<field name="degree">bachelor</field>
</record>
<record id="org_major_1_2" model="student.organization">
<field name="name">软件工程</field>
<field name="code">080902</field>
<field name="org_type">major</field>
<field name="parent_id" ref="org_college_1"/>
<field name="sequence">20</field>
<field name="duration">4</field>
<field name="degree">bachelor</field>
</record>
<record id="org_major_1_3" model="student.organization">
<field name="name">数据科学与大数据技术</field>
<field name="code">080910T</field>
<field name="org_type">major</field>
<field name="parent_id" ref="org_college_1"/>
<field name="sequence">30</field>
<field name="duration">4</field>
<field name="degree">bachelor</field>
</record>
<!-- ==================== 经济管理学院专业 ==================== -->
<record id="org_major_2_1" model="student.organization">
<field name="name">工商管理</field>
<field name="code">120201K</field>
<field name="org_type">major</field>
<field name="parent_id" ref="org_college_2"/>
<field name="sequence">10</field>
<field name="duration">4</field>
<field name="degree">bachelor</field>
</record>
<record id="org_major_2_2" model="student.organization">
<field name="name">市场营销</field>
<field name="code">120202</field>
<field name="org_type">major</field>
<field name="parent_id" ref="org_college_2"/>
<field name="sequence">20</field>
<field name="duration">4</field>
<field name="degree">bachelor</field>
</record>
<record id="org_major_2_3" model="student.organization">
<field name="name">会计学</field>
<field name="code">120203K</field>
<field name="org_type">major</field>
<field name="parent_id" ref="org_college_2"/>
<field name="sequence">30</field>
<field name="duration">4</field>
<field name="degree">bachelor</field>
</record>
<!-- ==================== 信息工程学院 - 计算机科学与技术专业班级 ==================== -->
<record id="org_class_1_1_1" model="student.organization">
<field name="name">计算机1班</field>
<field name="code">08090101</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_1_1"/>
<field name="sequence">10</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_1_1_2" model="student.organization">
<field name="name">计算机2班</field>
<field name="code">08090102</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_1_1"/>
<field name="sequence">20</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_1_1_3" model="student.organization">
<field name="name">计算机3班</field>
<field name="code">08090103</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_1_1"/>
<field name="sequence">30</field>
<field name="grade">2024级</field>
</record>
<!-- 信息工程学院 - 软件工程专业班级 -->
<record id="org_class_1_2_1" model="student.organization">
<field name="name">软件1班</field>
<field name="code">08090201</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_1_2"/>
<field name="sequence">10</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_1_2_2" model="student.organization">
<field name="name">软件2班</field>
<field name="code">08090202</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_1_2"/>
<field name="sequence">20</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_1_2_3" model="student.organization">
<field name="name">软件3班</field>
<field name="code">08090203</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_1_2"/>
<field name="sequence">30</field>
<field name="grade">2024级</field>
</record>
<!-- 信息工程学院 - 数据科学与大数据技术专业班级 -->
<record id="org_class_1_3_1" model="student.organization">
<field name="name">大数据1班</field>
<field name="code">080910T01</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_1_3"/>
<field name="sequence">10</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_1_3_2" model="student.organization">
<field name="name">大数据2班</field>
<field name="code">080910T02</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_1_3"/>
<field name="sequence">20</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_1_3_3" model="student.organization">
<field name="name">大数据3班</field>
<field name="code">080910T03</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_1_3"/>
<field name="sequence">30</field>
<field name="grade">2024级</field>
</record>
<!-- ==================== 经济管理学院 - 工商管理专业班级 ==================== -->
<record id="org_class_2_1_1" model="student.organization">
<field name="name">工商1班</field>
<field name="code">120201K01</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_2_1"/>
<field name="sequence">10</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_2_1_2" model="student.organization">
<field name="name">工商2班</field>
<field name="code">120201K02</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_2_1"/>
<field name="sequence">20</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_2_1_3" model="student.organization">
<field name="name">工商3班</field>
<field name="code">120201K03</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_2_1"/>
<field name="sequence">30</field>
<field name="grade">2024级</field>
</record>
<!-- 经济管理学院 - 市场营销专业班级 -->
<record id="org_class_2_2_1" model="student.organization">
<field name="name">营销1班</field>
<field name="code">12020201</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_2_2"/>
<field name="sequence">10</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_2_2_2" model="student.organization">
<field name="name">营销2班</field>
<field name="code">12020202</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_2_2"/>
<field name="sequence">20</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_2_2_3" model="student.organization">
<field name="name">营销3班</field>
<field name="code">12020203</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_2_2"/>
<field name="sequence">30</field>
<field name="grade">2024级</field>
</record>
<!-- 经济管理学院 - 会计学专业班级 -->
<record id="org_class_2_3_1" model="student.organization">
<field name="name">会计1班</field>
<field name="code">120203K01</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_2_3"/>
<field name="sequence">10</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_2_3_2" model="student.organization">
<field name="name">会计2班</field>
<field name="code">120203K02</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_2_3"/>
<field name="sequence">20</field>
<field name="grade">2024级</field>
</record>
<record id="org_class_2_3_3" model="student.organization">
<field name="name">会计3班</field>
<field name="code">120203K03</field>
<field name="org_type">class</field>
<field name="parent_id" ref="org_major_2_3"/>
<field name="sequence">30</field>
<field name="grade">2024级</field>
</record>
</odoo>

View File

@ -0,0 +1,3 @@
from . import student_organization
from . import basic_grade
from . import student_info

View File

@ -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='序号')

View File

@ -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

View File

@ -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)

View File

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_student_organization_sys student.organization.access.sys model_student_organization base.group_system 1 1 1 1
3 access_student_info_sys student.info.access.sys model_student_info base.group_system 1 1 1 1
4 access_basic_grade_xx_sys basic.grade.access.sys model_basic_grade_xx base.group_system 1 1 1 1
5 access_basic_grade_bx_sys basic.grade.access.sys model_basic_grade_bx base.group_system 1 1 1 1

View File

@ -0,0 +1,95 @@
<odoo>
<record id="list_student_info" model="ir.ui.view">
<field name="name">student.info.list</field>
<field name="model">student.info</field>
<field name="arch" type="xml">
<list>
<field name="stu_num"/>
<field name="stu_name"/>
<field name="stu_sex"/>
<field name="stu_grade_xx"/>
<field name="stu_status"/>
<field name="stu_phone"/>
</list>
</field>
</record>
<record id="form_student_info" model="ir.ui.view">
<field name="name">student.info.form</field>
<field name="model">student.info</field>
<field name="arch" type="xml">
<form string="学生信息">
<sheet>
<div class="oe_button_box" name="button_box"/>
<div class="oe_title">
<div class="d-flex justify-content-between align-items-start">
<h1 class="flex-grow-1">
<field name="stu_name" placeholder="姓名"/>
</h1>
<field name="stu_photo" widget="image" class="rounded-circle"
style="width: 64px; height: 64px; object-fit: cover; margin: 0 12px;"/>
</div>
</div>
<notebook>
<page string="基本信息">
<group>
<group>
<field name="stu_num" placeholder="学号"/>
<field name="stu_sex"/>
<field name="stu_birthday"/>
<field name="stu_id_card"/>
<field name="stu_nation"/>
<field name="stu_shenfen"/>
<field name="department_id"/>
<field name="major_id"/>
<field name="class_id"/>
</group>
<group>
<field name="stu_phone"/>
<field name="stu_email"/>
<field name="stu_address"/>
<field name="stu_emergency_contact"/>
<field name="stu_emergency_phone"/>
</group>
</group>
</page>
<page string="学业信息">
<group>
<field name="stu_begin_date"/>
<field name="stu_end_date"/>
<field name="stu_grade_xx"/>
<field name="stu_grade_bx"/>
<field name="stu_status"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="act_student_info" model="ir.actions.act_window">
<field name="name">学生信息</field>
<field name="res_model">student.info</field>
<field name="view_mode">list,form</field>
</record>
<record id="view_student_info_search" model="ir.ui.view">
<field name="name">student.info.search</field>
<field name="model">student.info</field>
<field name="arch" type="xml">
<search>
<searchpanel>
<field name="class_id" string="组织架构" />
</searchpanel>
</search>
</field>
</record>
<record id="menu_student_info_second" model="ir.ui.menu">
<field name="name">学生</field>
<field name="parent_id" ref="student_organization.menu_student_organization_root"/>
<field name="sequence">9</field>
<field name="action" ref="act_student_info"/>
</record>
</odoo>

View File

@ -0,0 +1,105 @@
<odoo>
<!-- 列表视图 -->
<record id="view_student_organization_list" model="ir.ui.view">
<field name="name">student.organization.list</field>
<field name="model">student.organization</field>
<field name="arch" type="xml">
<list>
<field name="code"/>
<field name="name"/>
<field name="org_type"/>
<field name="manager_id"/>
<field name="student_count" widget="integer"/>
<field name="sequence" widget="handle"/>
</list>
</field>
</record>
<!-- 表单视图 -->
<record id="view_student_organization_form" model="ir.ui.view">
<field name="name">student.organization.form</field>
<field name="model">student.organization</field>
<field name="arch" type="xml">
<form string="组织架构">
<sheet>
<div class="oe_title">
<h1>
<field name="name" placeholder="名称"/>
</h1>
</div>
<group name="basic" string="基本信息">
<group>
<field name="code"/>
<field name="sequence"/>
<field name="org_type"/>
<field name="parent_id"/>
<field name="full_name" readonly="1"/>
</group>
<group>
<field name="manager_id"/>
<field name="phone"/>
<field name="mobile"/>
<field name="email"/>
<field name="address"/>
</group>
</group>
<group name="major_fields" string="专业信息" invisible="org_type != 'major'">
<group>
<field name="duration"/>
<field name="degree"/>
</group>
</group>
<group name="class_fields" string="班级信息" invisible="org_type != 'class'">
<group>
<field name="grade"/>
<field name="homeroom_teacher"/>
</group>
</group>
<notebook>
<page string="下级组织">
<field name="child_ids" view_id="view_student_organization_sub_list"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<!-- 内嵌子列表视图 -->
<record id="view_student_organization_sub_list" model="ir.ui.view">
<field name="name">student.organization.sub.list</field>
<field name="model">student.organization</field>
<field name="arch" type="xml">
<list editable="bottom">
<field name="code"/>
<field name="name"/>
<field name="org_type"/>
<field name="manager_id"/>
<field name="sequence" widget="handle"/>
</list>
</field>
</record>
<!-- 动作 -->
<record id="act_student_organization" model="ir.actions.act_window">
<field name="name">组织架构</field>
<field name="res_model">student.organization</field>
<field name="view_mode">list,form,search</field>
</record>
<!-- 菜单 -->
<record id="menu_student_organization_root" model="ir.ui.menu">
<field name="name">学生架构</field>
<field name="sequence">11</field>
</record>
<record id="menu_student_organization_second" model="ir.ui.menu">
<field name="name">组织架构</field>
<field name="parent_id" ref="menu_student_organization_root"/>
<field name="action" ref="act_student_organization"/>
</record>
</odoo>