- market.php: Buy new aircraft (6 types: Dash8 to A380) - aircraft.php: Fleet management, crew assignment, route planning - finances.php: Account balance, transactions, loans - Database.php: Extended with loan/transaction/aircraft methods - schema.sql: Aircraft types table, 10M starting balance, INSERT OR IGNORE - dashboard.php: Updated with fleet/financial overview - Fixed: duplicate function declarations in Database.php - Fixed: initializeSchema() with idempotent error handling
440 lines
20 KiB
PHP
440 lines
20 KiB
PHP
<?php
|
||
/**
|
||
* Airline Tycoon - Finanz-Übersicht
|
||
* Zeigt alle Finanzen, Transaktionen, Kredite und Bilanzen
|
||
*/
|
||
|
||
require_once __DIR__ . '/../src/bootstrap.php';
|
||
|
||
// Auth Check
|
||
if (!Session::isLoggedIn()) {
|
||
header('Location: login.php');
|
||
exit;
|
||
}
|
||
|
||
$userId = Session::getUserId();
|
||
$user = $db->getUserById($userId);
|
||
$config = $db->getConfig();
|
||
$csrfToken = Session::generateCsrfToken();
|
||
|
||
// Kredite holen
|
||
$loans = $db->getUserLoans($userId);
|
||
$totalDebt = $db->getTotalDebt($userId);
|
||
$totalDebtWithInterest = $db->getTotalDebtWithInterest($userId);
|
||
|
||
// Transaktionen holen
|
||
$transactions = $db->getUserTransactions($userId, 50);
|
||
|
||
// Heutige Statistik
|
||
$today = date('Y-m-d');
|
||
$todayStats = $db->getTransactionSummary($userId, $today, $today);
|
||
|
||
// Letzte 7 Tage
|
||
$weekAgo = date('Y-m-d', strtotime('-7 days'));
|
||
$weekStats = $db->getTransactionSummary($userId, $weekAgo, $today);
|
||
|
||
// Letzte 30 Tage
|
||
$monthAgo = date('Y-m-d', strtotime('-30 days'));
|
||
$monthStats = $db->getTransactionSummary($userId, $monthAgo, $today);
|
||
|
||
// Wartungshistorie
|
||
$maintenanceHistory = $db->getMaintenanceHistory($userId, 10);
|
||
|
||
// Net Worth (Balance - Schulden)
|
||
$netWorth = $user['balance'] - $totalDebtWithInterest;
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Finanzen - <?= htmlspecialchars($user['airline_name']) ?> - <?= $config['app']['name'] ?></title>
|
||
<link rel="stylesheet" href="<?= $basePath ?>assets/css/style.css">
|
||
<link href="https://fonts.googleapis.com/css2?family=Bangers&family=Patrick+Hand&family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
|
||
</head>
|
||
<body data-theme="<?= $theme ?? 'light' ?>">
|
||
<!-- Navbar -->
|
||
<nav class="navbar">
|
||
<div class="navbar-brand">
|
||
<a href="<?= $basePath ?>dashboard.php" class="navbar-logo">✈️ <?= $config['app']['name'] ?></a>
|
||
</div>
|
||
<button class="navbar-toggle" aria-label="Menü">☰</button>
|
||
<div class="navbar-menu">
|
||
<a href="<?= $basePath ?>dashboard.php" class="navbar-item">Dashboard</a>
|
||
<a href="<?= $basePath ?>airports.php" class="navbar-item">Flughäfen</a>
|
||
<a href="<?= $basePath ?>aircraft.php" class="navbar-item">Flotte</a>
|
||
<a href="<?= $basePath ?>finances.php" class="navbar-item active">Finanzen</a>
|
||
<a href="<?= $basePath ?>market.php" class="navbar-item">Markt</a>
|
||
<button class="theme-toggle" id="themeToggle" title="Theme wechseln">🌙</button>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- Main Content -->
|
||
<main class="container">
|
||
<!-- Header -->
|
||
<section class="hero hero-sm">
|
||
<div class="hero-content">
|
||
<h1 class="hero-title">💰 Finanzen</h1>
|
||
<p class="hero-subtitle">Deine Einnahmen, Ausgaben und Kredite im Überblick</p>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Kontostand & Net Worth -->
|
||
<div class="stats-grid mt-lg">
|
||
<div class="stat-card">
|
||
<div class="stat-icon">💵</div>
|
||
<div class="stat-value <?= $user['balance'] >= 0 ? 'text-success' : 'text-danger' ?>">
|
||
<?= number_format($user['balance'], 0, ',', '.') ?> €
|
||
</div>
|
||
<div class="stat-label">Kontostand</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">📊</div>
|
||
<div class="stat-value <?= $netWorth >= 0 ? 'text-success' : 'text-danger' ?>">
|
||
<?= number_format($netWorth, 0, ',', '.') ?> €
|
||
</div>
|
||
<div class="stat-label">Net Worth</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">🏦</div>
|
||
<div class="stat-value <?= $totalDebt > 0 ? 'text-warning' : 'text-success' ?>">
|
||
<?= number_format($totalDebtWithInterest, 0, ',', '.') ?> €
|
||
</div>
|
||
<div class="stat-label">Kredit-Schuld</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">📈</div>
|
||
<div class="stat-value">
|
||
<?= $config['loans']['interest_rate'] * 100 ?>%
|
||
</div>
|
||
<div class="stat-label">Kredit-Zins/Ticket</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Zeitraum-Statistiken -->
|
||
<section class="panel mt-xl">
|
||
<h2 class="panel-title">📅 Bilanz nach Zeitraum</h2>
|
||
<div class="period-stats mt-md">
|
||
<div class="period-card">
|
||
<h3>Heute</h3>
|
||
<div class="period-amounts">
|
||
<div class="amount-row">
|
||
<span>Einnahmen:</span>
|
||
<span class="text-success">+<?= number_format($todayStats['total_income'] ?? 0, 2, ',', '.') ?> €</span>
|
||
</div>
|
||
<div class="amount-row">
|
||
<span>Ausgaben:</span>
|
||
<span class="text-danger">-<?= number_format($todayStats['total_expenses'] ?? 0, 2, ',', '.') ?> €</span>
|
||
</div>
|
||
<div class="amount-row total">
|
||
<span>Saldo:</span>
|
||
<span class="<?= ($todayStats['total_income'] - $todayStats['total_expenses']) >= 0 ? 'text-success' : 'text-danger' ?>">
|
||
<?= number_format(($todayStats['total_income'] ?? 0) - ($todayStats['total_expenses'] ?? 0), 2, ',', '.') ?> €
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="period-card">
|
||
<h3>Letzte 7 Tage</h3>
|
||
<div class="period-amounts">
|
||
<div class="amount-row">
|
||
<span>Einnahmen:</span>
|
||
<span class="text-success">+<?= number_format($weekStats['total_income'] ?? 0, 2, ',', '.') ?> €</span>
|
||
</div>
|
||
<div class="amount-row">
|
||
<span>Ausgaben:</span>
|
||
<span class="text-danger">-<?= number_format($weekStats['total_expenses'] ?? 0, 2, ',', '.') ?> €</span>
|
||
</div>
|
||
<div class="amount-row total">
|
||
<span>Saldo:</span>
|
||
<span class="<?= ($weekStats['total_income'] - $weekStats['total_expenses']) >= 0 ? 'text-success' : 'text-danger' ?>">
|
||
<?= number_format(($weekStats['total_income'] ?? 0) - ($weekStats['total_expenses'] ?? 0), 2, ',', '.') ?> €
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="period-card">
|
||
<h3>Letzte 30 Tage</h3>
|
||
<div class="period-amounts">
|
||
<div class="amount-row">
|
||
<span>Einnahmen:</span>
|
||
<span class="text-success">+<?= number_format($monthStats['total_income'] ?? 0, 2, ',', '.') ?> €</span>
|
||
</div>
|
||
<div class="amount-row">
|
||
<span>Ausgaben:</span>
|
||
<span class="text-danger">-<?= number_format($monthStats['total_expenses'] ?? 0, 2, ',', '.') ?> €</span>
|
||
</div>
|
||
<div class="amount-row total">
|
||
<span>Saldo:</span>
|
||
<span class="<?= ($monthStats['total_income'] - $monthStats['total_expenses']) >= 0 ? 'text-success' : 'text-danger' ?>">
|
||
<?= number_format(($monthStats['total_income'] ?? 0) - ($monthStats['total_expenses'] ?? 0), 2, ',', '.') ?> €
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Kredite -->
|
||
<section class="panel mt-xl">
|
||
<div class="panel-header">
|
||
<h2 class="panel-title">🏦 Kredite</h2>
|
||
<button class="btn btn-primary" onclick="openModal('takeLoanModal')">
|
||
➕ Kredit aufnehmen
|
||
</button>
|
||
</div>
|
||
|
||
<?php if (empty($loans)): ?>
|
||
<div class="empty-state">
|
||
<div class="empty-icon">🏦</div>
|
||
<h3>Keine Kredite</h3>
|
||
<p>Du hast keine Kredite offen. Nimm einen Kredit auf, um dein Geschäft zu finanzieren!</p>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="table-container mt-md">
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Datum</th>
|
||
<th>Originalbetrag</th>
|
||
<th>Aktueller Betrag</th>
|
||
<th>Tageszins</th>
|
||
<th>Tage offen</th>
|
||
<th>Gesamtzins</th>
|
||
<th>Aktion</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($loans as $loan):
|
||
$totalInterest = $loan['daily_interest'] * $loan['days_outstanding'];
|
||
$totalOwed = $loan['current_amount'] + $totalInterest;
|
||
?>
|
||
<tr>
|
||
<td><?= date('d.m.Y', strtotime($loan['created_at'])) ?></td>
|
||
<td><?= number_format($loan['principal'], 2, ',', '.') ?> €</td>
|
||
<td><?= number_format($loan['current_amount'], 2, ',', '.') ?> €</td>
|
||
<td><?= number_format($loan['daily_interest'], 2, ',', '.') ?> €</td>
|
||
<td><?= $loan['days_outstanding'] ?></td>
|
||
<td><?= number_format($totalInterest, 2, ',', '.') ?> €</td>
|
||
<td>
|
||
<button class="btn btn-sm btn-warning"
|
||
onclick="repayLoan(<?= $loan['id'] ?>, <?= $totalOwed ?>)">
|
||
💵 Zurückzahlen
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="loan-info panel mt-lg">
|
||
<h3>📖 Kredit-Informationen</h3>
|
||
<ul>
|
||
<li>Maximaler Kreditbetrag: <?= number_format($config['loans']['max_amount'], 0, ',', '.') ?> €</li>
|
||
<li>Zinssatz: <?= $config['loans']['interest_rate'] * 100 ?>% pro Spieltag</li>
|
||
<li>Mindestkreditbetrag: <?= number_format($config['loans']['min_amount'], 0, ',', '.') ?> €</li>
|
||
</ul>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Letzte Transaktionen -->
|
||
<section class="panel mt-xl">
|
||
<h2 class="panel-title">📜 Letzte Transaktionen</h2>
|
||
|
||
<?php if (empty($transactions)): ?>
|
||
<div class="empty-state">
|
||
<div class="empty-icon">📋</div>
|
||
<h3>Keine Transaktionen</h3>
|
||
<p>Es gibt noch keine Transaktionen.</p>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="table-container mt-md">
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Datum</th>
|
||
<th>Typ</th>
|
||
<th>Beschreibung</th>
|
||
<th>Betrag</th>
|
||
<th>Saldo</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($transactions as $tx): ?>
|
||
<tr>
|
||
<td><?= date('d.m.Y H:i', strtotime($tx['created_at'])) ?></td>
|
||
<td>
|
||
<?php
|
||
$typeColors = [
|
||
'flight' => 'badge-primary',
|
||
'fuel' => 'badge-warning',
|
||
'maintenance' => 'badge-danger',
|
||
'aircraft_purchase' => 'badge-info',
|
||
'loan' => 'badge-secondary',
|
||
'loan_repayment' => 'badge-warning'
|
||
];
|
||
$badgeClass = $typeColors[$tx['type']] ?? 'badge-secondary';
|
||
?>
|
||
<span class="badge <?= $badgeClass ?>"><?= htmlspecialchars($tx['type']) ?></span>
|
||
</td>
|
||
<td><?= htmlspecialchars($tx['description']) ?></td>
|
||
<td class="<?= $tx['amount'] >= 0 ? 'text-success' : 'text-danger' ?>">
|
||
<?= number_format($tx['amount'], 2, ',', '.') ?> €
|
||
</td>
|
||
<td><?= number_format($tx['balance_after'], 2, ',', '.') ?> €</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
</section>
|
||
|
||
<!-- Wartungshistorie -->
|
||
<?php if (!empty($maintenanceHistory)): ?>
|
||
<section class="panel mt-xl">
|
||
<h2 class="panel-title">🔧 Wartungshistorie</h2>
|
||
<div class="table-container mt-md">
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Datum</th>
|
||
<th>Flugzeug</th>
|
||
<th>Typ</th>
|
||
<th>Zustand vorher</th>
|
||
<th>Zustand nachher</th>
|
||
<th>Kosten</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($maintenanceHistory as $m): ?>
|
||
<tr>
|
||
<td><?= date('d.m.Y', strtotime($m['created_at'])) ?></td>
|
||
<td><?= htmlspecialchars($m['aircraft_name']) ?></td>
|
||
<td><span class="badge"><?= htmlspecialchars($m['maintenance_type']) ?></span></td>
|
||
<td><?= number_format($m['condition_before'], 1, ',', '.') ?>%</td>
|
||
<td><?= number_format($m['condition_after'], 1, ',', '.') ?>%</td>
|
||
<td class="text-danger">-<?= number_format($m['cost'], 2, ',', '.') ?> €</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
<?php endif; ?>
|
||
</main>
|
||
|
||
<!-- Kredit aufnehmen Modal -->
|
||
<div class="modal-overlay" id="takeLoanModal">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">🏦 Kredit aufnehmen</h3>
|
||
<button class="modal-close" onclick="closeModal('takeLoanModal')">×</button>
|
||
</div>
|
||
<form action="api/game.php" method="POST" data-ajax>
|
||
<input type="hidden" name="action" value="take_loan">
|
||
<input type="hidden" name="csrf_token" value="<?= $csrfToken ?>">
|
||
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label class="form-label">Kreditbetrag (€)</label>
|
||
<input type="number" name="amount" class="form-input"
|
||
min="<?= $config['loans']['min_amount'] ?>"
|
||
max="<?= $config['loans']['max_amount'] - $totalDebt ?>"
|
||
step="10000"
|
||
placeholder="z.B. 1000000" required>
|
||
<small class="form-hint">
|
||
Min: <?= number_format($config['loans']['min_amount'], 0, ',', '.') ?> € |
|
||
Max: <?= number_format($config['loans']['max_amount'] - $totalDebt, 0, ',', '.') ?> € verfügbar
|
||
</small>
|
||
</div>
|
||
|
||
<div class="alert alert-info mt-md">
|
||
<p><strong>Zinssatz:</strong> <?= $config['loans']['interest_rate'] * 100 ?>% pro Spieltag</p>
|
||
<p><strong>Tageszins für max. Kredit:</strong> <?= number_format(($config['loans']['max_amount'] - $totalDebt) * $config['loans']['interest_rate'], 2, ',', '.') ?> €</p>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" onclick="closeModal('takeLoanModal')">Abbrechen</button>
|
||
<button type="submit" class="btn btn-primary">Kredit aufnehmen</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Kredit zurückzahlen Modal -->
|
||
<div class="modal-overlay" id="repayLoanModal">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">💵 Kredit zurückzahlen</h3>
|
||
<button class="modal-close" onclick="closeModal('repayLoanModal')">×</button>
|
||
</div>
|
||
<form action="api/game.php" method="POST" data-ajax>
|
||
<input type="hidden" name="action" value="repay_loan">
|
||
<input type="hidden" name="csrf_token" value="<?= $csrfToken ?>">
|
||
<input type="hidden" name="loan_id" id="repayLoanId">
|
||
|
||
<div class="modal-body">
|
||
<p>Gesamtbetrag inkl. Zinsen: <strong id="repayTotalAmount"></strong></p>
|
||
|
||
<div class="form-group mt-md">
|
||
<label class="form-label">Zurückzahlungsbetrag (€)</label>
|
||
<input type="number" name="amount" class="form-input"
|
||
min="100" step="1000"
|
||
id="repayAmountInput" required>
|
||
<small class="form-hint">Du kannst auch nur einen Teil zurückzahlen.</small>
|
||
</div>
|
||
|
||
<div class="alert alert-warning mt-md">
|
||
<p>💡 Du kannst den Kredit jederzeit (teilweise oder vollständig) zurückzahlen.</p>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" onclick="closeModal('repayLoanModal')">Abbrechen</button>
|
||
<button type="submit" class="btn btn-warning">Zurückzahlen</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Toast Container -->
|
||
<div id="toastContainer" class="toast-container"></div>
|
||
|
||
<script src="<?= $basePath ?>assets/js/main.js"></script>
|
||
<script>
|
||
let currentLoanId = null;
|
||
let currentTotalOwed = 0;
|
||
|
||
function repayLoan(loanId, totalOwed) {
|
||
currentLoanId = loanId;
|
||
currentTotalOwed = totalOwed;
|
||
|
||
document.getElementById('repayLoanId').value = loanId;
|
||
document.getElementById('repayTotalAmount').textContent =
|
||
new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(totalOwed);
|
||
document.getElementById('repayAmountInput').max = totalOwed;
|
||
document.getElementById('repayAmountInput').value = totalOwed;
|
||
|
||
openModal('repayLoanModal');
|
||
}
|
||
|
||
// Erfolg-Callbacks
|
||
window.loanSuccess = function(data) {
|
||
showToast('Kredit über ' + formatMoney(data.amount) + ' aufgenommen!', 'success');
|
||
setTimeout(() => location.reload(), 1500);
|
||
};
|
||
|
||
window.repaySuccess = function(data) {
|
||
showToast('Kredit zurückgezahlt: ' + formatMoney(data.repaid), 'success');
|
||
setTimeout(() => location.reload(), 1500);
|
||
};
|
||
|
||
function formatMoney(amount) {
|
||
return new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(amount);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|