Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d08cb0e427 | ||
|
|
23b1768be7 | ||
|
|
542cbfdcd8 | ||
|
|
4400059ffb | ||
|
|
9d18268130 | ||
|
|
21bfe5a1aa | ||
|
|
a872630369 | ||
|
|
dbdf081738 | ||
|
|
e2fc2a786e | ||
|
|
6c3b7a9cd0 | ||
|
|
81a7960af7 | ||
|
|
85c7108091 | ||
|
|
bef9de2c71 | ||
|
|
8a9d7bee75 | ||
|
|
b963ab9ca1 | ||
|
|
a71d401864 | ||
|
|
18b91cc4a5 | ||
|
|
bc76739320 | ||
|
|
df620a4897 | ||
|
|
e1d9e01fdb | ||
|
|
feb4be6621 | ||
|
|
5a88d0780a | ||
|
|
ecf9db4a13 | ||
|
|
e4283df099 | ||
|
|
6e5e63adc5 | ||
|
|
d8123ef332 | ||
|
|
c26b54c08b |
@@ -1,3 +1,4 @@
|
|||||||
config.php
|
config.php
|
||||||
.vscode
|
.vscode
|
||||||
sftp.json
|
sftp.json
|
||||||
|
.claude
|
||||||
+13
-5
@@ -325,12 +325,12 @@ $Epi = new Epirent();
|
|||||||
<th scope="col">Event</th>
|
<th scope="col">Event</th>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout || ShowDispoEndOnCheckin){
|
||||||
echo '<th scope="col">';
|
echo '<th scope="col">';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ShowCheckoutTimeOnCheckout){
|
if(ShowCheckoutTimeOnCheckout){
|
||||||
echo "Dispo-Start";
|
echo "Checkout-Start";
|
||||||
}
|
}
|
||||||
if(ShowVorbereitungTimeOnCheckout){
|
if(ShowVorbereitungTimeOnCheckout){
|
||||||
if(ShowCheckoutTimeOnCheckout){echo "<br>";}
|
if(ShowCheckoutTimeOnCheckout){echo "<br>";}
|
||||||
@@ -344,7 +344,11 @@ $Epi = new Epirent();
|
|||||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
|
||||||
echo "Liefern";
|
echo "Liefern";
|
||||||
}
|
}
|
||||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
if(ShowDispoStartOnCheckout){
|
||||||
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){echo "<br>";}
|
||||||
|
echo "Auftrag Start";
|
||||||
|
}
|
||||||
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout || ShowDispoStartOnCheckout){
|
||||||
echo '</th>';
|
echo '</th>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +380,7 @@ $Epi = new Epirent();
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(ShowCheckInTimeOnCheckin){
|
if(ShowCheckInTimeOnCheckin){
|
||||||
echo "Dispo-Ende";
|
echo "CheckIn";
|
||||||
}
|
}
|
||||||
if(ShowNachbereitungTimeOnCheckin){
|
if(ShowNachbereitungTimeOnCheckin){
|
||||||
if(ShowCheckInTimeOnCheckin){echo "<br>";}
|
if(ShowCheckInTimeOnCheckin){echo "<br>";}
|
||||||
@@ -390,7 +394,11 @@ $Epi = new Epirent();
|
|||||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
|
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
|
||||||
echo "Rückliefern";
|
echo "Rückliefern";
|
||||||
}
|
}
|
||||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
|
if(ShowDispoEndOnCheckin){
|
||||||
|
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin | ShowReDeliveryTimeOnCheckin){echo "<br>";}
|
||||||
|
echo "Auftrag Ende";
|
||||||
|
}
|
||||||
|
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin || ShowDispoEndOnCheckin){
|
||||||
echo '</th>';
|
echo '</th>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-5
@@ -289,12 +289,12 @@ $Epi = new Epirent();
|
|||||||
<th scope="col">Kunde</th>
|
<th scope="col">Kunde</th>
|
||||||
<th scope="col">Event</th>
|
<th scope="col">Event</th>
|
||||||
<?php
|
<?php
|
||||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout || ShowDispoEndOnCheckin){
|
||||||
echo '<th scope="col">';
|
echo '<th scope="col">';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ShowCheckoutTimeOnCheckout){
|
if(ShowCheckoutTimeOnCheckout){
|
||||||
echo "Dispo-Start";
|
echo "Checkout-Start";
|
||||||
}
|
}
|
||||||
if(ShowVorbereitungTimeOnCheckout){
|
if(ShowVorbereitungTimeOnCheckout){
|
||||||
if(ShowCheckoutTimeOnCheckout){echo "<br>";}
|
if(ShowCheckoutTimeOnCheckout){echo "<br>";}
|
||||||
@@ -308,7 +308,11 @@ $Epi = new Epirent();
|
|||||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
|
||||||
echo "Liefern";
|
echo "Liefern";
|
||||||
}
|
}
|
||||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
if(ShowDispoStartOnCheckout){
|
||||||
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){echo "<br>";}
|
||||||
|
echo "Auftrag Start";
|
||||||
|
}
|
||||||
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout || ShowDispoStartOnCheckout){
|
||||||
echo '</th>';
|
echo '</th>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +346,7 @@ $Epi = new Epirent();
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(ShowCheckInTimeOnCheckin){
|
if(ShowCheckInTimeOnCheckin){
|
||||||
echo "Dispo-Ende";
|
echo "CheckIn";
|
||||||
}
|
}
|
||||||
if(ShowNachbereitungTimeOnCheckin){
|
if(ShowNachbereitungTimeOnCheckin){
|
||||||
if(ShowCheckInTimeOnCheckin){echo "<br>";}
|
if(ShowCheckInTimeOnCheckin){echo "<br>";}
|
||||||
@@ -356,7 +360,11 @@ $Epi = new Epirent();
|
|||||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
|
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
|
||||||
echo "Rückliefern";
|
echo "Rückliefern";
|
||||||
}
|
}
|
||||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
|
if(ShowDispoEndOnCheckin){
|
||||||
|
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin | ShowReDeliveryTimeOnCheckin){echo "<br>";}
|
||||||
|
echo "Auftrag Ende";
|
||||||
|
}
|
||||||
|
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin || ShowDispoEndOnCheckin){
|
||||||
echo '</th>';
|
echo '</th>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# EPIWebview
|
# EPIWebview
|
||||||
|
|
||||||
- **aktuellste stable Version:** 1.5.0
|
- **aktuellste stable Version:** 1.11
|
||||||
- **Lizenz:** Creative Commons Attribution-NonCommercial-ShareAlike 4.0 (CC BY-NC-SA 4.0). Einsehbar unter: (LICENSE.MD)
|
- **Lizenz:** Creative Commons Attribution-NonCommercial-ShareAlike 4.0 (CC BY-NC-SA 4.0). Einsehbar unter: (LICENSE.MD)
|
||||||
- **Kompatibilität:** Erweiterung für **Epirent** und **CrewBrain**
|
- **Kompatibilität:** Erweiterung für **Epirent** und **CrewBrain**
|
||||||
|
|
||||||
@@ -25,7 +25,8 @@ Die Anwendung ist speziell für den Einsatz in Lagerprozessen entwickelt.
|
|||||||
|
|
||||||
## Systemanforderungen
|
## Systemanforderungen
|
||||||
|
|
||||||
- **Server:** PHP ≥ 8.1, Apache oder Nginx
|
- **Server:** PHP ≥ 8.2, Apache oder Nginx
|
||||||
|
- Achtung !!: Für die Return of Invest Funktion oder den Warengruppencheck / Imagechecks sollten in der php.ini die max_execution_time und die maximale Dateigröße deutlich nach oben korrigiert werden. Die ROI Funktion kann gut und gerne 20 Minuten laden!. Foglende PHP Funktionen müssen aktiviert werden: curl, gzcompress, Imagick, gd
|
||||||
- **Client:** Aktueller Browser (Chrome, Edge, Firefox, Safari)
|
- **Client:** Aktueller Browser (Chrome, Edge, Firefox, Safari)
|
||||||
- **Datenquelle:** Bestehende Epirent-Installation mit aktivierter API sowie optional CrewBrain
|
- **Datenquelle:** Bestehende Epirent-Installation mit aktivierter API sowie optional CrewBrain
|
||||||
|
|
||||||
@@ -36,7 +37,100 @@ Die Anwendung ist speziell für den Einsatz in Lagerprozessen entwickelt.
|
|||||||
1. Repository klonen oder Dateien ins Webverzeichnis kopieren:
|
1. Repository klonen oder Dateien ins Webverzeichnis kopieren:
|
||||||
```bash
|
```bash
|
||||||
git clone http://srvgitea01.vtm.zone:3000/epi/EpiWebview
|
git clone http://srvgitea01.vtm.zone:3000/epi/EpiWebview
|
||||||
2. Dashboard Aufrufen und auf die Config-Seite wechseln, notwendige Einstellungen vornehmen.
|
2. Config-Seite unter /dist/editconfig.php aufrufen, notwendige Einstellungen vornehmen -> Speichern. Wichtig! Verzeichnis muss für Webserver beschreibbar sein!
|
||||||
|
3. Alternativ: example.config.php nach config.php kopieren! (example.config.php darf nicht gelöscht werden!)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Update
|
||||||
|
|
||||||
|
1. Repository klonen oder Dateien ins Webverzeichnis kopieren, dabei alte Dateien löschen (bis auf config.php):
|
||||||
|
```bash
|
||||||
|
git clone http://srvgitea01.vtm.zone:3000/epi/EpiWebview
|
||||||
|
2. Einmalig die Config-Seite aufrufen und speichern drücken. Dadurch werden geupdatete Config-Felder übernommen
|
||||||
|
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
Verschoben in Releases (Git)
|
Verschoben in Releases (Git)
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# Installation als Docker Containerr
|
||||||
|
Folgende drei Dateien anlegen:
|
||||||
|
docker-compose.yml
|
||||||
|
```
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
container_name: epiwebview-apache-php
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- /volume1/docker/epiwebview/www:/var/www/html
|
||||||
|
- /volume1/docker/epiwebview/php.ini:/usr/local/etc/php/php.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
Dockerfile
|
||||||
|
```
|
||||||
|
FROM php:8.2-apache
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
git \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
|
libpng-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
zlib1g-dev \
|
||||||
|
libmagickwand-dev \
|
||||||
|
unzip \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||||
|
&& docker-php-ext-install curl gd \
|
||||||
|
&& pecl install imagick \
|
||||||
|
&& docker-php-ext-enable imagick
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
```
|
||||||
|
|
||||||
|
entrypoint.sh
|
||||||
|
```
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ -z "$(ls -A /var/www/html 2>/dev/null)" ]; then
|
||||||
|
echo "Klonen von EpiWebview..."
|
||||||
|
git clone https://srvgitea01.vtm.zone/epi/EpiWebview /var/www/html
|
||||||
|
else
|
||||||
|
echo "www-Verzeichnis ist nicht leer, Clone wird übersprungen."
|
||||||
|
fi
|
||||||
|
|
||||||
|
chown -R www-data:www-data /var/www/html
|
||||||
|
|
||||||
|
apache2-foreground
|
||||||
|
```
|
||||||
|
|
||||||
|
Container starten:
|
||||||
|
```
|
||||||
|
sudo docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Containerstatus prüfen:
|
||||||
|
```
|
||||||
|
sudo docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
Dann die config example in eine config.php kopieren.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Update des Containers aus dem Git Repo:
|
||||||
|
```
|
||||||
|
cd /volume1/docker/epiwebview
|
||||||
|
sudo docker exec -it epiwebview-apache-php bash
|
||||||
|
cd /var/www/html
|
||||||
|
git pull
|
||||||
|
```
|
||||||
Vendored
+1529
File diff suppressed because it is too large
Load Diff
Vendored
+128
@@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require('../config.php');
|
||||||
|
require('../EpiApi.php');
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
date_default_timezone_set('Europe/Berlin');
|
||||||
|
|
||||||
|
$Epi = new Epirent();
|
||||||
|
$productList = json_decode($Epi->requestEpiApi('/v1/product/all?ia=true&ir=true&cl=' . Epirent_Mandant))->payload;
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||||
|
<title>Imagecheck - EpiWebview</title>
|
||||||
|
|
||||||
|
<!-- Styles -->
|
||||||
|
<link href="css/styles.css" rel="stylesheet" />
|
||||||
|
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
|
||||||
|
|
||||||
|
<!-- JS -->
|
||||||
|
<script src="js/jquery-3.5.1.min.js"></script>
|
||||||
|
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.opacity-50 {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .h2,
|
||||||
|
.card .display-4 {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-updated {
|
||||||
|
font-size: .82rem;
|
||||||
|
opacity: .85;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
$('#layoutSidenav_nav').load('../sources/getSidenav.php');
|
||||||
|
$('#footerholder').load('../sources/getFooter.php');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="sb-nav-fixed">
|
||||||
|
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
|
||||||
|
<a class="navbar-brand" href="index.php">Epi Webview</a>
|
||||||
|
<button class="btn btn-link btn-sm order-1 order-lg-0" id="sidebarToggle"><i class="fas fa-bars"></i></button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="layoutSidenav">
|
||||||
|
<div id="layoutSidenav_nav"></div>
|
||||||
|
<div id="layoutSidenav_content">
|
||||||
|
<main>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<h1 class="mt-4">Warengruppenprüfung</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<div><i class="fas fa-table mr-1"></i> Zeigt welche Artikel keine Warengruppe haben</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="dataTable" class="table table-bordered" width="100%" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>PN</th>
|
||||||
|
<th>Name</th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php
|
||||||
|
foreach ($productList as $product) {
|
||||||
|
|
||||||
|
if( count($product->group_of_goods) == 0 || $product->group_of_goods ==null){
|
||||||
|
|
||||||
|
echo "<tr>";
|
||||||
|
echo "<td>" . htmlspecialchars($product->primary_key) . "</td>";
|
||||||
|
echo "<td>" . htmlspecialchars($product->product_no) . "</td>";
|
||||||
|
echo "<td>" . htmlspecialchars($product->name) . "</td>";
|
||||||
|
echo "</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<div id="footerholder"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bootstrap Bundle -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#dataTable').DataTable({
|
||||||
|
"pageLength": 25,
|
||||||
|
"language": {
|
||||||
|
"url": "//cdn.datatables.net/plug-ins/1.10.20/i18n/German.json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Vendored
+128
@@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require('../config.php');
|
||||||
|
require('../EpiApi.php');
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
date_default_timezone_set('Europe/Berlin');
|
||||||
|
|
||||||
|
$Epi = new Epirent();
|
||||||
|
$productList = json_decode($Epi->requestEpiApi('/v1/product/all?ia=true&ir=true&cl=' . Epirent_Mandant))->payload;
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||||
|
<title>Imagecheck - EpiWebview</title>
|
||||||
|
|
||||||
|
<!-- Styles -->
|
||||||
|
<link href="css/styles.css" rel="stylesheet" />
|
||||||
|
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
|
||||||
|
|
||||||
|
<!-- JS -->
|
||||||
|
<script src="js/jquery-3.5.1.min.js"></script>
|
||||||
|
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.opacity-50 {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .h2,
|
||||||
|
.card .display-4 {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-updated {
|
||||||
|
font-size: .82rem;
|
||||||
|
opacity: .85;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
$('#layoutSidenav_nav').load('../sources/getSidenav.php');
|
||||||
|
$('#footerholder').load('../sources/getFooter.php');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="sb-nav-fixed">
|
||||||
|
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
|
||||||
|
<a class="navbar-brand" href="index.php">Epi Webview</a>
|
||||||
|
<button class="btn btn-link btn-sm order-1 order-lg-0" id="sidebarToggle"><i class="fas fa-bars"></i></button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="layoutSidenav">
|
||||||
|
<div id="layoutSidenav_nav"></div>
|
||||||
|
<div id="layoutSidenav_content">
|
||||||
|
<main>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<h1 class="mt-4">Imagecheck</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<div><i class="fas fa-table mr-1"></i> Zeigt welche Artikel keine Bilder haben</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="dataTable" class="table table-bordered" width="100%" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>PN</th>
|
||||||
|
<th>Name</th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php
|
||||||
|
foreach ($productList as $product) {
|
||||||
|
$hasImages=(json_decode($Epi->requestEpiApi('/v1/product/image/'.$product->primary_key.'?cl=' . Epirent_Mandant))->payload_length);
|
||||||
|
if( $hasImages == 0 || $hasImages ==null){
|
||||||
|
|
||||||
|
echo "<tr>";
|
||||||
|
echo "<td>" . htmlspecialchars($product->primary_key) . "</td>";
|
||||||
|
echo "<td>" . htmlspecialchars($product->product_no) . "</td>";
|
||||||
|
echo "<td>" . htmlspecialchars($product->name) . "</td>";
|
||||||
|
echo "</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<div id="footerholder"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bootstrap Bundle -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#dataTable').DataTable({
|
||||||
|
"pageLength": 25,
|
||||||
|
"language": {
|
||||||
|
"url": "//cdn.datatables.net/plug-ins/1.10.20/i18n/German.json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Vendored
+18
-2
@@ -76,7 +76,20 @@ foreach ($filteredProductArray as $filteredProduct) {
|
|||||||
$stockRes = $Epi->requestEpiApi('/v1/stock/filter?ppk=' . $insuredProduct->primary_key . '&cl=' . Epirent_Mandant);
|
$stockRes = $Epi->requestEpiApi('/v1/stock/filter?ppk=' . $insuredProduct->primary_key . '&cl=' . Epirent_Mandant);
|
||||||
$deviceStockObjectArray = json_decode($stockRes)->payload ?? [];
|
$deviceStockObjectArray = json_decode($stockRes)->payload ?? [];
|
||||||
|
|
||||||
|
//Sortiere alle Geräte aus die "Nicht Versichert" im Status stehen haben
|
||||||
foreach ($deviceStockObjectArray as $device) {
|
foreach ($deviceStockObjectArray as $device) {
|
||||||
|
if($device->condition=="Nicht Versichert"){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Filtere alle bei denen Sold!=0 ist raus:
|
||||||
|
if($device->sold_state != 0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Sortiere jetzt die Geräte aus, die Nicht aussortiert wurden
|
||||||
|
if(!$device->is_sorted_out){
|
||||||
$rows[] = [
|
$rows[] = [
|
||||||
'pk' => $insuredProduct->product_no,
|
'pk' => $insuredProduct->product_no,
|
||||||
'name' => $insuredProduct->name,
|
'name' => $insuredProduct->name,
|
||||||
@@ -90,6 +103,7 @@ foreach ($filteredProductArray as $filteredProduct) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* =========================
|
/* =========================
|
||||||
2) XLSX sofort erzeugen (ohne setCellValueByColumnAndRow)
|
2) XLSX sofort erzeugen (ohne setCellValueByColumnAndRow)
|
||||||
@@ -173,8 +187,10 @@ foreach ($rows as $r) {
|
|||||||
$sheet->getStyle($cell($c, $rowIdx))->getNumberFormat()->setFormatCode('#,##0.00 [$€-de-DE]');
|
$sheet->getStyle($cell($c, $rowIdx))->getNumberFormat()->setFormatCode('#,##0.00 [$€-de-DE]');
|
||||||
$c++;
|
$c++;
|
||||||
|
|
||||||
// Seriennummer
|
// Seriennummer (immer als Text, damit Excel rein numerische Werte nicht als Zahl interpretiert)
|
||||||
$sheet->setCellValue($cell($c++, $rowIdx), $r['serial_no']);
|
$sheet->getStyle($cell($c, $rowIdx))->getNumberFormat()->setFormatCode('@');
|
||||||
|
$sheet->setCellValueExplicit($cell($c, $rowIdx), $r['serial_no'], DataType::TYPE_STRING);
|
||||||
|
$c++;
|
||||||
|
|
||||||
// Anschaffung (Datum)
|
// Anschaffung (Datum)
|
||||||
$excelDate = '';
|
$excelDate = '';
|
||||||
|
|||||||
Vendored
+28
-1
@@ -6,7 +6,7 @@ require_once __DIR__ . '/../vendor/autoload.php';
|
|||||||
date_default_timezone_set('Europe/Berlin');
|
date_default_timezone_set('Europe/Berlin');
|
||||||
|
|
||||||
$Epi = new Epirent();
|
$Epi = new Epirent();
|
||||||
$productList = json_decode($Epi->requestEpiApi('/v1/product/all?cl=' . Epirent_Mandant))->payload;
|
$productList = json_decode($Epi->requestEpiApi('/v1/product/all?ia=true&ir=true&cl=' . Epirent_Mandant))->payload;
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -85,6 +85,12 @@ $productList = json_decode($Epi->requestEpiApi('/v1/product/all?cl=' . Epirent_M
|
|||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php
|
||||||
foreach ($productList as $product) {
|
foreach ($productList as $product) {
|
||||||
|
if (!$product->is_rent & $product->is_sale){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$product->is_active){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
echo "<tr>";
|
echo "<tr>";
|
||||||
echo "<td>" . htmlspecialchars($product->primary_key) . "</td>";
|
echo "<td>" . htmlspecialchars($product->primary_key) . "</td>";
|
||||||
echo "<td>" . htmlspecialchars($product->product_no) . "</td>";
|
echo "<td>" . htmlspecialchars($product->product_no) . "</td>";
|
||||||
@@ -96,6 +102,11 @@ $productList = json_decode($Epi->requestEpiApi('/v1/product/all?cl=' . Epirent_M
|
|||||||
href='../sources/getProductLabel.php?id=" . urlencode($product->primary_key) . "'>
|
href='../sources/getProductLabel.php?id=" . urlencode($product->primary_key) . "'>
|
||||||
Export
|
Export
|
||||||
</a>
|
</a>
|
||||||
|
<a class='btn btn-sm btn-outline-primary'
|
||||||
|
target='_blank'
|
||||||
|
href='../sources/getProductLabelSmall.php?id=" . urlencode($product->primary_key) . "'>
|
||||||
|
Export small
|
||||||
|
</a>
|
||||||
|
|
||||||
<input type='number'
|
<input type='number'
|
||||||
id='nrInput_" . htmlspecialchars($product->primary_key) . "'
|
id='nrInput_" . htmlspecialchars($product->primary_key) . "'
|
||||||
@@ -118,6 +129,22 @@ $productList = json_decode($Epi->requestEpiApi('/v1/product/all?cl=' . Epirent_M
|
|||||||
\">
|
\">
|
||||||
Export mit ID
|
Export mit ID
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a class='btn btn-sm btn-outline-secondary'
|
||||||
|
target='_blank'
|
||||||
|
id='exportWithIdBtn_" . htmlspecialchars($product->primary_key) . "'
|
||||||
|
href='#'
|
||||||
|
onclick=\"
|
||||||
|
var nr = document.getElementById('nrInput_" . htmlspecialchars($product->primary_key) . "').value;
|
||||||
|
if(nr) {
|
||||||
|
this.href = '../sources/getProductLabelSmall.php?id=" . urlencode($product->primary_key) . "&nr=' + encodeURIComponent(nr);
|
||||||
|
} else {
|
||||||
|
alert('Bitte eine Nummer zwischen 1 und 9999 eingeben.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
\">
|
||||||
|
Export mit ID small
|
||||||
|
</a>
|
||||||
</td>";
|
</td>";
|
||||||
echo "</tr>";
|
echo "</tr>";
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-2
@@ -26,17 +26,19 @@ define('EnableScrollingCheckOut', true); //Aktiviert das automatische Scrollen d
|
|||||||
define('EnableScrollingCheckIn', true); //Aktiviert das automatische Scrollen der CheckIn Liste
|
define('EnableScrollingCheckIn', true); //Aktiviert das automatische Scrollen der CheckIn Liste
|
||||||
define('EnableScrollingAufgaben', true); //Aktiviert das automatische Scrollen der Aufgabenliste
|
define('EnableScrollingAufgaben', true); //Aktiviert das automatische Scrollen der Aufgabenliste
|
||||||
// @note: -------------------- CheckOutRowMarkSource: Konfig Zusände --------------------
|
// @note: -------------------- CheckOutRowMarkSource: Konfig Zusände --------------------
|
||||||
// @note: 1 = $packingjob->date_start (Dispo Start)
|
// @note: 1 = $packingjob->date_start (Packjob Start)
|
||||||
// @note: 2 = $VorbereitungsTimeDetail->date_start (Vorbereitung Start)
|
// @note: 2 = $VorbereitungsTimeDetail->date_start (Vorbereitung Start)
|
||||||
// @note: 3 = $PackingNoteDetail->date_packing (Packen Zeit)
|
// @note: 3 = $PackingNoteDetail->date_packing (Packen Zeit)
|
||||||
// @note: 4 = $PackingNoteDetail->date_delivery (Delivery Zeit)
|
// @note: 4 = $PackingNoteDetail->date_delivery (Delivery Zeit)
|
||||||
|
// @note: 5 = $order->date_start (Dispo Start Zeit)
|
||||||
define('CheckOutRowMarkSource', 4);
|
define('CheckOutRowMarkSource', 4);
|
||||||
|
|
||||||
// @note: -------------------- CheckInRowMarkSource: Konfig Zusände --------------------
|
// @note: -------------------- CheckInRowMarkSource: Konfig Zusände --------------------
|
||||||
// @note: 1 = $packingjob->date_end (Dispo Ende)
|
// @note: 1 = $packingjob->date_end (Packjob Ende)
|
||||||
// @note: 2 = $NachbereitungssTimeDetail->date_start (Nachbereitung Start)
|
// @note: 2 = $NachbereitungssTimeDetail->date_start (Nachbereitung Start)
|
||||||
// @note: 3 = $RePackagingTimeDetail->date_start (Rückpacken Zeit AUS AUFTRAG)
|
// @note: 3 = $RePackagingTimeDetail->date_start (Rückpacken Zeit AUS AUFTRAG)
|
||||||
// @note: 4 = $PackingNoteDetail->date_redelivery (ReDelivery Zeit)
|
// @note: 4 = $PackingNoteDetail->date_redelivery (ReDelivery Zeit)
|
||||||
|
// @note: 5 = $order->date_end (Dispo End Zeit)
|
||||||
define('CheckInRowMarkSource', 4);
|
define('CheckInRowMarkSource', 4);
|
||||||
|
|
||||||
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
|
||||||
@@ -45,12 +47,15 @@ define('ShowCheckoutTimeOnCheckout', true); //Zeigt die Checkout Zeit im Checkou
|
|||||||
define('ShowVorbereitungTimeOnCheckout', true); //Zeigt die Vorbereitungs Zeitvariable im Checkout
|
define('ShowVorbereitungTimeOnCheckout', true); //Zeigt die Vorbereitungs Zeitvariable im Checkout
|
||||||
define('ShowPackagingTimeOnCheckout', true); //Zeigt die Packenzeit im Checkout
|
define('ShowPackagingTimeOnCheckout', true); //Zeigt die Packenzeit im Checkout
|
||||||
define('ShowDeliveryTimeOnCheckout', true); //Zeigt die Lieferzeit im Checkout
|
define('ShowDeliveryTimeOnCheckout', true); //Zeigt die Lieferzeit im Checkout
|
||||||
|
define('ShowDispoStartOnCheckout', true); //Zeigt die DispoStart im Checkout
|
||||||
define('ShowTimesOnCheckout', true); //Aktiviert das anzeigen der Uhrzeit im Checkout
|
define('ShowTimesOnCheckout', true); //Aktiviert das anzeigen der Uhrzeit im Checkout
|
||||||
|
|
||||||
// @hr
|
// @hr
|
||||||
define('ShowCheckInTimeOnCheckin', true); //Zeigt die CheckIn Zeit im CheckIn
|
define('ShowCheckInTimeOnCheckin', true); //Zeigt die CheckIn Zeit im CheckIn
|
||||||
define('ShowNachbereitungTimeOnCheckin', true); //Zeigt die Nachbereitungs Zeitvariable im CheckIn
|
define('ShowNachbereitungTimeOnCheckin', true); //Zeigt die Nachbereitungs Zeitvariable im CheckIn
|
||||||
define('ShowRePackagingTimeOnCheckin', true); //Zeigt die Rückpackzeit im Checkin <br><b>Achtung: Zeit nur Im Auftrag festlegbar</b>
|
define('ShowRePackagingTimeOnCheckin', true); //Zeigt die Rückpackzeit im Checkin <br><b>Achtung: Zeit nur Im Auftrag festlegbar</b>
|
||||||
define('ShowReDeliveryTimeOnCheckin', true); //Zeigt die geplante Rücklieferung im Checkin
|
define('ShowReDeliveryTimeOnCheckin', true); //Zeigt die geplante Rücklieferung im Checkin
|
||||||
|
define('ShowDispoEndOnCheckin', true); //Zeigt die DispoEnd Zeit im CheckIn
|
||||||
define('ShowTimesOnCheckin', true); //Aktiviert das anzeigen der Uhrzeit im CheckIn
|
define('ShowTimesOnCheckin', true); //Aktiviert das anzeigen der Uhrzeit im CheckIn
|
||||||
|
|
||||||
// @section: Epirent-Spezifische Einstellungen - Shipping
|
// @section: Epirent-Spezifische Einstellungen - Shipping
|
||||||
@@ -61,6 +66,15 @@ define('DHLContainsText', 'DHL'); //Text, der in der Versandart enthalten sein m
|
|||||||
define('LKWContainsText', 'LKW'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener LKW erkannt wird.
|
define('LKWContainsText', 'LKW'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener LKW erkannt wird.
|
||||||
define('TransporterContainsText', 'Sprinter'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener Transporter erkannt wird.
|
define('TransporterContainsText', 'Sprinter'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener Transporter erkannt wird.
|
||||||
define('PKWContainsText', 'PKW'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener PKW erkannt wird.
|
define('PKWContainsText', 'PKW'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener PKW erkannt wird.
|
||||||
|
// @hr
|
||||||
|
// @note: Informationen zu den Icons: Die Icons basieren auf FontAwesome und bestehen aus drei Teilen: Sammlung (fa-solid), Icon (fa-truck) und Größe (fa-lg). Weitere Informationen bitte der FA-Doku entnehmen
|
||||||
|
define('KurierIcon', 'fa-solid fa-hand-holding-dollar fa-lg');
|
||||||
|
define('SpeditionIcon', 'fa-solid fa-truck fa-lg');
|
||||||
|
define('DHLIcon', 'fa-brands fa-dhl fa-2xl');
|
||||||
|
define('LKWIcon', 'fa-solid fa-truck fa-lg');
|
||||||
|
define('TransporterIcon', 'fa-solid fa-van-shuttle fa-lg');
|
||||||
|
define('PKWIcon', 'fa-solid fa-car fa-lg');
|
||||||
|
define('SelbstabholerIcon', 'fa-solid fa-person-walking fa-lg');
|
||||||
|
|
||||||
define('UseShippingStatus', true); //Nutzt den Packscheinstatus als Shipping Status. Hierdurch können die Icons unter verschiedenen Vorraussetzungen Grün werden, wenn der Versand organisiert wurde
|
define('UseShippingStatus', true); //Nutzt den Packscheinstatus als Shipping Status. Hierdurch können die Icons unter verschiedenen Vorraussetzungen Grün werden, wenn der Versand organisiert wurde
|
||||||
define('ShippingOutOrganizedStatus', 'Hinversand OK'); //Packschein Status (EpirentDropdown) der Gematcht werden muss, dass der Hinversand Organisiert / Bestellt wurde
|
define('ShippingOutOrganizedStatus', 'Hinversand OK'); //Packschein Status (EpirentDropdown) der Gematcht werden muss, dass der Hinversand Organisiert / Bestellt wurde
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ function resolveRowMarkDateCheckIn(
|
|||||||
case 2: $candidate = dt($NachbereitungsTimeDetail->date_start ?? null); break;
|
case 2: $candidate = dt($NachbereitungsTimeDetail->date_start ?? null); break;
|
||||||
case 3: $candidate = dt($RePackagingTimeDetail->date_start ?? null); break;
|
case 3: $candidate = dt($RePackagingTimeDetail->date_start ?? null); break;
|
||||||
case 4: $candidate = dt($PackingNoteDetail->date_redelivery ?? null); break;
|
case 4: $candidate = dt($PackingNoteDetail->date_redelivery ?? null); break;
|
||||||
|
case 5: $candidate = dt($DispoTimeDetail->date_end ?? null); break;
|
||||||
default: $candidate = null; break;
|
default: $candidate = null; break;
|
||||||
}
|
}
|
||||||
if (!$candidate) {
|
if (!$candidate) {
|
||||||
@@ -88,6 +89,7 @@ foreach ($data_output as $packingjob) {
|
|||||||
// Zeit-Slots aus dem Schedule
|
// Zeit-Slots aus dem Schedule
|
||||||
$NachbereitungsTimeDetail = null;
|
$NachbereitungsTimeDetail = null;
|
||||||
$RePackagingTimeDetail = null;
|
$RePackagingTimeDetail = null;
|
||||||
|
$DispoTimeDetail = null;
|
||||||
if ($orderdetail_output && !empty($orderdetail_output->order_schedule)) {
|
if ($orderdetail_output && !empty($orderdetail_output->order_schedule)) {
|
||||||
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
||||||
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
|
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
|
||||||
@@ -96,6 +98,9 @@ foreach ($data_output as $packingjob) {
|
|||||||
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
|
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
|
||||||
$RePackagingTimeDetail = $scheduledetail;
|
$RePackagingTimeDetail = $scheduledetail;
|
||||||
}
|
}
|
||||||
|
if ($scheduledetail->name == "Dispo") {
|
||||||
|
$DispoTimeDetail = $scheduledetail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-16
@@ -59,6 +59,7 @@ function resolveRowMarkDateCheckIn(
|
|||||||
$NachbereitungsTimeDetail,
|
$NachbereitungsTimeDetail,
|
||||||
$RePackagingTimeDetail,
|
$RePackagingTimeDetail,
|
||||||
$PackingNoteDetail,
|
$PackingNoteDetail,
|
||||||
|
$DispoTimeDetail,
|
||||||
int $source
|
int $source
|
||||||
): ?DateTimeImmutable {
|
): ?DateTimeImmutable {
|
||||||
$candidate = null;
|
$candidate = null;
|
||||||
@@ -67,6 +68,8 @@ function resolveRowMarkDateCheckIn(
|
|||||||
case 2: $candidate = dt($NachbereitungsTimeDetail->date_start ?? null); break;
|
case 2: $candidate = dt($NachbereitungsTimeDetail->date_start ?? null); break;
|
||||||
case 3: $candidate = dt($RePackagingTimeDetail->date_start ?? null); break;
|
case 3: $candidate = dt($RePackagingTimeDetail->date_start ?? null); break;
|
||||||
case 4: $candidate = dt($PackingNoteDetail->date_redelivery ?? null); break;
|
case 4: $candidate = dt($PackingNoteDetail->date_redelivery ?? null); break;
|
||||||
|
case 5: $candidate = dt($DispoTimeDetail->date_end ?? null); break;
|
||||||
|
|
||||||
default: $candidate = null; break;
|
default: $candidate = null; break;
|
||||||
}
|
}
|
||||||
if (!$candidate) {
|
if (!$candidate) {
|
||||||
@@ -121,7 +124,7 @@ foreach ($data_output as $packingjob) {
|
|||||||
// Zeit-Slots aus dem Schedule
|
// Zeit-Slots aus dem Schedule
|
||||||
$NachbereitungsTimeDetail = null;
|
$NachbereitungsTimeDetail = null;
|
||||||
$RePackagingTimeDetail = null;
|
$RePackagingTimeDetail = null;
|
||||||
|
$DispoTimeDetail = null;
|
||||||
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
||||||
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
|
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
|
||||||
$NachbereitungsTimeDetail = $scheduledetail;
|
$NachbereitungsTimeDetail = $scheduledetail;
|
||||||
@@ -129,6 +132,9 @@ foreach ($data_output as $packingjob) {
|
|||||||
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
|
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
|
||||||
$RePackagingTimeDetail = $scheduledetail;
|
$RePackagingTimeDetail = $scheduledetail;
|
||||||
}
|
}
|
||||||
|
if ($scheduledetail->name == "Dispo") {
|
||||||
|
$DispoTimeDetail = $scheduledetail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Row-Marking bestimmen (konfigurierbar, analog Checkout) ---
|
// --- Row-Marking bestimmen (konfigurierbar, analog Checkout) ---
|
||||||
@@ -146,6 +152,7 @@ foreach ($data_output as $packingjob) {
|
|||||||
$NachbereitungsTimeDetail,
|
$NachbereitungsTimeDetail,
|
||||||
$RePackagingTimeDetail,
|
$RePackagingTimeDetail,
|
||||||
$PackingNoteDetail,
|
$PackingNoteDetail,
|
||||||
|
$DispoTimeDetail,
|
||||||
$source
|
$source
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -163,7 +170,7 @@ foreach ($data_output as $packingjob) {
|
|||||||
echo "<td>" . $packingjob->event . "</td>";
|
echo "<td>" . $packingjob->event . "</td>";
|
||||||
|
|
||||||
// Zeitspalte öffnen, wenn mindestens eine Anzeige aktiv ist
|
// Zeitspalte öffnen, wenn mindestens eine Anzeige aktiv ist
|
||||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) {
|
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin || ShowDispoEndOnCheckin) {
|
||||||
echo "<td>";
|
echo "<td>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +197,13 @@ foreach ($data_output as $packingjob) {
|
|||||||
echoMarkedTimeLine($PackingNoteDetail->date_redelivery, (int)$PackingNoteDetail->time_redelivery, $today, ShowTimesOnCheckin);
|
echoMarkedTimeLine($PackingNoteDetail->date_redelivery, (int)$PackingNoteDetail->time_redelivery, $today, ShowTimesOnCheckin);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) {
|
// Dispo Ende
|
||||||
|
if (ShowDispoEndOnCheckin && $DispoTimeDetail && $DispoTimeDetail->date_end != null) {
|
||||||
|
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) echo "<br>";
|
||||||
|
echoMarkedTimeLine($DispoTimeDetail->date_end, (int)$DispoTimeDetail->time_end, $today, ShowTimesOnCheckin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin || ShowDispoEndOnCheckin) {
|
||||||
echo "</td>";
|
echo "</td>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,25 +231,25 @@ foreach ($data_output as $packingjob) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($PackingNoteDetail->is_self_redeliver) {
|
if ($PackingNoteDetail->is_self_redeliver) {
|
||||||
echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
|
echo '<i class="'.SelbstabholerIcon.'"></i>';
|
||||||
} else {
|
} else {
|
||||||
if (preg_match('/' . KurierContainsText . '/i', $PackingNoteDetail->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="'.KurierIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||||
echo '<i class="fa-solid fa-truck fa-lg"></i>';
|
echo '<i class="'.SpeditionIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||||
echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
|
echo '<i class="'.DHLIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . LKWContainsText . '/i', $PackingNoteDetail->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="'.LKWIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . TransporterContainsText . '/i', $PackingNoteDetail->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="'.TransporterIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . PKWContainsText . '/i', $PackingNoteDetail->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="'.PKWIcon.'"></i>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo "</td>";
|
echo "</td>";
|
||||||
@@ -250,10 +263,21 @@ foreach ($data_output as $packingjob) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTimeFromSeconds(string $timestring) {
|
function getTimeFromSeconds($seconds): string {
|
||||||
$hours = floor($timestring / 3600);
|
if ($seconds === null || $seconds === '') {
|
||||||
$mins = floor($timestring / 60 % 60);
|
return '';
|
||||||
$secs = floor($timestring % 60);
|
}
|
||||||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
|
||||||
return $timeFormat;
|
// robust normalisieren: String/Float -> Float -> gerundet -> Int
|
||||||
|
$seconds = (int) round((float) $seconds);
|
||||||
|
|
||||||
|
if ($seconds < 0) {
|
||||||
|
$seconds = 0; // oder: return ''; je nach gewünschtem Verhalten
|
||||||
|
}
|
||||||
|
|
||||||
|
// reine Integer-Arithmetik, keine impliziten float->int Casts
|
||||||
|
$hours = intdiv($seconds, 3600);
|
||||||
|
$mins = intdiv($seconds % 3600, 60);
|
||||||
|
|
||||||
|
return sprintf('%02d:%02d', $hours, $mins);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ function dayStart(DateTimeImmutable $d): DateTimeImmutable {
|
|||||||
return $d->setTime(0, 0, 0);
|
return $d->setTime(0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, int $source): ?DateTimeImmutable {
|
function resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, $DispoTimeDetail, int $source): ?DateTimeImmutable {
|
||||||
$candidate = null;
|
$candidate = null;
|
||||||
switch ($source) {
|
switch ($source) {
|
||||||
case 1: $candidate = dt($packingjob->date_start ?? null);
|
case 1: $candidate = dt($packingjob->date_start ?? null);
|
||||||
@@ -50,6 +50,8 @@ function resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteD
|
|||||||
break;
|
break;
|
||||||
case 4: $candidate = dt($PackingNoteDetail->date_delivery ?? null);
|
case 4: $candidate = dt($PackingNoteDetail->date_delivery ?? null);
|
||||||
break;
|
break;
|
||||||
|
case 5: $candidate = dt($DispoTimeDetail->date_start ?? null);
|
||||||
|
break;
|
||||||
default: $candidate = null;
|
default: $candidate = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -131,11 +133,15 @@ if (
|
|||||||
}
|
}
|
||||||
|
|
||||||
$VorbereitungsTimeDetail = null;
|
$VorbereitungsTimeDetail = null;
|
||||||
|
$DispoTimeDetail = null;
|
||||||
|
|
||||||
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
||||||
if ($scheduledetail->name == Vorbereitungs_Zeitvariable) {
|
if ($scheduledetail->name == Vorbereitungs_Zeitvariable) {
|
||||||
$VorbereitungsTimeDetail = $scheduledetail;
|
$VorbereitungsTimeDetail = $scheduledetail;
|
||||||
}
|
}
|
||||||
|
if($scheduledetail->name == "Dispo"){
|
||||||
|
$DispoTimeDetail = $scheduledetail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Row-Marking Datum bestimmen (konfigurierbar) ---
|
// --- Row-Marking Datum bestimmen (konfigurierbar) ---
|
||||||
@@ -146,7 +152,7 @@ if (
|
|||||||
$limit = $todayFilter->modify('+' . (int) CheckOut_FutureDays . ' day');
|
$limit = $todayFilter->modify('+' . (int) CheckOut_FutureDays . ' day');
|
||||||
}
|
}
|
||||||
|
|
||||||
$markDate = resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, (int) CheckOutRowMarkSource);
|
$markDate = resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, $DispoTimeDetail, (int) CheckOutRowMarkSource);
|
||||||
|
|
||||||
if (CheckOut_FutureDays == -1 || ($markDate && $markDate <= $limit)) {
|
if (CheckOut_FutureDays == -1 || ($markDate && $markDate <= $limit)) {
|
||||||
$trClass = rowClassForDate($markDate, $today);
|
$trClass = rowClassForDate($markDate, $today);
|
||||||
@@ -165,7 +171,7 @@ if (
|
|||||||
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 (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout || ShowDispoStartOnCheckout) { echo "<td>";}
|
||||||
|
|
||||||
|
|
||||||
if (ShowCheckoutTimeOnCheckout && $packingjob->date_start != null) {
|
if (ShowCheckoutTimeOnCheckout && $packingjob->date_start != null) {
|
||||||
@@ -206,9 +212,14 @@ if (
|
|||||||
}
|
}
|
||||||
echoMarkedTimeLine($PackingNoteDetail->date_delivery, (int) $PackingNoteDetail->time_delivery, $today, ShowTimesOnCheckout);
|
echoMarkedTimeLine($PackingNoteDetail->date_delivery, (int) $PackingNoteDetail->time_delivery, $today, ShowTimesOnCheckout);
|
||||||
}
|
}
|
||||||
|
if (ShowDispoStartOnCheckout && $DispoTimeDetail->date_start && $DispoTimeDetail->time_start != null) {
|
||||||
|
|
||||||
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout) {
|
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout) {
|
||||||
|
echo "<br>";
|
||||||
|
}
|
||||||
|
echoMarkedTimeLine($DispoTimeDetail->date_start, (int) $DispoTimeDetail->time_start, $today, ShowTimesOnCheckout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout || ShowDispoStartOnCheckout) {
|
||||||
echo "</td>";
|
echo "</td>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,25 +247,25 @@ if (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($PackingNoteDetail->is_self_pickup) {
|
if ($PackingNoteDetail->is_self_pickup) {
|
||||||
echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
|
echo '<i class="'.SelbstabholerIcon.'"></i>';
|
||||||
} else {
|
} else {
|
||||||
if (preg_match('/' . KurierContainsText . '/i', $PackingNoteDetail->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="'.KurierIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||||
echo '<i class="fa-solid fa-truck fa-lg"></i>';
|
echo '<i class="'.SpeditionIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||||
echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
|
echo '<i class="'.DHLIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . LKWContainsText . '/i', $PackingNoteDetail->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="'.LKWIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . TransporterContainsText . '/i', $PackingNoteDetail->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="'.TransporterIcon.'"></i>';
|
||||||
}
|
}
|
||||||
if (preg_match('/' . PKWContainsText . '/i', $PackingNoteDetail->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="'.PKWIcon.'"></i>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo "</td>";
|
echo "</td>";
|
||||||
@@ -284,15 +295,35 @@ function echoMarkedTimeLine(?string $dateStr, ?int $timeSeconds, DateTimeImmutab
|
|||||||
echo $cls ? "</span>" : "";
|
echo $cls ? "</span>" : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
function getTimeFromSeconds(string $timestring) {
|
function getTimeFromSeconds(string $timestring) {
|
||||||
|
|
||||||
$hours = floor($timestring / 3600);
|
$hours = floor($timestring / 3600);
|
||||||
$mins = floor($timestring / 60 % 60);
|
$mins = floor(round($timestring / 60 % 60));
|
||||||
$secs = floor($timestring % 60);
|
$secs = floor(round($timestring % 60));
|
||||||
|
|
||||||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
||||||
|
|
||||||
return $timeFormat;
|
return $timeFormat;
|
||||||
|
}*/
|
||||||
|
function getTimeFromSeconds($seconds): string {
|
||||||
|
if ($seconds === null || $seconds === '') {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// robust normalisieren: String/Float -> Float -> gerundet -> Int
|
||||||
|
$seconds = (int) round((float) $seconds);
|
||||||
|
|
||||||
|
if ($seconds < 0) {
|
||||||
|
$seconds = 0; // oder: return ''; je nach gewünschtem Verhalten
|
||||||
|
}
|
||||||
|
|
||||||
|
// reine Integer-Arithmetik, keine impliziten float->int Casts
|
||||||
|
$hours = intdiv($seconds, 3600);
|
||||||
|
$mins = intdiv($seconds % 3600, 60);
|
||||||
|
|
||||||
|
return sprintf('%02d:%02d', $hours, $mins);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -402,18 +402,31 @@ $indentMm = max(0, ($lvl - 1) * $INDENT_MM_PER_LVL);
|
|||||||
|
|
||||||
// Name-Spalte: ohne Spacer-Tabelle bei Level 1 (indent=0), sonst mit
|
// Name-Spalte: ohne Spacer-Tabelle bei Level 1 (indent=0), sonst mit
|
||||||
if ($indentMm > 0) {
|
if ($indentMm > 0) {
|
||||||
$nameCellHtml = '
|
/*$nameCellHtml = '
|
||||||
<table cellspacing="0" cellpadding="0" width="100%">
|
<table cellspacing="0" cellpadding="0" width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:'.$indentMm.'mm; padding:0; margin:0;"></td>
|
<td style="width:'.$indentMm.'mm; padding:0; margin:0;"></td>
|
||||||
<td style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.'; padding:0; margin:0;">'.$text.'</td>
|
<td style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.'; padding:0; margin:0;">'.$text.'</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>';
|
</table>';*/
|
||||||
|
|
||||||
|
$indentMm = max(0, ($lvl - 1) * $INDENT_MM_PER_LVL);
|
||||||
|
$indentSpaces = str_repeat(' ', max(0, ($lvl - 1) * 4));
|
||||||
|
|
||||||
|
$nameCellHtml = '
|
||||||
|
<span style="
|
||||||
|
font-size:'.$textFontPx.'px;
|
||||||
|
line-height:'.$lineHeight.';
|
||||||
|
">'.$indentSpaces.$text.'</span>';
|
||||||
|
|
||||||
|
//Ende ersetzter Teil
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Level 1: direkt rendern
|
// Level 1: direkt rendern
|
||||||
$nameCellHtml = '<span style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.';">'.$text.'</span>';
|
$nameCellHtml = '<span style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.';">'.$text.'</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$bundleRows .= '
|
$bundleRows .= '
|
||||||
<tr>
|
<tr>
|
||||||
<!-- Anzahl -->
|
<!-- Anzahl -->
|
||||||
@@ -429,7 +442,7 @@ if ($indentMm > 0) {
|
|||||||
|
|
||||||
<!-- Name mit Einrückung -->
|
<!-- Name mit Einrückung -->
|
||||||
<td style="
|
<td style="
|
||||||
width:68%;
|
width:78%;
|
||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
padding:'.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm;">
|
padding:'.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm;">
|
||||||
'.$nameCellHtml.'
|
'.$nameCellHtml.'
|
||||||
@@ -437,7 +450,7 @@ if ($indentMm > 0) {
|
|||||||
|
|
||||||
<!-- Gewicht rechts mit extra Innenabstand -->
|
<!-- Gewicht rechts mit extra Innenabstand -->
|
||||||
<td style="
|
<td style="
|
||||||
width:20%;
|
width:10%;
|
||||||
text-align:right;
|
text-align:right;
|
||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
font-size:'.$weightFontPx.'px;
|
font-size:'.$weightFontPx.'px;
|
||||||
@@ -606,7 +619,8 @@ $metaWrap = '
|
|||||||
<tr>
|
<tr>
|
||||||
<td width="62%">'.$leftMeta.'</td>
|
<td width="62%">'.$leftMeta.'</td>
|
||||||
<td width="2%"></td>
|
<td width="2%"></td>
|
||||||
<td width="36%">'.$rightMeta.'</td>
|
<td width="36%"></td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>';
|
</table>';
|
||||||
|
|
||||||
@@ -779,6 +793,12 @@ function addMaterialsRecursive($Epi, array $materials, int $level, array &$bundl
|
|||||||
if ($level > $MAX_BUNDLE_LEVEL || empty($materials)) {
|
if ($level > $MAX_BUNDLE_LEVEL || empty($materials)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Nach "position" aufsteigend sortieren (pro Level)
|
||||||
|
usort($materials, function ($a, $b) {
|
||||||
|
$posA = isset($a->position) ? (int)$a->position : PHP_INT_MAX;
|
||||||
|
$posB = isset($b->position) ? (int)$b->position : PHP_INT_MAX;
|
||||||
|
return $posA <=> $posB;
|
||||||
|
});
|
||||||
|
|
||||||
foreach ($materials as $mat) {
|
foreach ($materials as $mat) {
|
||||||
$isFree = !empty($mat->is_free_material);
|
$isFree = !empty($mat->is_free_material);
|
||||||
|
|||||||
@@ -0,0 +1,795 @@
|
|||||||
|
<?php
|
||||||
|
error_reporting(E_ALL & ~E_DEPRECATED);
|
||||||
|
|
||||||
|
require('../config.php');
|
||||||
|
require('../EpiApi.php');
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/../vendor/tecnickcom/tcpdf/tcpdf.php';
|
||||||
|
date_default_timezone_set('Europe/Berlin');
|
||||||
|
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||||
|
$deviceNumber = (!empty($_GET['nr'])) ? '--' . trim($_GET['nr']) : '';
|
||||||
|
|
||||||
|
$Epi = new Epirent();
|
||||||
|
$product =json_decode($Epi->requestEpiApi('/v1/product/'.$id.'?cl=' . Epirent_Mandant))->payload[0];
|
||||||
|
$productImage =json_decode($Epi->requestEpiApi('/v1/product/image/'.$id.'?cl=' . Epirent_Mandant))->payload[0]->image_data;
|
||||||
|
|
||||||
|
|
||||||
|
function s(?string $v): string { return trim((string)$v); }
|
||||||
|
function fnum(?float $v, int $dec = 2): string {
|
||||||
|
if ($v === null) return '';
|
||||||
|
$n = number_format((float)$v, $dec, ',', '.');
|
||||||
|
return rtrim(rtrim($n, '0'), ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set Variables
|
||||||
|
$productName = $product->name;
|
||||||
|
$productNumber = $product->product_no;
|
||||||
|
|
||||||
|
$bruttoWeightSum = $product->tech_data->weight_gro;
|
||||||
|
|
||||||
|
$bundle = buildBundleForProduct($Epi, $product);
|
||||||
|
|
||||||
|
/** Test-Daten
|
||||||
|
*/
|
||||||
|
$dimsBHT =$product->tech_data->width_gro." x ".$product->tech_data->depth_gro." x ".$product->tech_data->height_gro;
|
||||||
|
$volume = ((float)$product->tech_data->height_gro * (float)$product->tech_data->width_gro * (float)$product->tech_data->depth_gro)* 0.000001;
|
||||||
|
$storageLoc = $product->product_data->storage_location_nearby;
|
||||||
|
|
||||||
|
/** Layout */
|
||||||
|
$PAGE_SIZE = 'A4';
|
||||||
|
$MARGIN_LEFT = 8;
|
||||||
|
$MARGIN_TOP = 8;
|
||||||
|
$MARGIN_RIGHT = 8;
|
||||||
|
$MARGIN_BOTTOM = 10;
|
||||||
|
|
||||||
|
$CELL_PAD = 1.6;
|
||||||
|
$LINE_GRAY = '#efefef';
|
||||||
|
|
||||||
|
$LEVEL_MIN = 1; $LEVEL_MAX = 5;
|
||||||
|
$INDENT_PX_PER_LEVEL = 10;
|
||||||
|
$BASE_FONT_PX = 10;
|
||||||
|
$FONT_STEP_PX = 1.5;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** TCPDF */
|
||||||
|
$pdf = new TCPDF('P', 'mm', $PAGE_SIZE, true, 'UTF-8', false);
|
||||||
|
|
||||||
|
// 1) Pfade sauber
|
||||||
|
$fontDir = realpath(__DIR__ . '/../src/assets/font') . '/';
|
||||||
|
$tcpdfFontsDir = defined('K_PATH_FONTS') ? K_PATH_FONTS : (dirname((new \ReflectionClass('TCPDF'))->getFileName()) . '/fonts/');
|
||||||
|
|
||||||
|
// 2) Rechte (einmalig sicherstellen)
|
||||||
|
// -> hast du schon gefixt
|
||||||
|
|
||||||
|
// 3) TTFs registrieren und Rückgabewerte merken
|
||||||
|
$fontReg = file_exists($fontDir.'Hurme3/HurmeGeometricSans3-Regular.ttf')
|
||||||
|
? TCPDF_FONTS::addTTFfont($fontDir.'Hurme3/HurmeGeometricSans3-Regular.ttf', 'TrueTypeUnicode', '', 96)
|
||||||
|
: false;
|
||||||
|
$fontBold = file_exists($fontDir.'Hurme1/HurmeGeometricSans1-Bold.ttf')
|
||||||
|
? TCPDF_FONTS::addTTFfont($fontDir.'Hurme1/HurmeGeometricSans1-Bold.ttf', 'TrueTypeUnicode', '', 96)
|
||||||
|
: false;
|
||||||
|
$fontItal = file_exists($fontDir.'Hurme3/HurmeGeometricSans3-Italic.ttf')
|
||||||
|
? TCPDF_FONTS::addTTFfont($fontDir.'Hurme3/HurmeGeometricSans3-Italic.ttf', 'TrueTypeUnicode', '', 96)
|
||||||
|
: false;
|
||||||
|
$fontBI = file_exists($fontDir.'Hurme1/HurmeGeometricSans1-BoldItalic.ttf')
|
||||||
|
? TCPDF_FONTS::addTTFfont($fontDir.'Hurme1/HurmeGeometricSans1-BoldItalic.ttf', 'TrueTypeUnicode', '', 96)
|
||||||
|
: false;
|
||||||
|
|
||||||
|
// 4) Fallback setzen
|
||||||
|
$baseFont = $fontReg ?: 'dejavusans';
|
||||||
|
|
||||||
|
// 5) Header/Footer und Defaults
|
||||||
|
$pdf->setHeaderFont([$baseFont, '', 10]);
|
||||||
|
$pdf->setFooterFont([$baseFont, '', 9]);
|
||||||
|
$pdf->SetDefaultMonospacedFont('courier');
|
||||||
|
|
||||||
|
// 6) *** AB JETZT KEIN dejavusans MEHR SETZEN! ***
|
||||||
|
$pdf->SetFont($baseFont, '', 10.5);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$pdf->SetCreator('VT-Media EpiWebview');
|
||||||
|
$pdf->SetAuthor('VT-Media');
|
||||||
|
$pdf->SetTitle('Kistenetikett small'.$productName);
|
||||||
|
$pdf->SetSubject('Kistenetikett small');
|
||||||
|
|
||||||
|
$pdf->setPrintHeader(false);
|
||||||
|
$pdf->setPrintFooter(false);
|
||||||
|
$pdf->SetMargins($MARGIN_LEFT, $MARGIN_TOP, $MARGIN_RIGHT);
|
||||||
|
$pdf->SetAutoPageBreak(true, $MARGIN_BOTTOM);
|
||||||
|
$pdf->setImageScale(1.0);
|
||||||
|
$pdf->setFontSubsetting(true);
|
||||||
|
$pdf->setCellPadding($CELL_PAD);
|
||||||
|
|
||||||
|
$pdf->AddPage();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --- LOGO in fester Box mit Rahmen, zentriert & verzerrungsfrei ---
|
||||||
|
$logoPath = __DIR__ . Labelprint_Logopath;
|
||||||
|
|
||||||
|
$startX = $pdf->GetX();
|
||||||
|
$startY = $pdf->GetY();
|
||||||
|
|
||||||
|
// feste Rahmen-Größe (in mm)
|
||||||
|
$frameW = 20.0; // Gesamtbreite des Rahmens
|
||||||
|
$frameH = 9.8; // Gesamthöhe des Rahmens
|
||||||
|
$frameOffsetX = 1.4; // Abstand vom linken Margin (wie vorher genutzt)
|
||||||
|
$frameOffsetY = 1.5; // Abstand von oben (wie vorher genutzt)
|
||||||
|
|
||||||
|
// Rahmen-Position
|
||||||
|
$frameX = $startX + $frameOffsetX;
|
||||||
|
$frameY = $startY + $frameOffsetY;
|
||||||
|
|
||||||
|
// Rahmen zeichnen
|
||||||
|
$pdf->SetLineWidth(0.3);
|
||||||
|
$pdf->Rect($frameX, $frameY, $frameW, $frameH, 'D');
|
||||||
|
|
||||||
|
// Logo innerhalb des Rahmens mit Innenabstand
|
||||||
|
$innerPad = 1.2; // mm Luft im Rahmen
|
||||||
|
$maxW = $frameW - 2 * $innerPad;
|
||||||
|
$maxH = $frameH - 2 * $innerPad;
|
||||||
|
|
||||||
|
if (is_file($logoPath)) {
|
||||||
|
// Seitenverhältnis ermitteln
|
||||||
|
$imgWpx = $imgHpx = 0;
|
||||||
|
if (@list($imgWpx, $imgHpx) = @getimagesize($logoPath)) {
|
||||||
|
$ratio = ($imgHpx > 0) ? ($imgWpx / $imgHpx) : 1.0;
|
||||||
|
|
||||||
|
// proportional in den Rahmen einpassen
|
||||||
|
if ($maxW / $maxH > $ratio) {
|
||||||
|
$drawH = $maxH;
|
||||||
|
$drawW = $maxH * $ratio;
|
||||||
|
} else {
|
||||||
|
$drawW = $maxW;
|
||||||
|
$drawH = $maxW / $ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// zentrieren
|
||||||
|
$imgX = $frameX + ($frameW - $drawW) / 2;
|
||||||
|
$imgY = $frameY + ($frameH - $drawH) / 2;
|
||||||
|
|
||||||
|
// ausgeben
|
||||||
|
$pdf->Image($logoPath, $imgX, $imgY, $drawW, $drawH, '', '', '', false, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$afterLogoX = $frameX + $frameW - 1.4; // gleicher Abstand wie zuvor, jetzt basierend auf Rahmenbreite
|
||||||
|
$afterLogoBottomY = $frameY + $frameH; // falls du unten bündig etwas brauchst
|
||||||
|
$pdf->SetXY($afterLogoX, $startY); // Cursor rechts neben dem Logo positionieren
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Feste Box rechts neben dem Logo (direkt bündig)
|
||||||
|
$addrBoxW = 0.0; // feste Breite (mm)
|
||||||
|
$addrBoxH = 0.0; // feste Höhe (mm)
|
||||||
|
$addrPad = 0.5; // Innenabstand Inhalt → Rahmen (mm)
|
||||||
|
|
||||||
|
// bündig rechts neben dem Logo-Rahmen starten
|
||||||
|
$addrX = $frameX + $frameW; // kein zusätzlicher Abstand
|
||||||
|
$addrY = $startY+1.5; // gleiche Oberkante wie Logo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$pdf->StopTransform();
|
||||||
|
|
||||||
|
// Cursor steht bereits auf: $pdf->SetXY($addrX + $addrBoxW, $addrY);
|
||||||
|
|
||||||
|
// 1) HTML OHNE border (sonst Doppelrahmen)
|
||||||
|
$productionHtml = '
|
||||||
|
<div style="line-height:1.3; font-size:4px; margin:0; padding:0;">
|
||||||
|
<b>Produktion</b><br/><br><br><br>
|
||||||
|
</div>';
|
||||||
|
|
||||||
|
// 2) Feste Box rechts neben der Adress-Box
|
||||||
|
$prodX = $addrX + $addrBoxW; // bündig rechts neben Adresse
|
||||||
|
$prodY = $addrY; // gleiche Oberkante
|
||||||
|
// Breite: restliche Seitenbreite bis zum rechten Rand
|
||||||
|
$prodW = 27;
|
||||||
|
// Höhe: fix (anpassen nach Wunsch)
|
||||||
|
$prodH = 10.0; // mm
|
||||||
|
$prodPad = 0; // Innenabstand
|
||||||
|
|
||||||
|
// 3) EIN sichtbarer Rahmen zeichnen
|
||||||
|
$pdf->SetLineWidth(0.3);
|
||||||
|
$pdf->Rect($prodX, $prodY, $prodW, $prodH, 'D');
|
||||||
|
|
||||||
|
// 4) Inhalt in die Box schreiben und clippen (Überhang abgeschnitten)
|
||||||
|
$pdf->StartTransform();
|
||||||
|
$pdf->Rect($prodX, $prodY, $prodW, $prodH, 'CNZ'); // Clip auf Box setzen
|
||||||
|
|
||||||
|
$pdf->writeHTMLCell(
|
||||||
|
$prodW - 2*$prodPad, // feste Breite des Inhalts
|
||||||
|
$prodH - 2*$prodPad, // feste Höhe des Inhalts
|
||||||
|
$prodX + $prodPad, // x mit Innenabstand
|
||||||
|
$prodY + $prodPad, // y mit Innenabstand
|
||||||
|
$productionHtml, // Inhalt (ohne border!)
|
||||||
|
0, // kein Border (Rahmen haben wir schon)
|
||||||
|
0, // ln
|
||||||
|
false, // fill
|
||||||
|
true, // reseth
|
||||||
|
'L', // align
|
||||||
|
true // autopadding
|
||||||
|
);
|
||||||
|
|
||||||
|
$pdf->StopTransform();
|
||||||
|
|
||||||
|
// 5) (Optional) Cursor direkt rechts neben der Box positionieren
|
||||||
|
$pdf->SetXY($prodX + $prodW, $prodY);
|
||||||
|
|
||||||
|
// === Name-Box: feste Größe, Text bricht, kein Doppelrahmen ===
|
||||||
|
|
||||||
|
// Breite: gesamte Restbreite bis zum rechten Rand (unter Adresse + Produktion)
|
||||||
|
$nameX = $frameX; // beginnt rechts neben dem Logo/Adressbereich
|
||||||
|
$nameW = 67.1;
|
||||||
|
|
||||||
|
// Höhe & Padding der Box (anpassen nach Wunsch)
|
||||||
|
$nameBoxH = 8; // mm - feste Höhe
|
||||||
|
$namePad = 0.4; // mm - Innenabstand
|
||||||
|
|
||||||
|
// Y: direkt unter die höhere der beiden Boxen (Adresse vs. Produktion)
|
||||||
|
$yAfterRow = max($addrY + $addrBoxH, $prodY + $prodH) + 0.0; // +2 mm Abstand
|
||||||
|
$nameY = $yAfterRow;
|
||||||
|
|
||||||
|
// Inhalt OHNE border (sonst Doppelrahmen)
|
||||||
|
$nameHtml = '
|
||||||
|
<div style="font-size:8px; font-weight:700; line-height:1.3; margin:0; padding:0;">' . htmlspecialchars($productName) . '
|
||||||
|
</div>';
|
||||||
|
|
||||||
|
// 1) Sichtbaren Rahmen zeichnen (feste Größe)
|
||||||
|
$pdf->SetLineWidth(0.3);
|
||||||
|
$pdf->Rect($nameX, $nameY, $nameW, $nameBoxH, 'D');
|
||||||
|
|
||||||
|
// 2) Inhalt in die Box schreiben und clippen
|
||||||
|
$pdf->StartTransform();
|
||||||
|
$pdf->Rect($nameX, $nameY, $nameW, $nameBoxH, 'CNZ'); // Clip auf Box setzen
|
||||||
|
|
||||||
|
$pdf->writeHTMLCell(
|
||||||
|
$nameW - 2*$namePad, // feste Breite für den Text
|
||||||
|
$nameBoxH - 2*$namePad, // feste Höhe für den Text
|
||||||
|
$nameX + $namePad, // x mit Innenabstand
|
||||||
|
$nameY + $namePad, // y mit Innenabstand
|
||||||
|
$nameHtml,
|
||||||
|
0, // kein HTML-Border
|
||||||
|
0, // ln
|
||||||
|
false, // fill
|
||||||
|
true, // reseth
|
||||||
|
'L', // align
|
||||||
|
true // autopadding
|
||||||
|
);
|
||||||
|
|
||||||
|
$pdf->StopTransform();
|
||||||
|
|
||||||
|
// Optional: Cursor unter die Box setzen (falls du danach im Fluss weitermachen willst)
|
||||||
|
$pdf->SetXY($nameX, $nameY + $nameBoxH);
|
||||||
|
|
||||||
|
// 1) Box-Geometrie (immer gleich groß)
|
||||||
|
$pnBoxW = 20.0; // mm – Gesamtbreite der Box
|
||||||
|
$pnBoxH = 10.0; // mm – Gesamthöhe der Box
|
||||||
|
$pnBoxPad = 0.2; // mm – Innenabstand innerhalb der Box
|
||||||
|
|
||||||
|
// Position der Box (aktuell am rechten Rand oben; passe frei an)
|
||||||
|
$pnBoxX = 56.5; // bündig am rechten Rand
|
||||||
|
$pnBoxY = $startY+1.5; // gleiche Oberkante wie Kopf
|
||||||
|
|
||||||
|
// 2) Sichtbaren Rahmen zeichnen (einmalig)
|
||||||
|
$pdf->SetLineWidth(0.3);
|
||||||
|
$pdf->Rect($pnBoxX, $pnBoxY, $pnBoxW, $pnBoxH, 'D');
|
||||||
|
|
||||||
|
// 3) Clipping aktivieren, damit Inhalt in der Box bleibt
|
||||||
|
$pdf->StartTransform();
|
||||||
|
$pdf->Rect($pnBoxX, $pnBoxY, $pnBoxW, $pnBoxH, 'CNZ'); // Clip auf die Box
|
||||||
|
|
||||||
|
// 4) Produktnummer: frei verschiebbar innerhalb der Box
|
||||||
|
$pnCombined = trim($productNumber . $deviceNumber);
|
||||||
|
$pnText = htmlspecialchars($pnCombined !== '' ? $pnCombined : 'N/A');
|
||||||
|
|
||||||
|
// Offsets innerhalb der Box – hier stellst du die Position ein
|
||||||
|
$pnTextOffsetX = -2.9; // mm von linker Innenkante
|
||||||
|
$pnTextOffsetY = 2; // mm von oberer Innenkante
|
||||||
|
$pnTextW = 0; // nutzbare Breite
|
||||||
|
$pnTextH = 8.0; // feste Höhe für den PN-Textbereich (mm)
|
||||||
|
|
||||||
|
$pnHtml = '
|
||||||
|
<div style="font-size:8px; line-height:1.25; margin:0; padding:0;">
|
||||||
|
'.$pnText.'
|
||||||
|
</div>';
|
||||||
|
|
||||||
|
// PN-Text rendern (ohne HTML-Border!)
|
||||||
|
$pdf->writeHTMLCell(
|
||||||
|
$pnTextW, // Breite Textbereich
|
||||||
|
$pnTextH, // Höhe Textbereich
|
||||||
|
$pnBoxX + $pnBoxPad + $pnTextOffsetX, // X in der Box
|
||||||
|
$pnBoxY + $pnBoxPad + $pnTextOffsetY, // Y in der Box
|
||||||
|
$pnHtml,
|
||||||
|
0, 0, false, true, 'L', true
|
||||||
|
);
|
||||||
|
|
||||||
|
// 5) QR-Code: frei verschiebbar innerhalb der Box, verzerrungsfrei per size
|
||||||
|
$qrData = trim((string)($productNumber . $deviceNumber));
|
||||||
|
if ($qrData === '') {
|
||||||
|
$qrData = 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
$qrSize = 7.8; // mm – Kantenlänge des QR
|
||||||
|
$qrOffsetX = 11; // mm von linker Innenkante
|
||||||
|
$qrOffsetY = 1.2; // mm von oberer Innenkante
|
||||||
|
|
||||||
|
$qrX = $pnBoxX + $qrOffsetX;
|
||||||
|
$qrY = $pnBoxY + $qrOffsetY;
|
||||||
|
|
||||||
|
$qrStyle = [
|
||||||
|
'border' => 0,
|
||||||
|
'vpadding' => 0,
|
||||||
|
'hpadding' => 0,
|
||||||
|
'fgcolor' => [0,0,0],
|
||||||
|
'bgcolor' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
// QR rendern (Warnings/Deprecated für diesen Block stumm schalten)
|
||||||
|
$_old_reporting = error_reporting();
|
||||||
|
error_reporting($_old_reporting & ~(E_WARNING | E_DEPRECATED | E_NOTICE));
|
||||||
|
$pdf->write2DBarcode($qrData, 'QRCODE,H', $qrX, $qrY, $qrSize, $qrSize, $qrStyle, 'N');
|
||||||
|
error_reporting($_old_reporting);
|
||||||
|
|
||||||
|
// 6) Clipping beenden
|
||||||
|
$pdf->StopTransform();
|
||||||
|
|
||||||
|
// 7) Optional: Cursor unter die PN-Box setzen, falls du im Fluss weiter willst
|
||||||
|
$pdf->SetXY($pnBoxX, $pnBoxY + $pnBoxH);
|
||||||
|
|
||||||
|
/** Bundle */
|
||||||
|
|
||||||
|
$bundleHeader = '<div style=" background-color: black; color: white; font-size:7px; font-weight:700;line-height:1.4; margin:4px 0 3px 0;"> Bundleinhalt</div>';
|
||||||
|
$pdf->writeHTMLCell(40, 50, 7.65, 26, $bundleHeader, 0, 0, 0, true, 'L', true);
|
||||||
|
$bundleHeaderFillable = '<div style="border: 1px solid black; line-heigt:0.6"></div>';
|
||||||
|
//$pdf->writeHTMLCell(130.8, 39.8, 74.5, 46, $bundleHeaderFillable, 0, 0, 0, false, 'L', true);
|
||||||
|
drawFixedBox($pdf, 46.0, 27.5, 30.5, 3.45);
|
||||||
|
|
||||||
|
|
||||||
|
$bundleRows = '';
|
||||||
|
|
||||||
|
$BASE_FONT_PX = 7.0; // Level 1
|
||||||
|
$FONT_STEP_PX = 1; // je Ebene kleiner
|
||||||
|
$MIN_FONT_PX = 3.0;
|
||||||
|
$LINE_HEIGHT_BASE = 0.35; // Basis Zeilenhöhe
|
||||||
|
$LINE_HEIGHT_STEP = 0.05; // je Ebene etwas kompakter
|
||||||
|
$ROW_PAD_V_MM = 0.15; // vertikales Padding je Zeile
|
||||||
|
$ROW_PAD_H_MM = 0.0; // horizontales Padding
|
||||||
|
|
||||||
|
$WEIGHT_PAD_RIGHT_MM = 1.6; // mehr Abstand zum rechten Rand
|
||||||
|
$INDENT_MM_PER_LVL = 1.8; // Einrückung je Ebene in mm (stabil!)
|
||||||
|
|
||||||
|
foreach ($bundle as $item) {
|
||||||
|
$qty = (int)($item['qty'] ?? 1);
|
||||||
|
$text = htmlspecialchars($item['text'] ?? '');
|
||||||
|
$lvl = max($LEVEL_MIN, min($LEVEL_MAX, (int)($item['level'] ?? 1)));
|
||||||
|
$wkg = isset($item['weight_kg']) ? (float)$item['weight_kg'] : null;
|
||||||
|
|
||||||
|
$textFontPx = max($MIN_FONT_PX, $BASE_FONT_PX - ($lvl - 1) * $FONT_STEP_PX);
|
||||||
|
$weightFontPx = max($MIN_FONT_PX, $BASE_FONT_PX - ($lvl - 1) * $FONT_STEP_PX);
|
||||||
|
$lineHeight = max(1.0, $LINE_HEIGHT_BASE - ($lvl - 1) * $LINE_HEIGHT_STEP);
|
||||||
|
|
||||||
|
// Einrückung robust über mm – aber nur Tabelle nutzen, wenn > 0 mm
|
||||||
|
$indentMm = max(0, ($lvl - 1) * $INDENT_MM_PER_LVL);
|
||||||
|
|
||||||
|
// Name-Spalte: ohne Spacer-Tabelle bei Level 1 (indent=0), sonst mit
|
||||||
|
if ($indentMm > 0) {
|
||||||
|
/*$nameCellHtml = '
|
||||||
|
<table cellspacing="0" cellpadding="0" width="100%">
|
||||||
|
<tr>
|
||||||
|
<td style="width:'.$indentMm.'mm; padding:0; margin:0;"></td>
|
||||||
|
<td style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.'; padding:0; margin:0;">'.$text.'</td>
|
||||||
|
</tr>
|
||||||
|
</table>';*/
|
||||||
|
|
||||||
|
$indentMm = max(0, ($lvl - 1) * $INDENT_MM_PER_LVL);
|
||||||
|
$indentSpaces = str_repeat(' ', max(0, ($lvl - 1) * 4));
|
||||||
|
|
||||||
|
$nameCellHtml = '
|
||||||
|
<span style="
|
||||||
|
font-size:'.$textFontPx.'px;
|
||||||
|
line-height:'.$lineHeight.';
|
||||||
|
">'.$indentSpaces.$text.'</span>';
|
||||||
|
|
||||||
|
//Ende ersetzter Teil
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Level 1: direkt rendern
|
||||||
|
$nameCellHtml = '<span style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.';">'.$text.'</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$bundleRows .= '
|
||||||
|
<tr>
|
||||||
|
<!-- Anzahl -->
|
||||||
|
<td style="
|
||||||
|
line-height: 10px;
|
||||||
|
width:8%;
|
||||||
|
text-align:right;
|
||||||
|
vertical-align:middle;
|
||||||
|
font-size:'.$textFontPx.'px;
|
||||||
|
|
||||||
|
padding:'.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm;">
|
||||||
|
'.$qty.'
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Name mit Einrückung -->
|
||||||
|
<td style="
|
||||||
|
line-height: 8px;
|
||||||
|
width:88%;
|
||||||
|
vertical-align:middle;
|
||||||
|
padding:'.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm;">
|
||||||
|
'.$nameCellHtml.'
|
||||||
|
</td>
|
||||||
|
|
||||||
|
|
||||||
|
</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$bundleHtml = '
|
||||||
|
<table cellspacing="0" cellpadding="0" style="border:1px solid #000; border-collapse:collapse;" width="100%">
|
||||||
|
<tbody>'.$bundleRows.'</tbody>
|
||||||
|
</table>';
|
||||||
|
|
||||||
|
$pdf->writeHTMLCell(
|
||||||
|
73.1,
|
||||||
|
0,
|
||||||
|
7.8,
|
||||||
|
29.3,
|
||||||
|
$bundleHtml,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
'L',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** ---------- Meta-Block dynamisch unterhalb der Bundle-Tabelle ---------- */
|
||||||
|
|
||||||
|
// Höhe der zuletzt gerenderten Bundle-Tabelle holen
|
||||||
|
$bundleH = $pdf->getLastH();
|
||||||
|
|
||||||
|
// Y-Position direkt nach der Bundle-Tabelle (mit 3 mm Abstand)
|
||||||
|
$metaY = 26.2+ $bundleH; // 50 = dein bisheriges Y der Bundle-Tabelle
|
||||||
|
$metaX = $MARGIN_LEFT;
|
||||||
|
|
||||||
|
|
||||||
|
// Falls Meta-Block zu nah am Seitenende wäre → neue Seite
|
||||||
|
$usableBottom = $pdf->getPageHeight() - $MARGIN_BOTTOM;
|
||||||
|
if ($metaY > $usableBottom - 40) { // 40 mm Puffer für Meta
|
||||||
|
$pdf->AddPage();
|
||||||
|
$metaY = $MARGIN_TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Inhalt wie gehabt ---
|
||||||
|
$leftMeta = '
|
||||||
|
<table cellspacing="0" cellpadding="'.$CELL_PAD.'" width="100%">
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Lagerplatz mit innerer Tabelle (optisch perfekt ausgerichtet) -->
|
||||||
|
<tr>
|
||||||
|
<td width="45%" border="1" style="padding-left:20px;">
|
||||||
|
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
|
<tr>
|
||||||
|
<td style="font-size: 8px" width="100%">Lagerplatz</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td width="55%" border="1" style="font-size: 8px"><b> '.htmlspecialchars($storageLoc).'</b></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>';
|
||||||
|
|
||||||
|
// ---- Koordinaten der Meta-Zeile bestimmen (nimm deine Werte) ----
|
||||||
|
// Falls du sie schon berechnet hast, nutze deine $metaX, $metaY, $metaW.
|
||||||
|
// Beispiel, wie du sie meist setzt:
|
||||||
|
$metaX = $MARGIN_LEFT;
|
||||||
|
$metaW = 111.2;
|
||||||
|
// $metaY hast du zuvor als Ziel-Y für den Meta-Block berechnet:
|
||||||
|
/// $metaY = ... (z.B. 50 + $bundleH + 3);
|
||||||
|
|
||||||
|
// ---- Spaltenbreiten wie in deinem metaWrap: 62% | 2% | 36% ----
|
||||||
|
$leftW = $metaW * 0.62;
|
||||||
|
$gapW = $metaW * 0.02;
|
||||||
|
$rightW = $metaW * 0.36;
|
||||||
|
|
||||||
|
// Obere linke Ecke der rechten Spalte:
|
||||||
|
$rightX = $metaX + $leftW + $gapW;
|
||||||
|
$rightY = $metaY;
|
||||||
|
|
||||||
|
// ---- Fester Rahmen in der rechten Spalte ----
|
||||||
|
|
||||||
|
$metaWrap = '
|
||||||
|
<table cellspacing="0" cellpadding="0" border="0" width="100%" style="margin-top:2px;">
|
||||||
|
<tr>
|
||||||
|
<td width="62%">'.$leftMeta.'</td>
|
||||||
|
<td width="2%"></td>
|
||||||
|
<td width="36%"></td>
|
||||||
|
</tr>
|
||||||
|
</table>';
|
||||||
|
|
||||||
|
// Meta-Block exakt an berechneter Position ausgeben
|
||||||
|
$pdf->writeHTMLCell($metaW, 0, $metaX, $metaY, $metaWrap, 0, 0, 0, true, 'L', true);
|
||||||
|
/** Output */
|
||||||
|
// Falls trotz allem vorher etwas ausgegeben wurde (Warnings), Buffer leeren:
|
||||||
|
if (ob_get_length()) { ob_end_clean(); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Äußerer Rahmen, Größenberechnung
|
||||||
|
|
||||||
|
// 1) Höhe der META-Zelle (zuletzt geschrieben) holen:
|
||||||
|
$metaH = $pdf->getLastH();
|
||||||
|
|
||||||
|
// 2) Y der Bundle-Tabelle kennst du (50.8 in deinem Code) + deren Höhe:
|
||||||
|
$bundleY = 33.7; // dein fixer Y für die Bundle-Tabelle
|
||||||
|
// $bundleH hast du bereits oben: $bundleH = $pdf->getLastH();
|
||||||
|
|
||||||
|
// 3) Bottom-Kanten aller Boxen berechnen:
|
||||||
|
$bottoms = [
|
||||||
|
$frameY + $frameH, // Logo-Box
|
||||||
|
$addrY + $addrBoxH, // Adress-Box
|
||||||
|
$prodY + $prodH, // Produktion-Box
|
||||||
|
$nameY + $nameBoxH, // Name-Box
|
||||||
|
$pnBoxY + $pnBoxH, // PN/QR-Box
|
||||||
|
$bundleY + $bundleH, // Bundle-Tabelle
|
||||||
|
$metaY + $metaH, // Meta-Block (linke Tabelle + rechter Bildrahmen)
|
||||||
|
];
|
||||||
|
|
||||||
|
// 4) Start-Y des Contentbereichs: oben an deinem Kopf beginnen
|
||||||
|
$outerY = min($frameY, $addrY, $prodY, $nameY, $pnBoxY, 46.0 /*Bundle-Header-Y*/, $metaY);
|
||||||
|
|
||||||
|
// 5) Untere Kante ist die größte Bottom-Kante:
|
||||||
|
$outerBottom = max($bottoms);
|
||||||
|
|
||||||
|
// 6) Außenrahmen-Geometrie:
|
||||||
|
$outerX = $MARGIN_LEFT+1.2;
|
||||||
|
$outerW = 67.5;
|
||||||
|
$outerH = max(2, $outerBottom - $outerY)-1.2; // Mindeshöhe absichern
|
||||||
|
|
||||||
|
// 7) Dicke Linie zeichnen (z.B. 1.2 mm)
|
||||||
|
drawFixedBox($pdf, $outerX, $outerY, $outerW, $outerH, 1, '', 0, false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$filename = 'Kistenetikett_'.preg_replace('/\s+/', '_', trim($productName)).'_'.$productNumber.$deviceNumber.'_'.date('Ymd_His').'.pdf';
|
||||||
|
$pdf->Output($filename, 'I');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet eine flexible, leere oder beschriftete Box mit TCPDF.
|
||||||
|
*
|
||||||
|
* @param TCPDF $pdf Referenz auf dein TCPDF-Objekt
|
||||||
|
* @param float $x Linke obere Ecke (mm)
|
||||||
|
* @param float $y Obere Position (mm)
|
||||||
|
* @param float $w Breite (mm)
|
||||||
|
* @param float $h Höhe (mm)
|
||||||
|
* @param float $border Linienstärke in mm (z. B. 0.3)
|
||||||
|
* @param string $text (Optional) Inhalt oder Beschriftung
|
||||||
|
* @param float $pad Innenabstand in mm
|
||||||
|
* @param bool $fill true = grau hinterlegt, false = leer
|
||||||
|
* @param string|null $style (Optional) Linienstil – 'solid', 'dashed', 'dotted'
|
||||||
|
*/
|
||||||
|
function drawFixedBox(
|
||||||
|
TCPDF $pdf,
|
||||||
|
float $x,
|
||||||
|
float $y,
|
||||||
|
float $w,
|
||||||
|
float $h,
|
||||||
|
float $border = 0.3,
|
||||||
|
string $text = '',
|
||||||
|
float $pad = 1.5,
|
||||||
|
bool $fill = false,
|
||||||
|
?string $style = 'solid'
|
||||||
|
): void {
|
||||||
|
// Farbe für Füllung (hellgrau, wenn aktiviert)
|
||||||
|
$fillColor = [240, 240, 240];
|
||||||
|
|
||||||
|
// Linienstil (falls angegeben)
|
||||||
|
switch ($style) {
|
||||||
|
case 'dashed':
|
||||||
|
$pdf->SetLineStyle(['width' => $border, 'dash' => '3,2']);
|
||||||
|
break;
|
||||||
|
case 'dotted':
|
||||||
|
$pdf->SetLineStyle(['width' => $border, 'dash' => '1,2']);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$pdf->SetLineWidth($border);
|
||||||
|
$pdf->SetLineStyle(['width' => $border]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Rahmen (und ggf. Füllung)
|
||||||
|
if ($fill) {
|
||||||
|
$pdf->SetFillColorArray($fillColor);
|
||||||
|
$pdf->Rect($x, $y, $w, $h, 'DF'); // Draw + Fill
|
||||||
|
} else {
|
||||||
|
$pdf->Rect($x, $y, $w, $h, 'D'); // nur Rahmen
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Optionaler Textinhalt – sauber eingepasst
|
||||||
|
if (trim($text) !== '') {
|
||||||
|
$pdf->StartTransform();
|
||||||
|
$pdf->Rect($x, $y, $w, $h, 'CNZ'); // Clip auf Box setzen
|
||||||
|
|
||||||
|
$pdf->writeHTMLCell(
|
||||||
|
$w - 2*$pad, $h - 2*$pad,
|
||||||
|
$x + $pad, $y + $pad,
|
||||||
|
'<div style="font-size:9px; line-height:1.2;">'.htmlspecialchars($text).'</div>',
|
||||||
|
0, 0, false, true, 'L', true
|
||||||
|
);
|
||||||
|
|
||||||
|
$pdf->StopTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linienstil zurücksetzen
|
||||||
|
$pdf->SetLineStyle(['width' => 0.3, 'dash' => 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produkt-Details mit Cache holen (vermeidet doppelte API-Calls).
|
||||||
|
*
|
||||||
|
* @param Epirent $Epi
|
||||||
|
* @param int|string $productPk
|
||||||
|
* @param array $cache Referenz-Array für Produkt-Cache
|
||||||
|
* @return object|null
|
||||||
|
*/
|
||||||
|
function fetchProductDetailCached($Epi, $productPk, array &$cache) {
|
||||||
|
$key = (string)$productPk;
|
||||||
|
if (isset($cache[$key])) {
|
||||||
|
return $cache[$key];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$res = $Epi->requestEpiApi('/v1/product/' . $key . '?cl=' . Epirent_Mandant);
|
||||||
|
$payload = json_decode($res, false);
|
||||||
|
$prod = $payload->payload[0] ?? null;
|
||||||
|
if ($prod) {
|
||||||
|
$cache[$key] = $prod;
|
||||||
|
return $prod;
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// optional: loggen
|
||||||
|
}
|
||||||
|
$cache[$key] = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Rekursive Hilfsfunktion: fügt Materialien (und deren Submaterialien) zu $bundle hinzu
|
||||||
|
* und summiert alle Bruttogewichte in $bruttoWeightSum.
|
||||||
|
*
|
||||||
|
* @param Epirent $Epi
|
||||||
|
* @param array $materials Materialliste (vom Produkt)
|
||||||
|
* @param int $level aktuelle Ebene (1..MAX_BUNDLE_LEVEL)
|
||||||
|
* @param array &$bundle Ergebnisliste (wird befüllt)
|
||||||
|
* @param array &$cache Produkt-Cache
|
||||||
|
* @param array &$seenStack Schutz gegen Zyklen (Stack von Produkt-Keys)
|
||||||
|
* @param float $qtyMultiplier Multiplikator für Mengen aus übergeordneten Ebenen
|
||||||
|
* @param float &$bruttoWeightSum Summiert alle weight_gro-Werte × Menge
|
||||||
|
*/
|
||||||
|
function addMaterialsRecursive($Epi, array $materials, int $level, array &$bundle, array &$cache, array &$seenStack, float $qtyMultiplier = 1.0): void
|
||||||
|
{
|
||||||
|
global $bruttoWeightSum;
|
||||||
|
|
||||||
|
$MAX_BUNDLE_LEVEL = 5;
|
||||||
|
if ($level > $MAX_BUNDLE_LEVEL || empty($materials)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Nach "position" aufsteigend sortieren (pro Level)
|
||||||
|
usort($materials, function ($a, $b) {
|
||||||
|
$posA = isset($a->position) ? (int)$a->position : PHP_INT_MAX;
|
||||||
|
$posB = isset($b->position) ? (int)$b->position : PHP_INT_MAX;
|
||||||
|
return $posA <=> $posB;
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach ($materials as $mat) {
|
||||||
|
$isFree = !empty($mat->is_free_material);
|
||||||
|
$amount = isset($mat->amount) ? (float)$mat->amount : 1.0;
|
||||||
|
$effQty = $amount * $qtyMultiplier;
|
||||||
|
|
||||||
|
// 🟢 1. FREIES MATERIAL → nur Name + Menge, kein weiterer Drilldown
|
||||||
|
if ($isFree) {
|
||||||
|
$name = isset($mat->name) ? (string)$mat->name : '(Unbenanntes freies Material)';
|
||||||
|
$bundle[] = [
|
||||||
|
'qty' => $effQty,
|
||||||
|
'text' => $name,
|
||||||
|
'level' => $level,
|
||||||
|
'weight_kg' => null,
|
||||||
|
];
|
||||||
|
continue; // ⬅️ ganz wichtig: hier abbrechen
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🟡 2. "Normales" Produkt-Material
|
||||||
|
$childPk = $mat->mat_product_pk ?? null;
|
||||||
|
if (!$childPk) {
|
||||||
|
$bundle[] = [
|
||||||
|
'qty' => $effQty,
|
||||||
|
'text' => '(Unbekanntes Material)',
|
||||||
|
'level' => $level,
|
||||||
|
'weight_kg' => null,
|
||||||
|
];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zyklen-Schutz
|
||||||
|
$childKey = (string)$childPk;
|
||||||
|
if (in_array($childKey, $seenStack, true)) {
|
||||||
|
$bundle[] = [
|
||||||
|
'qty' => $effQty,
|
||||||
|
'text' => '[Zyklus erkannt bei Produkt ' . $childKey . ']',
|
||||||
|
'level' => $level,
|
||||||
|
'weight_kg' => null,
|
||||||
|
];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produktdetail abrufen (Cache-basiert)
|
||||||
|
$child = fetchProductDetailCached($Epi, $childPk, $cache);
|
||||||
|
|
||||||
|
// Basisinfos
|
||||||
|
$childName = $child->name ?? ('Produkt ' . $childKey);
|
||||||
|
$childWeightNet = $child->tech_data->weight_net ?? null;
|
||||||
|
$childWeightGross = $child->tech_data->weight_gro ?? null;
|
||||||
|
$childNumber = $child->product_no ?? null;
|
||||||
|
|
||||||
|
// 🧮 Bruttogewicht zur globalen Summe addieren
|
||||||
|
if (!empty($childWeightGross)) {
|
||||||
|
$bruttoWeightSum += $effQty * (float)$childWeightGross;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 📦 Anzeige: Artikelnummer in Klammern voranstellen
|
||||||
|
$displayName = $childNumber ? '(' . $childNumber . ') ' . $childName : $childName;
|
||||||
|
|
||||||
|
$bundle[] = [
|
||||||
|
'qty' => $effQty,
|
||||||
|
'text' => (string)$displayName,
|
||||||
|
'level' => $level,
|
||||||
|
'weight_kg' => $childWeightNet ? (float)$childWeightNet : null,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 🔁 Rekursion für Sub-Materialien
|
||||||
|
if ($child && !empty($child->materials) && $level < $MAX_BUNDLE_LEVEL) {
|
||||||
|
$seenStack[] = $childKey;
|
||||||
|
addMaterialsRecursive($Epi, (array)$child->materials, $level + 1, $bundle, $cache, $seenStack, $effQty);
|
||||||
|
array_pop($seenStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Einstieg: Baut das Bundle eines Produkts bis Tiefe MAX_BUNDLE_LEVEL
|
||||||
|
* und liefert zusätzlich die Bruttogewichtsumme zurück.
|
||||||
|
*
|
||||||
|
* @param Epirent $Epi
|
||||||
|
* @param object $product
|
||||||
|
* @param float &$bruttoWeightSum Rückgabe der aufsummierten weight_gro
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function buildBundleForProduct($Epi, $product, float &$bruttoWeightSum = 0.0): array
|
||||||
|
{
|
||||||
|
$bundle = [];
|
||||||
|
$cache = [];
|
||||||
|
$seen = [];
|
||||||
|
$materials = isset($product->materials) ? (array)$product->materials : [];
|
||||||
|
|
||||||
|
addMaterialsRecursive($Epi, $materials, 1, $bundle, $cache, $seen, 1.0, $bruttoWeightSum);
|
||||||
|
return $bundle;
|
||||||
|
}
|
||||||
@@ -19,6 +19,18 @@
|
|||||||
<div class="sb-nav-link-icon"><i class="fas fa-tags"></i></div>
|
<div class="sb-nav-link-icon"><i class="fas fa-tags"></i></div>
|
||||||
Labelprint
|
Labelprint
|
||||||
</a>
|
</a>
|
||||||
|
<a class="nav-link" href=imagecheck.php>
|
||||||
|
<div class="sb-nav-link-icon"><i class="fas fa-image"></i></div>
|
||||||
|
Imagecheck
|
||||||
|
</a>
|
||||||
|
<a class="nav-link" href=groupofgoodscheck.php>
|
||||||
|
<div class="sb-nav-link-icon"><i class="fas fa-layer-group"></i></div>
|
||||||
|
Warengruppencheck
|
||||||
|
</a>
|
||||||
|
<a class="nav-link" href=ROI.php>
|
||||||
|
<div class="sb-nav-link-icon"><i class="fas fa-comment-dollar"></i></div>
|
||||||
|
Return-Of-Invest
|
||||||
|
</a>
|
||||||
|
|
||||||
<div class="sb-sidenav-menu-heading">Addons</div>
|
<div class="sb-sidenav-menu-heading">Addons</div>
|
||||||
<a class="nav-link" target="_blank" href="../Packmonitor.php">
|
<a class="nav-link" target="_blank" href="../Packmonitor.php">
|
||||||
|
|||||||
Reference in New Issue
Block a user