This commit is contained in:
kknobloch 2025-04-21 21:34:26 +02:00
commit b294ceeec8
22 changed files with 977 additions and 0 deletions

18
config/config.php Normal file
View File

@ -0,0 +1,18 @@
<?php
return [
'host' => '127.0.0.1',
'dbname' => 'vwl',
'user' => 'root',
'password' => '',
'charset' => 'utf8mb4'
];
/*
return [
'host' => '148.251.96.181',
'dbname' => 'c1vwl',
'user' => 'c1gutscheinserver',
'password' => 'SommerNacht!2025',
'charset' => 'utf8mb4'
];
*/

23
core/App.php Normal file
View File

@ -0,0 +1,23 @@
<?php
class App {
protected $controller = 'MainController';
protected $method = 'home';
protected $params = [];
public function __construct() {
if (isset($_GET['page'])) {
$this->method = $_GET['page'];
}
require_once '../src/controller/' . $this->controller . '.php';
$this->controller = new $this->controller;
if (!method_exists($this->controller, $this->method)) {
$this->method = 'home';
}
call_user_func_array([$this->controller, $this->method], $this->params);
}
}

26
core/Database.php Normal file
View File

@ -0,0 +1,26 @@
<?php
class Database
{
private static ?PDO $pdo = null;
public static function connect(): PDO
{
if (self::$pdo === null) {
$config = require __DIR__ . '/../config/config.php';
$dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset={$config['charset']}";
try {
self::$pdo = new PDO($dsn, $config['user'], $config['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
} catch (PDOException $e) {
die("Datenbankverbindung fehlgeschlagen: " . $e->getMessage());
}
}
return self::$pdo;
}
}

58
core/Request.php Normal file
View File

@ -0,0 +1,58 @@
<?php
class Request {
public static function post(string $key, mixed $default = null): mixed {
return $_POST[$key] ?? $default;
}
public static function get(string $key, mixed $default = null): mixed {
return $_GET[$key] ?? $default;
}
public static function hasPost(string $key): bool {
return isset($_POST[$key]);
}
public static function hasGet(string $key): bool {
return isset($_GET[$key]);
}
public static function allPost(): array {
return $_POST;
}
public static function allGet(): array {
return $_GET;
}
// Optional: Sanitize input
public static function postSanitized(string $key, mixed $default = null): mixed {
return htmlspecialchars($_POST[$key] ?? $default);
}
// Optional: Boolean inputs (z.B. Checkbox, Radio, etc.)
public static function postBool(string $key): bool {
return isset($_POST[$key]) && ($_POST[$key] === '1' || $_POST[$key] === 'true' || $_POST[$key] === 'on');
}
public static function server(string $key, mixed $default = null): mixed {
return $_SERVER[$key] ?? $default;
}
public static function isPost(): bool {
return self::server('REQUEST_METHOD') === 'POST';
}
public static function isGet(): bool {
return self::server('REQUEST_METHOD') === 'GET';
}
public static function clientIp(): string {
return self::server('REMOTE_ADDR', '0.0.0.0');
}
public static function userAgent(): string {
return self::server('HTTP_USER_AGENT', '');
}
}

View File

@ -0,0 +1,7 @@
include.path=${php.global.include.path}
php.version=PHP_82
source.encoding=UTF-8
src.dir=.
tags.asp=false
tags.short=false
web.root=.

9
nbproject/project.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.php.project</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/php-project/1">
<name>vwlsupport</name>
</data>
</configuration>
</project>

View File

@ -0,0 +1,25 @@
<?php
class MainController {
public function home() {
$pageTitle = "Home";
require '../src/view/templates/header.php';
require '../src/view/home.php';
require '../src/view/templates/footer.php';
}
public function hotels() {
$pageTitle = "Hotels";
require '../src/view/templates/header.php';
require '../src/view/hotels.php';
require '../src/view/templates/footer.php';
}
public function hotel_edit() {
$pageTitle = "Hotel bearbeiten";
require '../src/view/templates/header.php';
require '../src/view/hotel_edit.php';
require '../src/view/templates/footer.php';
}
}

View File

@ -0,0 +1,74 @@
<?php
require_once __DIR__ . '/../../core/Database.php';
class AnschriftModel {
private PDO $db;
private array $dbFields = [
'land', 'plz', 'ort', 'strasse', 'ansprechpartner',
'telefon', 'email'
];
private array $frmFields;
private array $rgFrmFields = [
'rg_land', 'rg_plz', 'rg_ort', 'rg_strasse', 'rg_ansprechpartner',
'rg_telefon', 'rg_email'
];
public function __construct() {
$this->db = Database::connect();
$this->frmFields = $this->dbFields; // Standard-Feldmapping
}
public function findById($id): ?array {
$stmt = $this->db->prepare("SELECT * FROM vwl_anschrift WHERE id = :id");
$stmt->execute(['id' => $id]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}
public function update($id, $formData, $rgAnschrift = false) {
$formFields = $rgAnschrift ? $this->rgFrmFields : $this->frmFields;
$data = $this->mapFormToDbFields($formData, $formFields);
if (empty($data)) {
return false;
}
if (empty($id)) {
return $this->insertData($data);
}
return $this->updateData($id, $data);
}
private function insertData(array $data): int {
$fields = array_keys($data);
$placeholders = array_map(fn($f) => ":$f", $fields);
$sql = "INSERT INTO vwl_anschrift (" . implode(', ', $fields) . ") VALUES (" . implode(', ', $placeholders) . ")";
$stmt = $this->db->prepare($sql);
$stmt->execute($data);
return (int)$this->db->lastInsertId();
}
private function updateData($id, array $data): int {
$assignments = array_map(fn($f) => "$f = :$f", array_keys($data));
$sql = "UPDATE vwl_anschrift SET " . implode(', ', $assignments) . " WHERE id = :id";
$stmt = $this->db->prepare($sql);
$data['id'] = $id;
$stmt->execute($data);
return $id;
}
private function mapFormToDbFields(array $formData, array $formFields): array {
$mapped = [];
foreach ($this->dbFields as $i => $dbField) {
$formField = $formFields[$i] ?? null;
if ($formField && isset($formData[$formField])) {
$mapped[$dbField] = $formData[$formField];
}
}
return $mapped;
}
}

View File

@ -0,0 +1,33 @@
<?php
require_once __DIR__ . '/../../core/Database.php';
class BankverbindungModel
{
public static function findById($id)
{
$pdo = Database::connect();
$stmt = $pdo->prepare("SELECT * FROM vwl_bankverbindung WHERE id = :id");
$stmt->execute(['id' => $id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
public static function update($id, $data)
{
$pdo = Database::connect();
$stmt = $pdo->prepare("
UPDATE vwl_bankverbindung SET
bank = :bank,
iban = :iban,
bic = :bic,
inhaber = :inhaber
WHERE id = :id
");
$stmt->execute([
'bank' => $data['bank'],
'iban' => $data['iban'],
'bic' => $data['bic'],
'inhaber' => $data['inhaber'],
'id' => $data['bankid']
]);
}
}

131
src/model/HotelModel.php Normal file
View File

@ -0,0 +1,131 @@
<?php
require_once __DIR__ . '/../../core/Database.php';
require_once 'AnschriftModel.php';
require_once 'BankverbindungModel.php';
class HotelModel {
private PDO $db;
private AnschriftModel $mdlAnschr;
private $allowedFields = [
'hotelname', 'hotelnummer', 'provsatz', 'rg_hotelname', 'rg_interval',
'rg_vondatum', 'rg_bisdatum', 'rg_laufdatum', 'rg_ustid', 'rg_ustfrei',
'rg_provmode', 'status', 'notiz', 'intern', 'frueherechnung',
'passwort', 'loginname', 'nichtanzeigen', 'anschriftid', 'rg_anschriftid',
'bankid', 'rg_bankid', 'betreuer'
];
public function __construct() {
$this->db = Database::connect();
$this->mdlAnschr = new AnschriftModel();
}
public function getHotelById($id) {
$stmt = $this->db->prepare("SELECT * FROM vwl_hotel WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
public function getAnschriftById($id) {
$stmt = $this->db->prepare("SELECT * FROM vwl_anschrift WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
public function getBankverbindungById($id) {
$stmt = $this->db->prepare("SELECT * FROM vwl_bankverbindung WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
public function updateHotel($data) {
$fieldsToUpdate = array_intersect_key($data, array_flip($this->allowedFields));
if (empty($fieldsToUpdate)) {
return false;
}
// Baue SQL-Statement
$setParts = [];
foreach ($fieldsToUpdate as $field => $value) {
$setParts[] = "`$field` = :$field";
}
$sql = "UPDATE vwl_hotel SET " . implode(', ', $setParts) . " WHERE id = :id";
$stmt = $this->db->prepare($sql);
// Bind-Werte
$fieldsToUpdate['id'] = $data['id'];
return $stmt->execute($fieldsToUpdate);
}
public function findByIdWithRelations($id) {
$stmt = $this->db->prepare("
SELECT * FROM vwl_hotel WHERE id = :id
");
$stmt->execute(['id' => $id]);
$hotel = $stmt->fetch(PDO::FETCH_ASSOC);
if ($hotel) {
$hotel['anschrift'] = $this->mdlAnschr->findById($hotel['anschriftid']);
$hotel['bankverbindung'] = BankverbindungModel::findById($hotel['bankid']);
$hotel['rg_anschrift'] = $this->mdlAnschr->findById($hotel['rg_anschriftid']);
$hotel['rg_bankverbindung'] = BankverbindungModel::findById($hotel['rg_bankid']);
}
return $hotel;
}
public function getRechnungen($id) {
$stmt = $this->db->prepare("
SELECT r.*, h.hotelnummer
FROM vwl_hotel_rechnung r
JOIN vwl_hotel h ON r.hotelid = h.id
WHERE h.id = :hotelid
ORDER BY r.rechnungsdatum DESC
");
$stmt->execute(['hotelid' => $id]);
$stmt->fetchAll();
}
public function updateWithRelations($id, $data) {
$this->db->beginTransaction();
try {
// 1. Anschrift aktualisieren
$subid = $this->mdlAnschr->update($data['anschriftid'], $data, false);
if(empty($subid) == false) {
$data['anschriftid'] = $subid;
}
// 1.1 Rechungsanschrift aktualisieren
$subidRg = $this->mdlAnschr->update($data['rg_anschriftid'], $data, true);
if(empty($subidRg) == false) {
$data['rg_anschriftid'] = $subidRg;
}
// 2. Bankverbindung aktualisieren
BankverbindungModel::update($data['bankid'], $data);
// 3. Hotel aktualisieren
$fieldsToUpdate = array_intersect_key($data, array_flip($this->allowedFields));
if (empty($fieldsToUpdate)) {
return false;
}
$this->updateHotel($data);
$this->db->commit();
return true;
} catch (Exception $e) {
$this->db->rollBack();
return false;
}
}
}

2
src/view/home.php Normal file
View File

@ -0,0 +1,2 @@
<h4>Startseite</h4>
<p>Willkommen auf der Startseite deiner Anwendung!</p>

83
src/view/hotel_edit.php Normal file
View File

@ -0,0 +1,83 @@
<?php
require_once '../core/Request.php';
require_once '../src/model/HotelModel.php';
$hotelId = Request::get('id');
$message = '';
$hotelModel = new HotelModel();
// POST-Verarbeitung
if (Request::isPost()) {
$data = Request::allPost();
// ID-Felder ausblenden, aber für Speicherung mitgeben
$data['anschriftid'] = Request::post('anschriftid') ?? null;
$data['rg_anschriftid'] = Request::post('rg_anschriftid') ?? null;
$data['bankid'] = Request::post('bankid') ?? null;
$success = $hotelModel->updateWithRelations($hotelId, $data);
if ($success) {
$message = '<div class="alert alert-success">✅ Daten erfolgreich gespeichert.</div>';
} else {
$message = '<div class="alert alert-danger">❌ Fehler beim Speichern der Daten.</div>';
}
}
// Daten neu laden nach dem Speichern (oder beim ersten Aufruf)
$hotel = $hotelModel->findByIdWithRelations($hotelId);
$anschrift = $hotel['anschrift'];
$bankverbindung = $hotel['bankverbindung'];
$rganschrift = $hotel['rg_anschrift'] ?? [];
$rgbankverbindung = $hotel['rg_bankverbindung'];
$rechnungen = $hotelModel->getRechnungen($hotelId);
?>
<h2>Hotel: <?= htmlspecialchars($hotel['hotelname']) ?></h2>
<?= $message ?>
<form method="POST">
<input type="hidden" name="id" value="<?= $hotel['id'] ?>">
<input type="hidden" name="anschriftid" value="<?= $hotel['anschrift']['id'] ?>">
<input type="hidden" name="rg_anschriftid" value="<?= array_key_exists('id', $rganschrift) ? $rganschrift['id'] : null ?>">
<input type="hidden" name="bankid" value="<?= $hotel['bankverbindung']['id'] ?>">
<!-- Tabs Navigation -->
<ul class="nav nav-tabs" id="hotelTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="adresse-tab" data-bs-toggle="tab" data-bs-target="#adresse" type="button" role="tab">
Anschrift & Bankverbindung
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="rechnung-tab" data-bs-toggle="tab" data-bs-target="#rechnung" type="button" role="tab">
Rechnungsdaten
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="archiv-tab" data-bs-toggle="tab" data-bs-target="#archiv" type="button" role="tab">
Rechnungsarchiv
</button>
</li>
</ul>
<!-- Tabs Content -->
<div class="tab-content pt-3" id="hotelTabContent">
<!-- Tab 1: Anschrift & Bankverbindung -->
<div class="tab-pane fade show active" id="adresse" role="tabpanel">
<?php include 'hotel_edit_adresse.php'; ?>
</div>
<!-- Tab 2: Rechnungsdaten -->
<div class="tab-pane fade" id="rechnung" role="tabpanel">
<?php include 'hotel_edit_rechnung.php'; ?>
</div>
<!-- Tab 3: Rechnungsarchiv -->
<div class="tab-pane fade" id="archiv" role="tabpanel">
<?php include 'hotel_edit_rechnung_archiv.php'; ?>
</div>
</div>
<button type="submit" class="btn btn-primary">Speichern</button>
</form>

View File

@ -0,0 +1,102 @@
<div class="container">
<div class="row mb-3">
<div class="col-md-2">
<label for="hotelnummer" class="form-label">Hotelnummer</label>
<input type="number" class="form-control" id="hotelnummer" name="hotelnummer" min="101"
value="<?= htmlspecialchars($hotel['hotelnummer'] ?? '') ?>" required>
</div>
<div class="col-md-4">
<label for="hotelname" class="form-label">Hotelname</label>
<input type="text" class="form-control" id="hotelname" name="hotelname"
value="<?= htmlspecialchars($hotel['hotelname'] ?? '') ?>" required>
</div>
<div class="col-md-2">
<label for="intern" class="form-label">Intern</label>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="intern" name="intern"
<?= ($hotel['intern'] ?? '') === '1' ? 'checked' : '' ?>>
<label class="form-check-label" for="intern">Eigenes Hotel</label>
</div>
</div>
<div class="col-md-2">
<label for="status" class="form-label">Status</label>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="status" name="status"
<?= ($hotel['status'] ?? '') === 'aktiv' ? 'checked' : '' ?>>
<label class="form-check-label" for="status">Aktiv</label>
</div>
</div>
<div class="col-md-2">
<label for="betreuer" class="form-label">Betreuer</label>
<select class="form-select" name="betreuer" id="betreuer">
<?php
$optionen = ['Moritz', 'Petra', 'Robert', 'Sonstige'];
foreach ($optionen as $opt) {
$selected = ($hotel['betreuer'] ?? '') === $opt ? 'selected' : '';
echo "<option value=\"$opt\" $selected>$opt</option>";
}
?>
</select>
</div>
</div>
<h5>Anschrift</h5>
<div class="row mb-3">
<div class="col-md-2">
<label for="land" class="form-label">Land</label>
<input type="text" class="form-control" name="land" value="<?= htmlspecialchars($anschrift['land'] ?? '') ?>">
</div>
<div class="col-md-2">
<label for="plz" class="form-label">PLZ</label>
<input type="text" class="form-control" name="plz" value="<?= htmlspecialchars($anschrift['plz'] ?? '') ?>">
</div>
<div class="col-md-4">
<label for="ort" class="form-label">Ort</label>
<input type="text" class="form-control" name="ort" value="<?= htmlspecialchars($anschrift['ort'] ?? '') ?>">
</div>
<div class="col-md-4">
<label for="strasse" class="form-label">Straße</label>
<input type="text" class="form-control" name="strasse" value="<?= htmlspecialchars($anschrift['strasse'] ?? '') ?>">
</div>
</div>
<div class="row mb-3">
<div class="col-md-4">
<label for="ansprechpartner" class="form-label">Ansprechpartner</label>
<input type="text" class="form-control" name="ansprechpartner" value="<?= htmlspecialchars($anschrift['ansprechpartner'] ?? '') ?>">
</div>
<div class="col-md-4">
<label for="telefon" class="form-label">Telefon</label>
<input type="text" class="form-control" name="telefon" value="<?= htmlspecialchars($anschrift['telefon'] ?? '') ?>">
</div>
<div class="col-md-4">
<label for="email" class="form-label">E-Mail</label>
<input type="email" class="form-control" name="email" value="<?= htmlspecialchars($anschrift['email'] ?? '') ?>">
</div>
</div>
<h5>Bankverbindung</h5>
<div class="row mb-3">
<div class="col-md-3">
<label for="bank" class="form-label">Bank</label>
<input type="text" class="form-control" name="bank" value="<?= htmlspecialchars($bankverbindung['bank'] ?? '') ?>">
</div>
<div class="col-md-3">
<label for="iban" class="form-label">IBAN</label>
<input type="text" class="form-control" name="iban" value="<?= htmlspecialchars($bankverbindung['iban'] ?? '') ?>">
</div>
<div class="col-md-3">
<label for="bic" class="form-label">BIC</label>
<input type="text" class="form-control" name="bic" value="<?= htmlspecialchars($bankverbindung['bic'] ?? '') ?>">
</div>
<div class="col-md-3">
<label for="inhaber" class="form-label">Kontoinhaber</label>
<input type="text" class="form-control" name="inhaber" value="<?= htmlspecialchars($bankverbindung['inhaber'] ?? '') ?>">
</div>
</div>
<div class="mb-3">
<label for="notiz" class="form-label">Notizen</label>
<textarea class="form-control" name="notiz" rows="4"><?= htmlspecialchars($hotel['notiz'] ?? '') ?></textarea>
</div>
</div>

View File

@ -0,0 +1,109 @@
<div class="container">
<h5>Rechnungsanschrift</h5>
<div class="row">
<div class="col">
<label for="land" class="form-label">Abweichender Hotelname</label>
<input type="text" class="form-control" name="rg_hotelname" value="<?= htmlspecialchars($hotel['rg_hotelname'] ?? '') ?>">
</div>
</div>
<div class="row mb-3">
<div class="col-md-2">
<label for="rg_land" class="form-label">Land</label>
<input type="text" class="form-control" name="rg_land" value="<?= htmlspecialchars($rganschrift['land'] ?? '') ?>">
</div>
<div class="col-md-2">
<label for="rg_plz" class="form-label">PLZ</label>
<input type="text" class="form-control" name="rg_plz" value="<?= htmlspecialchars($rganschrift['plz'] ?? '') ?>">
</div>
<div class="col-md-4">
<label for="rg_ort" class="form-label">Ort</label>
<input type="text" class="form-control" name="rg_ort" value="<?= htmlspecialchars($rganschrift['ort'] ?? '') ?>">
</div>
<div class="col-md-4">
<label for="rg_strasse" class="form-label">Straße</label>
<input type="text" class="form-control" name="rg_strasse" value="<?= htmlspecialchars($rganschrift['strasse'] ?? '') ?>">
</div>
</div>
<div class="row mb-3">
<div class="col-md-4">
<label for="ansprechpartner" class="form-label">Ansprechpartner</label>
<input type="text" class="form-control" name="rgansprechpartner" value="<?= htmlspecialchars($rganschrift['ansprechpartner'] ?? '') ?>">
</div>
<div class="col-md-4">
<label for="telefon" class="form-label">Telefon</label>
<input type="text" class="form-control" name="rgtelefon" value="<?= htmlspecialchars($rganschrift['telefon'] ?? '') ?>">
</div>
<div class="col-md-4">
<label for="email" class="form-label">E-Mail</label>
<input type="email" class="form-control" name="rgemail" value="<?= htmlspecialchars($rganschrift['email'] ?? '') ?>">
</div>
</div>
<h5>Umsatzsteuer</h5>
<div class="row align-items-end mb-3">
<!-- USt-ID -->
<div class="col-md-3">
<label for="rg_ustid" class="form-label">USt-ID</label>
<input type="text" class="form-control" id="rg_ustid" name="rg_ustid" value="<?= htmlspecialchars($hotel['rg_ustid'] ?? '') ?>">
</div>
<!-- Umsatzsteuer-Optionen -->
<div class="col-md-6">
<label class="form-label d-block">Umsatzsteuer-Status</label>
<div class="btn-group w-100" role="group" aria-label="Umsatzsteuerstatus">
<input type="radio" class="btn-check" name="rg_ustfrei" id="ust0" value="0" autocomplete="off" <?= ($hotel['rg_ustfrei'] ?? 0) == 0 ? 'checked' : '' ?>>
<label class="btn btn-outline-primary" for="ust0">Umsatzsteuer pflichtig</label>
<input type="radio" class="btn-check" name="rg_ustfrei" id="ust1" value="1" autocomplete="off" <?= ($hotel['rg_ustfrei'] ?? 0) == 1 ? 'checked' : '' ?>>
<label class="btn btn-outline-primary" for="ust1">Umsatzsteuer befreit</label>
<input type="radio" class="btn-check" name="rg_ustfrei" id="ust2" value="2" autocomplete="off" <?= ($hotel['rg_ustfrei'] ?? 0) == 2 ? 'checked' : '' ?>>
<label class="btn btn-outline-primary" for="ust2">keine VAT-ID vorhanden</label>
</div>
</div>
</div>
<h5>Provision</h5>
<div class="row align-items-end mb-3">
<!-- Provision -->
<div class="col-md-3">
<label for="rg_ustid" class="form-label">Provisionssatz</label>
<input type="text" class="form-control" id="provsatz" name="provsatz" value="<?= htmlspecialchars($hotel['provsatz'] ?? '') ?>">
</div>
<!-- Umsatzsteuer-Optionen -->
<div class="col-md-6">
<label class="form-label d-block">Provision-Status</label>
<div class="btn-group w-100" role="group" aria-label="Provisionstatus">
<input type="radio" class="btn-check" name="rg_provmode" id="provmode0" value="vonDenVerkauften" autocomplete="off" <?= ($hotel['rg_provmode'] ?? 0) == 'vonDenVerkauften' ? 'checked' : '' ?>>
<label class="btn btn-outline-primary" for="provmode0">von den Verkauften</label>
<input type="radio" class="btn-check" name="rg_provmode" id="provmode1" value="vonDenBezahlten" autocomplete="off" <?= ($hotel['rg_provmode'] ?? 0) == 'vonDenBezahlten' ? 'checked' : '' ?>>
<label class="btn btn-outline-primary" for="provmode1">von den Bezahlten</label>
</div>
</div>
</div>
<h5>Rechnungslauf</h5>
<!--
<h5>Bankverbindung für Rechnung</h5>
<div class="row mb-3">
<div class="col-md-3">
<label for="bank" class="form-label">Bank</label>
<input type="text" class="form-control" name="bank" value="<?= htmlspecialchars($rgbankverbindung['bank'] ?? '') ?>">
</div>
<div class="col-md-3">
<label for="iban" class="form-label">IBAN</label>
<input type="text" class="form-control" name="iban" value="<?= htmlspecialchars($rgbankverbindung['iban'] ?? '') ?>">
</div>
<div class="col-md-3">
<label for="bic" class="form-label">BIC</label>
<input type="text" class="form-control" name="bic" value="<?= htmlspecialchars($rgbankverbindung['bic'] ?? '') ?>">
</div>
<div class="col-md-3">
<label for="inhaber" class="form-label">Kontoinhaber</label>
<input type="text" class="form-control" name="inhaber" value="<?= htmlspecialchars($rgbankverbindung['inhaber'] ?? '') ?>">
</div>
</div>
-->
</div>

View File

@ -0,0 +1,43 @@
<?php ?>
<div class="container">
<table class="table table-bordered table-hover mt-3">
<thead class="thead-light">
<tr>
<th>Rechnungsnummer</th>
<th>Rechnungsdatum</th>
<th>Zeitraum</th>
<th>Rechnungsbetrag</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<?php if (!empty($rechnungen) && is_array($rechnungen)): ?>
<?php foreach ($rechnungen as $r): ?>
<tr>
<td><?= htmlspecialchars($r['hotelnummer']) . '-' . htmlspecialchars($r['lfdnummer']) ?></td>
<td><?= date('d.m.Y', strtotime($r['rechnungsdatum'])) ?></td>
<td><?= date('d.m.Y', strtotime($r['vondatum'])) . ' - ' . date('d.m.Y', strtotime($r['bisdatum'])) ?></td>
<td class="text-end"><?= number_format($r['saldo'], 2, ',', '.') ?> €</td>
<td>
<a href="/public/rechnungen/<?= $r['pdfdateiname'] ?>" class="btn btn-sm btn-outline-primary" target="_blank">
<i class="bi bi-file-earmark-pdf"></i> PDF
</a>
<form method="post" action="/controller/RechnungController.php" class="d-inline">
<input type="hidden" name="aktion" value="senden">
<input type="hidden" name="rechnungid" value="<?= $r['id'] ?>">
<button type="submit" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-envelope"></i> Mail
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5" class="text-center text-muted">Keine Rechnungen vorhanden.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>

71
src/view/hotels.php Normal file
View File

@ -0,0 +1,71 @@
<?php
require_once '../core/Database.php';
$pdo = Database::connect();
$stmt = $pdo->query("SELECT id, hotelnummer, hotelname, rg_vondatum, rg_bisdatum, status FROM vwl_hotel ORDER BY hotelname");
$hotels = $stmt->fetchAll();
?>
<h4>Hotel-Liste</h4>
<div class="table-responsive">
<table id="hotelTable" class="table table-striped table-hover align-middle">
<thead>
<tr>
<th data-column="hotelnummer">Hotel-Nr</th>
<th data-column="hotelname">
Name
<input type="text" id="hotelFilter" class="form-control form-control-sm d-inline-block ms-2" placeholder="Filtern..." style="width: 200px;">
</th>
<th data-column="rg_vondatum">Von-Datum</th>
<th data-column="rg_bisdatum">Bis-Datum</th>
<th data-column="status">Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($hotels as $hotel): ?>
<tr ondblclick="openHotelForm(<?= $hotel['id'] ?>)">
<td><?= htmlspecialchars($hotel['hotelnummer']) ?></td>
<td><?= htmlspecialchars($hotel['hotelname']) ?></td>
<td><?= isset($hotel['rg_vondatum']) && ($d = DateTime::createFromFormat('Y-m-d', $hotel['rg_vondatum'])) ? $d->format('d.m.Y') : '' ?></td>
<td><?= isset($hotel['rg_bisdatum']) && ($d = DateTime::createFromFormat('Y-m-d', $hotel['rg_bisdatum'])) ? $d->format('d.m.Y') : '' ?></td>
<td><?= htmlspecialchars($hotel['status']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<script>
function openHotelForm(id) {
window.open('index.php?page=hotel_edit&id=' + id, '_blank');
}
// Sortierung bei Klick auf Überschriften
document.querySelectorAll("#hotelTable th").forEach(th => {
th.addEventListener("click", () => {
const table = th.closest("table");
const column = th.cellIndex;
const order = th.dataset.order = -(th.dataset.order || -1);
const rows = Array.from(table.tBodies[0].rows);
rows.sort((a, b) => {
const cellA = a.cells[column].innerText.trim();
const cellB = b.cells[column].innerText.trim();
return cellA.localeCompare(cellB, undefined, { numeric: true }) * order;
});
rows.forEach(row => table.tBodies[0].appendChild(row));
});
});
// Live-Filter für Hotelnamen
document.getElementById('hotelFilter').addEventListener('input', function () {
const filter = this.value.toLowerCase();
const rows = document.querySelectorAll("#hotelTable tbody tr");
rows.forEach(row => {
const hotelname = row.cells[1].innerText.toLowerCase();
row.style.display = hotelname.includes(filter) ? '' : 'none';
});
});
</script>

View File

@ -0,0 +1,3 @@
</div> <!-- container -->
</body>
</html>

View File

@ -0,0 +1,77 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
// Ablaufzeit in Sekunden (15 Minuten)
if (!defined('SESSION_TIMEOUT')) {
define('SESSION_TIMEOUT', 15 * 60);
}
// Wenn User nicht eingeloggt ist → zur Loginseite
if (!isset($_SESSION['user'])) {
header("Location: login.php");
exit;
}
// Session-Timeout prüfen
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > SESSION_TIMEOUT)) {
session_unset();
session_destroy();
header("Location: login.php?timeout=1");
exit;
}
// Zeit der letzten Aktivität aktualisieren
$_SESSION['last_activity'] = time();
$username = $_SESSION['user'] ?? 'Gast';
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title><?= $pageTitle ?? "Seite" ?> | Meine Anwendung</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap JS (für Tabs) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="/assets/css/vwl.css" rel="stylesheet">
</head>
<body>
<div class="bg-primary text-white py-2 px-4 d-flex justify-content-between align-items-center">
<div><strong>Meine Anwendung</strong></div>
<div class="text-center flex-grow-1"><?= htmlspecialchars($pageTitle) ?? '' ?></div>
<div>Angemeldet als: <?= htmlspecialchars($username) ?></div>
</div>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<?php
$menu = [
'home' => 'Home',
'gutscheinsuche' => 'Gutscheinsuche',
'hotels' => 'Hotels',
'rechnungen' => 'Rechnungen',
'partner' => 'Partner',
'auswertungen' => 'Auswertungen',
'postversand' => 'Postversand',
'einzahlungen' => 'Einzahlungen',
'einstellungen' => 'Einstellungen',
'direct:/logout.php' => 'Logout'
];
foreach ($menu as $key => $value) {
if (str_starts_with($key, 'direct:')) {
$link = substr($key, 7);
} else {
$link = "?page=$key";
}
echo "<li class='nav-item'><a class='nav-link' href='$link'>$value</a></li>";
}
?>
</ul>
</div>
</nav>
<div class="container mt-4">

8
web/assets/css/vwl.css Normal file
View File

@ -0,0 +1,8 @@
/*
Created on : 13.04.2025, 12:22:35
Author : KNOB023
*/
body {
font-family: 'Calibri', sans-serif;
}

10
web/index.php Normal file
View File

@ -0,0 +1,10 @@
<?php
session_start();
if (!isset($_SESSION['user'])) {
header("Location: login.php");
exit;
}
require_once '../core/App.php';
$app = new App();

60
web/login.php Normal file
View File

@ -0,0 +1,60 @@
<?php
if (isset($_GET['timeout']) && $_GET['timeout'] == 1) {
echo '<div class="alert alert-warning">Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.</div>';
}
session_start();
require_once '../core/Database.php';
$pdo = Database::connect();
$error = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
$stmt = $pdo->prepare("SELECT * FROM vwl_internal_user WHERE vwlusername = :username LIMIT 1");
$stmt->execute(['username' => $username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['vwlpassword'])) {
$_SESSION['user'] = $user['vwlusername'];
header("Location: index.php");
exit;
} else {
$error = "Ungültiger Benutzername oder Passwort.";
}
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-4">
<form method="POST">
<h3 class="text-center">Login</h3>
<?php if ($error): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php endif; ?>
<div class="mb-3">
<label class="form-label">Benutzername</label>
<input type="text" name="username" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Passwort</label>
<input type="password" name="password" class="form-control" required>
</div>
<button class="btn btn-primary w-100" type="submit">Einloggen</button>
</form>
</div>
</div>
</div>
</body>
</html>

5
web/logout.php Normal file
View File

@ -0,0 +1,5 @@
<?php
session_start();
session_destroy();
header("Location: login.php");
exit;