new module AKTIEN - see README.md for details V.2.0

This commit is contained in:
fds2610 2026-02-16 21:04:36 +01:00
parent c927cb5b5c
commit d47b84fa22
20 changed files with 1231 additions and 11 deletions

View file

@ -1,6 +1,23 @@
# FSCash
Dokumentiere Ausgaben on-the-go als Webapp
& behalte den Überblick über Dein Aktiendepot
## ToDo
- [ ] Zusammenfassen von mehreren Einträgen zu einer Aktie
- [ ] Prüfen ob APIKEYS leer sind und ggfs die Aktualisierung ausblenden
## V.2.0
- [X] neue Haupt-Funktion: Aktien zeigt eine Liste mit gespeicherten, zu beobachtenden Aktien
- [X] Die Tabelle errechnet automatisch den aktuellen Gewinn oder Verluststand jeder Aktieneintragung
- [X] neue Aktie hinzufügen mit Kaufdatum und Kaufpreis, wird je Benutzer separat hinterlegt (user_id)
- [X] es KANN eine ISIN oder WKN eingetragen werden
- [X] Beim Abruf von aktuellen Kursdaten muss vorher ein API Key von https://www.alphavantage.co/support/#api-key beantragt werden.
- [X] Suchen von Symbolen passend zur ISIN geht über einen API Key von https://www.openfigi.com/user/signup
- [X] Alternativ kann auch nach dem Aktiennamen bei alphavantage gesucht werden - dafür muss man auf den Namen klicken
- [X] Aktien können geändert und gelöscht werden.
## V.1.2

View file

@ -0,0 +1,132 @@
<?php
require 'config.php';
error_log('Session user_id: ' . (isset($_SESSION['user_id']) ? $_SESSION['user_id'] : 'nicht gesetzt'));
// 1. Session-Prüfung (nur für geschützte Seiten)
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
// Ab hier: Nur für angemeldete Benutzer
error_log($_SESSION['user_id']);
// Prüfen, ob die user_id im Admin-Array ist
if (in_array($_SESSION['user_id'], $admin_ids, true)) {
$is_admin = "true";
}
// Prüfen, ob die user_id im Aktien-Array ist
if (in_array($_SESSION['user_id'], $aktien_ids, true)) {
$is_aktien = "true";
}
// Aktuelles Jahr und Monat ermitteln, ggf. aus GET-Parametern
$year = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
$month = isset($_GET['month']) ? (int)$_GET['month'] : (int)date('m');
// Monats- und Jahresgrenzen prüfen und anpassen
if ($month < 1) {
$month = 12;
$year--;
} elseif ($month > 12) {
$month = 1;
$year++;
}
// Daten abrufen
$sql="SELECT
id,
WKNISIN,
Aktie,
kaufdatum,
kaufpreis,
anzahl,
(kaufpreis * anzahl) AS Kaufsumme
FROM
aktien;
";
$stmt = $pdo->prepare($sql);
$stmt->execute([
'year' => $year,
'month' => $month
]);
$entries = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<title>FSCash - Dashboard</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<div class="container">
<!-- Navigation -->
<nav class="nav">
<button class="hamburger">☰</button>
<ul class="nav-list">
<li><a href="dashboard.php">Start</a></li>
<?php if ($is_aktien === "true") echo '<li><a href="aktien.php">Aktien</a></li>'; ?>
<?php if ($is_admin === "true") echo '<li><a href="register.php">Neue User</a></li>'; ?>
<li><a href="rep-month.php">Report Monat</a></li>
<li><a href="rep-person.php">Report Personen</a></li>
<li><a href="menu5.php" style="display: none;">Menüpunkt 5</a></li>
</ul>
<!-- Container für die rechten Buttons -->
<div class="nav-right">
<a href="?year=<?= $year ?>&month=<?= $month - 1 ?>" class="nav-button"> &lt; </a>
<a href="?year=<?= $year ?>&month=<?= $month + 1 ?>" class="nav-button"> &gt; </a>
<a href="logout.php" class="logout">Logout</a>
</div>
</nav>
<h2><?= strftime('%B %Y', mktime(0, 0, 0, $month, 1, $year)) ?></h2>
<!-- Tabelle -->
<table>
<thead>
<tr>
<th>ID</th>
<th>WKN/ISIN</th>
<th>Aktie</th>
<th>Kaufdatum</th>
<th>Kaufpreis</th>
<th>Anzahl</th>
<th>Kaufsumme</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
<?php foreach ($entries as $entry): ?>
<tr>
<td><?= htmlspecialchars($entry['id']) ?></td>
<td><?= htmlspecialchars($entry['WKNISIN']) ?></td>
<td><?= htmlspecialchars($entry['Aktie']) ?></td>
<td><?= htmlspecialchars($entry['kaufdatum']) ?></td>
<td class="align-right"><?= htmlspecialchars(number_format($entry['kaufpreis'], 2, ',', '.')) ?></td>
<td class="align-right"><?= htmlspecialchars(number_format($entry['anzahl'], 4, ',', '.')) ?></td>
<td class="align-right"><?= htmlspecialchars(number_format($entry['kaufpreis'] * $entry['anzahl'], 2, ',', '.')) ?></td>
<td>
<a href="edit_entry.php?id=<?= $entry['id'] ?>">
<img class="pencil" src="/assets/img/pencil.png" width="20px" />
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- Neu-Button -->
<a href="new_entry.php" class="btn-xr">+</a>
</div>
<script>
// Hamburger-Menü-Funktion
document.querySelector('.hamburger').addEventListener('click', function() {
document.querySelector('.nav-list').classList.toggle('show');
});
</script>
</body>
</html>

View file

@ -0,0 +1,3 @@
[Trash Info]
Path=aktien2.php
DeletionDate=2026-02-16T15:16:44

397
html/aktien.php Normal file
View file

@ -0,0 +1,397 @@
<?php
require 'config.php';
error_log('Session user_id: ' . (isset($_SESSION['user_id']) ? $_SESSION['user_id'] : 'nicht gesetzt'));
// 1. Session-Prüfung (nur für geschützte Seiten)
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
// Ab hier: Nur für angemeldete Benutzer
error_log($_SESSION['user_id']);
$userid = $_SESSION['user_id'];
// Prüfen, ob die user_id im Admin-Array ist
if (in_array($_SESSION['user_id'], $admin_ids, true)) {
$is_admin = "true";
}
// Prüfen, ob die user_id im Aktien-Array ist
if (in_array($_SESSION['user_id'], $aktien_ids, true)) {
$is_aktien = "true";
}
// Aktuelles Jahr und Monat ermitteln, ggf. aus GET-Parametern
$year = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
$month = isset($_GET['month']) ? (int)$_GET['month'] : (int)date('m');
// Monats- und Jahresgrenzen prüfen und anpassen
if ($month < 1) {
$month = 12;
$year--;
} elseif ($month > 12) {
$month = 1;
$year++;
}
// Daten abrufen
$sql="SELECT
id,
user_id,
WKNISIN,
Symbol,
Aktie,
kaufdatum,
kaufpreis,
anzahl,
kurs,
kursdatum,
(kaufpreis * anzahl) AS Kaufsumme
FROM
aktien
WHERE user_id = :userid;
";
$stmt = $pdo->prepare($sql);
$stmt->execute([
'userid' => $userid
]);
$entries = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<title>FSCash - Dashboard</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<div class="container">
<!-- Navigation -->
<nav class="nav">
<button class="hamburger">☰</button>
<ul class="nav-list">
<li><a href="dashboard.php">Start</a></li>
<li><a href="menu5.php" style="display: none;">Menüpunkt 5</a></li>
</ul>
<!-- Container für die rechten Buttons -->
<div class="nav-right">
<a href="#" id="openUpdate" title="Hier klicken, um Kurse zu aktualisieren">
<svg class="update" xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21.5 2v6h-6M2.5 22v-6h6M22 11.5a10 10 0 0 1-10 10 10 10 0 0 1-10-10 10 10 0 0 1 10-10 10 10 0 0 1 10 10z"/>
</svg>
</a>
<a href="logout.php" class="logout">Logout</a>
</div>
</nav>
<!-- h2><?= strftime('%B %Y', mktime(0, 0, 0, $month, 1, $year)) ?></h2 -->
<!-- Tabelle -->
<table>
<thead>
<tr>
<th>ID</th>
<th>ISIN/Symbol</th>
<th>Aktie</th>
<th>Kaufdatum</th>
<th>Kaufpreis</th>
<th>Anzahl</th>
<th>Kaufsumme</th>
<th>Kurs</th>
<th>GuV</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
<?php
$sumKaufSumme = 0;
$sumAktuellerWert = 0;
$sumGuV = 0;
foreach ($entries as $entry):
$kaufpreis = (float)$entry['kaufpreis'];
$anzahl = (float)$entry['anzahl'];
$kaufSumme = $kaufpreis * $anzahl;
$kurs = isset($entry['kurs']) ? (float)$entry['kurs'] : 0;
$kursdatum = $entry['kursdatum'];
$aktuellerWert = $anzahl * $kurs;
$guv = $aktuellerWert - $kaufSumme;
$sumKaufSumme += $kaufSumme;
$sumAktuellerWert += $aktuellerWert;
$sumGuV += $guv;
$guvClass = '';
if ($guv > 0) {
$guvClass = 'guv-positive';
} elseif ($guv < 0) {
$guvClass = 'guv-negative';
}
?>
<tr>
<td><?= htmlspecialchars($entry['id']) ?></td>
<td class="kurs-cell" style="white-space: nowrap;">
<?= htmlspecialchars($entry['WKNISIN']) ?><br>
<span style="font-size: inherit;"><?= htmlspecialchars($entry['Symbol']) ?></span>
</td>
<td>
<a href="#" class="isin-link" data-id="<?= $entry['id'] ?>" data-name="<?= htmlspecialchars($entry['Aktie'], ENT_QUOTES) ?>" title="Hier klicken, um das Symbol zu suchen">
<?= htmlspecialchars($entry['Aktie']) ?>
</a>
</td>
<td><?= htmlspecialchars($entry['kaufdatum']) ?></td>
<td class="align-right">
<?= number_format($kaufpreis, 2, ',', '.') ?>
</td>
<td class="align-right">
<?= number_format($anzahl, 4, ',', '.') ?>
</td>
<td class="align-right">
<?= number_format($kaufSumme, 2, ',', '.') ?>
</td>
<td class="align-right kurs-cell">
<?php if ($kurs > 0): ?>
<?= number_format($kurs, 2, ',', '.') ?><br>
<span class="kurs-datum"><?= htmlspecialchars($kursdatum) ?></span>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="align-right kurs-cell">
<?php if ($kurs > 0): ?>
<!-- Aktueller Gesamtwert -->
<?= number_format($aktuellerWert, 2, ',', '.') ?><br>
<!-- Gewinn / Verlust -->
<span class="<?= $guvClass ?>">
<?= number_format($guv, 2, ',', '.') ?>
</span>
<?php else: ?>
-
<?php endif; ?>
</td>
<td>
<a href="edit_aktie.php?id=<?= $entry['id'] ?>">
<img class="pencil" src="/assets/img/pencil.png" width="20px" />
</a>
</td>
</tr>
<?php endforeach; ?>
<tr class="sum-row">
<td colspan="6" class="align-right"><strong>Summe:</strong></td>
<td class="align-right">
<strong><?= number_format($sumKaufSumme, 2, ',', '.') ?></strong>
</td>
<td></td>
<td class="align-right">
<strong><?= number_format($sumAktuellerWert, 2, ',', '.') ?></strong><br>
<span class="<?= $sumGuV > 0 ? 'guv-positive' : ($sumGuV < 0 ? 'guv-negative' : '') ?>">
<strong><?= number_format($sumGuV, 2, ',', '.') ?></strong>
</span>
</td>
<td></td>
</tr>
</tbody>
</table>
<!-- Neu-Button -->
<a href="new_aktie.php" class="btn-xr">+</a>
</div>
<!-- Modal -->
<div id="updateModal" class="modal">
<div class="modal-content">
<h3>Kursaktualisierung</h3>
<div id="updateOutput" class="modal-body"></div>
<div class="modal-footer">
<button id="closeModal" class="btn">Schließen</button>
</div>
</div>
</div>
<!-- Modal für ISIN-Suche -->
<div id="isinModal" class="modal">
<div class="modal-content">
<h3>Symbol suchen</h3>
<div id="isinOutput" class="modal-body">
<p>Lade Ergebnisse...</p>
</div>
<div class="modal-footer">
<button id="saveSymbol" class="btn">Speichern</button>
<button id="closeIsinModal" class="btn">Schließen</button>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Hamburger-Menü
const hamburger = document.querySelector('.hamburger');
if (hamburger) {
hamburger.addEventListener('click', function() {
document.querySelector('.nav-list').classList.toggle('show');
});
}
// Modal-Elemente
const modal = document.getElementById('updateModal');
const openBtn = document.getElementById('openUpdate');
const closeBtn = document.getElementById('closeModal');
const output = document.getElementById('updateOutput');
if (!modal || !openBtn || !closeBtn || !output) {
console.error("Modal-Elemente nicht gefunden!");
return;
}
// Modal öffnen + Script laden
openBtn.addEventListener('click', function(e) {
e.preventDefault();
modal.style.display = 'flex';
output.innerHTML = "Starte Kursaktualisierung...<br>";
fetch('update_kurse.php')
.then(response => response.text())
.then(data => {
output.innerHTML += data.replace(/\n/g, "<br>");
output.scrollTop = output.scrollHeight;
})
.catch(error => {
output.innerHTML += "Fehler beim Laden.<br>";
});
});
// Modal schließen
closeBtn.addEventListener('click', function() {
modal.style.display = 'none';
location.reload();
});
// Klick außerhalb schließt Modal
window.addEventListener('click', function(e) {
if (e.target === modal) {
modal.style.display = 'none';
}
});
});
document.addEventListener("DOMContentLoaded", function() {
const isinLinks = document.querySelectorAll('.isin-link');
const isinModal = document.getElementById('isinModal');
const isinOutput = document.getElementById('isinOutput');
const closeIsinModal = document.getElementById('closeIsinModal');
const saveSymbolBtn = document.getElementById('saveSymbol');
let selectedEntryId = null;
let selectedSymbol = null;
// ISIN anklicken -> Modal öffnen
isinLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
selectedEntryId = this.dataset.id;
const aktienName = this.dataset.name;
isinModal.style.display = 'flex';
isinOutput.innerHTML = "Suche Symbole für " + aktienName + "...<br>";
// API abfragen
fetch('search_symbol.php?name=' + encodeURIComponent(aktienName))
.then(res => res.json())
.then(data => {
if (!data || data.length === 0) {
isinOutput.innerHTML = "Keine Ergebnisse gefunden.";
return;
}
// Radioboxen anzeigen
let html = '<form id="symbolForm">';
data.forEach((item, idx) => {
html += `<label>
<input type="radio" name="symbol" value="${item.symbol}" ${idx===0 ? 'checked' : ''}>
${item.symbol} (${item.name}, ${item.region})
</label><br>`;
});
html += '</form>';
isinOutput.innerHTML = html;
})
.catch(err => {
isinOutput.innerHTML = "Fehler bei der Suche.";
console.error(err);
});
});
});
// Modal schließen
closeIsinModal.addEventListener('click', () => isinModal.style.display = 'none');
window.addEventListener('click', function(e) {
if (e.target === isinModal) {
isinModal.style.display = 'none';
location.reload();
}
});
// Symbol speichern
saveSymbolBtn.addEventListener('click', () => {
const form = document.getElementById('symbolForm');
if (!form) return;
const formData = new FormData(form);
selectedSymbol = formData.get('symbol');
if (!selectedSymbol) return;
fetch('save_symbol.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ id: selectedEntryId, symbol: selectedSymbol })
})
.then(res => res.json())
.then(data => {
if (data.success) {
alert(data.message);
isinModal.style.display = 'none';
location.reload();
} else {
alert('Fehler: ' + data.message);
}
});
});
});
</script>
</body>
</html>

View file

@ -30,6 +30,35 @@ h1 {
color: var(--dark-color);
}
.kurs-cell {
font-size: 0.85em;
line-height: 1.2em;
}
.kurs-datum {
font-size: 0.75em;
color: #666;
}
.guv-positive {
color: #1a7f37;
font-weight: 600;
}
.guv-negative {
color: #c62828;
font-weight: 600;
}
.sum-row {
background-color: #f3f3f3;
border-top: 2px solid #999;
}
.sum-row td {
padding-top: 10px;
}
.debug_hidden {
display: none;
}
@ -289,6 +318,15 @@ tr:hover {
}
}
.update {
color: white;
width: 25px;
height: 25px;
vertical-align: middle;
display: inline-block;
}
/* Button */
.btn-xr {
position: fixed;
@ -380,7 +418,7 @@ max-height: 0;
}
/* Modal */
.modal {
.modal-l {
display: flex;
justify-content: center;
align-items: center;
@ -393,7 +431,7 @@ max-height: 0;
z-index: 1001;
}
.modal-content {
.modal-l-content {
background: white;
padding: 40px;
border-radius: var(--border-radius);
@ -405,14 +443,14 @@ max-height: 0;
text-align: center;
}
.modal-content h2 {
.modal-l-content h2 {
font-size: 24px;
margin-bottom: 20px;
color: var(--primary-color);
}
.modal-content input[type="text"],
.modal-content input[type="password"] {
.modal-l-content input[type="text"],
.modal-l-content input[type="password"] {
width: 100%;
padding: 15px;
margin: 10px 0;
@ -423,13 +461,13 @@ max-height: 0;
transition: border-color 0.3s ease;
}
.modal-content input[type="text"]:focus,
.modal-content input[type="password"]:focus {
.modal-l-content input[type="text"]:focus,
.modal-l-content input[type="password"]:focus {
border-color: var(--primary-color);
outline: none;
}
.modal-content button {
.modal-l-content button {
width: 100%;
padding: 15px;
background-color: var(--primary-color);
@ -474,6 +512,59 @@ input[type="number"] {
.modal-content input[type="password"],
.modal-content button {
font-size: 14px;
width: 100px;
}
}
/* Modales Fenster 2 */
.modal {
display: none;
position: fixed;
z-index: 9999;
inset: 0;
background: rgba(0,0,0,0.5);
justify-content: center;
align-items: center;
}
.modal-content {
background: #fff;
width: 80%;
height: 80%;
max-width: 1000px;
max-height: 800px;
border-radius: 8px;
display: flex;
flex-direction: column;
padding: 20px;
}
.modal-body {
flex: 1;
overflow-y: auto;
background: #f8f8f8;
padding: 10px;
font-family: monospace;
font-size: 14px;
border: 1px solid #ddd;
}
.modal-footer {
text-align: right;
margin-top: 10px;
}
.btn {
padding: 8px 16px;
background: #333;
color: white;
border: none;
cursor: pointer;
border-radius: 4px;
width: 100px;
}
.btn:hover {
background: #555;
}

BIN
html/assets/img/update.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -12,7 +12,14 @@ $charset = 'utf8mb4';
$selectedCategories = [1, 2, 3]; // Anpassen! Dies sind die Standard-Ketegorien die für rep_person als default angenommen werden.
$admin_ids = [1,2];
$aktien_ids = [1,2];
// Standard: kein Admin, keine Aktien
$is_admin = "false";
$is_aktien = "false";
// API Keys
$alphaApiKey = '98JPDX06439OYCVD';
$openFigiApiKey = '62d16cf8-457d-41a6-83d0-84b26b8d5e6c';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [

View file

@ -14,6 +14,10 @@ error_log($_SESSION['user_id']);
if (in_array($_SESSION['user_id'], $admin_ids, true)) {
$is_admin = "true";
}
// Prüfen, ob die user_id im Aktien-Array ist
if (in_array($_SESSION['user_id'], $aktien_ids, true)) {
$is_aktien = "true";
}
// Aktuelles Jahr und Monat ermitteln, ggf. aus GET-Parametern
$year = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
@ -82,6 +86,7 @@ $entries = $stmt->fetchAll();
<button class="hamburger">☰</button>
<ul class="nav-list">
<li><a href="dashboard.php">Start</a></li>
<?php if ($is_aktien === "true") echo '<li><a href="aktien.php">Aktien</a></li>'; ?>
<?php if ($is_admin === "true") echo '<li><a href="register.php">Neue User</a></li>'; ?>
<li><a href="rep-month.php">Report Monat</a></li>
<li><a href="rep-person.php">Report Personen</a></li>

161
html/edit_aktie.php Normal file
View file

@ -0,0 +1,161 @@
<?php
require 'config.php';
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
// Prüfen, ob die ID im GET-Parameter vorhanden ist
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header('Location: aktien.php');
exit;
}
$aktie_id = (int)$_GET['id'];
$aktie = null;
$data_saved = "nix";
// Daten des Eintrags laden
try {
$stmtAktie = $pdo->prepare("
SELECT * FROM aktien
WHERE id = :id
");
$stmtAktie->bindParam(':id', $aktie_id, PDO::PARAM_INT);
$stmtAktie->execute();
$aktie = $stmtAktie->fetch(PDO::FETCH_ASSOC);
if (!$aktie) {
header('Location: aktien.php');
exit;
}
} catch (PDOException $e) {
$data_saved = "Fehler: Aktie konnte nicht geladen werden.";
}
// Lösch-Logik
if (isset($_POST['delete'])) {
try {
$stmt = $pdo->prepare("DELETE FROM aktien WHERE id = :id");
$stmt->bindParam(':id', $aktie_id, PDO::PARAM_INT);
$stmt->execute();
echo '<meta http-equiv="refresh" content="5;url=aktien.php">Aktie wurde gelöscht. Weiterleitung…'; exit;
} catch (PDOException $e) {
$data_saved = "Fehler: " . $e->getMessage();
}
}
// Speicher-Logik
if (!empty($_POST) && !isset($_POST['delete'])) {
$WKNISIN = isset($_POST['WKNISIN']) ? trim($_POST['WKNISIN']) : '';
$Aktie = isset($_POST['Aktie']) ? trim($_POST['Aktie']) : '';
$kaufdatum = isset($_POST['kaufdatum']) ? trim($_POST['kaufdatum']) : '';
$kaufpreis = isset($_POST['kaufpreis']) ? (float)$_POST['kaufpreis'] : 0.0;
$anzahl = isset($_POST['anzahl']) ? (float)$_POST['anzahl'] : 0.0;
try {
$stmt = $pdo->prepare("
UPDATE aktien
SET
WKNISIN = :WKNISIN,
Aktie = :Aktie,
kaufdatum = :kaufdatum,
kaufpreis = :kaufpreis,
anzahl = :anzahl
WHERE id = :id
");
$stmt->bindParam(':id', $aktie_id, PDO::PARAM_INT);
$stmt->bindParam(':WKNISIN', $WKNISIN);
$stmt->bindParam(':Aktie', $Aktie);
$stmt->bindParam(':kaufdatum', $kaufdatum);
$stmt->bindParam(':kaufpreis', $kaufpreis);
$stmt->bindParam(':anzahl', $anzahl);
$stmt->execute();
$data_saved = "true";
} catch (PDOException $e) {
$data_saved = "Fehler: " . $e->getMessage();
}
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<title>Aktie bearbeiten</title>
<link rel="stylesheet" href="assets/css/style.css">
<style>
.data-notice {
display: none;
padding: 10px;
margin: 10px 0;
border-radius: 4px;
text-align: center;
}
.data-notice.success {
background-color: #4CAF50;
color: white;
display: block;
}
</style>
</head>
<body>
<?php if ($data_saved == "true"): ?>
<div id="data-notice" class="data-notice success">
Aktie erfolgreich aktualisiert! Sie werden in 5 Sekunden weitergeleitet ...
</div>
<script>
setTimeout(function() {
window.location.href = 'aktien.php';
}, 5000);
</script>
<?php elseif ($data_saved != "nix"): ?>
<div id="data-notice" class="data-notice error">
<?php echo $data_saved; ?>
</div>
<?php endif; ?>
<div class="container">
<!-- Navigation -->
<nav class="nav">
<button class="hamburger">☰</button>
<ul class="nav-list">
<li><a href="dashboard.php">Start</a></li>
<li><a href="aktien.php">Aktien</a></li>
<li><a href="logout.php" class="logout">Logout</a></li>
</ul>
</nav>
<form action="edit_aktie.php?id=<?= $aktie_id ?>" method="post" class="form-container">
<div class="form-group">
<label for="WKNISIN">WKN/ISIN</label>
<input type="text" name="WKNISIN" value="<?= htmlspecialchars($aktie['WKNISIN']) ?>" placeholder="WKN oder ISIN" required>
</div>
<div class="form-group">
<label for="Aktie">Aktie</label>
<input type="text" name="Aktie" value="<?= htmlspecialchars($aktie['Aktie']) ?>" placeholder="Name der Aktie" required>
</div>
<div class="form-group">
<label for="kaufdatum">Kaufdatum</label>
<input type="date" name="kaufdatum" value="<?= htmlspecialchars($aktie['kaufdatum']) ?>" required>
</div>
<div class="form-group">
<label for="kaufpreis">Kaufpreis</label>
<input type="number" name="kaufpreis" step="0.01" value="<?= htmlspecialchars($aktie['kaufpreis']) ?>" placeholder="Kaufpreis" required>
</div>
<div class="form-group">
<label for="anzahl">Anzahl</label>
<input type="number" name="anzahl" step="0.0001" value="<?= htmlspecialchars($aktie['anzahl']) ?>" placeholder="Anzahl" required>
</div>
<div class="button-group">
<button type="submit" name="delete" class="btn-xr1l" style="background-color: #d9534f;">Löschen</button>
<button type="submit" class="btn-xr">Speichern</button>
</div>
</form>
</div>
<script src="assets/js/script.js"></script>
</body>
</html>

63
html/get_symbol.php Normal file
View file

@ -0,0 +1,63 @@
<?php
function get2symbol(string $isin, string $openFigiApiKey): ?string
{
$url = "https://api.openfigi.com/v3/mapping";
$postData = json_encode([
[
"idType" => "ID_ISIN",
"idValue" => $isin
]
]);
$headers = [
"Content-Type: application/json",
"X-OPENFIGI-APIKEY: $openFigiApiKey"
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
curl_close($ch);
return null;
}
curl_close($ch);
$data = json_decode($response, true);
if (!isset($data[0]['data']) || empty($data[0]['data'])) {
return null;
}
// Wir suchen zuerst nach einem Eintrag mit deutschem Handelsplatz (DE)
$symbolEntry = null;
foreach ($data[0]['data'] as $entry) {
if (isset($entry['exchCode']) && $entry['exchCode'] === 'XETR') { // XETR = Deutsche Börse Xetra
$symbolEntry = $entry;
break;
}
}
// Wenn kein DE/XETR Eintrag gefunden, nehmen wir einfach den ersten
if ($symbolEntry === null) {
$symbolEntry = $data[0]['data'][0];
}
if (isset($symbolEntry['ticker']) && !empty($symbolEntry['ticker'])) {
// Kombiniere Ticker und Exchange, um ein vollständiges Symbol zu liefern
$exchange = $symbolEntry['exchCode'] ?? '';
return $exchange ? "{$symbolEntry['ticker']}.{$exchange}" : $symbolEntry['ticker'];
}
return null;
}

View file

@ -43,8 +43,8 @@ Mit der weiteren Nutzung der Webseite erklären Sie sich mit dem Einsatz dieser
</div>
<!-- Modal Login Dialog -->
<div id="login-modal" class="modal">
<div class="modal-content">
<div id="login-modal" class="modal-l">
<div class="modal-l-content">
<img src="assets/img/euro.gif"/>
<h2>FSCash</h2>
<?php if ($error): ?>

119
html/new_aktie.php Normal file
View file

@ -0,0 +1,119 @@
<?php
require 'config.php';
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<title>Neue Aktie erfassen</title>
<link rel="stylesheet" href="assets/css/style.css">
<style>
.data-notice {
display: none;
padding: 10px;
margin: 10px 0;
border-radius: 4px;
text-align: center;
}
.data-notice.success {
background-color: #4CAF50;
color: white;
display: block;
}
</style>
</head>
<body>
<?php
$data_saved = "nix";
$debug = "";
$currentDate = date('Y-m-d');
// save-Logik
if (!empty($_POST)) {
$WKNISIN = isset($_POST['WKNISIN']) ? trim($_POST['WKNISIN']) : '';
$Aktie = isset($_POST['Aktie']) ? trim($_POST['Aktie']) : '';
$kaufdatum = isset($_POST['kaufdatum']) ? trim($_POST['kaufdatum']) : '';
$kaufpreis = isset($_POST['kaufpreis']) ? (float)$_POST['kaufpreis'] : 0.0;
$anzahl = isset($_POST['anzahl']) ? (float)$_POST['anzahl'] : 0.0;
try {
$stmt = $pdo->prepare("
INSERT INTO aktien (WKNISIN, user_id, Aktie, kaufdatum, kaufpreis, anzahl)
VALUES (:WKNISIN, :user_id, :Aktie, :kaufdatum, :kaufpreis, :anzahl)
");
$stmt->bindParam(':WKNISIN', $WKNISIN);
$stmt->bindParam(':Aktie', $Aktie);
$stmt->bindParam(':kaufdatum', $kaufdatum);
$stmt->bindParam(':kaufpreis', $kaufpreis);
$stmt->bindParam(':anzahl', $anzahl);
$stmt->bindParam(':user_id', $_SESSION['user_id']);
$stmt->execute();
$data_saved = "true";
} catch (PDOException $e) {
$data_saved = "Fehler: " . $e->getMessage();
}
}
?>
<?php if ($data_saved == "true"): ?>
<div id="data-notice" class="data-notice success">
Aktie erfolgreich gespeichert! Sie werden in 5 Sekunden weitergeleitet ...
</div>
<script>
setTimeout(function() {
window.location.href = 'aktien.php';
}, 5000);
</script>
<?php elseif ($data_saved != "nix"): ?>
<div id="data-notice" class="data-notice error">
<?php echo $data_saved; ?>
</div>
<?php endif; ?>
<div class="container">
<!-- Navigation -->
<nav class="nav">
<button class="hamburger">☰</button>
<ul class="nav-list">
<li><a href="dashboard.php">Start</a></li>
<li><a href="aktien.php">Aktien</a></li>
<li><a href="rep-month.php">Report Monat</a></li>
<li><a href="logout.php" class="logout">Logout</a></li>
</ul>
</nav>
<form action="new_aktie.php" method="post" class="form-container">
<div class="form-group">
<label for="WKNISIN">WKN/ISIN</label>
<input type="text" name="WKNISIN" placeholder="WKN oder ISIN" required>
</div>
<div class="form-group">
<label for="Aktie">Aktie</label>
<input type="text" name="Aktie" placeholder="Name der Aktie" required>
</div>
<div class="form-group">
<label for="kaufdatum">Kaufdatum</label>
<input type="date" name="kaufdatum" value="<?php echo $currentDate; ?>" required>
</div>
<div class="form-group">
<label for="kaufpreis">Kaufpreis</label>
<input type="number" name="kaufpreis" step="0.01" placeholder="Kaufpreis" required>
</div>
<div class="form-group">
<label for="anzahl">Anzahl</label>
<input type="number" name="anzahl" step="0.0001" placeholder="Anzahl" required>
</div>
<button type="submit" class="btn-xr">Speichern</button>
</form>
</div>
<script src="assets/js/script.js"></script>
</body>
</html>

View file

@ -8,6 +8,15 @@ if (!isset($_SESSION['user_id'])) {
// echo $_SESSION['user_id'];
error_log($_SESSION['user_id']);
// Prüfen, ob die user_id im Admin-Array ist
if (in_array($_SESSION['user_id'], $admin_ids, true)) {
$is_admin = "true";
}
// Prüfen, ob die user_id im Aktien-Array ist
if (in_array($_SESSION['user_id'], $aktien_ids, true)) {
$is_aktien = "true";
}
// Verbindung herstellen
$conn = new mysqli($host, $user, $pass, $db);
@ -53,7 +62,7 @@ $conn->close();
<button class="hamburger">☰</button>
<ul class="nav-list">
<li><a href="dashboard.php">Start</a></li>
<li><a href="register.php" style="display: none;">Neue User</a></li>
<?php if ($is_aktien === "true") echo '<li><a href="aktien.php">Aktien</a></li>'; ?>
<li><a href="rep-month.php">Report Monat</a></li>
<li><a href="rep-person.php">Report Personen</a></li>
<li><a href="menu5.php" style="display: none;">Menüpunkt 5</a></li>

View file

@ -9,6 +9,10 @@ if (!isset($_SESSION['user_id'])) {
if (in_array($_SESSION['user_id'], $admin_ids, true)) {
$is_admin = "true";
}
// Prüfen, ob die user_id im Aktien-Array ist
if (in_array($_SESSION['user_id'], $aktien_ids, true)) {
$is_aktien = "true";
}
// Aktuelles Jahr und Monat ermitteln, ggf. aus GET-Parametern
$year = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
@ -120,6 +124,7 @@ if (isset($_GET['export']) && $_GET['export'] === 'csv') {
<button class="hamburger">☰</button>
<ul class="nav-list">
<li><a href="dashboard.php">Start</a></li>
<?php if ($is_aktien === "true") echo '<li><a href="aktien.php">Aktien</a></li>'; ?>
<?php if ($is_admin === "true") echo '<li><a href="register.php">Neue User</a></li>'; ?>
<li><a href="rep-person.php">Report Personen</a></li>
<li><a href="menu5.php" style="display: none;">Menüpunkt 5</a></li>

View file

@ -9,6 +9,10 @@ if (!isset($_SESSION['user_id'])) {
if (in_array($_SESSION['user_id'], $admin_ids, true)) {
$is_admin = "true";
}
// Prüfen, ob die user_id im Aktien-Array ist
if (in_array($_SESSION['user_id'], $aktien_ids, true)) {
$is_aktien = "true";
}
// Aktuelles Jahr und Monat ermitteln, ggf. aus GET-Parametern
$year = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
@ -72,6 +76,7 @@ $entries = $stmt->fetchAll();
<button class="hamburger">☰</button>
<ul class="nav-list">
<li><a href="dashboard.php">Start</a></li>
<?php if ($is_aktien === "true") echo '<li><a href="aktien.php">Aktien</a></li>'; ?>
<?php if ($is_admin === "true") echo '<li><a href="register.php">Neue User</a></li>'; ?>
<li><a href="rep-month.php">Report Monat</a></li>
<li><a href="menu5.php" style="display: none;">Menüpunkt 5</a></li>

45
html/save_symbol.php Normal file
View file

@ -0,0 +1,45 @@
<?php
require 'config.php';
header('Content-Type: application/json');
// JSON-Input einlesen
$data = json_decode(file_get_contents('php://input'), true);
$id = $data['id'] ?? null;
$symbol = $data['symbol'] ?? null;
// Input validieren
if (!$id || !$symbol) {
echo json_encode([
'success' => false,
'message' => 'Ungültige Eingabedaten.'
]);
exit;
}
try {
$stmt = $pdo->prepare("UPDATE aktien SET Symbol = :symbol WHERE id = :id AND user_id = :userid");
$stmt->execute([
'symbol' => $symbol,
'id' => $id,
'userid' => $_SESSION['user_id']
]);
// Prüfen, ob ein Datensatz tatsächlich aktualisiert wurde
if ($stmt->rowCount() > 0) {
echo json_encode([
'success' => true,
'symbol' => $symbol,
'message' => "Symbol erfolgreich gespeichert: $symbol"
]);
} else {
echo json_encode([
'success' => false,
'message' => 'Keine Änderung – evtl. ID existiert nicht oder gehört nicht zum Benutzer.'
]);
}
} catch (PDOException $e) {
echo json_encode([
'success' => false,
'message' => 'Datenbankfehler: ' . $e->getMessage()
]);
}

32
html/search_symbol.php Normal file
View file

@ -0,0 +1,32 @@
<?php
require 'config.php';
header('Content-Type: application/json');
if (!isset($_GET['name'])) {
echo json_encode([]);
exit;
}
$name = $_GET['name'];
$url = 'https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=' . urlencode($name) . '&apikey=' . $alphaApiKey;
$response = file_get_contents($url);
if (!$response) {
echo json_encode([]);
exit;
}
$data = json_decode($response, true);
$matches = $data['bestMatches'] ?? [];
$result = [];
foreach ($matches as $match) {
$result[] = [
'symbol' => $match['1. symbol'],
'name' => $match['2. name'],
'region' => $match['4. region']
];
}
echo json_encode($result);

101
html/update_kurse.php Normal file
View file

@ -0,0 +1,101 @@
<?php
require 'config.php';
require 'get_symbol.php';
try {
// Jetzt auch symbol mit abrufen!
$stmt = $pdo->prepare("
SELECT id, WKNISIN, symbol
FROM aktien
WHERE WKNISIN IS NOT NULL
");
$stmt->execute();
$aktien = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($aktien as $aktie) {
$id = $aktie['id'];
$isin = trim($aktie['WKNISIN']);
$symbol = $aktie['symbol'];
// 🔹 Wenn noch kein Symbol vorhanden → über OpenFIGI holen
if (empty($symbol)) {
echo "Hole Symbol für ISIN $isin...\n";
$symbol = get2symbol($isin, $openFigiApiKey);
if ($symbol) {
// Symbol in DB speichern
$symbolUpdate = $pdo->prepare("
UPDATE aktien
SET symbol = :symbol
WHERE id = :id
");
$symbolUpdate->bindParam(':symbol', $symbol);
$symbolUpdate->bindParam(':id', $id);
$symbolUpdate->execute();
echo "Symbol $symbol gespeichert.\n";
// OpenFIGI Limit beachten
sleep(1);
} else {
echo "Kein Symbol für ISIN $isin gefunden. Überspringe...\n";
continue;
}
}
// 🔹 Kurs über Alpha Vantage abrufen
$url = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=$symbol&apikey=$alphaApiKey";
// echo " rufe: $url";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$json = curl_exec($ch);
curl_close($ch);
$data = json_decode($json, true);
if (isset($data['Global Quote']['05. price'])) {
$kurs = (float)$data['Global Quote']['05. price'];
$kursdatum = date('Y-m-d');
// 🔹 Kurs UND Symbol speichern
$updateStmt = $pdo->prepare("
UPDATE aktien
SET kurs = :kurs,
kursdatum = :kursdatum,
symbol = :symbol
WHERE id = :id
");
$updateStmt->bindParam(':kurs', $kurs);
$updateStmt->bindParam(':kursdatum', $kursdatum);
$updateStmt->bindParam(':symbol', $symbol);
$updateStmt->bindParam(':id', $id);
$updateStmt->execute();
echo "Aktie ID $id ($isin / $symbol): Kurs auf $kurs aktualisiert.\n";
} else {
echo "Kurs für $symbol konnte nicht abgerufen werden.\n";
}
// Alpha Vantage Limit: 5 Anfragen / Minute
sleep(13);
}
echo "Kursaktualisierung abgeschlossen.\n";
} catch (PDOException $e) {
echo "Datenbankfehler: " . $e->getMessage() . "\n";
} catch (Exception $e) {
echo "Fehler: " . $e->getMessage() . "\n";
}
?>

View file

@ -56,3 +56,18 @@ INSERT INTO payment_methods (name) VALUES
('Bar'),
('Karte'),
('Paypal');
CREATE TABLE aktien (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
WKNISIN VARCHAR(20) NOT NULL,
Symbol VARCHAR(20) NULL,
Aktie VARCHAR(100) NOT NULL,
kaufdatum DATE NOT NULL,
kaufpreis DECIMAL(10, 2) NOT NULL,
anzahl DECIMAL(10, 4) NOT NULL,
kursdatum DATE NULL,
kurs DECIMAL(10, 2) NULL,
PRIMARY KEY (id)
);

View file

@ -0,0 +1,13 @@
CREATE TABLE aktien (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
WKNISIN VARCHAR(20) NOT NULL,
Symbol VARCHAR(20) NULL,
Aktie VARCHAR(100) NOT NULL,
kaufdatum DATE NOT NULL,
kaufpreis DECIMAL(10, 2) NOT NULL,
anzahl DECIMAL(10, 4) NOT NULL,
kursdatum DATE NULL,
kurs DECIMAL(10, 2) NULL,
PRIMARY KEY (id)
);