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 @@

-
+ +
+ +
+ + + + +
+ P.V. ( + + ) : + + {{ "{:,.2f}".format(total_plus_value_globale or 0) }} € + +
+
+
+ + +
@@ -366,18 +419,24 @@
Total Frais : 0.0000.0000
Prix Net : 0.0000.0000 +
+
+ Eng. Net : + 0.00
@@ -453,7 +512,7 @@ calculerTotauxModal(); }); - // Calcul dynamique : Prix Net unitaire = Prix Brut + Frais Totaux + // Calcul dynamique : Prix Net unitaire = Prix Brut +/- Frais Totaux function calculerTotauxModal() { const quantite = parseFloat(document.getElementById("input-quantite").value) || 0; @@ -483,7 +542,7 @@ // Engagement Net = Prix Net * Quantité const engNet = quantite * prixNet; - // Mise à jour des éléments dans le DOM (adaptez les IDs selon vos balises HTML) + // Mise à jour des éléments dans le DOM document.getElementById("apercu-frais").textContent = totalFrais.toFixed(4); document.getElementById("apercu-prix-net").textContent = prixNet.toFixed(4); const apercuEngNet = document.getElementById("apercu-eng-net"); @@ -552,7 +611,8 @@ const btnValider = document.getElementById("btn-valider"); modal.classList.remove("hidden"); - form.action = `/ordre/${ordreId}/ajouter-ligne`; + const anneeActive = "{{ selected_annee if selected_annee else 'tout' }}"; + form.action = `/ordre/${ordreId}/ajouter-ligne?annee=${anneeActive}`; title.innerHTML = ' Ajouter une ligne à l\'ordre'; @@ -603,7 +663,8 @@ const btnValider = document.getElementById("btn-valider"); modal.classList.remove("hidden"); - form.action = `/modifier-ligne-ordre/${piedId}`; + const anneeActive = "{{ selected_annee if selected_annee else 'tout' }}"; + form.action = `/modifier-ligne-ordre/${piedId}?annee=${anneeActive}`; title.innerHTML = ' Modifier la ligne d\'ordre';