If-Schleife zum Finden von offenen Aufträgen robuster gestaltet

This commit is contained in:
2025-10-10 15:12:22 +02:00
parent f8c8725622
commit 957d52dd21
5 changed files with 392 additions and 9 deletions

193
sources/getCheckInCards.php Normal file
View File

@@ -0,0 +1,193 @@
<?php
// ../sources/getCheckInCards.php
error_reporting(E_ALL);
require('../config.php');
require('../EpiApi.php');
$Epi = new Epirent();
use chillerlan\QRCode\{ QRCode, QROptions };
require('../vendor/autoload.php');
const APP_TZ = 'Europe/Berlin';
/** ---------- Helpers (wie in deiner Check-In-Datei) ---------- */
function dt(?string $s): ?DateTimeImmutable {
if (!$s) return null;
try { return new DateTimeImmutable($s, new DateTimeZone(APP_TZ)); }
catch (Throwable $e) { return null; }
}
function dayStart(DateTimeImmutable $d): DateTimeImmutable {
return $d->setTime(0, 0, 0);
}
function rowClassForDate(?DateTimeImmutable $markDate, ?DateTimeImmutable $today): string {
if (!$markDate || !$today) return '';
if ($markDate == $today) return 'text-dark bg-warning';
if ($markDate < $today) return 'bg-danger';
return '';
}
/**
* Row-Marking Quelle (Check-In) identisch zu deiner Tabelle:
* 1 = $packingjob->date_end
* 2 = $NachbereitungsTimeDetail->date_start
* 3 = $RePackagingTimeDetail->date_start
* 4 = $PackingNoteDetail->date_redelivery
* Fallback: $packingjob->date_end
*/
function resolveRowMarkDateCheckIn(
$packingjob,
$NachbereitungsTimeDetail,
$RePackagingTimeDetail,
$PackingNoteDetail,
int $source
): ?DateTimeImmutable {
$candidate = null;
switch ($source) {
case 1: $candidate = dt($packingjob->date_end ?? null); break;
case 2: $candidate = dt($NachbereitungsTimeDetail->date_start ?? null); break;
case 3: $candidate = dt($RePackagingTimeDetail->date_start ?? null); break;
case 4: $candidate = dt($PackingNoteDetail->date_redelivery ?? null); break;
default: $candidate = null; break;
}
if (!$candidate) {
$candidate = dt($packingjob->date_end ?? null);
}
return $candidate ? dayStart($candidate) : null;
}
/** ---------- Daten holen (wie in deiner Check-In-Datei) ---------- */
// Offene Packingnotes für Check-IN-Seite: isco=False
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
$data_output = json_decode($result)->payload ?? [];
/** ---------- Zähler ---------- */
$today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
$limit = null;
if (CheckIn_FutureDays != -1) {
$limit = $today->modify('+' . (int)CheckIn_FutureDays . ' day');
}
$cntOverdue = 0;
$cntToday = 0;
$cntFuture = 0;
/** ---------- Zählen mit exakt deiner Logik ---------- */
foreach ($data_output as $packingjob) {
if ($packingjob->is_archived == true) continue;
// OrderDetails (für Nachbereitung / Rückpacken)
$orderRes = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
$orderdetail_output = json_decode($orderRes)->payload[0] ?? null;
// PackingNote Details (für Redelivery)
$pnRes = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
$PackingNoteDetail = json_decode($pnRes)->payload[0] ?? null;
// Zeit-Slots aus dem Schedule
$NachbereitungsTimeDetail = null;
$RePackagingTimeDetail = null;
if ($orderdetail_output && !empty($orderdetail_output->order_schedule)) {
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
$NachbereitungsTimeDetail = $scheduledetail;
}
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
$RePackagingTimeDetail = $scheduledetail;
}
}
}
// Row-Mark Source (mit Override-Flag)
$source = CheckIn_UseDispoEndForRowMarking ? 1 : (int)CheckInRowMarkSource;
$markDate = resolveRowMarkDateCheckIn(
$packingjob,
$NachbereitungsTimeDetail,
$RePackagingTimeDetail,
$PackingNoteDetail,
$source
);
if (!$markDate) continue;
// Fenster berücksichtigen (gleich wie in deiner Tabelle)
if (CheckIn_FutureDays != -1 && $limit && $markDate > $limit) {
continue;
}
// Zählen
if ($markDate < $today) {
$cntOverdue++;
} elseif ($markDate == $today) {
$cntToday++;
} else {
$cntFuture++;
}
}
$ts = (new DateTimeImmutable('now', new DateTimeZone(APP_TZ)))->format('H:i:s');
?>
<h3 class="mb-3">
<span class="badge badge-secondary d-block w-100 py-2 text-center">CheckIn</span>
</h3>
<div class="row">
<div class="col-xl-4 col-md-6">
<div class="card bg-danger text-white mb-4">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<div class="small">Überfällige CheckIns</div>
<div class="h2 mb-0"><?= (int)$cntOverdue ?></div>
</div>
<i class="fa-solid fa-triangle-exclamation fa-2x" style="opacity:.6"></i>
</div>
<div class="card-footer d-flex justify-content-between">
<span class="small text-white-50">Stand <?= htmlspecialchars($ts) ?></span>
<span class="small">bis <?= htmlspecialchars($today->format('d.m.Y')) ?></span>
</div>
</div>
</div>
<div class="col-xl-4 col-md-6">
<div class="card bg-warning text-dark mb-4">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<div class="small">Heutige CheckIns</div>
<div class="h2 mb-0"><?= (int)$cntToday ?></div>
</div>
<i class="fa-solid fa-boxes-packing fa-2x" style="opacity:.6"></i>
</div>
<div class="card-footer d-flex justify-content-between">
<span class="small text-muted">Stand <?= htmlspecialchars($ts) ?></span>
<span class="small"><?= htmlspecialchars($today->format('d.m.Y')) ?></span>
</div>
</div>
</div>
<div class="col-xl-4 col-md-6">
<div class="card bg-success text-white mb-4">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<div class="small">
Zukünftige CheckIns
<?php if (CheckIn_FutureDays != -1 && $limit): ?>
(≤ <?= htmlspecialchars($limit->format('d.m.Y')) ?>)
<?php endif; ?>
</div>
<div class="h2 mb-0"><?= (int)$cntFuture ?></div>
</div>
<i class="fa-solid fa-forward-step fa-2x" style="opacity:.6"></i>
</div>
<div class="card-footer d-flex justify-content-between">
<span class="small text-white-50">Stand <?= htmlspecialchars($ts) ?></span>
<span class="small">
<?php if (CheckIn_FutureDays == -1): ?>
unbegrenzt
<?php else: ?>
bis <?= htmlspecialchars($limit->format('d.m.Y')) ?>
<?php endif; ?>
</span>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,189 @@
<?php
// sources/getCheckoutTable.php zählt statt Tabellenausgabe drei KPIs als Karten
error_reporting(E_ALL);
require('../config.php');
require('../EpiApi.php');
$Epi = new Epirent();
const APP_TZ = 'Europe/Berlin';
/** ---- Helpers (aus deiner bisherigen Datei) ---- */
function dt(?string $s): ?DateTimeImmutable {
if (!$s) return null;
try { return new DateTimeImmutable($s, new DateTimeZone(APP_TZ)); }
catch (Throwable $e) { return null; }
}
function dayStart(DateTimeImmutable $d): DateTimeImmutable {
return $d->setTime(0, 0, 0);
}
/**
* Quelle für das Markierungsdatum (identisch zu deiner Liste):
* 1 = $packingjob->date_start
* 2 = $VorbereitungsTimeDetail->date_start
* 3 = $PackingNoteDetail->date_packing
* 4 = $PackingNoteDetail->date_delivery
* Fallback: $packingjob->date_start
*/
function resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, int $source): ?DateTimeImmutable {
$candidate = null;
switch ($source) {
case 1: $candidate = dt($packingjob->date_start ?? null); break;
case 2: $candidate = dt($VorbereitungsTimeDetail->date_start ?? null); break;
case 3: $candidate = dt($PackingNoteDetail->date_packing ?? null); break;
case 4: $candidate = dt($PackingNoteDetail->date_delivery ?? null); break;
default:$candidate = null; break;
}
if (!$candidate) $candidate = dt($packingjob->date_start ?? null);
return $candidate ? dayStart($candidate) : null;
}
/** ---- Daten laden wie bisher (Checkout-Quelle) ---- */
if (UseDeliveredForCheckOutCompleted) {
// isco=False -> Checkout-Ansicht offen
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
} else {
// isci=False -> Alternative Logik (wie in deiner Datei)
$result = $Epi->requestEpiApi('/v1/packingnote/open?isci=False&cl=' . Epirent_Mandant);
}
$data_output = json_decode($result)->payload ?? [];
/** ---- Zähler vorbereiten ---- */
$today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
$limit = null; // optionales Fenster wie in deiner Liste
if (CheckOut_FutureDays != -1) {
$limit = $today->modify('+' . (int)CheckOut_FutureDays . ' day');
}
$cntOverdue = 0;
$cntToday = 0;
$cntFuture = 0;
/** ---- Iteration identisch zur bisherigen Logik ---- */
foreach ($data_output as $packingjob) {
// Details nur holen, wenn nötig & wie gehabt
$PackingNoteDetail = null;
if ($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted) {
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0] ?? null;
}
// Aufnahmebedingung exakt wie bei dir:
if (
$packingjob->is_archived != true
&& (
!UseDeliveredForCheckOutCompleted
|| ($PackingNoteDetail && ($PackingNoteDetail->date_delivered !== "0000-00-00" || (int)$PackingNoteDetail->time_delivered !== 0))
)
&& ($PackingNoteDetail && (int)$PackingNoteDetail->is_all_out !== 0)
) {
// OrderDetails (für Vorbereitungszeit)
$result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
$orderdetail_output = (json_decode($result)->payload[0] ?? null);
// PackingNoteDetail nachladen, falls oben noch nicht geholt
if (!UseDeliveredForCheckOutCompleted) {
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0] ?? null;
if (!$PackingNoteDetail) continue;
}
// Vorbereitungs-Zeitdetail ermitteln
$VorbereitungsTimeDetail = null;
if ($orderdetail_output && !empty($orderdetail_output->order_schedule)) {
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
if ($scheduledetail->name == Vorbereitungs_Zeitvariable) {
$VorbereitungsTimeDetail = $scheduledetail;
break;
}
}
}
// Markierungsdatum wie in der Tabelle
$markDate = resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, (int)CheckOutRowMarkSource);
if (!$markDate) continue;
// Optionales Zukunftsfenster berücksichtigen (wie bisher)
if (CheckOut_FutureDays != -1 && $limit && $markDate > $limit) {
// außerhalb des Fensters ignorieren
continue;
}
// Zählen
if ($markDate < $today) {
$cntOverdue++;
} elseif ($markDate == $today) {
$cntToday++;
} else { // $markDate > $today
$cntFuture++;
}
}
}
// Zeitstempel für die Karten
$ts = (new DateTimeImmutable('now', new DateTimeZone(APP_TZ)))->format('H:i:s');
/** ---- Ausgabe: 3 Bootstrap-Karten ---- */
?>
<h3 class="mb-3">
<span class="badge badge-secondary d-block w-100 py-2 text-center">Checkout</span>
</h3>
<div class="row">
<div class="col-xl-4 col-md-6">
<div class="card bg-danger text-white mb-4">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<div class="small">Überfällige CheckOuts</div>
<div class="h2 mb-0"><?= (int)$cntOverdue ?></div>
</div>
<i class="fa-solid fa-triangle-exclamation fa-2x" style="opacity:.6"></i>
</div>
<div class="card-footer d-flex justify-content-between">
<span class="small text-white-50">Stand <?= htmlspecialchars($ts) ?></span>
<span class="small">bis <?= htmlspecialchars($today->format('d.m.Y')) ?></span>
</div>
</div>
</div>
<div class="col-xl-4 col-md-6">
<div class="card bg-warning text-dark mb-4">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<div class="small">Heutige CheckOuts</div>
<div class="h2 mb-0"><?= (int)$cntToday ?></div>
</div>
<i class="fa-solid fa-dolly fa-2x" style="opacity:.6"></i>
</div>
<div class="card-footer d-flex justify-content-between">
<span class="small text-muted">Stand <?= htmlspecialchars($ts) ?></span>
<span class="small"><?= htmlspecialchars($today->format('d.m.Y')) ?></span>
</div>
</div>
</div>
<div class="col-xl-4 col-md-6">
<div class="card bg-success text-white mb-4">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<div class="small">Zukünftige CheckOuts<?= (CheckOut_FutureDays != -1 ? ' (≤ '.$limit->format('d.m.Y').')' : '') ?></div>
<div class="h2 mb-0"><?= (int)$cntFuture ?></div>
</div>
<i class="fa-solid fa-forward-step fa-2x" style="opacity:.6"></i>
</div>
<div class="card-footer d-flex justify-content-between">
<span class="small text-white-50">Stand <?= htmlspecialchars($ts) ?></span>
<span class="small">
<?php if (CheckOut_FutureDays == -1): ?>
unbegrenzt
<?php else: ?>
bis <?= htmlspecialchars($limit->format('d.m.Y')) ?>
<?php endif; ?>
</span>
</div>
</div>
</div>
</div>

View File

@@ -71,6 +71,7 @@ function rowClassForDate(?DateTimeImmutable $markDate, ?DateTimeImmutable $today
if (UseDeliveredForCheckOutCompleted) {
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
} else {
$result = $Epi->requestEpiApi('/v1/packingnote/open?isci=False&cl=' . Epirent_Mandant);
}
@@ -107,15 +108,17 @@ foreach ($data_output as $packingjob) {
if ($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted) {
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
}
if (
$packingjob->is_archived != true
if (
($packingjob->is_archived ?? false) != true
&& (
!UseDeliveredForCheckOutCompleted
|| ($PackingNoteDetail->date_delivered !== "0000-00-00" || (int)$PackingNoteDetail->time_delivered !== 0)
((int)($PackingNoteDetail->is_all_out ?? 0)) !== 0
|| (($PackingNoteDetail->date_delivered ?? "0000-00-00") === "0000-00-00"
|| (int)($PackingNoteDetail->time_delivered ?? 0) === 0)
)
&& (int)$PackingNoteDetail->is_all_out !== 0
) {
//get OrderDetails
$result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);