From b2c241797b5f9473c35ffeff236d8c2097c41002 Mon Sep 17 00:00:00 2001
From: jfgiraud
Date: Mon, 27 Jul 2026 12:32:47 +0200
Subject: [PATCH] =?UTF-8?q?Manu=201=202=204=205=207=20fini=20avec=20combo?=
=?UTF-8?q?=20ann=C3=A9e?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/routes.py | 292 ++++++++++++++++++------------
app/templates/gestion_ordres.html | 93 ++++++++--
2 files changed, 253 insertions(+), 132 deletions(-)
diff --git a/app/routes.py b/app/routes.py
index 706bdce..87b3909 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -298,6 +298,8 @@ def gestion_import_export_actions_csv():
return redirect(url_for('main.index'))
return render_template('gestion_import_export_actions_csv.html')
+
+# Route pour l'exportation des actions vers un fichier CSV
@main.route('/export-actions-csv', methods=['GET'])
def export_actions_csv():
if 'user_id' not in session:
@@ -335,6 +337,8 @@ def export_actions_csv():
flash("Erreur lors de l'exportation du fichier CSV.", "danger")
return redirect(url_for('main.gestion_import_export_actions_csv'))
+
+# Route pour l'importation des actions depuis un fichier CSV
@main.route('/import-actions-csv', methods=['POST'])
def import_actions_csv():
if 'user_id' not in session:
@@ -410,9 +414,18 @@ def gestion_ordres():
or session.get("selected_societe_id")
)
+ # Récupération de l'année sélectionnée (par défaut "Tout")
+ selected_annee = (
+ request.form.get("annee")
+ or request.args.get("annee")
+ or session.get("selected_annee", "Tout")
+ )
+ session["selected_annee"] = selected_annee
+
societes = []
selected_societe = ""
groupes_dict = {}
+ total_plus_value_globale = 0.0
with db.engine.connect() as connection:
result_soc = connection.execute(
@@ -433,6 +446,34 @@ def gestion_ordres():
selected_societe = soc["Nom"]
break
+ # --- CALCUL DE LA PLUS-VALUE TOTALE ( GLOBALE OU FILTRÉE PAR ANNÉE ) ---
+ annee_str = str(selected_annee).strip().lower()
+
+ if not annee_str or annee_str == "tout":
+ query_pv = text("""
+ SELECT SUM(PlusValue) as total_pv
+ FROM ordre
+ WHERE id_societe = :soc_id AND PlusValue IS NOT NULL
+ """)
+ res_pv = connection.execute(query_pv, {"soc_id": selected_societe_id}).fetchone()
+ else:
+ query_pv = text("""
+ SELECT SUM(PlusValue) as total_pv
+ FROM ordre
+ WHERE id_societe = :soc_id
+ AND PlusValue IS NOT NULL
+ AND LEFT(CAST(date_plusvalue AS CHAR), 4) = :annee
+ """)
+ res_pv = connection.execute(
+ query_pv, {"soc_id": selected_societe_id, "annee": annee_str}
+ ).fetchone()
+
+ if res_pv and res_pv.total_pv is not None:
+ total_plus_value_globale = float(res_pv.total_pv)
+ else:
+ total_plus_value_globale = 0.0
+
+ # --- CHARGEMENT DES ORDRES ---
query = text("""
SELECT o.ID as ordre_id, o.isin, o.PlusValue, o.date_plusvalue,
op.ID as pied_id, op.Date_op, op.Quantite, op.Prix_brut, op.Frais_brocker,
@@ -442,7 +483,7 @@ def gestion_ordres():
JOIN ordre_pied op ON o.ID = op.Id_entete
JOIN actions a ON o.isin = a.isin
WHERE o.id_societe = :soc_id
- ORDER BY op.Date_op DESC
+ ORDER BY o.ID DESC, op.Date_op DESC
""")
result_ordres = connection.execute(
@@ -451,6 +492,7 @@ def gestion_ordres():
for row in result_ordres:
r = dict(row._mapping)
+ ordre_id = r["ordre_id"] # Utilisation de l'ID d'en-tête comme clé unique
isin = r["isin"]
quantite = float(r["Quantite"]) if r["Quantite"] is not None else 0.0
@@ -466,11 +508,11 @@ def gestion_ordres():
)
frais_total = frais_brocker + frais_etat + frais_autre
- sens_ordre = str(r["Ordre"]).strip().lower() # 'achat' ou 'vente'
+ sens_ordre = str(r["Ordre"]).strip().lower()
type_ordre = (
str(r["Type"]).strip().lower()
if r["Type"]
- else "long" # 'long' ou 'short'
+ else "long"
)
# Calcul Prix Net unitaire
@@ -500,9 +542,10 @@ def gestion_ordres():
"etat": r["Etat"],
}
- if isin not in groupes_dict:
- groupes_dict[isin] = {
- "id": r["ordre_id"],
+ # MODIFICATION ICI : On utilise ordre_id comme clé du dictionnaire pour séparer les blocs
+ if ordre_id not in groupes_dict:
+ groupes_dict[ordre_id] = {
+ "id": ordre_id,
"isin": isin,
"company_name": r["company_name"],
"ticker": r["ticker"],
@@ -517,55 +560,40 @@ def gestion_ordres():
"date_plusvalue": r["date_plusvalue"],
}
- groupes_dict[isin]["pieds"].append(pied_data)
- groupes_dict[isin]["total_prix_brut"] += prix_brut
- groupes_dict[isin]["total_frais"] += frais_total
- groupes_dict[isin]["total_prix_net"] += prix_net
+ groupes_dict[ordre_id]["pieds"].append(pied_data)
+ groupes_dict[ordre_id]["total_prix_brut"] += prix_brut
+ groupes_dict[ordre_id]["total_frais"] += frais_total
+ groupes_dict[ordre_id]["total_prix_net"] += prix_net
# --- APPLICATION DE VOS RÈGLES POUR LES TOTAUX ---
- # 1. QUANTITÉ
- if sens_ordre == "achat" and type_ordre == "long":
- groupes_dict[isin]["total_quantite"] += quantite
- elif sens_ordre == "vente" and type_ordre == "long":
- groupes_dict[isin]["total_quantite"] -= quantite
- elif sens_ordre == "achat" and type_ordre == "short":
- groupes_dict[isin]["total_quantite"] -= quantite
- elif sens_ordre == "vente" and type_ordre == "short":
- groupes_dict[isin]["total_quantite"] += quantite
+ # 1. QUANTITÉ
+ if sens_ordre == "achat":
+ groupes_dict[ordre_id]["total_quantite"] += quantite
+ elif sens_ordre == "vente":
+ groupes_dict[ordre_id]["total_quantite"] -= quantite
# 2. ENGAGEMENT BRUT
- if sens_ordre == "achat" and type_ordre == "long":
- groupes_dict[isin]["total_eng_brut"] += eng_brut
- elif sens_ordre == "vente" and type_ordre == "long":
- groupes_dict[isin]["total_eng_brut"] -= eng_brut
- elif sens_ordre == "achat" and type_ordre == "short":
- groupes_dict[isin]["total_eng_brut"] -= eng_brut
- elif sens_ordre == "vente" and type_ordre == "short":
- groupes_dict[isin]["total_eng_brut"] += eng_brut
+ if sens_ordre == "achat":
+ groupes_dict[ordre_id]["total_eng_brut"] -= eng_brut
+ elif sens_ordre == "vente":
+ groupes_dict[ordre_id]["total_eng_brut"] += eng_brut
# 3. ENGAGEMENT NET
- if sens_ordre == "achat" and type_ordre == "long":
- groupes_dict[isin]["total_eng_net"] += eng_net
- elif sens_ordre == "vente" and type_ordre == "long":
- groupes_dict[isin]["total_eng_net"] -= eng_net
- elif sens_ordre == "achat" and type_ordre == "short":
- groupes_dict[isin]["total_eng_net"] -= eng_net
- elif sens_ordre == "vente" and type_ordre == "short":
- groupes_dict[isin]["total_eng_net"] += eng_net
+ if sens_ordre == "achat":
+ groupes_dict[ordre_id]["total_eng_net"] -= eng_net
+ elif sens_ordre == "vente":
+ groupes_dict[ordre_id]["total_eng_net"] += eng_net
# --- VÉRIFICATION ET MISE À JOUR AUTOMATIQUE (EN BASE ET EN MÉMOIRE) ---
try:
with db.engine.begin() as conn:
- for isin, groupe in groupes_dict.items():
+ for ordre_id, groupe in groupes_dict.items():
if abs(groupe["total_quantite"]) < 1e-9:
- ordre_id = groupe["id"]
total_eng_net = groupe["total_eng_net"]
- # Recherche de la date la plus grande (récente) parmi toutes les lignes (pieds) de l'ordre
max_date = max((p["date_op"] for p in groupe["pieds"] if p["date_op"] is not None), default=None)
- # 1. Mise à jour de l'état des lignes dans la table ordre_pied
conn.execute(
text(
"UPDATE ordre_pied SET Etat = :nouvel_etat WHERE Id_entete = :ordre_id"
@@ -573,7 +601,6 @@ def gestion_ordres():
{"nouvel_etat": "soldé", "ordre_id": ordre_id},
)
- # 2. Mise à jour de PlusValue et date_plusvalue dans la table ordre
conn.execute(
text(
"UPDATE ordre SET PlusValue = :plus_value, date_plusvalue = :date_pv WHERE ID = :ordre_id"
@@ -581,16 +608,17 @@ def gestion_ordres():
{"plus_value": total_eng_net, "date_pv": max_date, "ordre_id": ordre_id},
)
- # 3. Mise à jour immédiate en mémoire pour l'affichage dans le template
groupe["plus_value"] = total_eng_net
groupe["date_plusvalue"] = max_date
for pied in groupe["pieds"]:
pied["etat"] = "soldé"
-
+
except Exception as e:
pass
groupes_ordres = list(groupes_dict.values())
+
+ annees_combo = ["Tout"] + [str(an) for an in range(2020, 2041)]
return render_template(
"gestion_ordres.html",
@@ -598,6 +626,9 @@ def gestion_ordres():
selected_societe_id=selected_societe_id,
selected_societe=selected_societe,
groupes_ordres=groupes_ordres,
+ total_plus_value_globale=total_plus_value_globale,
+ annees_combo=annees_combo,
+ selected_annee=selected_annee,
)
@@ -634,81 +665,84 @@ def verifier_isin():
# Route pour la création d'un ordre de traitement
@main.route("/creer-ordre-traitement", methods=["POST"])
def creer_ordre_traitement():
- if "user_id" not in session:
- return redirect(url_for("main.index"))
+ if "user_id" not in session:
+ return redirect(url_for("main.index"))
- isin = request.form.get("isin")
- date_op = request.form.get("date_op")
- quantite = float(request.form.get("quantite", 0))
- prix_brut = float(request.form.get("prix_brut", 0))
+ isin = request.form.get("isin")
+ date_op = request.form.get("date_op")
+ selected_annee = request.form.get("annee", "tout")
- # Récupération des trois types de frais avec les bons noms d'inputs
- frais_brocker = float(request.form.get("frais_brocker", 0))
- frais_etat = float(request.form.get("frais_etat", 0))
- frais_autre = float(request.form.get("frais_autre", 0))
+ try:
+ quantite = float(request.form.get("quantite") or 0)
+ prix_brut = float(request.form.get("prix_brut") or 0)
+ frais_brocker = float(request.form.get("frais_brocker") or 0)
+ frais_etat = float(request.form.get("frais_etat") or 0)
+ frais_autre = float(request.form.get("frais_autre") or 0)
+ except ValueError:
+ flash("Veuillez saisir des valeurs numériques valides pour les quantités, prix et frais.", "danger")
+ return redirect(url_for("main.gestion_ordres", annee=selected_annee))
- ordre = request.form.get("ordre")
- type_ordre = request.form.get("type_ordre")
- etat = request.form.get("etat")
+ ordre = request.form.get("ordre")
+ type_ordre = request.form.get("type_ordre")
+ etat = request.form.get("etat")
- # Récupération de l'id_societe courant stocké dans la session
- id_societe = session.get("selected_societe_id")
+ # Récupération de l'id_societe courant stocké dans la session
+ id_societe = session.get("selected_societe_id")
- if not id_societe:
- flash(
- "Veuillez sélectionner une société active avant de créer un ordre.",
- "danger",
- )
- return redirect(url_for("main.gestion_ordres"))
+ if not id_societe:
+ flash(
+ "Veuillez sélectionner une société active avant de créer un ordre.",
+ "danger",
+ )
+ return redirect(url_for("main.gestion_ordres", annee=selected_annee))
- try:
- # 1. Insertion dans la table 'ordre' avec l'isin et l'id_societe
- query_ordre = text(
- "INSERT INTO ordre (id_societe, isin) VALUES (:id_societe, :isin)"
- )
- db.session.execute(query_ordre, {"id_societe": id_societe, "isin": isin})
+ try:
+ # 1. Insertion dans la table 'ordre' avec l'isin et l'id_societe
+ query_ordre = text(
+ "INSERT INTO ordre (id_societe, isin) VALUES (:id_societe, :isin)"
+ )
+ db.session.execute(query_ordre, {"id_societe": id_societe, "isin": isin})
- # Récupérer l'ID généré pour l'en-tête (Id_entete)
- result_id = db.session.execute(
- text("SELECT LAST_INSERT_ID()")
- ).scalar()
- if not result_id:
- result_id = db.session.execute(
- text(
- "SELECT ID FROM ordre WHERE isin = :isin ORDER BY ID DESC LIMIT"
- " 1"
- ),
- {"isin": isin},
- ).fetchone()[0]
+ # Récupérer l'ID généré pour l'en-tête (Id_entete)
+ result_id = db.session.execute(
+ text("SELECT LAST_INSERT_ID()")
+ ).scalar()
+ if not result_id:
+ result_id = db.session.execute(
+ text(
+ "SELECT ID FROM ordre WHERE isin = :isin ORDER BY ID DESC LIMIT 1"
+ ),
+ {"isin": isin},
+ ).fetchone()[0]
- # 2. Insertion dans la table 'ordre_pied' avec les 3 frais distincts
- query_pied = text("""
+ # 2. Insertion dans la table 'ordre_pied' avec les 3 frais distincts
+ query_pied = text("""
INSERT INTO ordre_pied (Id_entete, Date_op, Quantite, Prix_brut, Frais_brocker, Frais_etat, Frais_autre, Ordre, Type, Etat)
VALUES (:id_entete, :date_op, :quantite, :prix_brut, :frais_brocker, :frais_etat, :frais_autre, :ordre, :type_ordre, :etat)
""")
- db.session.execute(
- query_pied,
- {
- "id_entete": result_id,
- "date_op": date_op,
- "quantite": quantite,
- "prix_brut": prix_brut,
- "frais_brocker": frais_brocker,
- "frais_etat": frais_etat,
- "frais_autre": frais_autre,
- "ordre": ordre,
- "type_ordre": type_ordre,
- "etat": etat,
- },
- )
+ db.session.execute(
+ query_pied,
+ {
+ "id_entete": result_id,
+ "date_op": date_op,
+ "quantite": quantite,
+ "prix_brut": prix_brut,
+ "frais_brocker": frais_brocker,
+ "frais_etat": frais_etat,
+ "frais_autre": frais_autre,
+ "ordre": ordre,
+ "type_ordre": type_ordre,
+ "etat": etat,
+ },
+ )
- db.session.commit()
- flash("Ordre créé avec succès !", "success")
- except Exception as e:
- db.session.rollback()
- flash(f"Erreur lors de la création de l'ordre : {e}", "danger")
+ db.session.commit()
+ flash("Ordre créé avec succès !", "success")
+ except Exception as e:
+ db.session.rollback()
+ flash(f"Erreur lors de la création de l'ordre : {e}", "danger")
- return redirect(url_for("main.gestion_ordres"))
+ return redirect(url_for("main.gestion_ordres", annee=selected_annee))
# Route pour créer une nouvelle ligne rattachée à un ordre existant (depuis le bouton "Créer")
@@ -730,7 +764,20 @@ def ajouter_ligne_ordre(ordre_id):
flash("Ordre introuvable.", "danger")
return redirect(url_for("main.gestion_ordres"))
+ selected_annee = request.args.get("annee", "tout")
+
if request.method == "POST":
+ selected_annee = request.form.get("annee", "tout")
+ try:
+ quantite = float(request.form.get("quantite") or 0)
+ prix_brut = float(request.form.get("prix_brut") or 0)
+ frais_brocker = float(request.form.get("frais_brocker") or 0)
+ frais_etat = float(request.form.get("frais_etat") or 0)
+ frais_autre = float(request.form.get("frais_autre") or 0)
+ except ValueError:
+ flash("Veuillez saisir des valeurs numériques valides.", "danger")
+ return redirect(url_for("main.ajouter_ligne_ordre", ordre_id=ordre_id, annee=selected_annee))
+
try:
query_pied = text("""
INSERT INTO ordre_pied (Id_entete, Date_op, Quantite, Prix_brut, Frais_brocker, Frais_etat, Frais_autre, Ordre, Type, Etat)
@@ -739,18 +786,18 @@ def ajouter_ligne_ordre(ordre_id):
db.session.execute(query_pied, {
"id_entete": ordre_id,
"date_op": request.form.get("date_op"),
- "quantite": float(request.form.get("quantite", 0)),
- "prix_brut": float(request.form.get("prix_brut", 0)),
- "frais_brocker": float(request.form.get("frais_brocker", 0)),
- "frais_etat": float(request.form.get("frais_etat", 0)),
- "frais_autre": float(request.form.get("frais_autre", 0)),
+ "quantite": quantite,
+ "prix_brut": prix_brut,
+ "frais_brocker": frais_brocker,
+ "frais_etat": frais_etat,
+ "frais_autre": frais_autre,
"ordre": request.form.get("ordre"),
"type_ordre": request.form.get("type_ordre"),
"etat": request.form.get("etat"),
})
db.session.commit()
flash("Ligne ajoutée avec succès à l'ordre !", "success")
- return redirect(url_for("main.gestion_ordres"))
+ return redirect(url_for("main.gestion_ordres", annee=selected_annee))
except Exception as e:
db.session.rollback()
flash(f"Erreur lors de l'ajout : {e}", "danger")
@@ -784,7 +831,20 @@ def modifier_ligne_ordre(pied_id):
flash("Ligne d'ordre introuvable.", "danger")
return redirect(url_for("main.gestion_ordres"))
+ selected_annee = request.args.get("annee", "tout")
+
if request.method == "POST":
+ selected_annee = request.form.get("annee", "tout")
+ try:
+ quantite = float(request.form.get("quantite") or 0)
+ prix_brut = float(request.form.get("prix_brut") or 0)
+ frais_brocker = float(request.form.get("frais_brocker") or 0)
+ frais_etat = float(request.form.get("frais_etat") or 0)
+ frais_autre = float(request.form.get("frais_autre") or 0)
+ except ValueError:
+ flash("Veuillez saisir des valeurs numériques valides.", "danger")
+ return redirect(url_for("main.modifier_ligne_ordre", pied_id=pied_id, annee=selected_annee))
+
try:
query_update = text("""
UPDATE ordre_pied
@@ -795,11 +855,11 @@ def modifier_ligne_ordre(pied_id):
""")
db.session.execute(query_update, {
"date_op": request.form.get("date_op"),
- "quantite": float(request.form.get("quantite", 0)),
- "prix_brut": float(request.form.get("prix_brut", 0)),
- "frais_brocker": float(request.form.get("frais_brocker", 0)),
- "frais_etat": float(request.form.get("frais_etat", 0)),
- "frais_autre": float(request.form.get("frais_autre", 0)),
+ "quantite": quantite,
+ "prix_brut": prix_brut,
+ "frais_brocker": frais_brocker,
+ "frais_etat": frais_etat,
+ "frais_autre": frais_autre,
"ordre": request.form.get("ordre"),
"type_ordre": request.form.get("type_ordre"),
"etat": request.form.get("etat"),
@@ -807,7 +867,7 @@ def modifier_ligne_ordre(pied_id):
})
db.session.commit()
flash("Ligne modifiée avec succès !", "success")
- return redirect(url_for("main.gestion_ordres"))
+ return redirect(url_for("main.gestion_ordres", annee=selected_annee))
except Exception as e:
db.session.rollback()
flash(f"Erreur lors de la modification : {e}", "danger")
diff --git a/app/templates/gestion_ordres.html b/app/templates/gestion_ordres.html
index 8d0bddd..0408ddf 100644
--- a/app/templates/gestion_ordres.html
+++ b/app/templates/gestion_ordres.html
@@ -35,12 +35,58 @@
-