school_gengxin #2

Merged
liuhaoran merged 1 commits from dev_haoran into master 2026-06-16 19:30:44 +08:00
15 changed files with 256 additions and 83 deletions

View File

@ -25,7 +25,7 @@
'views/view_course_teaching_class.xml', 'views/view_course_teaching_class.xml',
'views/view_course_student_score.xml', 'views/view_course_student_score.xml',
'views/view_course_homework_student.xml', 'views/view_course_homework_student.xml',
'views/view_teach_class.xml', # 'views/view_teach_class.xml',
], ],

View File

@ -7,4 +7,4 @@ from . import course_homework_submit
from . import course_score_item from . import course_score_item
from . import course_student_score from . import course_student_score
from . import course_teaching_class from . import course_teaching_class
from . import teach_class # from . import teach_class

View File

@ -1,5 +1,6 @@
from odoo import api, fields, models from odoo import api, fields, models
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from datetime import datetime
class CourseHomework(models.Model): class CourseHomework(models.Model):
_name = 'course.homework' _name = 'course.homework'
@ -14,7 +15,7 @@ class CourseHomework(models.Model):
course_id = fields.Many2one('learning.course', string='所属课程', required=True, ondelete='cascade') course_id = fields.Many2one('learning.course', string='所属课程', required=True, ondelete='cascade')
chapter_id = fields.Many2one('course.chapter', string='关联章节') chapter_id = fields.Many2one('course.chapter', string='关联章节')
total_score = fields.Float(string='作业总分', default=100.0, required=True) total_score = fields.Float(string='作业总分', default=100.0, required=True)
publish_time = fields.Datetime(string='发布时间', default=fields.Datetime.now) publish_time = fields.Datetime(string='发布时间')
deadline = fields.Datetime(string='提交截止时间', required=True) deadline = fields.Datetime(string='提交截止时间', required=True)
late_deadline = fields.Datetime(string='补交截止时间') late_deadline = fields.Datetime(string='补交截止时间')
state = fields.Selection([ state = fields.Selection([
@ -22,7 +23,7 @@ class CourseHomework(models.Model):
('published', '已发布'), ('published', '已发布'),
('closed', '已关闭'), ('closed', '已关闭'),
], string='状态', default='draft', tracking=True) ], string='状态', default='draft', tracking=True)
is_submit_open = fields.Boolean(string='提交通道开启', default=True) is_submit_open = fields.Boolean(string='提交通道开启', compute='_compute_submit_channel', store=True)
allow_late = fields.Boolean(string='允许补交', default=False) allow_late = fields.Boolean(string='允许补交', default=False)
total_students = fields.Integer(string='应提交人数', compute='_compute_submit_stats') total_students = fields.Integer(string='应提交人数', compute='_compute_submit_stats')
submitted_count = fields.Integer(string='已提交人数', compute='_compute_submit_stats') submitted_count = fields.Integer(string='已提交人数', compute='_compute_submit_stats')
@ -32,60 +33,133 @@ class CourseHomework(models.Model):
teaching_class_id = fields.Many2one('course.teaching_class', string='教学班', teaching_class_id = fields.Many2one('course.teaching_class', string='教学班',
ondelete='cascade', domain="[('course_id', '=', course_id)]") ondelete='cascade', domain="[('course_id', '=', course_id)]")
submit_ids = fields.One2many('course.homework.submit', 'homework_id', string='提交记录') submit_ids = fields.One2many('course.homework.submit', 'homework_id', string='提交记录')
score_item_ids = fields.One2many('course.score.item', 'homework_id', string='提交记录') score_item_ids = fields.One2many('course.score.item', 'homework_id', string='成绩项')
# ====================== 新增:自动计算提交通道是否开启 ======================
@api.depends('state', 'deadline')
def _compute_submit_channel(self):
now = fields.Datetime.now()
for record in self:
# 仅已发布、且当前时间未到截止时间,通道才开启
if record.state == 'published' and record.deadline and record.deadline > now:
record.is_submit_open = True
else:
record.is_submit_open = False
# ====================== 统计提交人数 ======================
def _compute_submit_stats(self): def _compute_submit_stats(self):
for record in self: for record in self:
# 按教学班 + 选课状态筛选学生
domain = [('state', '=', 'enrolled')] domain = [('state', '=', 'enrolled')]
if record.teaching_class_id: if record.teaching_class_id:
# 优先按当前作业所属教学班统计(核心修正)
domain.append(('teaching_class_id', '=', record.teaching_class_id.id)) domain.append(('teaching_class_id', '=', record.teaching_class_id.id))
else: else:
# 无教学班时,回退到整门课程统计
domain.append(('course_id', '=', record.course_id.id)) domain.append(('course_id', '=', record.course_id.id))
enrollments = self.env['course.teaching_class.enrollment'].search(domain) enrollments = self.env['course.teaching_class.enrollment'].search(domain)
student_ids = enrollments.mapped('student_id') student_ids = enrollments.mapped('student_id')
record.total_students = len(student_ids) record.total_students = len(student_ids)
# 该作业所有提交记录
base_domain = [('homework_id', '=', record.id)] base_domain = [('homework_id', '=', record.id)]
all_submit = self.env['course.homework.submit'].search(base_domain) all_submit = self.env['course.homework.submit'].search(base_domain)
total_submit = len(all_submit)
deadline_val = record.deadline # 已提交 = 状态submitted/graded
if deadline_val: submit_ok = all_submit.filtered(lambda s: s.state in ['submitted', 'graded'])
# 按时提交 late_submit = submit_ok.filtered(lambda s: s.is_late)
on_time_submit = all_submit.filtered(lambda s: s.submit_time and s.submit_time <= deadline_val)
# 逾期提交 record.submitted_count = len(submit_ok)
late_submit = all_submit.filtered(lambda s: s.submit_time and s.submit_time > deadline_val)
record.submitted_count = len(on_time_submit)
record.late_count = len(late_submit) record.late_count = len(late_submit)
else: # 未提交 = 总人数 - 已提交人数
record.submitted_count = total_submit record.unsubmitted_count = record.total_students - len(submit_ok)
record.late_count = 0
# 未提交 = 班级总人数 - 所有提交人数
record.unsubmitted_count = record.total_students - total_submit
# ====================== 发布作业(核心逻辑修正) ======================
def action_publish(self): def action_publish(self):
self.ensure_one()
if self.state == 'published':
return
self.state = 'published' self.state = 'published'
self.publish_time = fields.Datetime.now() self.publish_time = fields.Datetime.now()
# 1. 批量为本班学生生成空白未提交记录
self._auto_create_empty_submit()
# 2. 推送作业通知给学生
self._send_homework_notice_to_students()
def _send_homework_notice_to_students(self):
"""发布作业后推送消息给教学班学生"""
self.ensure_one()
if not self.teaching_class_id:
return
# 获取当前教学班所有已选课学生
enrolls = self.env['course.teaching_class.enrollment'].search([
('teaching_class_id', '=', self.teaching_class_id.id),
('state', '=', 'enrolled')
])
student_users = enrolls.mapped('student_id.user_id').filtered(lambda u: u)
if not student_users:
return
# 消息内容
subject = f"新作业发布:{self.name}"
body = f"""
<p>课程{self.course_id.name}</p>
<p>作业名称{self.name}</p>
<p>截止时间{self.deadline or ''}</p>
<p>作业要求{self.requirement or ''}</p>
"""
# 发送消息(关联当前作业记录,学生在消息中心可直接跳转)
self.message_post(
subject=subject,
body=body,
partner_ids=student_users.mapped('partner_id').ids,
subtype_xmlid='mail.mt_comment'
)
def _auto_create_empty_submit(self):
"""仅发布时执行:自动给教学班所有已选课学生生成空白未提交记录"""
self.ensure_one()
if not self.teaching_class_id:
return
# 获取当前教学班已注册学生
enroll_records = self.env['course.teaching_class.enrollment'].search([
('teaching_class_id', '=', self.teaching_class_id.id),
('state', '=', 'enrolled')
])
student_ids = enroll_records.mapped('student_id').ids
if not student_ids:
return
# 过滤掉已经存在提交记录的学生,避免重复生成
exist_student_ids = self.submit_ids.mapped('student_id').ids
need_create_stu = [sid for sid in student_ids if sid not in exist_student_ids]
if not need_create_stu:
return
submit_model = self.env['course.homework.submit']
batch_vals = []
for stu_id in need_create_stu:
batch_vals.append({
'homework_id': self.id,
'student_id': stu_id,
'state': 'unsubmit' # 默认未提交
})
# 批量创建空白提交记录
submit_model.create(batch_vals)
# ====================== 草稿/关闭作业 ======================
def action_draft(self): def action_draft(self):
self.state = 'draft' self.state = 'draft'
def action_close(self): def action_close(self):
self.state = 'closed' self.state = 'closed'
# 关闭作业强制关闭提交通道
self.is_submit_open = False self.is_submit_open = False
# ====================== 时间校验约束 ======================
@api.constrains('deadline', 'late_deadline') @api.constrains('deadline', 'late_deadline')
def _check_deadline(self): def _check_deadline(self):
for record in self: for record in self:
if record.late_deadline and record.late_deadline < record.deadline: if record.late_deadline and record.late_deadline < record.deadline:
raise ValidationError('补交截止时间必须晚于提交截止时间') raise ValidationError('补交截止时间必须晚于提交截止时间')
# ====================== 学生提交相关计算字段 ======================
def _get_current_student(self): def _get_current_student(self):
"""获取当前登录的学生""" """获取当前登录的学生"""
student = self.env['student.info'].search([('user_id', '=', self.env.uid)], limit=1) student = self.env['student.info'].search([('user_id', '=', self.env.uid)], limit=1)
@ -103,7 +177,7 @@ class CourseHomework(models.Model):
my_submit_filename = fields.Char(string='文件名', compute='_compute_my_submit') my_submit_filename = fields.Char(string='文件名', compute='_compute_my_submit')
my_submit_content = fields.Html(string='提交内容', compute='_compute_my_submit') my_submit_content = fields.Html(string='提交内容', compute='_compute_my_submit')
# 提交状态(用于前端显示) # 前端展示提交状态
submit_state = fields.Selection([ submit_state = fields.Selection([
('not_submitted', '未提交'), ('not_submitted', '未提交'),
('submitted', '已提交'), ('submitted', '已提交'),
@ -111,18 +185,24 @@ class CourseHomework(models.Model):
('late', '逾期提交'), ('late', '逾期提交'),
], string='提交状态', compute='_compute_my_submit') ], string='提交状态', compute='_compute_my_submit')
# 是否可以提交 # 是否可以提交:严格依赖提交通道开启状态
can_submit = fields.Boolean(string='可以提交', compute='_compute_can_submit') can_submit = fields.Boolean(string='可以提交', compute='_compute_can_submit')
@api.depends('submit_ids') @api.depends('submit_ids', 'submit_ids.student_id', 'submit_ids.state', 'submit_ids.is_late')
def _compute_my_submit(self): def _compute_my_submit(self):
for record in self:
student = self._get_current_student() student = self._get_current_student()
if student: for record in self:
if not student:
record.my_submit_id = False
record.submit_state = 'not_submitted'
continue
submit = record.submit_ids.filtered(lambda s: s.student_id == student) submit = record.submit_ids.filtered(lambda s: s.student_id == student)
if submit: if not submit:
record.my_submit_id = False
record.submit_state = 'not_submitted'
continue
submit = submit[0]
record.my_submit_id = submit.id record.my_submit_id = submit.id
record.my_submit_state = submit.state
record.my_submit_time = submit.submit_time record.my_submit_time = submit.submit_time
record.my_score = submit.score record.my_score = submit.score
record.my_grade_level = submit.grade_level record.my_grade_level = submit.grade_level
@ -131,49 +211,62 @@ class CourseHomework(models.Model):
record.my_submit_filename = submit.submit_filename record.my_submit_filename = submit.submit_filename
record.my_submit_content = submit.submit_content record.my_submit_content = submit.submit_content
# 计算提交状态 # 适配前端状态展示
if submit.state == 'graded': if submit.state == 'unsubmit':
record.submit_state = 'not_submitted'
elif submit.state == 'graded':
record.submit_state = 'graded' record.submit_state = 'graded'
elif submit.is_late: elif submit.is_late:
record.submit_state = 'late' record.submit_state = 'late'
else: else:
record.submit_state = 'submitted' record.submit_state = 'submitted'
else:
record.submit_state = 'not_submitted'
else:
record.submit_state = 'not_submitted'
@api.depends('state', 'is_submit_open', 'my_submit_state')
def _compute_can_submit(self): def _compute_can_submit(self):
for record in self: for record in self:
# 作业已发布、提交通道开启、未截止、且未提交 # 核心条件:提交通道必须开启,且未提交/未批改
can = (record.state == 'published' and can = False
record.is_submit_open and if record.is_submit_open and record.my_submit_state not in ['submitted', 'graded']:
record.deadline and can = True
record.deadline > fields.Datetime.now() and
record.my_submit_state != 'submitted' and
record.my_submit_state != 'graded')
record.can_submit = can record.can_submit = can
# ====================== 提交作业按钮方法 ======================
def action_submit_homework(self): def action_submit_homework(self):
"""学生提交作业"""
self.ensure_one() self.ensure_one()
student = self._get_current_student() student = self._get_current_student()
if not student: if not student:
raise ValidationError('请先关联学生信息') raise ValidationError('请先关联学生信息')
# 检查提交权限 # 核心校验:通道关闭直接报错,禁止进入提交弹窗
if not self.can_submit: if not self.can_submit:
raise ValidationError('当前无法提交作业') raise ValidationError('当前提交通道已关闭,无法提交作业')
# 打开提交表单 # 后续打开提交表单逻辑不变
exist_submit = self.env['course.homework.submit'].search([
('homework_id', '=', self.id),
('student_id', '=', student.id)
], limit=1)
if exist_submit:
return {
'type': 'ir.actions.act_window',
'name': '提交作业',
'res_model': 'course.homework.submit',
'res_id': exist_submit.id,
'view_mode': 'form',
'target': 'new',
}
else:
return { return {
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'name': '提交作业', 'name': '提交作业',
'res_model': 'course.homework.submit', 'res_model': 'course.homework.submit',
'view_mode': 'form', 'view_mode': 'form',
'target': 'new', 'target': 'new',
'context': { 'context': {'default_homework_id': self.id, 'default_student_id': student.id},
'default_homework_id': self.id,
'default_student_id': student.id,
},
} }
# 移除原create里自动生成空白提交的代码草稿不生成
def create(self, vals):
homework_record = super().create(vals)
# 草稿状态不执行自动生成提交记录移到action_publish
return homework_record

View File

@ -38,6 +38,7 @@ class CourseHomeworkSubmit(models.Model):
grader_id = fields.Many2one('res.users', string='批改人') grader_id = fields.Many2one('res.users', string='批改人')
grade_time = fields.Datetime(string='批改时间') grade_time = fields.Datetime(string='批改时间')
state = fields.Selection([ state = fields.Selection([
('unsubmit', '未提交'),
('submitted', '已提交'), ('submitted', '已提交'),
('graded', '已批改'), ('graded', '已批改'),
], string='状态', default='submitted') ], string='状态', default='submitted')
@ -87,6 +88,32 @@ class CourseHomeworkSubmit(models.Model):
else: else:
record.grade_level = False record.grade_level = False
@api.model
def create(self, vals):
submit_rec = super().create(vals)
self._check_submit_change_state(submit_rec)
return submit_rec
def write(self, vals):
res = super().write(vals)
for rec in self:
self._check_submit_change_state(rec)
return res
def _check_submit_change_state(self, submit_rec):
"""统一提取状态判断逻辑create和write共用"""
# 仅空白未提交状态才自动切换
if submit_rec.state != 'unsubmit':
return
# 判断:有提交文件 或者 填写了提交内容 = 完成提交
has_file = bool(submit_rec.submit_file)
has_content = bool(submit_rec.submit_content)
if has_file or has_content:
submit_rec.write({
'state': 'submitted',
'submit_time': fields.Datetime.now()
})
# class CourseHomeworkSubmitBatchGrade(models.TransientModel): # class CourseHomeworkSubmitBatchGrade(models.TransientModel):
# _name = 'course.homework.submit.batch.grade' # _name = 'course.homework.submit.batch.grade'
# _description = '作业批量批改向导' # _description = '作业批量批改向导'

View File

@ -289,3 +289,6 @@ class CourseTeachingClassEnrollmentWizard(models.TransientModel):
if create_list: if create_list:
self.env['course.teaching_class.enrollment'].create(create_list) self.env['course.teaching_class.enrollment'].create(create_list)
return {'type': 'ir.actions.act_window_close'} return {'type': 'ir.actions.act_window_close'}

View File

@ -11,3 +11,4 @@ class TeachClass(models.Model):
stu_sex = fields.Selection(related='student_id.stu_sex', string="性别") stu_sex = fields.Selection(related='student_id.stu_sex', string="性别")
stu_phone = fields.Char(related='student_id.stu_phone', string="手机号") stu_phone = fields.Char(related='student_id.stu_phone', string="手机号")
stu_grade_xx = fields.Many2one(related='student_id.stu_grade_xx', string="年级") stu_grade_xx = fields.Many2one(related='student_id.stu_grade_xx', string="年级")

View File

@ -11,8 +11,6 @@ access_course_syllabus_sys,course.syllabus.access.sys,model_course_syllabus,base
access_course_teaching_class_sys,course.teaching.class.access.sys,model_course_teaching_class,base.group_system,1,1,1,1 access_course_teaching_class_sys,course.teaching.class.access.sys,model_course_teaching_class,base.group_system,1,1,1,1
access_course_teaching_class_enrollment_sys,course.teaching.class.enrollment.access.sys,model_course_teaching_class_enrollment,base.group_system,1,1,1,1 access_course_teaching_class_enrollment_sys,course.teaching.class.enrollment.access.sys,model_course_teaching_class_enrollment,base.group_system,1,1,1,1
access_course_teaching_class_enrollment_wizard_sys,course.teaching.class.enrollment.access.sys,model_course_teaching_class_enrollment_wizard,base.group_system,1,1,1,1 access_course_teaching_class_enrollment_wizard_sys,course.teaching.class.enrollment.access.sys,model_course_teaching_class_enrollment_wizard,base.group_system,1,1,1,1
access_teach_class_teacher,teach.class.access.teacher,model_teach_class,edu_base.group_teacher,1,1,0,0
access_teach_class_sys,teach.class.access.sys,model_teach_class,base.group_system,1,1,1,1
access_course_teaching_class_teacher,course.teaching.class.access.teacher,model_course_teaching_class,edu_base.group_teacher,1,0,0,0 access_course_teaching_class_teacher,course.teaching.class.access.teacher,model_course_teaching_class,edu_base.group_teacher,1,0,0,0
access_course_teaching_class_enrollment_teacher,course.teaching.class.enrollment.access.teacher,model_course_teaching_class_enrollment,edu_base.group_teacher,1,0,0,0 access_course_teaching_class_enrollment_teacher,course.teaching.class.enrollment.access.teacher,model_course_teaching_class_enrollment,edu_base.group_teacher,1,0,0,0
access_course_homework_teacher,course.homework.access.teacher,model_course_homework,edu_base.group_teacher,1,1,1,0 access_course_homework_teacher,course.homework.access.teacher,model_course_homework,edu_base.group_teacher,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
11 access_course_teaching_class_sys course.teaching.class.access.sys model_course_teaching_class base.group_system 1 1 1 1
12 access_course_teaching_class_enrollment_sys course.teaching.class.enrollment.access.sys model_course_teaching_class_enrollment base.group_system 1 1 1 1
13 access_course_teaching_class_enrollment_wizard_sys course.teaching.class.enrollment.access.sys model_course_teaching_class_enrollment_wizard base.group_system 1 1 1 1
access_teach_class_teacher teach.class.access.teacher model_teach_class edu_base.group_teacher 1 1 0 0
access_teach_class_sys teach.class.access.sys model_teach_class base.group_system 1 1 1 1
14 access_course_teaching_class_teacher course.teaching.class.access.teacher model_course_teaching_class edu_base.group_teacher 1 0 0 0
15 access_course_teaching_class_enrollment_teacher course.teaching.class.enrollment.access.teacher model_course_teaching_class_enrollment edu_base.group_teacher 1 0 0 0
16 access_course_homework_teacher course.homework.access.teacher model_course_homework edu_base.group_teacher 1 1 1 0

View File

@ -204,9 +204,14 @@
</record> </record>
<!-- 二级菜单 --> <!-- 二级菜单 -->
<record id="menu_course_teaching_class_root" model="ir.ui.menu">
<field name="name">我的班级</field>
<field name="groups_id" eval="[(6,0,[ref('base.group_system'),ref('edu_base.group_teacher')])]"/>
</record>
<record id="menu_course_teaching_class" model="ir.ui.menu"> <record id="menu_course_teaching_class" model="ir.ui.menu">
<field name="name">教学班管理</field> <field name="name">我的班级</field>
<field name="parent_id" ref="menu_learning_center_root"/> <field name="parent_id" ref="menu_course_teaching_class_root"/>
<field name="action" ref="act_course_teaching_class"/> <field name="action" ref="act_course_teaching_class"/>
<field name="sequence">15</field> <field name="sequence">15</field>
<field name="groups_id" eval="[(6,0,[ref('base.group_system'),ref('edu_base.group_teacher')])]"/> <field name="groups_id" eval="[(6,0,[ref('base.group_system'),ref('edu_base.group_teacher')])]"/>

View File

@ -1,4 +1,6 @@
from odoo import api,models,fields from odoo import api,models,fields
from odoo.exceptions import ValidationError
class StudentInfo(models.Model): class StudentInfo(models.Model):
_name="student.info" _name="student.info"
@ -24,7 +26,7 @@ class StudentInfo(models.Model):
stu_begin_date = fields.Date(string="入学日期", required=True, tracking=True) stu_begin_date = fields.Date(string="入学日期", required=True, tracking=True)
stu_end_date = fields.Date(string="毕业日期") 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_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_grade_bx=fields.Many2one('basic.grade.xx',string='年级',readonly=1)
stu_status = fields.Selection([ stu_status = fields.Selection([
('studying', '在读'), ('studying', '在读'),
('graduated', '已毕业'), ('graduated', '已毕业'),
@ -42,13 +44,57 @@ class StudentInfo(models.Model):
major_name = fields.Char(string="专业名称", related='major_id.name', store=True) major_name = fields.Char(string="专业名称", related='major_id.name', store=True)
department_id = fields.Many2one('student.organization', string="院系",store=True,domain="[('org_type', '=', 'college')]") department_id = fields.Many2one('student.organization', string="院系",store=True,domain="[('org_type', '=', 'college')]")
# 新增关联系统登录用户解决之前作业通知报错缺少user_id的问题
user_id = fields.Many2one('res.users', string="系统登录账号", ondelete='restrict')
# user_id = fields.Many2one('res.users', string="系统用户", ondelete='restrict')
@api.depends('parent_path') @api.depends('parent_path')
def _compute_org_path(self): def _compute_org_path(self):
for record in self: for record in self:
if record.class_id: if record.class_id:
# 直接使用班级的 parent_path + 班级ID
record.org_path = f"{record.class_id.parent_path}{record.class_id.id}/" record.org_path = f"{record.class_id.parent_path}{record.class_id.id}/"
else: else:
record.org_path = False record.org_path = False
@api.model
def create(self, vals):
# 1. 先校验学号,提前取出学号避免多次读取
stu_num = vals.get('stu_num', '').strip()
new_user = None
# 2. 学号不为空才执行创建用户逻辑
if stu_num:
# 校验学号对应的登录账号是否已存在
exist_user = self.env['res.users'].search([('login', '=', stu_num)], limit=1)
if not exist_user:
# 获取两个权限组,并双重校验存在性
internal_group = self.env.ref('base.group_user', raise_if_not_found=False)
student_group = self.env.ref('edu_base.group_student', raise_if_not_found=False)
if not internal_group:
raise ValidationError("系统内置内部用户组 base.group_user 缺失,无法创建登录账号!")
if not student_group:
raise ValidationError("权限组 edu_base.group_student 不存在,请先创建该学生权限组!")
# 组装用户参数,优先取学生填写的邮箱,无则自动生成
stu_email = vals.get('stu_email')
if not stu_email:
stu_email = f"{stu_num}@edu.local"
user_vals = {
'name': vals.get('stu_name', ''),
'login': stu_num,
'password': stu_num,
'email': stu_email,
'phone': vals.get('stu_phone', ''),
# 同时分配后台登录组 + 学生业务权限组
'groups_id': [(6, 0, [student_group.id, internal_group.id])],
}
# 先创建用户,事务内失败会整体回滚
new_user = self.env['res.users'].create(user_vals)
# 3. 创建学生档案如果已生成用户则绑定user_id
if new_user:
vals['user_id'] = new_user.id
student_info = super().create(vals)
return student_info