Files
drl_2/xuexiao/routes/refund.py

116 lines
5.2 KiB
Python
Raw Normal View History

2026-05-29 10:28:07 +08:00
"""退费管理路由"""
from flask import render_template, request, redirect, url_for, flash, session, jsonify
from utils import permission_required, export_excel, login_required, log_operation
from models import db, Student, Course, StudentAccount, RefundRecord
import business_rules as rules
def register_routes(app):
@app.route('/refunds', endpoint='refund_list')
@login_required
@permission_required('refund_view')
def refund_list():
records = RefundRecord.query.order_by(RefundRecord.created_at.desc()).all()
return render_template('refund_list.html', records=records)
@app.route('/refunds/add', methods=['GET', 'POST'], endpoint='refund_add')
@login_required
@permission_required('refund_add')
def refund_add():
if request.method == 'POST':
student_id = int(request.form.get('student_id'))
course_id = int(request.form.get('course_id'))
reason = request.form.get('reason', '')
partial_raw = request.form.get('partial_amount', '').strip()
partial_amount = float(partial_raw) if partial_raw else None
account = rules.lock_student_account(student_id, course_id)
if not account:
flash('未找到该学员的课时账户', 'danger')
return redirect(url_for('refund_add'))
before = rules.account_snapshot_dict(account)
calc = rules.calculate_refund(student_id, course_id, partial_amount)
record = RefundRecord(
student_id=student_id,
course_id=course_id,
refund_amount=calc['refund_amount'],
remaining_normal_hours=calc['remaining_normal'],
remaining_gifted_hours=calc['remaining_gifted'],
deduct_gifted_hours=calc['deduct_gifted_amount'],
deduct_gifted_amount=calc['deduct_gifted_amount'],
promo_clawback_hours=calc['promo_clawback_hours'],
consumed_hours_value=calc['consumed_hours_value'],
material_fee_per_quarter=rules.MATERIAL_FEE_PER_QUARTER,
quarters=calc['quarters'],
total_deduct=calc['total_deduct'],
reason=reason,
operator_id=session.get('user_id'),
)
try:
db.session.add(record)
rules.apply_refund(
account, calc,
student_id=student_id, course_id=course_id,
)
after = rules.account_snapshot_dict(account)
log_operation(
'退费',
f'学员ID:{student_id} 退费{calc["refund_amount"]}元',
before=before, after=after, commit=False,
)
db.session.commit()
except Exception as e:
db.session.rollback()
flash(f'退费失败: {e}', 'danger')
return redirect(url_for('refund_add'))
extra = ''
if calc['promo_clawback_hours']:
extra = f',优惠回退扣赠课{calc["promo_clawback_hours"]}课时'
flash(f'退费成功!退费金额: {calc["refund_amount"]}元{extra}', 'success')
return redirect(url_for('refund_list'))
students = Student.query.filter_by(status=1).all()
courses = Course.query.filter_by(status=1).all()
return render_template('refund_form.html', students=students, courses=courses)
@app.route('/refunds/export', endpoint='refund_export')
@login_required
@permission_required('refund_export')
def refund_export():
records = RefundRecord.query.order_by(RefundRecord.created_at.desc()).all()
headers = ['序号', '学员姓名', '课程名称', '退费金额', '已消课时金额',
'材料费', '赠课扣回金额', '优惠回退课时', '退费原因', '创建时间']
rows = []
for i, r in enumerate(records, 1):
student = db.session.get(Student, r.student_id)
course = db.session.get(Course, r.course_id)
gift_amt = getattr(r, 'deduct_gifted_amount', None) or r.deduct_gifted_hours
rows.append([
i,
student.name if student else '',
course.name if course else '',
r.refund_amount or 0,
r.consumed_hours_value or 0,
(r.quarters or 0) * rules.MATERIAL_FEE_PER_QUARTER,
gift_amt or 0,
getattr(r, 'promo_clawback_hours', 0) or 0,
r.reason or '',
r.created_at.strftime('%Y-%m-%d %H:%M') if r.created_at else '',
])
return export_excel('退费记录', headers, rows)
@app.route('/api/calculate_refund', methods=['POST'])
@login_required
@permission_required('refund_view')
def api_calculate_refund():
data = request.get_json() or {}
student_id = data.get('student_id')
course_id = data.get('course_id')
partial = data.get('partial_amount')
partial_amount = float(partial) if partial not in (None, '') else None
result = rules.calculate_refund(student_id, course_id, partial_amount)
return jsonify({'success': True, 'data': result})