"""转课/转赠路由""" from flask import render_template, request, redirect, url_for, flash, session from models import db, Student, Course, StudentAccount, TransferRecord from utils import login_required, log_operation, permission_required, export_excel 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() return render_template('transfer_list.html', records=records) @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_hours = rules.normalize_consumption_hours( request.form.get('transfer_hours', 1) ) 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)) if transfer_type == 'gift': 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_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': # §五 升阶:100% 正课全额结转,不新建订单 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_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')) 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_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() return render_template('transfer_form.html', students=students, courses=courses) @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()