Files
drl_2/xuexiao/routes/transfer.py
user9994793890 3468394c6c feat: 实现单价分离、班级转课、消课筛选和手动录入功能
Coze-Commit-Type: user
Coze-User-ID: 3722323274763196
Coze-Conversation-ID: 5260473
2026-05-29 10:40:41 +08:00

320 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""转课/转赠路由"""
from datetime import date
from flask import render_template, request, redirect, url_for, flash, session
from models import db, Student, Course, Class_, Teacher, StudentAccount, TransferRecord, ClassStudent
from utils import login_required, log_operation, permission_required, export_excel, parse_date
import business_rules as rules
def register_routes(app):
@app.route('/transfers', endpoint='transfer_list')
@login_required
@permission_required('transfer_view')
def transfer_list():
records = TransferRecord.query.order_by(TransferRecord.created_at.desc()).all()
# 获取关联信息用于显示
students = {s.id: s for s in Student.query.all()}
courses = {c.id: c for c in Course.query.all()}
classes = {c.id: c for c in Class_.query.all()}
teachers = {t.id: t for t in Teacher.query.all()}
return render_template('transfer_list.html', records=records,
students=students, courses=courses,
classes=classes, teachers=teachers)
@app.route('/transfers/add', methods=['GET', 'POST'], endpoint='transfer_add')
@login_required
@permission_required('transfer_add')
def transfer_add():
if request.method == 'POST':
from_student_id = int(request.form.get('from_student_id'))
from_course_id = int(request.form.get('from_course_id'))
transfer_type = request.form.get('transfer_type', 'transfer')
transfer_class_type = request.form.get('transfer_class_type', 'class_transfer')
remark = request.form.get('remark', '')
from_account = rules.lock_student_account(from_student_id, from_course_id)
if not from_account:
flash('未找到转出课时账户', 'danger')
return redirect(url_for('transfer_add'))
normal_avail = int(float(from_account.normal_hours or 0))
current_unit_price = float(from_account.unit_price or 0)
# 班级转课 - 无单价变更
if transfer_class_type == 'class_transfer':
to_class_id = request.form.get('to_class_id')
to_teacher_id = request.form.get('to_teacher_id')
price_change_type = request.form.get('price_change_type', 'no_change')
if not to_class_id:
flash('请选择目标班级', 'danger')
return redirect(url_for('transfer_add'))
to_class_id = int(to_class_id)
to_teacher_id = int(to_teacher_id) if to_teacher_id else None
# 获取原班级和老师信息
from_class_id = from_account.from_class_id if hasattr(from_account, 'from_class_id') and from_account.from_class_id else None
from_teacher_id = from_account.from_teacher_id if hasattr(from_account, 'from_teacher_id') and from_account.from_teacher_id else None
# 更新班级学员关系
if from_class_id:
old_cs = ClassStudent.query.filter_by(
student_id=from_student_id, class_id=from_class_id, status=1
).first()
if old_cs:
old_cs.status = 0
# 添加新的班级学员关系
existing_cs = ClassStudent.query.filter_by(
student_id=from_student_id, class_id=to_class_id, status=1
).first()
if not existing_cs:
cs = ClassStudent(
student_id=from_student_id,
class_id=to_class_id,
join_date=date.today(),
status=1
)
db.session.add(cs)
new_unit_price = current_unit_price
price_change_reason = ''
effective_date = None
# 有单价变更
if price_change_type == 'price_changed':
new_unit_price = float(request.form.get('new_unit_price', 0))
price_change_reason = request.form.get('price_change_reason', '')
effective_date_str = request.form.get('effective_date', '')
effective_date = parse_date(effective_date_str) if effective_date_str else date.today()
if not new_unit_price or new_unit_price <= 0:
flash('有单价变更时新单价必须大于0', 'danger')
return redirect(url_for('transfer_add'))
if not price_change_reason:
flash('有单价变更时,必须填写变更原因', 'danger')
return redirect(url_for('transfer_add'))
# 更新账户单价
from_account.unit_price = new_unit_price
from_account.original_price_per_lesson = new_unit_price
# 记录转课
record = TransferRecord(
from_student_id=from_student_id,
from_course_id=from_course_id,
to_course_id=from_course_id, # 同课程
from_class_id=from_class_id,
to_class_id=to_class_id,
from_teacher_id=from_teacher_id,
to_teacher_id=to_teacher_id,
transfer_type='class_transfer',
transfer_class_type='class_transfer',
price_change_type=price_change_type,
transfer_hours=normal_avail, # 保留全部课时
original_unit_price=current_unit_price,
new_unit_price=new_unit_price,
transfer_amount=normal_avail * current_unit_price,
price_change_reason=price_change_reason,
effective_date=effective_date,
operator_id=session.get('user_id'),
remark=remark,
)
db.session.add(record)
db.session.commit()
change_desc = f'班级:{from_class_id or ""}{to_class_id}'
if price_change_type == 'price_changed':
change_desc += f', 单价:{current_unit_price}{new_unit_price}'
log_operation('班级转课(有单价变更)', f'学员{from_student_id} {change_desc}, 原因:{price_change_reason}')
flash(f'转课成功(有单价变更)!单价已更新为{new_unit_price}元/课时', 'success')
else:
log_operation('班级转课(无单价变更)', f'学员{from_student_id} {change_desc}')
flash('转课成功(无单价变更)!班级和老师已更新,课时保持不变', 'success')
# 转赠 - 给其他学员
elif transfer_type == 'gift':
transfer_hours = rules.normalize_consumption_hours(
request.form.get('transfer_hours', 1)
)
to_student_id = int(request.form.get('to_student_id'))
if transfer_hours > normal_avail:
flash(
f'赠课不可转赠。仅可转赠正课,当前正课余额{normal_avail}课时',
'danger',
)
return redirect(url_for('transfer_add'))
from_account.normal_hours = normal_avail - transfer_hours
rules.update_account_unit_price(from_account)
rules.sync_account_snapshots(from_account)
to_account = StudentAccount.query.filter_by(
student_id=to_student_id, course_id=from_course_id,
).first()
if not to_account:
to_account = StudentAccount(
student_id=to_student_id, course_id=from_course_id,
)
db.session.add(to_account)
db.session.flush()
to_account.normal_hours = int(float(to_account.normal_hours or 0)) + transfer_hours
rules.update_account_unit_price(to_account)
rules.sync_account_snapshots(to_account)
record = TransferRecord(
from_student_id=from_student_id,
to_student_id=to_student_id,
from_course_id=from_course_id,
to_course_id=from_course_id,
transfer_type='gift',
transfer_class_type='gift',
transfer_hours=transfer_hours,
operator_id=session.get('user_id'),
remark=remark,
)
db.session.add(record)
db.session.commit()
log_operation(
'转赠',
f'学员{from_student_id} 转赠正课{transfer_hours}课时给学员{to_student_id}',
)
flash('转赠成功(仅正课)', 'success')
# 升阶 - 换课程
elif transfer_type == 'upgrade':
to_course_id = int(request.form.get('to_course_id'))
from_course = db.session.get(Course, from_course_id)
to_course = db.session.get(Course, to_course_id)
from_unit = rules.account_effective_unit_price(from_account, from_course)
to_unit = rules.account_effective_unit_price(
StudentAccount.query.filter_by(
student_id=from_student_id, course_id=to_course_id,
).first() or from_account, to_course,
) or float(to_course.price_per_hour or 0) if to_course else 0
if to_unit <= 0 and to_course:
to_unit = float(to_course.price_per_hour or 0)
transfer_normal = normal_avail
to_hours = rules.calc_transfer_to_hours(
transfer_normal, from_unit, to_unit,
)
transfer_amount = transfer_normal * from_unit
from_account.normal_hours = 0
rules.update_account_unit_price(from_account)
rules.sync_account_snapshots(from_account)
to_account = StudentAccount.query.filter_by(
student_id=from_student_id, course_id=to_course_id,
).first()
if not to_account:
to_account = StudentAccount(
student_id=from_student_id, course_id=to_course_id,
)
db.session.add(to_account)
db.session.flush()
to_account.normal_hours = int(float(to_account.normal_hours or 0)) + to_hours
rules.update_account_unit_price(to_account)
rules.sync_account_snapshots(to_account)
record = TransferRecord(
from_student_id=from_student_id,
from_course_id=from_course_id,
to_course_id=to_course_id,
transfer_type='upgrade',
transfer_class_type='upgrade',
transfer_hours=transfer_normal,
transfer_amount=transfer_amount,
operator_id=session.get('user_id'),
remark=remark or '升阶结转',
)
db.session.add(record)
db.session.commit()
log_operation(
'升阶',
f'学员{from_student_id} 正课{transfer_normal}课时→课程{to_course_id} 转入{to_hours}课时',
)
flash(f'升阶成功100%正课结转,转入{to_hours}课时', 'success')
# 普通转课 - 换课程课时
else:
to_course_id = int(request.form.get('to_course_id'))
transfer_hours = rules.normalize_consumption_hours(
request.form.get('transfer_hours', 1)
)
from_course = db.session.get(Course, from_course_id)
to_course = db.session.get(Course, to_course_id)
from_unit = rules.account_effective_unit_price(from_account, from_course)
to_account_pre = StudentAccount.query.filter_by(
student_id=from_student_id, course_id=to_course_id,
).first()
to_unit = rules.account_effective_unit_price(to_account_pre, to_course)
if to_unit <= 0 and to_course:
to_unit = float(to_course.price_per_hour or 0)
if transfer_hours > normal_avail:
flash(
f'仅可转出正课。当前正课{normal_avail}课时,赠课{int(float(from_account.gifted_hours or 0))}课时不可转课',
'danger',
)
return redirect(url_for('transfer_add'))
transfer_amount = transfer_hours * from_unit
to_hours = rules.calc_transfer_to_hours(
transfer_hours, from_unit, to_unit,
)
from_account.normal_hours = normal_avail - transfer_hours
rules.update_account_unit_price(from_account)
rules.sync_account_snapshots(from_account)
to_account = StudentAccount.query.filter_by(
student_id=from_student_id, course_id=to_course_id,
).first()
if not to_account:
to_account = StudentAccount(
student_id=from_student_id, course_id=to_course_id,
)
db.session.add(to_account)
db.session.flush()
to_account.normal_hours = int(float(to_account.normal_hours or 0)) + to_hours
rules.update_account_unit_price(to_account)
rules.sync_account_snapshots(to_account)
record = TransferRecord(
from_student_id=from_student_id,
from_course_id=from_course_id,
to_course_id=to_course_id,
transfer_type='transfer',
transfer_class_type='course_transfer',
transfer_hours=transfer_hours,
transfer_amount=transfer_amount,
operator_id=session.get('user_id'),
remark=remark,
)
db.session.add(record)
db.session.commit()
log_operation(
'转课',
f'学员{from_student_id} 转出正课{transfer_hours}课时→转入{to_hours}课时',
)
flash(f'转课成功!转入{to_hours}课时(仅正课参与换算)', 'success')
return redirect(url_for('transfer_list'))
students = Student.query.filter_by(status=1).all()
courses = Course.query.filter_by(status=1).all()
classes = Class_.query.filter_by(status=1).all()
teachers = Teacher.query.filter_by(status=1).all()
return render_template('transfer_form.html', students=students, courses=courses,
classes=classes, teachers=teachers)
@app.route('/transfers/export', endpoint='transfer_export')
@login_required
@permission_required('transfer_export')
def transfer_export():
from utils import export_transfers as do_export
return do_export()