From 39e5ae87ffaf6142ed4396f65b6ec132bd589daa Mon Sep 17 00:00:00 2001 From: jfgiraud Date: Wed, 8 Jul 2026 15:08:50 +0000 Subject: [PATCH] WIP: bolsa V 1.0 --- css/ordreJFG.css | 358 +++++++++++++++++++++++++++++ js/ordreJFG.js | 220 ++++++++++++++++++ php/api_enregistrer_detail_jfg.php | 27 +++ php/api_enregistrer_ordre_jfg.php | 12 + php/api_modifier_detail_jfg.php | 47 ++++ php/api_modifier_ordre.php | 2 +- php/api_modifier_ordre_jfg.php | 20 ++ php/api_rechercher_action_jfg.php | 12 + php/menu.php | 117 +++++----- php/ordreJFG.php | 337 +++++++++++++++++++++++++++ 10 files changed, 1092 insertions(+), 60 deletions(-) create mode 100644 css/ordreJFG.css create mode 100644 js/ordreJFG.js create mode 100644 php/api_enregistrer_detail_jfg.php create mode 100644 php/api_enregistrer_ordre_jfg.php create mode 100644 php/api_modifier_detail_jfg.php create mode 100644 php/api_modifier_ordre_jfg.php create mode 100644 php/api_rechercher_action_jfg.php create mode 100644 php/ordreJFG.php diff --git a/css/ordreJFG.css b/css/ordreJFG.css new file mode 100644 index 0000000..971f947 --- /dev/null +++ b/css/ordreJFG.css @@ -0,0 +1,358 @@ +:root { + --ordrejfg-bg: #13161f; + --ordrejfg-card: #1c2030; + --ordrejfg-border: rgba(255, 255, 255, 0.08); + --ordrejfg-text: #f0f2fc; +} + +/* Force le plein écran */ +.ordrejfg-page { + background-color: var(--ordrejfg-bg); + margin: 0; + padding: 0; +} + +.ordrejfg-shell { + width: 100%; + max-width: none; + margin: 0; + padding: 20px; + box-sizing: border-box; +} + +.ordrejfg-card { + background: var(--ordrejfg-card); + border-radius: 12px; + padding: 20px; + width: 100%; + box-sizing: border-box; +} + +/* Tables en pleine largeur */ +.ordrejfg-table, +.ordrejfg-detail-table { + width: 100% !important; + border-collapse: collapse; + table-layout: auto; +} + +/* Alignements entêtes */ +.ordrejfg-table th, +.ordrejfg-detail-table th { + background: rgba(255, 255, 255, 0.05); + padding: 12px; + text-align: left; + color: #fff; +} + +.ordrejfg-detail-table th { + text-align: center !important; + vertical-align: middle; +} + +.ordrejfg-detail-table td { + padding: 8px; + border-bottom: 1px solid var(--ordrejfg-border); +} + +/* Styles boutons */ +.ordrejfg-edit-button { + padding: 4px 12px; + cursor: pointer; +} + +/* Boutons principaux (Créer et Retour) */ +.ordrejfg-btn-menu, +.ordrejfg-btn-creer { + padding: 10px 24px !important; + border-radius: 50px !important; + border: none !important; + cursor: pointer !important; + font-size: 14px !important; + font-weight: 600 !important; + text-decoration: none !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + transition: background 0.3s ease !important; + color: #ffffff !important; +} + +/* Bouton Menu (Bleu) */ +.ordrejfg-btn-menu { + background-color: #1a237e !important; +} +.ordrejfg-btn-menu:hover { + background-color: #283593 !important; +} + +/* Bouton Créer (Vert) */ +.ordrejfg-btn-creer { + background-color: #28a745 !important; +} +.ordrejfg-btn-creer:hover { + background-color: #218838 !important; +} + +/* Styles lignes */ +.ordrejfg-header-style { + background: rgba(255, 255, 255, 0.05) !important; +} + +.ordrejfg-header-style td { + color: #fff !important; + font-weight: 600 !important; + padding: 12px !important; + border-bottom: 2px solid rgba(255, 255, 255, 0.1) !important; +} + +.ordrejfg-detail-row td { + padding: 10px 5px !important; + border-bottom: 1px solid var(--ordrejfg-border); + color: #e0e0e0; + font-family: monospace; +} + +.ordrejfg-detail-row:hover { + background-color: rgba(255, 255, 255, 0.03); +} + +/* Alignement colonnes numériques */ +.ordrejfg-detail-table .text-right { + text-align: right !important; + padding-right: 15px !important; +} + +/* Classe commune : force la taille et l'alignement */ +.ordrejfg-btn-petit { + width: 90px; + height: 30px; + padding: 0 10px; + font-size: 0.8em; + font-weight: 600; + cursor: pointer; + border: none; + border-radius: 4px; + display: inline-flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + transition: background 0.2s ease; +} + +/* Couleurs spécifiques */ +.ordrejfg-btn-creer-petit { + background-color: #28a745; + color: white; +} +.ordrejfg-btn-creer-petit:hover { + background-color: #218838; +} + +.ordrejfg-btn-modifier-petit { + background-color: #1a237e; + color: white; +} +.ordrejfg-btn-modifier-petit:hover { + background-color: #283593; +} + +.ordrejfg-master-row { + background: linear-gradient(90deg, #001f3f, #004cff) !important; + color: #ffffff; + font-weight: 600; + letter-spacing: 0.3px; + border-bottom: 1px solid rgba(255, 255, 255, 0.15); + box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.08); +} + +/**********************************************************/ + +/* 1. On nettoie le body */ +html, +body { + height: 100%; + margin: 0; + overflow: hidden; +} + +/* 2. On positionne le shell en absolu pour qu'il s'insère entre le header et le footer */ +.ordrejfg-shell { + position: absolute; + top: 80px; + bottom: 50px; + left: 0; + right: 0; + display: flex; + flex-direction: column; + padding: 10px; + box-sizing: border-box; +} + +/* 3. La carte occupe l'espace restant */ +.ordrejfg-card { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; +} + +/* 4. C'est ici que le scroll se passe */ +.ordrejfg-table-wrapper { + flex: 1; + overflow-y: auto; +} + +.ordrejfg-table { + width: 100%; + margin: 0; +} + +/* =========================== + MODALES +=========================== */ + +.ordrejfg-modal { + width: 520px; + max-width: 92vw; + border: none; + border-radius: 18px; + background: #20263a; + color: white; + padding: 30px; + box-shadow: 0 25px 60px rgba(0, 0, 0, 0.45); + animation: popup 0.18s ease-out; +} + +.ordrejfg-modal::backdrop { + background: rgba(0, 0, 0, 0.65); + backdrop-filter: blur(5px); +} + +@keyframes popup { + from { + opacity: 0; + transform: translateY(-20px) scale(0.95); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.ordrejfg-modal h2 { + margin-top: 0; + margin-bottom: 25px; + text-align: center; + font-size: 24px; + font-weight: 600; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 8px; + margin-bottom: 18px; +} + +.form-group label { + font-weight: 600; + color: #cfd8ff; +} + +.ordrejfg-modal input, +.ordrejfg-modal select { + width: 100%; + box-sizing: border-box; + padding: 12px 14px; + border-radius: 10px; + border: 1px solid #3b4668; + background: #151b2d; + color: white; + font-size: 15px; + outline: none; + transition: 0.25s; +} + +.ordrejfg-modal input:focus, +.ordrejfg-modal select:focus { + border-color: #4d84ff; + box-shadow: 0 0 0 3px rgba(77, 132, 255, 0.25); +} + +.modal-actions { + display: flex; + justify-content: flex-end; + gap: 12px; + margin-top: 28px; +} + +.modal-actions button { + border: none; + padding: 11px 22px; + border-radius: 10px; + font-weight: 600; + cursor: pointer; + transition: 0.2s; +} + +.modal-actions button:first-child { + background: #444b63; + color: white; +} + +.modal-actions button:first-child:hover { + background: #59617c; +} + +.modal-actions button:last-child { + background: #2dbf5a; + color: white; +} + +.modal-actions button:last-child:hover { + background: #27a84f; +} + +.form-group .triple { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 10px; +} + +#btnRechercher { + margin-top: 10px; + width: 100%; + padding: 12px; + border: none; + border-radius: 10px; + background: #2962ff; + color: white; + font-weight: 600; + cursor: pointer; +} + +#btnRechercher:hover { + background: #1d4ed8; +} + +dialog.ordrejfg-modal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + margin: 0; + width: 520px; + max-width: 90vw; + border: none; + border-radius: 18px; + padding: 30px; + background: #20263a; + color: white; + box-shadow: 0 25px 60px rgba(0, 0, 0, 0.45); +} + +dialog.ordrejfg-modal::backdrop { + background: rgba(0, 0, 0, 0.65); + backdrop-filter: blur(5px); +} diff --git a/js/ordreJFG.js b/js/ordreJFG.js new file mode 100644 index 0000000..dcde67e --- /dev/null +++ b/js/ordreJFG.js @@ -0,0 +1,220 @@ +document.addEventListener("DOMContentLoaded", () => { + // ===================================================== + // OUTILS MODALES + // ===================================================== + + function openModal(modal) { + if (!modal) return; + + modal.showModal(); + + // Force le centrage avec le CSS dialog + modal.style.transform = "translate(-50%, -50%)"; + } + + function closeModal(modal) { + if (!modal) return; + modal.close(); + modal.style.transform = ""; + } + + // Fermeture par clic en dehors de la fenêtre + document.querySelectorAll("dialog").forEach((dialog) => { + dialog.addEventListener("click", (e) => { + const rect = dialog.getBoundingClientRect(); + + const inside = + e.clientX >= rect.left && + e.clientX <= rect.right && + e.clientY >= rect.top && + e.clientY <= rect.bottom; + + if (!inside) { + closeModal(dialog); + } + }); + }); + + // ===================================================== + // MODAL CREATION ORDRE + // ===================================================== + + const modalCreerOrdre = document.getElementById("modalCreerOrdre"); + + const btnCreer = document.querySelector(".ordrejfg-btn-creer"); + + if (modalCreerOrdre && btnCreer) { + btnCreer.addEventListener("click", () => { + openModal(modalCreerOrdre); + }); + + document.getElementById("btnAnnuler")?.addEventListener("click", () => { + document.getElementById("formCreerOrdre")?.reset(); + + closeModal(modalCreerOrdre); + + const btnValider = document.getElementById("btnValider"); + + if (btnValider) { + btnValider.disabled = true; + } + }); + } + + // ===================================================== + // RECHERCHE ACTION + // ===================================================== + + const btnRechercher = document.getElementById("btnRechercher"); + + btnRechercher?.addEventListener("click", async () => { + const isin = document.getElementById("isinRecherche").value; + + if (!isin) { + alert("Veuillez entrer un code ISIN"); + return; + } + + const response = await fetch( + `/php/api_rechercher_action_jfg.php?isin=${encodeURIComponent(isin)}`, + ); + + const data = await response.json(); + + if (data.success) { + document.getElementById("Nom").value = data.company_name; + + document.getElementById("ticker").value = data.ticker; + + document.getElementById("code_yahoo").value = data.yahoo_code; + + document.getElementById("btnValider").disabled = false; + } else { + alert("Action non trouvée"); + } + }); + + // ===================================================== + // ENREGISTREMENT ORDRE + // ===================================================== + + document + .getElementById("formCreerOrdre") + ?.addEventListener("submit", async (e) => { + e.preventDefault(); + + const formData = new FormData(e.target); + + const response = await fetch("/php/api_enregistrer_ordre_jfg.php", { + method: "POST", + body: formData, + }); + + const result = await response.json(); + + if (result.success) { + location.reload(); + } else { + alert( + "Erreur lors de l'enregistrement : " + + (result.message || "Erreur inconnue"), + ); + } + }); + + // ===================================================== + // ENREGISTREMENT DETAIL + // ===================================================== + + document + .getElementById("formCreerDetail") + ?.addEventListener("submit", async (e) => { + e.preventDefault(); + + const response = await fetch("/php/api_enregistrer_detail_jfg.php", { + method: "POST", + body: new FormData(e.target), + }); + + const result = await response.json(); + + if (result.success) { + location.reload(); + } else { + alert( + "Erreur lors de l'enregistrement du détail : " + + (result.message || ""), + ); + } + }); + + // ===================================================== + // MODIFICATION DETAIL + // ===================================================== + + document.addEventListener("click", (e) => { + const button = e.target.closest(".ordrejfg-btn-modifier-petit"); + + if (!button) return; + + const detail = JSON.parse(button.getAttribute("data-detail")); + + const modal = document.getElementById("modalModifierDetail"); + + if (!modal) return; + + document.getElementById("edit_id").value = detail.Id; + + document.getElementById("edit_Id_entete").value = detail.Id_entete; + + document.getElementById("edit_date").value = detail.Date_op; + + document.getElementById("edit_quantite").value = detail.Quantite; + + document.getElementById("edit_prix").value = detail.Prix_brut; + + document.getElementById("edit_frais_broker").value = + detail.Frais_brocker || 0; + + document.getElementById("edit_frais_etat").value = detail.Frais_etat || 0; + + document.getElementById("edit_frais_autre").value = detail.Frais_autre || 0; + + document.getElementById("edit_type").value = ( + detail.Type || "" + ).toLowerCase(); + + document.getElementById("edit_etat").value = ( + detail.Etat || "" + ).toLowerCase(); + + document.getElementById("edit_ordre").value = ( + detail.Ordre || "" + ).toLowerCase(); + + openModal(modal); + }); + + // ===================================================== + // ENREGISTREMENT MODIFICATION + // ===================================================== + + document + .getElementById("formModifierDetail") + ?.addEventListener("submit", async (e) => { + e.preventDefault(); + + const response = await fetch("/php/api_modifier_detail_jfg.php", { + method: "POST", + body: new FormData(e.target), + }); + + const result = await response.json(); + + if (result.success) { + location.reload(); + } else { + alert("Erreur : " + result.message); + } + }); +}); diff --git a/php/api_enregistrer_detail_jfg.php b/php/api_enregistrer_detail_jfg.php new file mode 100644 index 0000000..5a557e4 --- /dev/null +++ b/php/api_enregistrer_detail_jfg.php @@ -0,0 +1,27 @@ +getConnection(); + +// Insertion dans la table ordreJFG_pied +$sql = "INSERT INTO ordreJFG_pied (Id_entete, Date_op, Quantite, Prix_brut, Frais_brocker, Frais_etat, Frais_autre, Ordre, Type, Etat) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + +$stmt = $db->prepare($sql); + +$success = $stmt->execute([ + $_POST['Id_entete'], + $_POST['Date_op'], + $_POST['Quantite'], + $_POST['Prix_brut'], + $_POST['Frais_brocker'], + $_POST['Frais_etat'], + $_POST['Frais_autre'], + $_POST['Ordre'], + $_POST['Type'], + $_POST['Etat'] +]); + +echo json_encode(['success' => $success]); diff --git a/php/api_enregistrer_ordre_jfg.php b/php/api_enregistrer_ordre_jfg.php new file mode 100644 index 0000000..0c55407 --- /dev/null +++ b/php/api_enregistrer_ordre_jfg.php @@ -0,0 +1,12 @@ +getConnection(); + +// Insertion dans la table ordreJFG +$stmt = $db->prepare("INSERT INTO ordreJFG (isin, Nom, ticker, code_yahoo) VALUES (?, ?, ?, ?)"); +$success = $stmt->execute([$_POST['isin'], $_POST['Nom'], $_POST['ticker'], $_POST['code_yahoo']]); + +echo json_encode(['success' => $success]); diff --git a/php/api_modifier_detail_jfg.php b/php/api_modifier_detail_jfg.php new file mode 100644 index 0000000..1015dcd --- /dev/null +++ b/php/api_modifier_detail_jfg.php @@ -0,0 +1,47 @@ +getConnection(); + + // Mise à jour de la table ordreJFG_pied + $sql = "UPDATE ordreJFG_pied SET + Date_op = :date, + Quantite = :qte, + Prix_brut = :prix, + Frais_brocker = :f_b, + Frais_etat = :f_e, + Frais_autre = :f_a, + Ordre = :ordre, + Type = :type, + Etat = :etat + WHERE Id = :id"; + + $stmt = $db->prepare($sql); + + $stmt->execute([ + ':date' => $_POST['Date_op'], + ':qte' => $_POST['Quantite'], + ':prix' => $_POST['Prix_brut'], + ':f_b' => $_POST['Frais_brocker'], + ':f_e' => $_POST['Frais_etat'], + ':f_a' => $_POST['Frais_autre'], + ':ordre' => $_POST['Ordre'], + ':type' => $_POST['Type'], + ':etat' => $_POST['Etat'], + ':id' => $_POST['id'] + ]); + + echo json_encode(['success' => true]); + } catch (Exception $e) { + echo json_encode([ + 'success' => false, + 'message' => $e->getMessage() + ]); + } +} diff --git a/php/api_modifier_ordre.php b/php/api_modifier_ordre.php index 21ea363..11f912d 100644 --- a/php/api_modifier_ordre.php +++ b/php/api_modifier_ordre.php @@ -10,7 +10,7 @@ $nom = $_POST['nom']; $ticker = $_POST['ticker']; try { - $stmt = $db->prepare("UPDATE ordres SET nom = ?, ticker = ? WHERE id = ?"); + $stmt = $db->prepare("UPDATE ordreGF SET nom = ?, ticker = ? WHERE id = ?"); $stmt->execute([$nom, $ticker, $id]); echo json_encode(['success' => true]); diff --git a/php/api_modifier_ordre_jfg.php b/php/api_modifier_ordre_jfg.php new file mode 100644 index 0000000..f1e9515 --- /dev/null +++ b/php/api_modifier_ordre_jfg.php @@ -0,0 +1,20 @@ +getConnection(); + +// Récupération des données POST +$id = $_POST['id']; +$nom = $_POST['nom']; +$ticker = $_POST['ticker']; + +try { + // Mise à jour de la table ordreJFG + $stmt = $db->prepare("UPDATE ordreJFG SET Nom = ?, ticker = ? WHERE ID = ?"); + $stmt->execute([$nom, $ticker, $id]); + + echo json_encode(['success' => true]); +} catch (Exception $e) { + echo json_encode(['success' => false, 'message' => $e->getMessage()]); +} diff --git a/php/api_rechercher_action_jfg.php b/php/api_rechercher_action_jfg.php new file mode 100644 index 0000000..fa75242 --- /dev/null +++ b/php/api_rechercher_action_jfg.php @@ -0,0 +1,12 @@ +getConnection(); + +// Si vous avez aussi une table actions_jfg spécifique, remplacez 'actions' par 'actions_jfg' +$stmt = $db->prepare("SELECT * FROM actions WHERE isin = ?"); +$stmt->execute([$isin]); +$action = $stmt->fetch(PDO::FETCH_ASSOC); + +echo json_encode($action ? ['success' => true, ...$action] : ['success' => false]); diff --git a/php/menu.php b/php/menu.php index 9159fc6..7431e15 100755 --- a/php/menu.php +++ b/php/menu.php @@ -17,71 +17,70 @@ $bodyClass = 'menu-page'; require_once __DIR__ . '/header.php'; ?> - - - - - - - - + + \ No newline at end of file diff --git a/php/ordreJFG.php b/php/ordreJFG.php new file mode 100644 index 0000000..b247cbe --- /dev/null +++ b/php/ordreJFG.php @@ -0,0 +1,337 @@ +getConnection(); + +function fetchOrdreJFG(PDO $connection): array +{ + $stmt = $connection->query('SELECT * FROM `ordreJFG` ORDER BY `ID` DESC'); + return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []; +} + +function fetchOrdreJFGPied(PDO $connection, string $masterId): array +{ + $stmt = $connection->prepare('SELECT * FROM `ordreJFG_pied` WHERE `Id_entete` = :masterId ORDER BY `Id` ASC'); + $stmt->execute([':masterId' => $masterId]); + return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []; +} + +$ordres = fetchOrdreJFG($connection); + +require_once __DIR__ . '/header.php'; +?> + +
+
+
+

Gestion des ordres JFG

+
+ + +
+
+ +
+ + + + prepare("UPDATE ordreJFG SET PlusValue = 0 WHERE ID = ?")->execute([$ordre['ID']]); + $ordre['PlusValue'] = 0; + } + } + + $isSold = (!$isCanceled && $totalQ === 0 && count($details) > 0); + if ($isSold) { + foreach ($details as $detail) { + $qte = (int)$detail['Quantite']; + $fraisDetail = (float)$detail['Frais_brocker'] + (float)$detail['Frais_etat'] + (float)$detail['Frais_autre']; + $oType = strtolower($detail['Ordre']); + $prixNet = ($oType === 'vente') ? (float)$detail['Prix_brut'] - $fraisDetail : (float)$detail['Prix_brut'] + $fraisDetail; + $tOp = strtolower($detail['Type']); + $signePV = ($oType === 'vente') ? 1 : -1; + $plusvalue += ($qte * $prixNet * $signePV); + } + $plusvalue = round($plusvalue, 4); + + if (abs((float)$ordre['PlusValue'] - $plusvalue) > 0.0001) { + $connection->prepare("UPDATE ordreJFG SET PlusValue = ? WHERE ID = ?")->execute([$plusvalue, $ordre['ID']]); + $ordre['PlusValue'] = $plusvalue; + } + + $connection->prepare("UPDATE ordreJFG_pied SET Etat = 'soldé' WHERE Id_entete = ? AND Etat = 'actif'")->execute([$ordre['ID']]); + $details = fetchOrdreJFGPied($connection, (string)$ordre['ID']); + } + + $isDisabled = $isCanceled || abs((float)$ordre['PlusValue']) > 0.0001; + $disabledAttr = $isDisabled ? 'disabled style="opacity:0.5; cursor:not-allowed;"' : ''; + ?> + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateQtéPrix BrutFraisPrix NetEng. BrutEng. NetOrdreTypeEtat
Plus value :TOTAL :0
+
+
+
+
+ + + +
+

Créer un nouvel ordre

+
+ + + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ + +
+ +

Ajouter un mouvement

+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + +
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ + +
+ + +

Modifier le mouvement

+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + +
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ + + + \ No newline at end of file