2026-03-18 16:20:09 +01:00
|
|
|
"""
|
|
|
|
|
DragonBank - Frontend Web Application
|
|
|
|
|
======================================
|
|
|
|
|
Interface web pour l'application bancaire DragonBank.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
from functools import wraps
|
|
|
|
|
|
|
|
|
|
import requests
|
|
|
|
|
from flask import Flask, render_template, request, redirect, url_for, session, flash
|
|
|
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'frontend-secret')
|
|
|
|
|
BACKEND_URL = os.environ.get('BACKEND_URL', 'http://backend:5000')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================
|
|
|
|
|
# UTILITAIRES
|
|
|
|
|
# ============================================
|
|
|
|
|
def api_request(method, endpoint, data=None, token=None):
|
|
|
|
|
"""Effectue une requête vers le backend API."""
|
|
|
|
|
url = f"{BACKEND_URL}{endpoint}"
|
|
|
|
|
headers = {'Content-Type': 'application/json'}
|
|
|
|
|
if token:
|
|
|
|
|
headers['Authorization'] = f'Bearer {token}'
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if method == 'GET':
|
|
|
|
|
resp = requests.get(url, headers=headers, timeout=10)
|
|
|
|
|
elif method == 'POST':
|
|
|
|
|
resp = requests.post(url, json=data, headers=headers, timeout=10)
|
|
|
|
|
elif method == 'DELETE':
|
|
|
|
|
resp = requests.delete(url, headers=headers, timeout=10)
|
|
|
|
|
else:
|
|
|
|
|
return None, 'Méthode non supportée'
|
|
|
|
|
|
|
|
|
|
return resp.json(), resp.status_code
|
|
|
|
|
except requests.exceptions.ConnectionError:
|
|
|
|
|
return {'error': 'Impossible de contacter le serveur'}, 503
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return {'error': str(e)}, 500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def login_required(f):
|
|
|
|
|
"""Décorateur pour protéger les routes nécessitant une connexion."""
|
|
|
|
|
@wraps(f)
|
|
|
|
|
def decorated(*args, **kwargs):
|
|
|
|
|
if 'token' not in session:
|
|
|
|
|
flash('Veuillez vous connecter', 'warning')
|
|
|
|
|
return redirect(url_for('login'))
|
|
|
|
|
return f(*args, **kwargs)
|
|
|
|
|
return decorated
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================
|
|
|
|
|
# ROUTES PUBLIQUES
|
|
|
|
|
# ============================================
|
|
|
|
|
@app.route('/')
|
|
|
|
|
def home():
|
|
|
|
|
"""Page d'accueil."""
|
|
|
|
|
if 'token' in session:
|
|
|
|
|
return redirect(url_for('dashboard'))
|
|
|
|
|
return render_template('login.html')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/login', methods=['GET', 'POST'])
|
|
|
|
|
def login():
|
|
|
|
|
"""Page de connexion."""
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
email = request.form.get('email')
|
|
|
|
|
password = request.form.get('password')
|
|
|
|
|
|
|
|
|
|
data, status = api_request('POST', '/api/auth/login', {
|
|
|
|
|
'email': email,
|
|
|
|
|
'password': password
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if status == 200:
|
|
|
|
|
session['token'] = data['token']
|
|
|
|
|
session['user'] = data['user']
|
|
|
|
|
flash(f'Bienvenue {data["user"]["first_name"]} ! 🐉', 'success')
|
|
|
|
|
return redirect(url_for('dashboard'))
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur de connexion'), 'danger')
|
|
|
|
|
|
|
|
|
|
return render_template('login.html')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/register', methods=['GET', 'POST'])
|
|
|
|
|
def register():
|
|
|
|
|
"""Page d'inscription."""
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
form_data = {
|
|
|
|
|
'email': request.form.get('email'),
|
|
|
|
|
'password': request.form.get('password'),
|
|
|
|
|
'first_name': request.form.get('first_name'),
|
|
|
|
|
'last_name': request.form.get('last_name'),
|
|
|
|
|
'phone': request.form.get('phone', ''),
|
|
|
|
|
'address': request.form.get('address', ''),
|
|
|
|
|
'date_of_birth': request.form.get('date_of_birth') or None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Vérifier confirmation mot de passe
|
|
|
|
|
if request.form.get('password') != request.form.get('confirm_password'):
|
|
|
|
|
flash('Les mots de passe ne correspondent pas', 'danger')
|
|
|
|
|
return render_template('register.html')
|
|
|
|
|
|
|
|
|
|
data, status = api_request('POST', '/api/auth/register', form_data)
|
|
|
|
|
|
|
|
|
|
if status == 201:
|
|
|
|
|
session['token'] = data['token']
|
|
|
|
|
session['user'] = data['user']
|
|
|
|
|
flash('Compte créé avec succès ! Un compte courant a été ouvert automatiquement. 🎉', 'success')
|
|
|
|
|
return redirect(url_for('dashboard'))
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur lors de l\'inscription'), 'danger')
|
|
|
|
|
|
|
|
|
|
return render_template('register.html')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/logout')
|
|
|
|
|
def logout():
|
|
|
|
|
"""Déconnexion."""
|
|
|
|
|
session.clear()
|
|
|
|
|
flash('Vous êtes déconnecté', 'info')
|
|
|
|
|
return redirect(url_for('login'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================
|
|
|
|
|
# ROUTES PROTÉGÉES - DASHBOARD
|
|
|
|
|
# ============================================
|
|
|
|
|
@app.route('/dashboard')
|
|
|
|
|
@login_required
|
|
|
|
|
def dashboard():
|
|
|
|
|
"""Tableau de bord principal."""
|
|
|
|
|
token = session['token']
|
|
|
|
|
|
|
|
|
|
accounts_data, _ = api_request('GET', '/api/accounts', token=token)
|
|
|
|
|
stats_data, _ = api_request('GET', '/api/stats', token=token)
|
|
|
|
|
transactions_data, _ = api_request('GET', '/api/transactions?limit=5', token=token)
|
|
|
|
|
|
|
|
|
|
return render_template('dashboard.html',
|
|
|
|
|
user=session.get('user', {}),
|
|
|
|
|
accounts=accounts_data.get('accounts', []),
|
|
|
|
|
stats=stats_data.get('stats', {}),
|
|
|
|
|
recent_transactions=transactions_data.get('transactions', [])
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-03-27 17:52:41 +01:00
|
|
|
# ============================================
|
|
|
|
|
# ROUTES - PROFIL
|
|
|
|
|
# ============================================
|
|
|
|
|
@app.route('/profile', methods=['GET', 'POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def profile():
|
|
|
|
|
"""Page de profil pour gerer la securite et les infos persos."""
|
|
|
|
|
token = session['token']
|
|
|
|
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
action = request.form.get('action')
|
|
|
|
|
|
|
|
|
|
if action == 'update_profile':
|
|
|
|
|
phone = request.form.get('phone', '')
|
|
|
|
|
address = request.form.get('address', '')
|
|
|
|
|
|
|
|
|
|
data, status = api_request('PUT', '/api/user/profile', {
|
|
|
|
|
'phone': phone,
|
|
|
|
|
'address': address
|
|
|
|
|
}, token=token)
|
|
|
|
|
|
|
|
|
|
if status == 200:
|
|
|
|
|
flash('Informations personnelles mises a jour !', 'success')
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur lors de la mise a jour'), 'danger')
|
|
|
|
|
|
|
|
|
|
elif action == 'update_password':
|
|
|
|
|
current_password = request.form.get('current_password')
|
|
|
|
|
new_password = request.form.get('new_password')
|
|
|
|
|
confirm_password = request.form.get('confirm_password')
|
|
|
|
|
|
|
|
|
|
if new_password != confirm_password:
|
|
|
|
|
flash('Les nouveaux mots de passe ne correspondent pas', 'danger')
|
|
|
|
|
else:
|
|
|
|
|
data, status = api_request('PUT', '/api/user/security/password', {
|
|
|
|
|
'current_password': current_password,
|
|
|
|
|
'new_password': new_password
|
|
|
|
|
}, token=token)
|
|
|
|
|
|
|
|
|
|
if status == 200:
|
|
|
|
|
flash('Mot de passe mis a jour avec succes !', 'success')
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur'), 'danger')
|
|
|
|
|
|
|
|
|
|
elif action == 'update_email':
|
|
|
|
|
password = request.form.get('password')
|
|
|
|
|
new_email = request.form.get('new_email')
|
|
|
|
|
|
|
|
|
|
data, status = api_request('PUT', '/api/user/security/email', {
|
|
|
|
|
'password': password,
|
|
|
|
|
'new_email': new_email
|
|
|
|
|
}, token=token)
|
|
|
|
|
|
|
|
|
|
if status == 200:
|
|
|
|
|
flash('Email mis a jour avec succes. Vous devez vous reconnecter pour valider les changements.', 'success')
|
|
|
|
|
session.clear()
|
|
|
|
|
return redirect(url_for('login'))
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur'), 'danger')
|
|
|
|
|
|
|
|
|
|
# Recuperation complete du profil et des comptes
|
|
|
|
|
profile_data, status_profile = api_request('GET', '/api/user/profile', token=token)
|
|
|
|
|
accounts_data, status_accounts = api_request('GET', '/api/accounts', token=token)
|
|
|
|
|
|
|
|
|
|
user_profile = profile_data.get('user', session.get('user', {})) if status_profile == 200 else session.get('user', {})
|
|
|
|
|
accounts_list = accounts_data.get('accounts', []) if status_accounts == 200 else []
|
|
|
|
|
|
|
|
|
|
return render_template('profile.html', user_profile=user_profile, accounts=accounts_list)
|
|
|
|
|
|
|
|
|
|
|
2026-03-18 16:20:09 +01:00
|
|
|
# ============================================
|
|
|
|
|
# ROUTES - COMPTES
|
|
|
|
|
# ============================================
|
|
|
|
|
@app.route('/accounts')
|
|
|
|
|
@login_required
|
|
|
|
|
def accounts():
|
|
|
|
|
"""Liste des comptes bancaires."""
|
|
|
|
|
token = session['token']
|
|
|
|
|
data, status = api_request('GET', '/api/accounts', token=token)
|
|
|
|
|
|
|
|
|
|
return render_template('accounts.html',
|
|
|
|
|
user=session.get('user', {}),
|
|
|
|
|
accounts=data.get('accounts', [])
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-03-28 01:33:24 +01:00
|
|
|
# Labels lisibles pour les types de comptes
|
|
|
|
|
ACCOUNT_LABELS = {
|
|
|
|
|
'courant': 'Compte Courant',
|
|
|
|
|
'livret_a': 'Livret A',
|
|
|
|
|
'assurance_vie': 'Assurance Vie',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2026-03-18 16:20:09 +01:00
|
|
|
@app.route('/accounts/open', methods=['GET', 'POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def open_account():
|
|
|
|
|
"""Ouverture d'un nouveau compte."""
|
2026-03-28 01:33:24 +01:00
|
|
|
token = session['token']
|
|
|
|
|
accounts_data, _ = api_request('GET', '/api/accounts', token=token)
|
|
|
|
|
accounts_list = accounts_data.get('accounts', [])
|
|
|
|
|
|
2026-03-18 16:20:09 +01:00
|
|
|
if request.method == 'POST':
|
2026-03-28 01:33:24 +01:00
|
|
|
type_compte = request.form.get('account_type')
|
2026-03-18 16:20:09 +01:00
|
|
|
form_data = {
|
2026-03-28 01:33:24 +01:00
|
|
|
'account_type': type_compte,
|
2026-03-18 16:20:09 +01:00
|
|
|
'initial_deposit': float(request.form.get('initial_deposit', 0))
|
|
|
|
|
}
|
2026-03-28 01:33:24 +01:00
|
|
|
|
2026-03-18 16:20:09 +01:00
|
|
|
data, status = api_request('POST', '/api/accounts', data=form_data, token=token)
|
2026-03-28 01:33:24 +01:00
|
|
|
|
2026-03-18 16:20:09 +01:00
|
|
|
if status == 201:
|
2026-03-28 01:33:24 +01:00
|
|
|
label = ACCOUNT_LABELS.get(type_compte, type_compte)
|
|
|
|
|
flash(f'{label} ouvert avec succès ! 🎉', 'success')
|
2026-03-18 16:20:09 +01:00
|
|
|
return redirect(url_for('accounts'))
|
|
|
|
|
else:
|
2026-03-28 01:33:24 +01:00
|
|
|
flash(data.get('error', "Erreur lors de l'ouverture du compte"), 'danger')
|
|
|
|
|
|
|
|
|
|
return render_template('open_account.html',
|
|
|
|
|
user=session.get('user', {}),
|
|
|
|
|
accounts=accounts_list)
|
2026-03-18 16:20:09 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================
|
|
|
|
|
# ROUTES - BÉNÉFICIAIRES
|
|
|
|
|
# ============================================
|
|
|
|
|
@app.route('/beneficiaries')
|
|
|
|
|
@login_required
|
|
|
|
|
def beneficiaries():
|
|
|
|
|
"""Liste des bénéficiaires."""
|
|
|
|
|
token = session['token']
|
|
|
|
|
data, status = api_request('GET', '/api/beneficiaries', token=token)
|
|
|
|
|
|
|
|
|
|
return render_template('beneficiaries.html',
|
|
|
|
|
user=session.get('user', {}),
|
|
|
|
|
beneficiaries=data.get('beneficiaries', [])
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/beneficiaries/add', methods=['GET', 'POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def add_beneficiary():
|
|
|
|
|
"""Ajout d'un bénéficiaire."""
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
token = session['token']
|
|
|
|
|
form_data = {
|
|
|
|
|
'beneficiary_name': request.form.get('beneficiary_name'),
|
|
|
|
|
'account_number': request.form.get('account_number'),
|
|
|
|
|
'bank_name': request.form.get('bank_name', 'DragonBank'),
|
|
|
|
|
'iban': request.form.get('iban', ''),
|
|
|
|
|
'bic': request.form.get('bic', '')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, status = api_request('POST', '/api/beneficiaries', data=form_data, token=token)
|
|
|
|
|
|
|
|
|
|
if status == 201:
|
|
|
|
|
flash('Bénéficiaire ajouté avec succès !', 'success')
|
|
|
|
|
return redirect(url_for('beneficiaries'))
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur'), 'danger')
|
|
|
|
|
|
|
|
|
|
return render_template('add_beneficiary.html', user=session.get('user', {}))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/beneficiaries/delete/<beneficiary_id>')
|
|
|
|
|
@login_required
|
|
|
|
|
def delete_beneficiary(beneficiary_id):
|
|
|
|
|
"""Suppression d'un bénéficiaire."""
|
|
|
|
|
token = session['token']
|
|
|
|
|
data, status = api_request('DELETE', f'/api/beneficiaries/{beneficiary_id}', token=token)
|
|
|
|
|
|
|
|
|
|
if status == 200:
|
|
|
|
|
flash('Bénéficiaire supprimé', 'success')
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur'), 'danger')
|
|
|
|
|
|
|
|
|
|
return redirect(url_for('beneficiaries'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================
|
|
|
|
|
# ROUTES - VIREMENTS
|
|
|
|
|
# ============================================
|
|
|
|
|
@app.route('/transfer', methods=['GET', 'POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def transfer():
|
|
|
|
|
"""Page de virement (interne et entre personnes)."""
|
|
|
|
|
token = session['token']
|
|
|
|
|
|
|
|
|
|
accounts_data, _ = api_request('GET', '/api/accounts', token=token)
|
|
|
|
|
beneficiaries_data, _ = api_request('GET', '/api/beneficiaries', token=token)
|
|
|
|
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
transfer_type = request.form.get('transfer_type')
|
|
|
|
|
amount = float(request.form.get('amount', 0))
|
|
|
|
|
from_account_id = request.form.get('from_account_id')
|
|
|
|
|
description = request.form.get('description', '')
|
|
|
|
|
|
|
|
|
|
if transfer_type == 'internal':
|
|
|
|
|
to_account_id = request.form.get('to_account_id')
|
|
|
|
|
data, status = api_request('POST', '/api/transfers/internal', {
|
|
|
|
|
'from_account_id': from_account_id,
|
|
|
|
|
'to_account_id': to_account_id,
|
|
|
|
|
'amount': amount,
|
|
|
|
|
'description': description
|
|
|
|
|
}, token=token)
|
|
|
|
|
elif transfer_type == 'person':
|
|
|
|
|
beneficiary_id = request.form.get('beneficiary_id')
|
|
|
|
|
data, status = api_request('POST', '/api/transfers/person', {
|
|
|
|
|
'from_account_id': from_account_id,
|
|
|
|
|
'beneficiary_id': beneficiary_id,
|
|
|
|
|
'amount': amount,
|
|
|
|
|
'description': description
|
|
|
|
|
}, token=token)
|
|
|
|
|
else:
|
|
|
|
|
flash('Type de virement invalide', 'danger')
|
|
|
|
|
return redirect(url_for('transfer'))
|
|
|
|
|
|
|
|
|
|
if status == 200:
|
|
|
|
|
flash(data.get('message', 'Virement effectué !'), 'success')
|
|
|
|
|
return redirect(url_for('dashboard'))
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur lors du virement'), 'danger')
|
|
|
|
|
|
|
|
|
|
return render_template('transfer.html',
|
|
|
|
|
user=session.get('user', {}),
|
|
|
|
|
accounts=accounts_data.get('accounts', []),
|
|
|
|
|
beneficiaries=beneficiaries_data.get('beneficiaries', [])
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/transfer/external', methods=['GET', 'POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def transfer_external():
|
|
|
|
|
"""Virement externe (depuis/vers autre banque)."""
|
|
|
|
|
token = session['token']
|
|
|
|
|
accounts_data, _ = api_request('GET', '/api/accounts', token=token)
|
|
|
|
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
form_data = {
|
|
|
|
|
'account_id': request.form.get('account_id'),
|
|
|
|
|
'amount': float(request.form.get('amount', 0)),
|
|
|
|
|
'external_bank_name': request.form.get('external_bank_name'),
|
|
|
|
|
'external_account_number': request.form.get('external_account_number'),
|
|
|
|
|
'direction': request.form.get('direction'),
|
|
|
|
|
'description': request.form.get('description', '')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, status = api_request('POST', '/api/transfers/external', form_data, token=token)
|
|
|
|
|
|
|
|
|
|
if status == 200:
|
|
|
|
|
flash(data.get('message', 'Virement externe effectué !'), 'success')
|
|
|
|
|
return redirect(url_for('dashboard'))
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur'), 'danger')
|
|
|
|
|
|
|
|
|
|
return render_template('transfer_external.html',
|
|
|
|
|
user=session.get('user', {}),
|
|
|
|
|
accounts=accounts_data.get('accounts', [])
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================
|
|
|
|
|
# ROUTES - HISTORIQUE
|
|
|
|
|
# ============================================
|
|
|
|
|
@app.route('/transactions')
|
|
|
|
|
@login_required
|
|
|
|
|
def transactions():
|
|
|
|
|
"""Historique des transactions."""
|
|
|
|
|
token = session['token']
|
|
|
|
|
account_id = request.args.get('account_id', '')
|
|
|
|
|
|
|
|
|
|
endpoint = '/api/transactions?limit=100'
|
|
|
|
|
if account_id:
|
|
|
|
|
endpoint += f'&account_id={account_id}'
|
|
|
|
|
|
|
|
|
|
data, status = api_request('GET', endpoint, token=token)
|
|
|
|
|
accounts_data, _ = api_request('GET', '/api/accounts', token=token)
|
|
|
|
|
|
|
|
|
|
return render_template('transactions.html',
|
|
|
|
|
user=session.get('user', {}),
|
|
|
|
|
transactions=data.get('transactions', []),
|
|
|
|
|
accounts=accounts_data.get('accounts', []),
|
|
|
|
|
selected_account=account_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-03-27 17:52:41 +01:00
|
|
|
# ============================================
|
|
|
|
|
# ROUTES - EXPORT CSV
|
|
|
|
|
# ============================================
|
|
|
|
|
@app.route('/transactions/export')
|
|
|
|
|
@login_required
|
|
|
|
|
def export_transactions():
|
|
|
|
|
"""Redirige vers le backend pour télécharger le CSV des transactions."""
|
|
|
|
|
import requests as req
|
|
|
|
|
token = session['token']
|
|
|
|
|
account_id = request.args.get('account_id', '')
|
|
|
|
|
|
|
|
|
|
endpoint = f"{BACKEND_URL}/api/transactions/export"
|
|
|
|
|
if account_id:
|
|
|
|
|
endpoint += f"?account_id={account_id}"
|
|
|
|
|
|
|
|
|
|
headers = {
|
|
|
|
|
'Authorization': f'Bearer {token}',
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
resp = req.get(endpoint, headers=headers, timeout=15)
|
|
|
|
|
if resp.status_code == 200:
|
|
|
|
|
from flask import Response
|
|
|
|
|
return Response(
|
|
|
|
|
resp.content,
|
|
|
|
|
mimetype='text/csv',
|
|
|
|
|
headers={
|
|
|
|
|
'Content-Disposition': 'attachment; filename="dragonbank_transactions.csv"'
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
flash('Erreur lors de la génération du CSV', 'danger')
|
|
|
|
|
return redirect(url_for('transactions'))
|
|
|
|
|
except Exception as e:
|
|
|
|
|
flash('Impossible de contacter le serveur', 'danger')
|
|
|
|
|
return redirect(url_for('transactions'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================
|
|
|
|
|
# ROUTES - SIMULATEUR D'ÉPARGNE
|
|
|
|
|
# ============================================
|
|
|
|
|
@app.route('/simulator', methods=['GET', 'POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def simulator():
|
|
|
|
|
"""Simulateur de croissance de l'épargne avec graphique."""
|
|
|
|
|
token = session['token']
|
|
|
|
|
resultat = None
|
|
|
|
|
|
|
|
|
|
# Récupération des comptes épargne pour pré-remplir le simulateur
|
|
|
|
|
accounts_data, _ = api_request('GET', '/api/accounts', token=token)
|
|
|
|
|
comptes_epargne = [
|
|
|
|
|
a for a in accounts_data.get('accounts', [])
|
|
|
|
|
if a.get('account_type') in ('livret_a', 'assurance_vie')
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
form_data = {
|
|
|
|
|
'capital_initial': float(request.form.get('capital_initial', 0)),
|
|
|
|
|
'taux_annuel': float(request.form.get('taux_annuel', 3)),
|
|
|
|
|
'duree_annees': int(request.form.get('duree_annees', 10)),
|
|
|
|
|
'versement_mensuel': float(request.form.get('versement_mensuel', 0))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, status = api_request('POST', '/api/simulator', data=form_data, token=token)
|
|
|
|
|
|
|
|
|
|
if status == 200:
|
|
|
|
|
resultat = data
|
|
|
|
|
else:
|
|
|
|
|
flash(data.get('error', 'Erreur de simulation'), 'danger')
|
|
|
|
|
|
|
|
|
|
return render_template('simulator.html',
|
|
|
|
|
user=session.get('user', {}),
|
|
|
|
|
comptes_epargne=comptes_epargne,
|
|
|
|
|
resultat=resultat
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-03-18 16:20:09 +01:00
|
|
|
# ============================================
|
|
|
|
|
# DÉMARRAGE
|
|
|
|
|
# ============================================
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
print("🐉 DragonBank Frontend starting on port 8080...")
|
|
|
|
|
app.run(host='0.0.0.0', port=8080, debug=False)
|