2 Commits

Author SHA1 Message Date
91c0a2d9d9 - Endzeiten bei CheckOut und Startzeiten bei CheckIn komplett entfernt
- Zeiten einzeln Ein- und Ausblendbar gemacht
- Quelle für Zeilenfarbe einstellbar gemacht
- Zeiten werden jetzt bei erreichen bzw überschreiten immer markiert (nur die Zeit)
- Nicht benötigte Spalten werden automatisch ausgeblendet
- Config Datei Ausgedünnt (Bitte komplette Epirent-Settings (nicht Login) Sektion ersetzen
2025-10-10 09:36:11 +02:00
0ff0c0d55e Endlos Scrollen der Listen implementiert (falls notwendig) 2025-10-09 20:42:19 +02:00
5 changed files with 886 additions and 529 deletions

View File

@@ -22,53 +22,181 @@ $Epi = new Epirent();
<link href="css/sticky-footer.css" rel="stylesheet"> <link href="css/sticky-footer.css" rel="stylesheet">
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<script type="text/javascript">
// Dynamische Höhe: Wrapper exakt bis Viewport-Unterkante <script type="text/javascript">
// === Höhe der Scroll-Container bis Viewport-Ende anpassen ===
function sizeScrollContainers() { function sizeScrollContainers() {
$('.tableFixHead').each(function () { $('.tableFixHead').each(function () {
const rect = this.getBoundingClientRect(); const rect = this.getBoundingClientRect();
const bottomMargin = 16; // kleiner Abstand zum Rand const bottomMargin = 16;
const available = window.innerHeight - rect.top - bottomMargin; const available = window.innerHeight - rect.top - bottomMargin;
this.style.maxHeight = (available > 120 ? available : 120) + 'px'; this.style.maxHeight = (available > 120 ? available : 120) + 'px';
}); });
} }
// Lädt tbody via AJAX und erhält die Scrollposition relativ zum unteren Rand // === Scroll-/Interaktionsstatus je Scroller ===
function smartLoad($scroller, $target, url, intervalMs) { const scrollState = new WeakMap(); // { userActive:boolean, autoTimer:number|null, loopHeight:number }
const scroller = $scroller.get(0);
// Abstand vom unteren Rand merken (wichtiger als absolute scrollTop) function ensureState(el) {
const fromBottomBefore = scroller.scrollHeight - scroller.clientHeight - scroller.scrollTop; if (!scrollState.get(el)) scrollState.set(el, { userActive: false, autoTimer: null, loopHeight: 0 });
return scrollState.get(el);
}
function attachScrollGuards($scroller) {
const el = $scroller.get(0);
if (!el || el.__guardsBound) return;
const state = ensureState(el);
const markActive = () => {
state.userActive = true;
clearTimeout(state._quietT);
state._quietT = setTimeout(() => { state.userActive = false; }, 800);
stopAutoScroll($scroller);
};
$scroller.on('wheel touchstart touchmove keydown mousedown mouseenter', markActive);
$scroller.on('mouseleave', () => setTimeout(() => maybeStartAutoScroll($scroller), 600));
el.__guardsBound = true;
}
// === Loop aufbauen: nahtloses Doppel nur bei Bedarf ===
// Klont den ersten <tbody> als __loopClone ans Tabellenende, wenn Inhalt > Sichtbereich.
function buildSeamlessLoop($table, $scroller) {
$table.find('tbody.__loopClone').remove();
const $main = $table.find('tbody').first();
const scEl = $scroller.get(0);
const st = ensureState(scEl);
if ($main.length === 0 || $main.children().length === 0) {
st.loopHeight = 0;
return;
}
const mainH = $main.get(0).offsetHeight;
const needScroll = mainH > scEl.clientHeight + 1;
if (!needScroll) {
st.loopHeight = 0;
return;
}
const $clone = $main.clone(false, false).addClass('__loopClone').attr('aria-hidden', 'true');
$table.append($clone);
st.loopHeight = mainH; // Höhe des Original-Inhalts als Loop-Länge
}
// === Auto-Scroll (nahtlos) ===
function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
const el = $scroller.get(0);
const st = ensureState(el);
if (st.autoTimer) return; // schon aktiv
if (st.loopHeight <= 0) return; // kein Overflow => nicht scrollen
const tick = () => {
if (st.userActive) { stopAutoScroll($scroller); return; }
el.scrollTop += speedPx;
// Nahtlos: sobald wir das Ende des Original-Blocks überschreiten, ziehen wir loopHeight ab
if (el.scrollTop >= st.loopHeight) {
el.scrollTop -= st.loopHeight;
}
};
st.autoTimer = setInterval(tick, stepMs);
}
function stopAutoScroll($scroller) {
const el = $scroller.get(0);
const st = ensureState(el);
if (st.autoTimer) {
clearInterval(st.autoTimer);
st.autoTimer = null;
}
}
function maybeStartAutoScroll($scroller) {
const el = $scroller.get(0);
const st = ensureState(el);
if (st.userActive) return;
if (st.loopHeight > 0) startAutoScroll($scroller, 1, 80);
else stopAutoScroll($scroller);
}
// === AJAX-Reload: relative Position innerhalb der Loop erhalten ===
// Wir merken die Position modulo loopHeight; nach dem Reload setzen wir proportional um.
function smartLoad($scroller, $table, $target, url, intervalMs) {
const scEl = $scroller.get(0);
const st = ensureState(scEl);
const oldLoop = st.loopHeight > 0 ? st.loopHeight : Math.max(1, scEl.scrollHeight - scEl.clientHeight);
const posInLoop = st.loopHeight > 0 ? (scEl.scrollTop % oldLoop) : scEl.scrollTop;
const posRatio = Math.min(1, posInLoop / oldLoop);
$target.load(url, function () { $target.load(url, function () {
// Nach dem Ersetzen: dieselbe Distanz zum unteren Rand wiederherstellen // Loop neu bewerten/aufbauen
const newScrollTop = scroller.scrollHeight - scroller.clientHeight - fromBottomBefore; buildSeamlessLoop($table, $scroller);
// Begrenzen, falls weniger Inhalt
scroller.scrollTop = Math.max(0, newScrollTop);
// Nächstes Update planen if (st.loopHeight > 0) {
setTimeout(function () { // Neue relative Position setzen (proportional)
smartLoad($scroller, $target, url, intervalMs); const newPos = Math.floor(posRatio * st.loopHeight);
}, intervalMs); if (Math.abs(scEl.scrollTop - newPos) > 1) {
// rAF für flüssiges Setzen außerhalb des load()-Layouts
requestAnimationFrame(() => { scEl.scrollTop = newPos; });
}
// Auto-Scroll (wieder) starten
stopAutoScroll($scroller);
startAutoScroll($scroller, 1, 80);
} else {
// kein Overflow -> ganz oben und Auto-Scroll aus
if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
stopAutoScroll($scroller);
}
setTimeout(() => smartLoad($scroller, $table, $target, url, intervalMs), intervalMs);
}); });
} }
// === Initialisierung ===
$(document).ready(function () { $(document).ready(function () {
sizeScrollContainers(); sizeScrollContainers();
$(window).on('resize', sizeScrollContainers); $(window).on('resize', function () {
sizeScrollContainers();
// Initial laden + Auto-Refresh, jeweils eigener Scroller // Nach Layoutwechsel neu entscheiden
smartLoad($('#checkout-scroll'), $('#getCheckOutTableHolder'), 'sources/getCheckOutTable.php', 5000); ['#checkout', '#checkin', '#aufgaben'].forEach(prefix => {
smartLoad($('#checkin-scroll'), $('#getCheckInTableHolder'), 'sources/getCheckInTable.php', 5000); const $scroller = $(`${prefix}-scroll`);
smartLoad($('#aufgaben-scroll'), $('#AufgabenTableHolder'), 'sources/getAufgabenTable.php', 30000); const $table = $(`${prefix}-table`);
buildSeamlessLoop($table, $scroller);
maybeStartAutoScroll($scroller);
}); });
</script> });
// Guards pro Scroller
attachScrollGuards($('#checkout-scroll'));
attachScrollGuards($('#checkin-scroll'));
attachScrollGuards($('#aufgaben-scroll'));
// Erstladen je Tabelle: laden -> Loop bauen -> ggf. Auto-Scroll -> zyklisch refreshen
function initOne(scrollerSel, tableSel, tbodySel, url, ms) {
const $scroller = $(scrollerSel);
const $table = $(tableSel);
const $target = $(tbodySel);
$target.load(url, function () {
buildSeamlessLoop($table, $scroller);
maybeStartAutoScroll($scroller);
setTimeout(() => smartLoad($scroller, $table, $target, url, ms), ms);
});
}
initOne('#checkout-scroll', '#checkout-table', '#getCheckOutTableHolder', 'sources/getCheckOutTable.php', 5000);
initOne('#checkin-scroll', '#checkin-table', '#getCheckInTableHolder', 'sources/getCheckInTable.php', 5000);
initOne('#aufgaben-scroll', '#aufgaben-table', '#AufgabenTableHolder', 'sources/getAufgabenTable.php', 30000);
});
</script>
<style> <style>
/* Scroll-Wrapper je Tabelle */ /* Scroll-Wrapper je Tabelle */
@@ -98,7 +226,7 @@ $Epi = new Epirent();
} }
</style> </style>
</head> </head>
<body style="background-color: black;"> <body style="margin-bottom:0px; background-color: black;">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@@ -111,25 +239,31 @@ $Epi = new Epirent();
<th scope="col">#</th> <th scope="col">#</th>
<th scope="col">Kunde</th> <th scope="col">Kunde</th>
<th scope="col">Event</th> <th scope="col">Event</th>
<th scope="col">
<?php <?php
if (!HideDispoTimes) { if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
echo '<th scope="col">';
}
if(ShowCheckoutTimeOnCheckout){
echo "Dispo-Start"; echo "Dispo-Start";
} }
if (UsePackingNoteDateForCheckout) { if(ShowVorbereitungTimeOnCheckout){
echo "<br><i>Packen-Start</i></th>"; if(ShowCheckoutTimeOnCheckout){echo "<br>";}
} else { echo "VB-Start";
echo "<br><i>VB-Start</i></th>";
} }
if (!HideCheckInTimeOnCheckout) { if(ShowPackagingTimeOnCheckout){
if(!HideDispoTimes){ if(ShowCheckoutTimeOnCheckout||ShowVorbereitungTimeOnCheckout){echo "<br>";}
echo "<th scope='col'>Dispo-Ende<br><i>VB-Ende</i></th>"; echo "Packen";
}else{ }
echo "<th scope='col'><i>VB-Ende</i></th>"; if(ShowDeliveryTimeOnCheckout){
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
echo "Liefern";
}
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
echo '</th>';
} }
}
?> ?>
<th scope="col">Status</th> <th scope="col">Status</th>
<?php <?php
@@ -152,24 +286,31 @@ $Epi = new Epirent();
<tr> <tr>
<th scope="col">#</th> <th scope="col">#</th>
<th scope="col">Kunde</th> <th scope="col">Kunde</th>
<th scope="col">Event</th> <th scope="col">Event</th> <?php
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin ||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
echo '<th scope="col">';
}
<?php if(ShowCheckInTimeOnCheckin){
if (!HideCheckOutTimeOnCheckin) { echo "Dispo-Ende";
echo "<th scope='col'>Dispo-Start<br><i>RP-Start</i></th>"; }
} if(ShowNachbereitungTimeOnCheckin){
if(ShowCheckInTimeOnCheckin){echo "<br>";}
echo "Nachbereitung";
}
if(ShowRePackagingTimeOnCheckin){
if(ShowCheckInTimeOnCheckin||ShowNachbereitungTimeOnCheckin){echo "<br>";}
echo "Zurückpacken";
}
if(ShowReDeliveryTimeOnCheckin){
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
echo "Rückliefern";
}
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
echo '</th>';
}
if(!HideDispoTimes){ ?>
echo "<th scope='col'>Dispo-Ende";
}else{
echo "<th scope='col'>";
}
if (UsePackingNoteDateForCheckin) {
echo "<br><i>Rücklieferung</i></th>";
} else {
echo "<br><i>RP-Ende</i></th>";
}
?>
@@ -218,7 +359,6 @@ if (UsePackingNoteDateForCheckin) {
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
<!-- Include all compiled plugins (below), or include individual files as needed --> <!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script> <script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
</body> </body>

View File

@@ -1,7 +1,4 @@
<?php <?php
error_reporting(E_ALL);
require('config.php'); require('config.php');
require('EpiApi.php'); require('EpiApi.php');
require('vendor/autoload.php'); require('vendor/autoload.php');
@@ -16,80 +13,252 @@ $Epi = new Epirent();
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Packmonitor</title> <title>Packmonitor</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css" > <link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
<script src="scripts/jquery-3.5.1.min.js"></script> <script src="scripts/jquery-3.5.1.min.js"></script>
<link href="css/sticky-footer.css" rel="stylesheet"> <link href="css/sticky-footer.css" rel="stylesheet">
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<script type="text/javascript"> <script type="text/javascript">
// === Höhe der Scroll-Container bis Viewport-Ende anpassen ===
function sizeScrollContainers() {
$('.tableFixHead').each(function () {
const rect = this.getBoundingClientRect();
const bottomMargin = 16;
const available = window.innerHeight - rect.top - bottomMargin;
this.style.maxHeight = (available > 120 ? available : 120) + 'px';
});
}
// === Scroll-/Interaktionsstatus je Scroller ===
const scrollState = new WeakMap(); // { userActive:boolean, autoTimer:number|null, loopHeight:number }
function ensureState(el) {
if (!scrollState.get(el)) scrollState.set(el, { userActive: false, autoTimer: null, loopHeight: 0 });
return scrollState.get(el);
}
function attachScrollGuards($scroller) {
const el = $scroller.get(0);
if (!el || el.__guardsBound) return;
const state = ensureState(el);
const markActive = () => {
state.userActive = true;
clearTimeout(state._quietT);
state._quietT = setTimeout(() => { state.userActive = false; }, 800);
stopAutoScroll($scroller);
};
$scroller.on('wheel touchstart touchmove keydown mousedown mouseenter', markActive);
$scroller.on('mouseleave', () => setTimeout(() => maybeStartAutoScroll($scroller), 600));
el.__guardsBound = true;
}
// === Loop aufbauen: nahtloses Doppel nur bei Bedarf ===
// Klont den ersten <tbody> als __loopClone ans Tabellenende, wenn Inhalt > Sichtbereich.
function buildSeamlessLoop($table, $scroller) {
$table.find('tbody.__loopClone').remove();
const $main = $table.find('tbody').first();
const scEl = $scroller.get(0);
const st = ensureState(scEl);
if ($main.length === 0 || $main.children().length === 0) {
st.loopHeight = 0;
return;
}
const mainH = $main.get(0).offsetHeight;
const needScroll = mainH > scEl.clientHeight + 1;
if (!needScroll) {
st.loopHeight = 0;
return;
}
const $clone = $main.clone(false, false).addClass('__loopClone').attr('aria-hidden', 'true');
$table.append($clone);
st.loopHeight = mainH; // Höhe des Original-Inhalts als Loop-Länge
}
// === Auto-Scroll (nahtlos) ===
function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
const el = $scroller.get(0);
const st = ensureState(el);
if (st.autoTimer) return; // schon aktiv
if (st.loopHeight <= 0) return; // kein Overflow => nicht scrollen
const tick = () => {
if (st.userActive) { stopAutoScroll($scroller); return; }
el.scrollTop += speedPx;
// Nahtlos: sobald wir das Ende des Original-Blocks überschreiten, ziehen wir loopHeight ab
if (el.scrollTop >= st.loopHeight) {
el.scrollTop -= st.loopHeight;
}
};
st.autoTimer = setInterval(tick, stepMs);
}
function stopAutoScroll($scroller) {
const el = $scroller.get(0);
const st = ensureState(el);
if (st.autoTimer) {
clearInterval(st.autoTimer);
st.autoTimer = null;
}
}
function maybeStartAutoScroll($scroller) {
const el = $scroller.get(0);
const st = ensureState(el);
if (st.userActive) return;
if (st.loopHeight > 0) startAutoScroll($scroller, 1, 40);
else stopAutoScroll($scroller);
}
// === AJAX-Reload: relative Position innerhalb der Loop erhalten ===
// Wir merken die Position modulo loopHeight; nach dem Reload setzen wir proportional um.
function smartLoad($scroller, $table, $target, url, intervalMs) {
const scEl = $scroller.get(0);
const st = ensureState(scEl);
const oldLoop = st.loopHeight > 0 ? st.loopHeight : Math.max(1, scEl.scrollHeight - scEl.clientHeight);
const posInLoop = st.loopHeight > 0 ? (scEl.scrollTop % oldLoop) : scEl.scrollTop;
const posRatio = Math.min(1, posInLoop / oldLoop);
$target.load(url, function () {
// Loop neu bewerten/aufbauen
buildSeamlessLoop($table, $scroller);
if (st.loopHeight > 0) {
// Neue relative Position setzen (proportional)
const newPos = Math.floor(posRatio * st.loopHeight);
if (Math.abs(scEl.scrollTop - newPos) > 1) {
// rAF für flüssiges Setzen außerhalb des load()-Layouts
requestAnimationFrame(() => { scEl.scrollTop = newPos; });
}
// Auto-Scroll (wieder) starten
stopAutoScroll($scroller);
startAutoScroll($scroller, 1, 40);
} else {
// kein Overflow -> ganz oben und Auto-Scroll aus
if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
stopAutoScroll($scroller);
}
setTimeout(() => smartLoad($scroller, $table, $target, url, intervalMs), intervalMs);
});
}
// === Initialisierung ===
$(document).ready(function () { $(document).ready(function () {
refreshCheckOutTable(); sizeScrollContainers();
refreshCheckInTable(); $(window).on('resize', function () {
sizeScrollContainers();
// Nach Layoutwechsel neu entscheiden (nur 2 Tabellen)
['#checkout', '#checkin'].forEach(prefix => {
const $scroller = $(`${prefix}-scroll`);
const $table = $(`${prefix}-table`);
buildSeamlessLoop($table, $scroller);
maybeStartAutoScroll($scroller);
});
}); });
function refreshCheckOutTable() { // Guards pro Scroller (nur 2)
$('#getCheckOutTableHolder').load('sources/getCheckOutTable.php', function () { attachScrollGuards($('#checkout-scroll'));
setTimeout(refreshCheckOutTable, 5000); attachScrollGuards($('#checkin-scroll'));
});
} // Erstladen je Tabelle: laden -> Loop bauen -> ggf. Auto-Scroll -> zyklisch refreshen
function refreshCheckInTable() { function initOne(scrollerSel, tableSel, tbodySel, url, ms) {
$('#getCheckInTableHolder').load('sources/getCheckInTable.php', function () { const $scroller = $(scrollerSel);
setTimeout(refreshCheckInTable, 5000); const $table = $(tableSel);
const $target = $(tbodySel);
$target.load(url, function () {
buildSeamlessLoop($table, $scroller);
maybeStartAutoScroll($scroller);
setTimeout(() => smartLoad($scroller, $table, $target, url, ms), ms);
}); });
} }
initOne('#checkout-scroll', '#checkout-table', '#getCheckOutTableHolder', 'sources/getCheckOutTable.php', 5000);
initOne('#checkin-scroll', '#checkin-table', '#getCheckInTableHolder', 'sources/getCheckInTable.php', 5000);
});
</script> </script>
<style>
/* Scroll-Wrapper je Tabelle */
.tableFixHead {
overflow-y: auto;
/* Höhe wird per JS dynamisch gesetzt, damit genau bis zum Viewport-Ende gescrollt wird */
max-height: 60vh; /* Fallback */
border: 1px solid rgba(255,255,255,.1);
border-radius: .25rem;
}
/* Sticky Header */
.tableFixHead thead th {
position: sticky;
top: 0;
z-index: 2; /* über Body-Zellen */
}
/* Saubere Hintergrundfarbe für sticky Header (Bootstrap .table-dark) */
.table-dark thead th {
background-color: #212529; /* gleiche Farbe wie .table-dark header */
}
/* Optional: dünne Trennlinien */
.table-dark tbody tr + tr td {
border-top: 1px solid rgba(255,255,255,.08);
}
body { background-color: black; margin-bottom: 0; }
</style>
</head> </head>
<body style="background-color: black;"> <body>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-lg"> <!-- Check-Out -->
<h2 class="text-light">Check-Out <div class="col-sm">
<?php <h2 class="text-light">Check-Out</h2>
if (CheckOut_FutureDays != -1) { <div class="tableFixHead" id="checkout-scroll">
echo "in den nächsten " . CheckOut_FutureDays . " Tagen"; <table class="table table-dark mb-0" id="checkout-table">
}
?></h2>
<table class="table table-dark">
<thead> <thead>
<tr> <tr>
<th scope="col">#</th> <th scope="col">#</th>
<th scope="col">Kunde</th> <th scope="col">Kunde</th>
<th scope="col">Event</th> <th scope="col">Event</th>
<th scope="col">
<?php <?php
if (!HideDispoTimes) { if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
echo '<th scope="col">';
}
if(ShowCheckoutTimeOnCheckout){
echo "Dispo-Start"; echo "Dispo-Start";
} }
if (UsePackingNoteDateForCheckout) { if(ShowVorbereitungTimeOnCheckout){
echo "<br><i>Packen-Start</i></th>"; if(ShowCheckoutTimeOnCheckout){echo "<br>";}
} else { echo "VB-Start";
echo "<br><i>VB-Start</i></th>";
} }
if (!HideCheckInTimeOnCheckout) { if(ShowPackagingTimeOnCheckout){
if(!HideDispoTimes){ if(ShowCheckoutTimeOnCheckout||ShowVorbereitungTimeOnCheckout){echo "<br>";}
echo "<th scope='col'>Dispo-Ende<br><i>VB-Ende</i></th>"; echo "Packen";
}else{ }
echo "<th scope='col'><i>VB-Ende</i></th>"; if(ShowDeliveryTimeOnCheckout){
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
echo "Liefern";
}
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
echo '</th>';
} }
}
?> ?>
<th scope="col">Status</th> <th scope="col">Status</th>
<?php <?php
@@ -99,48 +268,46 @@ if (CheckOut_FutureDays != -1) {
?> ?>
</tr> </tr>
</thead> </thead>
<tbody id="getCheckOutTableHolder"> <tbody id="getCheckOutTableHolder"></tbody>
</tbody>
</table> </table>
</div> </div>
<div class="col-lg"> </div>
<h2 class="text-light">Check-In
<?php <!-- Check-In -->
if (CheckIn_FutureDays != -1) { <div class="col-sm">
echo "in den nächsten " . CheckIn_FutureDays . " Tagen"; <h2 class="text-light">Check-In</h2>
} <div class="tableFixHead" id="checkin-scroll">
?></h2> <table class="table table-dark mb-0" id="checkin-table">
<table class="table table-dark">
<thead> <thead>
<tr> <tr>
<th scope="col">#</th> <th scope="col">#</th>
<th scope="col">Kunde</th> <th scope="col">Kunde</th>
<th scope="col">Event</th> <th scope="col">Event</th>
<?php
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin ||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
echo '<th scope="col">';
}
<?php if(ShowCheckInTimeOnCheckin){
if (!HideCheckOutTimeOnCheckin) { echo "Dispo-Ende";
echo "<th scope='col'>Dispo-Start<br><i>RP-Start</i></th>"; }
} if(ShowNachbereitungTimeOnCheckin){
if(ShowCheckInTimeOnCheckin){echo "<br>";}
if(!HideDispoTimes){ echo "Nachbereitung";
echo "<th scope='col'>Dispo-Ende"; }
}else{ if(ShowRePackagingTimeOnCheckin){
echo "<th scope='col'>"; if(ShowCheckInTimeOnCheckin||ShowNachbereitungTimeOnCheckin){echo "<br>";}
} echo "Zurückpacken";
if (UsePackingNoteDateForCheckin) { }
echo "<br><i>Rücklieferung</i></th>"; if(ShowReDeliveryTimeOnCheckin){
} else { if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
echo "<br><i>RP-Ende</i></th>"; echo "Rückliefern";
} }
?> if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
echo '</th>';
}
?>
<th scope="col">Status</th> <th scope="col">Status</th>
<?php <?php
if(ShowShippingIcons){ if(ShowShippingIcons){
@@ -149,43 +316,23 @@ if (UsePackingNoteDateForCheckin) {
?> ?>
</tr> </tr>
</thead> </thead>
<tbody id="getCheckInTableHolder"> <tbody id="getCheckInTableHolder"></tbody>
</tbody>
</table> </table>
</div> </div>
</div> </div>
</div>
</header>
</div> </div>
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" crossorigin="anonymous"></script>
</body>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
</body>
</html> </html>
<?php <?php
function getTimeFromSeconds(string $timestring) { function getTimeFromSeconds(string $timestring) {
$hours = floor($timestring / 3600); $hours = floor($timestring / 3600);
$mins = floor($timestring / 60 % 60); $mins = floor($timestring / 60 % 60);
$secs = floor($timestring % 60); return sprintf('%02d:%02d', $hours, $mins);
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
return $timeFormat;
} }
?> ?>

View File

@@ -20,18 +20,38 @@ define('Enable_QR_Code_CrewBrainAufgaben', true);
define('Enable_QR_Code_CheckOut', false); //Zeigt statt der Packscheinnummer einen Scanbaren QR Code für den CheckOut an define('Enable_QR_Code_CheckOut', false); //Zeigt statt der Packscheinnummer einen Scanbaren QR Code für den CheckOut an
define('Enable_QR_Code_CheckIn', false); //Zeigt statt der Packscheinnummer einen Scanbaren QR Code für den CheckIn an define('Enable_QR_Code_CheckIn', false); //Zeigt statt der Packscheinnummer einen Scanbaren QR Code für den CheckIn an
define('Vorbereitungs_Zeitvariable', 'Packen'); //Name des zu verwendenden Zeitabschnitts, der Zusätzlich zur DispoZeit beim Check Out Angezeigt werden soll define('Vorbereitungs_Zeitvariable', 'Packen'); //Name des zu verwendenden Zeitabschnitts, der Zusätzlich zur DispoZeit beim Check Out Angezeigt werden soll
define('Rueckpacken_Zeitvariable', 'Rückpacken'); //Name des zu verwendenden Zeitabschnitts, der Zusätzlich zur DispoZeit beim Check In Angezeigt werden soll define('Nachbereitung_Zeitvariable', 'Rückpacken'); //Name des zu verwendenden Zeitabschnitts, der Zusätzlich zur DispoZeit beim Check In Angezeigt werden soll
define('Rückpacken_Zeitvariable', 'Rückpacken');
define('UsePackingNoteDateForCheckout', true); // Nutzt statt den Zeitabscnitten aus dem Auftrag die Informationen aus dem Packschein für den Checkout. Wenn die UseDispo Variablen false sind, werden diese Variablen für das Rowmarking genutzt falls "true". /** -------------------- Row-Marking: Konfig Zusände --------------------
define('UsePackingNoteDateForCheckin', true); // Nutzt statt den Zeitabscnitten aus dem Auftrag die Informationen aus dem Packschein für den Checkout. Wenn die UseDispo Variablen false sind, werden diese Variablen für das Rowmarking genutzt falls "true". * 1 = $packingjob->date_start (Dispo Start)
* 2 = $VorbereitungsTimeDetail->date_start (Vorbereitung Start)
* 3 = $PackingNoteDetail->date_packing (Packen Zeit)
* 4 = $PackingNoteDetail->date_delivery (Delivery Zeit)
*/
define('CheckOutRowMarkSource', 4);
/** -------------------- Row-Marking: Konfig Zusände --------------------
* 1 = $packingjob->date_end (Dispo Ende)
* 2 = $NachbereitungssTimeDetail->date_start (Nachbereitung Start)
* 3 = $RePackagingTimeDetail->date_start (Rückpacken Zeit AUS AUFTRAG)
* 4 = $PackingNoteDetail->date_redelivery (ReDelivery Zeit)
*/
define('CheckInRowMarkSource', 4);
define('CheckOut_UseDispoStartForRowMarking', false); //else: Use Same Variable as "Vorbereitung Zeitvariable" | Konfiguration, welche Zeit für die Zeilenmarkierung beim CheckOut Verwendet werden soll
define('CheckIn_UseDispoEndForRowMarking', false); //else: Use Same Variable as "Rueckpacken Zeitvariable" | Konfiguration, welche Zeit für die Zeilenmarkierung beim Check In Verwendet werden soll define('CheckIn_UseDispoEndForRowMarking', false); //else: Use Same Variable as "Rueckpacken Zeitvariable" | Konfiguration, welche Zeit für die Zeilenmarkierung beim Check In Verwendet werden soll
define('HideCheckInTimeOnCheckout', true); //Versteckt die CheckIn Zeit im Checkout define('ShowCheckoutTimeOnCheckout', true);
define('HideCheckOutTimeOnCheckin', true); //Versteckt die CheckOut Zeit im CheckIn define('ShowVorbereitungTimeOnCheckout', true);
define('ShowPackagingTimeOnCheckout', true);
define('ShowDeliveryTimeOnCheckout', true);
define('ShowTimesOnCheckout', true);
define('HideDispoTimes', true); //Versteckt Dispo-Zeiten Komplett aus den Listen define('ShowCheckInTimeOnCheckin', true);
define('ShowNachbereitungTimeOnCheckin', true);
define('ShowRePackagingTimeOnCheckin', true);
define('ShowReDeliveryTimeOnCheckin', true);
define('ShowTimesOnCheckin', true);
define('ShowShippingIcons', true); //Zeigt Lieferung / Rücklieferungs Icons an define('ShowShippingIcons', true); //Zeigt Lieferung / Rücklieferungs Icons an
define('KurierContainsText', 'Kurier'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als KURIER erkannt wird. define('KurierContainsText', 'Kurier'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als KURIER erkannt wird.

View File

@@ -10,215 +10,250 @@ use chillerlan\QRCode\{
require('../vendor/autoload.php'); require('../vendor/autoload.php');
$options = new QROptions([ $options = new QROptions([
'imageBase64' => false, 'imageBase64' => false,
'qrCodeHeight' => 75, 'qrCodeHeight' => 75,
'qrCodeWidth' => 75, 'qrCodeWidth' => 75,
'version' => -1, 'version' => -1,
'quietzoneSize' => 1 'quietzoneSize' => 1
]); ]);
$Epi = new Epirent(); $Epi = new Epirent();
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant); $result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
$data_output = json_decode($result)->payload; $data_output = json_decode($result)->payload;
/** ---------- Helpers & Marking (analog Checkout) ---------- */
const APP_TZ = 'Europe/Berlin';
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):
* 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;
}
function echoMarkedTimeLine(?string $dateStr, ?int $timeSeconds, DateTimeImmutable $today, $ReturnTime): void {
if (!$dateStr) return;
$d = dt($dateStr);
if (!$d) return;
$mark = dayStart($d);
$cls = rowClassForDate($mark, $today);
echo $cls ? "<span class=\"{$cls}\">" : "";
echo date_format(new \DateTime($dateStr), 'd.m.Y');
if ($ReturnTime) {
echo " " . getTimeFromSeconds((string)$timeSeconds);
}
echo $cls ? "</span>" : "";
}
/** ---------- Sortierung nach Ende ---------- */
if (SortCheckIn == 2) { if (SortCheckIn == 2) {
// Prüfen, ob $data_output ein Array ist
if (is_array($data_output)) { if (is_array($data_output)) {
usort($data_output, function ($a, $b) { usort($data_output, function ($a, $b) {
// Konvertiere time_start von Millisekunden in Sekunden $timeEndA = $a->time_end / 1000;
$timeStartA = $a->time_end / 1000; // Zeit in Sekunden $timeEndB = $b->time_end / 1000;
$timeStartB = $b->time_end / 1000; $datetimeA = strtotime($a->date_end) + $timeEndA;
$datetimeB = strtotime($b->date_end) + $timeEndB;
// Kombiniere date_start mit time_start
$datetimeA = strtotime($a->date_end) + $timeStartA;
$datetimeB = strtotime($b->date_end) + $timeStartB;
// Vergleich für die Sortierung
return $datetimeA <=> $datetimeB; return $datetimeA <=> $datetimeB;
}); });
// Sortierte Daten ausgeben oder weiterverarbeiten
// print_r($data_output);
} else { } else {
echo "Daten konnten nicht verarbeitet werden."; echo "Daten konnten nicht verarbeitet werden.";
} }
} }
/** ---------- Tabelle ---------- */
foreach ($data_output as $packingjob) { foreach ($data_output as $packingjob) {
if ($packingjob->is_archived != true) { if ($packingjob->is_archived != true) {
//get OrderDetails // OrderDetails
$result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant); $result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
$orderdetail_output = json_decode($result)->payload[0]; $orderdetail_output = json_decode($result)->payload[0];
// get PackingNote Details // PackingNote Details
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant); $PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
$PackingNote_data_output = json_decode($PackingNoteDetailResult)->payload[0]; $PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
// Zeit-Slots aus dem Schedule
$NachbereitungsTimeDetail; $NachbereitungsTimeDetail = null;
$RePackagingTimeDetail = null;
foreach ($orderdetail_output->order_schedule as $scheduledetail) { foreach ($orderdetail_output->order_schedule as $scheduledetail) {
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
if (UsePackingNoteDateForCheckin) {
$tempTimeObject = new stdClass();
$tempTimeObject->date_end = $PackingNote_data_output->date_redelivery;
$tempTimeObject->time_end = $PackingNote_data_output->time_redelivery;
print($PackingNote_data_output->time_redelivery);
$NachbereitungsTimeDetail = $tempTimeObject;
} else {
if ($scheduledetail->name == Rueckpacken_Zeitvariable) {
$NachbereitungsTimeDetail = $scheduledetail; $NachbereitungsTimeDetail = $scheduledetail;
} }
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
$RePackagingTimeDetail = $scheduledetail;
}
} }
// --- Row-Marking bestimmen (konfigurierbar, analog Checkout) ---
$today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
$limit = null;
if (CheckIn_FutureDays != -1) {
$limit = $today->modify('+' . (int)CheckIn_FutureDays . ' day');
} }
// Override-Flag wie bei Checkout: Dispo-Ende erzwingen
$source = CheckIn_UseDispoEndForRowMarking ? 1 : (int)CheckInRowMarkSource;
//End Of get Order Details $markDate = resolveRowMarkDateCheckIn(
$packingjob,
$NachbereitungsTimeDetail,
$RePackagingTimeDetail,
$PackingNoteDetail,
$source
);
if (CheckIn_FutureDays == -1 || ($markDate && (!$limit || $markDate <= $limit))) {
$trClass = rowClassForDate($markDate, $today);
echo $trClass ? "<tr class='{$trClass}'>" : "<tr>";
// QR / Kopfspalten
if (CheckIn_UseDispoEndForRowMarking || ($NachbereitungsTimeDetail->date_start == null)) {
$date = new DateTime($packingjob->date_end);
} else {
$date = new DateTime($NachbereitungsTimeDetail->date_start);
}
$date->setTime(0, 0, 0);
$today = new DateTime();
$today->setTime(0, 0, 0);
$todayFilter = new DateTime();
$todayFilter->setTime(0, 0, 0);
if (CheckIn_FutureDays == -1 || $date <= ($todayFilter->modify('+' . CheckIn_FutureDays . ' day'))) {
//prüfe, ob entweder unbegrenzte (-1) Anzeige Aktiv ist, oder das Datum kleiner oder Gleich heute + Zukunftsspanne ist
if ($date == $today) {
echo "<tr class='text-dark bg-warning'>";
} else if ($date < $today) {
echo "<tr class=' bg-danger'>";
} else {
echo "<tr>";
}
if (Enable_QR_Code_CheckIn) { if (Enable_QR_Code_CheckIn) {
echo "<td>" . '<div style="width: 5vb;">' . (new QRCode($options))->render($packingjob->packingnote_no) . "</div></td>"; echo "<td><div style=\"width: 5vb;\">" . (new QRCode($options))->render($packingjob->packingnote_no) . "</div></td>";
} else { } else {
echo "<td>" . $packingjob->packingnote_no . "</td>"; echo "<td>" . $packingjob->packingnote_no . "</td>";
} }
echo "<td>" . $packingjob->contact->name . "</td>"; echo "<td>" . $packingjob->contact->name . "</td>";
echo "<td>" . $packingjob->event . "</td>"; echo "<td>" . $packingjob->event . "</td>";
if(!HideCheckOutTimeOnCheckin){
if ($NachbereitungsTimeDetail->date_start != null) { // Zeitspalte öffnen, wenn mindestens eine Anzeige aktiv ist
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) {
echo "<td>"; echo "<td>";
if(!HideDispoTimes){
echo "<small>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</small><br>";
} }
echo "<i>" . date_format(new \DateTime($NachbereitungsTimeDetail->date_start), 'd.m.Y') . " " . getTimeFromSeconds($NachbereitungsTimeDetail->time_start) . "</i></td>";
} else if(!HideDispoTimes){ // Dispo-Ende (Check-In Termin) — ENDE korrekt verwendet
echo "<td>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</td>"; if (ShowCheckInTimeOnCheckin && $packingjob->date_end != null) {
echoMarkedTimeLine($packingjob->date_end, (int)$packingjob->time_end, $today, ShowTimesOnCheckin);
} }
// Nachbereitung (START) — wie von dir bereits genutzt
if (ShowNachbereitungTimeOnCheckin && $NachbereitungsTimeDetail && $NachbereitungsTimeDetail->date_start != null) {
if (ShowCheckInTimeOnCheckin) echo "<br>";
echoMarkedTimeLine($NachbereitungsTimeDetail->date_start, (int)$NachbereitungsTimeDetail->time_start, $today, ShowTimesOnCheckin);
} }
if ($NachbereitungsTimeDetail->date_end != null) {
// Rückpacken (START)
if (ShowRePackagingTimeOnCheckin && $RePackagingTimeDetail && $RePackagingTimeDetail->date_start != null) {
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin) echo "<br>";
echoMarkedTimeLine($RePackagingTimeDetail->date_start, (int)$RePackagingTimeDetail->time_start, $today, ShowTimesOnCheckin);
}
// Rücklieferung (REDELIVERY) — delivery ↔ redelivery gespiegelt
if (ShowReDeliveryTimeOnCheckin && $PackingNoteDetail && $PackingNoteDetail->date_redelivery != null) {
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin) echo "<br>";
echoMarkedTimeLine($PackingNoteDetail->date_redelivery, (int)$PackingNoteDetail->time_redelivery, $today, ShowTimesOnCheckin);
}
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) {
echo "</td>";
}
// Fortschritt
echo "<td>"; echo "<td>";
if(!HideDispoTimes){ if ($packingjob->is_all_in == 0) {
echo "<small>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</small><br>";
}
echo "<i>" . date_format(new \DateTime($NachbereitungsTimeDetail->date_end), 'd.m.Y') . " " . getTimeFromSeconds($NachbereitungsTimeDetail->time_end) . "</i></td>";
} else if(!HideDispoTimes){
echo "<td>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</td>";
}
echo "<td>";
if ($packingjob->is_all_in ==0) {
echo "<span class='badge badge-success'>"; echo "<span class='badge badge-success'>";
} else { } else {
echo '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: ' .
echo '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: '.(( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) / ($packingjob->pieces_sum_total - $packingjob ->is_all_out)) *100 .'%" aria-valuenow="' .( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) . '" aria-valuemin="0" aria-valuemax="' . ($packingjob->pieces_sum_total - $packingjob ->is_all_out). '"></div></div>'; (( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) / ($packingjob->pieces_sum_total - $packingjob->is_all_out)) * 100 .
'%" aria-valuenow="' . ( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) .
'" aria-valuemin="0" aria-valuemax="' . ($packingjob->pieces_sum_total - $packingjob->is_all_out) . '"></div></div>';
echo "<span class='badge badge-info'>"; echo "<span class='badge badge-info'>";
} }
echo ( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) . "/" . ($packingjob->pieces_sum_total - $packingjob->is_all_out) . " (" . $packingjob->pieces_sum_total . ")";
echo ( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) . "/" . ($packingjob->pieces_sum_total - $packingjob ->is_all_out) . " (" . $packingjob->pieces_sum_total.")";
echo "</span></td>"; echo "</span></td>";
if(ShowShippingIcons){ // Shipping-Icons (inbound)
if (ShowShippingIcons) {
if(UseShippingStatus){ if (UseShippingStatus) {
if(($PackingNote_data_output->status==ShippingInOrganizedStatus)||($PackingNote_data_output->status==ShippingOrganizedStatus)){ if (($PackingNoteDetail->status == ShippingInOrganizedStatus) || ($PackingNoteDetail->status == ShippingOrganizedStatus)) {
echo "<td style='color:#66FF00; text-align:center;'>"; echo "<td style='color:#66FF00; text-align:center;'>";
}else{ } else {
echo "<td style='text-align:center;'>"; echo "<td style='text-align:center;'>";
} }
if($PackingNote_data_output->is_self_redeliver){ if ($PackingNoteDetail->is_self_redeliver) {
echo '<i class="fa-solid fa-person-walking fa-lg"></i>'; echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
} else{ } else {
if(preg_match('/'.KurierContainsText.'/i',$PackingNote_data_output->shipping_in)){ if (preg_match('/' . KurierContainsText . '/i', $PackingNoteDetail->shipping_in)) {
echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>'; echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>';
} }
if(preg_match('/'.SpeditionContainsText.'/i',$PackingNote_data_output->shipping_in)){ if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_in)) {
echo '<i class="fa-solid fa-truck fa-lg"></i>'; echo '<i class="fa-solid fa-truck fa-lg"></i>';
} }
if(preg_match('/'.DHLContainsText.'/i',$PackingNote_data_output->shipping_in)){ if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_in)) {
echo '<i class="fa-brands fa-dhl fa-2xl"></i>'; echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
} }
if(preg_match('/'.LKWContainsText.'/i',$PackingNote_data_output->shipping_in)){ if (preg_match('/' . LKWContainsText . '/i', $PackingNoteDetail->shipping_in)) {
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>'; echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>';
} }
if(preg_match('/'.TransporterContainsText.'/i',$PackingNote_data_output->shipping_in)){ if (preg_match('/' . TransporterContainsText . '/i', $PackingNoteDetail->shipping_in)) {
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>'; echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>';
} }
if(preg_match('/'.PKWContainsText.'/i',$PackingNote_data_output->shipping_in)){ if (preg_match('/' . PKWContainsText . '/i', $PackingNoteDetail->shipping_in)) {
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>'; echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>';
} }
} }
echo "</td>"; echo "</td>";
} }
echo "</tr>"; echo "</tr>";
} }
echo "</tr>";
} }
} }
} }
function getTimeFromSeconds(string $timestring) { function getTimeFromSeconds(string $timestring) {
$hours = floor($timestring / 3600); $hours = floor($timestring / 3600);
$mins = floor($timestring / 60 % 60); $mins = floor($timestring / 60 % 60);
$secs = floor($timestring % 60); $secs = floor($timestring % 60);
$timeFormat = sprintf('%02d:%02d', $hours, $mins); $timeFormat = sprintf('%02d:%02d', $hours, $mins);
return $timeFormat; return $timeFormat;
} }

View File

@@ -1,5 +1,6 @@
<?php <?php
error_reporting(E_ALL); error_reporting(E_ALL);
require('../config.php'); require('../config.php');
require('../EpiApi.php'); require('../EpiApi.php');
@@ -19,23 +20,64 @@ $options = new QROptions([
'quietzoneSize' => 1 'quietzoneSize' => 1
]); ]);
$Epi = new Epirent(); $Epi = new Epirent();
if(UseDeliveredForCheckOutCompleted){ const APP_TZ = 'Europe/Berlin';
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
}else{ /** Hilfsfunktionen für die Row-Marking-Logik (ohne weitere Änderungen am Rest) */
$result = $Epi->requestEpiApi('/v1/packingnote/open?isci=False&cl=' . Epirent_Mandant); 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 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;
}
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 '';
}
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);
} }
$data_output = json_decode($result)->payload; $data_output = json_decode($result)->payload;
if (SortCheckOut == 2) { if (SortCheckOut == 2) {
// Prüfen, ob $data_output ein Array ist // Prüfen, ob $data_output ein Array ist
if (is_array($data_output)) { if (is_array($data_output)) {
usort($data_output, function ($a, $b) { usort($data_output, function ($a, $b) {
@@ -60,70 +102,54 @@ if (SortCheckOut == 2) {
foreach ($data_output as $packingjob) { foreach ($data_output as $packingjob) {
$PackingNoteDetail = null;
$PackingNote_data_output; if ($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted) {
if($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted){
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant); $PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
$PackingNote_data_output = json_decode($PackingNoteDetailResult)->payload[0]; $PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
} }
if ($packingjob->is_archived != true && (!UseDeliveredForCheckOutCompleted || ($PackingNote_data_output->date_delivered == "0000-00-00" && $PackingNote_data_output->time_delivered == 0))) {
if (
$packingjob->is_archived != true
&& (
!UseDeliveredForCheckOutCompleted
|| ($PackingNoteDetail->date_delivered !== "0000-00-00" || (int)$PackingNoteDetail->time_delivered !== 0)
)
&& (int)$PackingNoteDetail->is_all_out !== 0
) {
//get OrderDetails //get OrderDetails
$result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant); $result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
$orderdetail_output = json_decode($result)->payload[0]; $orderdetail_output = json_decode($result)->payload[0];
// get PackingNote Details // get PackingNote Details, aber nur wenn nicht schon vor der schleife geholt.
if(!UseDeliveredForCheckOutCompleted){ if (!UseDeliveredForCheckOutCompleted) {
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant); $PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
$PackingNote_data_output = json_decode($PackingNoteDetailResult)->payload[0]; $PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
} }
$VorbereitungsTimeDetail;
$VorbereitungsTimeDetail = null;
foreach ($orderdetail_output->order_schedule as $scheduledetail) { foreach ($orderdetail_output->order_schedule as $scheduledetail) {
if (UsePackingNoteDateForCheckout) {
$tempTimeObject = new stdClass();
$tempTimeObject->date_start = $PackingNote_data_output->date_packing;
$tempTimeObject->time_start = $PackingNote_data_output->time_packing;
$VorbereitungsTimeDetail = $tempTimeObject;
} else {
if ($scheduledetail->name == Vorbereitungs_Zeitvariable) { if ($scheduledetail->name == Vorbereitungs_Zeitvariable) {
$VorbereitungsTimeDetail = $scheduledetail; $VorbereitungsTimeDetail = $scheduledetail;
} }
} }
}
//End Of get Order Details
if (CheckOut_UseDispoStartForRowMarking || ($VorbereitungsTimeDetail->date_start == null)) { // --- Row-Marking Datum bestimmen (konfigurierbar) ---
$date = new DateTime($packingjob->date_start); $today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
} else if(UseDeliveredForCheckOutCompleted ){ $todayFilter = $today; // für die Window-Berechnung
{ $limit = null;
$date = new DateTime($PackingNote_data_output->date_delivery); if (CheckOut_FutureDays != -1) {
$limit = $todayFilter->modify('+' . (int) CheckOut_FutureDays . ' day');
}
}else{
$date = new DateTime($VorbereitungsTimeDetail->date_start);
} }
$date->setTime(0, 0, 0); $markDate = resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, (int) CheckOutRowMarkSource);
$today = new DateTime();
$today->setTime(0, 0, 0);
$todayFilter = new DateTime(); if (CheckOut_FutureDays == -1 || ($markDate && $markDate <= $limit)) {
$todayFilter->setTime(0, 0, 0); $trClass = rowClassForDate($markDate, $today);
if (CheckOut_FutureDays == -1 || $date <= ($todayFilter->modify('+' . CheckOut_FutureDays . ' day'))) {
//prüfe, ob entweder unbegrenzte (-1) Anzeige Aktiv ist, oder das Datum kleiner oder Gleich heute + Zukunftsspanne ist
if ($date == $today) {
echo "<tr class='text-dark bg-warning'>"; if ($trClass) {
} else if ($date < $today) { echo "<tr class='{$trClass}'>";
echo "<tr class=' bg-danger'>";
} else { } else {
echo "<tr>"; echo "<tr>";
} }
@@ -136,62 +162,38 @@ if($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted){
echo "<td>" . $packingjob->contact->name . "</td>"; echo "<td>" . $packingjob->contact->name . "</td>";
echo "<td>" . $packingjob->event . "</td>"; echo "<td>" . $packingjob->event . "</td>";
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout) { echo "<td>";}
if(UseDeliveredForCheckOutCompleted){ if (ShowCheckoutTimeOnCheckout && $packingjob->date_start != null) {
echoMarkedTimeLine($packingjob->date_start, (int) $packingjob->time_start, $today, ShowTimesOnCheckout);
if (CheckOut_UseDispoStartForRowMarking || ($VorbereitungsTimeDetail->date_start == null)) { }
$date = new DateTime($packingjob->date_start); if (ShowVorbereitungTimeOnCheckout && $VorbereitungsTimeDetail && $VorbereitungsTimeDetail->date_start != null) {
} else { if (ShowCheckoutTimeOnCheckout) {
$date = new DateTime($VorbereitungsTimeDetail->date_start); echo "<br>";
}
echoMarkedTimeLine($VorbereitungsTimeDetail->date_start, (int) $VorbereitungsTimeDetail->time_start, $today, ShowTimesOnCheckout);
}
if (ShowPackagingTimeOnCheckout && $PackingNoteDetail && $PackingNoteDetail->date_packing != null) {
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout) {
echo "<br>";
}
echoMarkedTimeLine($PackingNoteDetail->date_packing, (int) $PackingNoteDetail->time_packing, $today, ShowTimesOnCheckout);
}
if (ShowDeliveryTimeOnCheckout && $PackingNoteDetail && $PackingNoteDetail->date_delivery != null) {
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout) {
echo "<br>";
}
echoMarkedTimeLine($PackingNoteDetail->date_delivery, (int) $PackingNoteDetail->time_delivery, $today, ShowTimesOnCheckout);
} }
if ($date == $today) {
echo "<td class='text-dark bg-warning'>"; if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout) {
} else if ($date < $today) { echo "</td>";
echo "<td class=' bg-danger'>";
} else {
echo "<td>";
} }
}else{
echo "<td>"; echo "<td>";
}
if ($VorbereitungsTimeDetail->date_start != null) {
if(!HideDispoTimes){
echo "<small>".date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</small><br>";
}
echo "<i>" . date_format(new \DateTime($VorbereitungsTimeDetail->date_start), 'd.m.Y') . " " . getTimeFromSeconds($VorbereitungsTimeDetail->time_start) . "</i></td>";
} else if(!HideDispoTimes){
date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</td>";
}
if (!HideCheckInTimeOnCheckout) {
if (($VorbereitungsTimeDetail->date_end != null)) {
if(!HideDispoTimes){
echo "<small>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</small><br>";
}
echo "<i>" . date_format(new \DateTime($VorbereitungsTimeDetail->date_end), 'd.m.Y') . " " . getTimeFromSeconds($VorbereitungsTimeDetail->time_end) . "</i></td>";
} else if(!HideDispoTimes){
echo date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</td>";
}
}
echo "<td>";
if ($packingjob->is_all_out == 0) { if ($packingjob->is_all_out == 0) {
echo "<span class='badge badge-success'>"; echo "<span class='badge badge-success'>";
} else { } else {
@@ -204,51 +206,64 @@ if($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted){
echo ($packingjob->pieces_sum_total - abs($packingjob->is_all_out)) . "/" . $packingjob->pieces_sum_total; echo ($packingjob->pieces_sum_total - abs($packingjob->is_all_out)) . "/" . $packingjob->pieces_sum_total;
echo "</span></td>"; echo "</span></td>";
if(ShowShippingIcons){ if (ShowShippingIcons) {
if(UseShippingStatus){ if (UseShippingStatus) {
if(($PackingNote_data_output->status==ShippingOutOrganizedStatus)||($PackingNote_data_output->status==ShippingOrganizedStatus)){ if (($PackingNoteDetail->status == ShippingOutOrganizedStatus) || ($PackingNoteDetail->status == ShippingOrganizedStatus)) {
echo "<td style='color:#66FF00; text-align:center;'>"; echo "<td style='color:#66FF00; text-align:center;'>";
}else{ } else {
echo "<td style='text-align:center;'>"; echo "<td style='text-align:center;'>";
} }
if($PackingNote_data_output->is_self_pickup){ if ($PackingNoteDetail->is_self_pickup) {
echo '<i class="fa-solid fa-person-walking fa-lg"></i>'; echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
} else{ } else {
if(preg_match('/'.KurierContainsText.'/i',$PackingNote_data_output->shipping_out)){ if (preg_match('/' . KurierContainsText . '/i', $PackingNoteDetail->shipping_out)) {
echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>'; echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>';
} }
if(preg_match('/'.SpeditionContainsText.'/i',$PackingNote_data_output->shipping_out)){ if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_out)) {
echo '<i class="fa-solid fa-truck fa-lg"></i>'; echo '<i class="fa-solid fa-truck fa-lg"></i>';
} }
if(preg_match('/'.DHLContainsText.'/i',$PackingNote_data_output->shipping_out)){ if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_out)) {
echo '<i class="fa-brands fa-dhl fa-2xl"></i>'; echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
} }
if(preg_match('/'.LKWContainsText.'/i',$PackingNote_data_output->shipping_out)){ if (preg_match('/' . LKWContainsText . '/i', $PackingNoteDetail->shipping_out)) {
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>'; echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>';
} }
if(preg_match('/'.TransporterContainsText.'/i',$PackingNote_data_output->shipping_out)){ if (preg_match('/' . TransporterContainsText . '/i', $PackingNoteDetail->shipping_out)) {
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>'; echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>';
} }
if(preg_match('/'.PKWContainsText.'/i',$PackingNote_data_output->shipping_out)){ if (preg_match('/' . PKWContainsText . '/i', $PackingNoteDetail->shipping_out)) {
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>'; echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>';
} }
} }
echo "</td>"; echo "</td>";
} }
echo "</tr>"; echo "</tr>";
} }
echo "</tr>"; echo "</tr>";
} }
} }
} }
function echoMarkedTimeLine(?string $dateStr, ?int $timeSeconds, DateTimeImmutable $today, $ReturnTime): void {
if (!$dateStr)
return;
$d = dt($dateStr);
if (!$d)
return;
$mark = dayStart($d);
$cls = rowClassForDate($mark, $today);
echo $cls ? "<span class=\"{$cls}\">" : "";
echo date_format(new \DateTime($dateStr), 'd.m.Y');
if($ReturnTime){echo " ".getTimeFromSeconds((string) $timeSeconds);}
echo $cls ? "</span>" : "";
}
function getTimeFromSeconds(string $timestring) { function getTimeFromSeconds(string $timestring) {
$hours = floor($timestring / 3600); $hours = floor($timestring / 3600);