"""Player contracts: upload, sign, download.""" import os import uuid from datetime import datetime from flask import flash, redirect, render_template, request, send_file, url_for from flask_babel import gettext as _ from flask_login import current_user, login_required from marshmallow import ValidationError from werkzeug.utils import secure_filename from app.extensions import db from app.models import Admin, Coach, Contract, Manager, Player, User from app.permissions import can_manage_player_contract, coach_player_ids from app.routes.users._shared import ( ALLOWED_CONTRACT_EXTENSIONS, ALLOWED_SIGNED_EXTENSIONS, pdf_upload_error, ) from app.routes.users.blueprint import users_bp from app.validators import UploadContractSchema def manageable_players(): """Players the current user may attach a contract to. A coach used to see the squad of one team — the first row matching the legacy coach_id column — so a coach of two teams could file a contract for half of their players and no more, and a coach attached only by the many-to-many relationship for none at all. """ if isinstance(current_user, Coach): player_ids = coach_player_ids(current_user) return ( User.query.filter(User.id.in_(player_ids)).order_by(User.username).all() if player_ids else [] ) return User.query.filter_by(role='player').order_by(User.username).all() @users_bp.route('/contracts') @login_required def list_contracts(): """View contracts for the current user or players they manage.""" contracts = None players = None if isinstance(current_user, Player): contracts = ( Contract.query.filter_by( player_id=current_user.id, ) .order_by(Contract.uploaded_at.desc()) .all() ) elif isinstance(current_user, (Admin, Manager, Coach)): players = manageable_players() if players: player_ids = [p.id for p in players] contracts = ( Contract.query.filter( Contract.player_id.in_(player_ids), ) .order_by(Contract.uploaded_at.desc()) .all() ) return render_template( 'pages/contracts.html', contracts=contracts, players=players if isinstance(current_user, (Admin, Manager, Coach)) else None, ) @users_bp.route('/contracts/upload', methods=['GET', 'POST']) @login_required def upload_contract(): """Upload a contract for a player.""" if not isinstance(current_user, (Admin, Manager, Coach)): flash(_('Only presidents, managers, and coaches can upload contracts.'), 'danger') return redirect(url_for('users.list_contracts')) players = manageable_players() if request.method == 'POST': contract_schema = UploadContractSchema() try: validated = contract_schema.load(request.form) except ValidationError as err: for field, messages in err.messages.items(): for msg in messages: flash(_('%(field)s: %(msg)s', field=field, msg=msg), 'danger') return render_template('pages/upload_contract.html', players=players) player_id = validated['player_id'] notes = validated.get('notes') if not can_manage_player_contract(current_user, player_id): flash(_('You do not have permission to upload a contract for this player.'), 'danger') return redirect(url_for('users.upload_contract')) file = request.files.get('contract_file') error = pdf_upload_error(file, ALLOWED_CONTRACT_EXTENSIONS) if error: flash(error, 'danger') return redirect(url_for('users.upload_contract')) upload_dir = os.path.join(os.getcwd(), 'documents', 'contrats signés') os.makedirs(upload_dir, exist_ok=True) player = User.query.get_or_404(player_id) player_teams = player.get_org_teams() team = player_teams[0] if player_teams else None if team: team_folder = os.path.join(upload_dir, secure_filename(team.name)) os.makedirs(team_folder, exist_ok=True) final_dir = team_folder else: final_dir = upload_dir original_filename = secure_filename(file.filename) file_uuid = str(uuid.uuid4()) stored_filename = f"{file_uuid}.pdf" file_path = os.path.join(final_dir, stored_filename) file.save(file_path) contract = Contract( player_id=player_id, team_id=team.id if team else None, uploaded_by_id=current_user.id, original_filename=original_filename, stored_filename=stored_filename, file_path=file_path, notes=notes if notes else None, ) db.session.add(contract) db.session.commit() flash( _('Contract uploaded successfully for %(username)s!', username=player.username), 'success', ) return redirect(url_for('users.list_contracts')) return render_template('pages/upload_contract.html', players=players) @users_bp.route('/contracts//upload_signed', methods=['POST']) @login_required def upload_signed_contract(contract_id): """Upload a signed contract (player only).""" contract = Contract.query.get_or_404(contract_id) if not contract.can_upload_signed(current_user): flash(_('Only the player can upload their signed contract.'), 'danger') return redirect(url_for('users.list_contracts')) file = request.files.get('signed_file') error = pdf_upload_error(file, ALLOWED_SIGNED_EXTENSIONS) if error: flash(error, 'danger') return redirect(url_for('users.list_contracts')) signed_filename = f"signed_{contract.stored_filename}" file.save(contract.file_path.replace(contract.stored_filename, signed_filename)) contract.signed_filename = signed_filename contract.signed_file_path = contract.file_path.replace( contract.stored_filename, signed_filename ) contract.status = 'signed' contract.signed_at = datetime.utcnow() db.session.commit() flash(_('Signed contract uploaded successfully!'), 'success') return redirect(url_for('users.list_contracts')) @users_bp.route('/contracts//download') @login_required def download_contract(contract_id): """Download a contract file.""" contract = Contract.query.get_or_404(contract_id) if not contract.can_view(current_user): flash(_('You do not have permission to download this contract.'), 'danger') return redirect(url_for('users.list_contracts')) return send_file( contract.file_path, as_attachment=True, download_name=contract.original_filename ) @users_bp.route('/contracts//download_signed') @login_required def download_signed_contract(contract_id): """Download a signed contract file.""" contract = Contract.query.get_or_404(contract_id) if not contract.can_view(current_user): flash(_('You do not have permission to download this contract.'), 'danger') return redirect(url_for('users.list_contracts')) if not contract.signed_file_path: flash(_('No signed contract available.'), 'danger') return redirect(url_for('users.list_contracts')) return send_file( contract.signed_file_path, as_attachment=True, download_name=contract.signed_filename )