Appearance
Manuelle Sortierung Modul
Das Modul dient zur manuellen Erfassung von Sortierergebnissen. Sortierklassen werden pro Warengruppe aus dem EDBS geladen und beim Erstellen eines Auftrags anhand der Warengruppe der Partie zugewiesen. Über eine Admin-Konfigurationsseite kann festgelegt werden, welche EDBS-Klassen pro Warengruppe aktiv sind.
Architektur
Modulstruktur
src/modules/manualSorting/
├── controllers/
│ ├── SortingController.php # Web-Ansicht für Sortierergebnisse
│ ├── admin/
│ │ └── ConfigController.php # Admin: Sortierklassen-Konfiguration
│ └── api/
│ └── SupplierController.php # Select2-Suche für Lieferanten
├── messages/
│ └── de/
│ └── manualsorting.php # Übersetzungen
├── migrations/ # Namespace: app\modules\manualSorting\migrations
│ ├── M260223075838AddManualSortingLicense.php
│ ├── M260223075858CreateManualSortingResultsTable.php
│ ├── M260223075926AddManualSortingUsedSortClassSetting.php
│ ├── M260223091644AddTimestampsToManualSortingResults.php
│ ├── M260223092035AddSettingForCurrentSortNumber.php
│ ├── M260223094957AddCompletedFlagToManualSortingResults.php
│ ├── M260223100040AddSortingJobPositionTableAndRenameManualSortingResults.php
│ ├── M260223101752AddForeignKeyForSortingJobs.php
│ ├── M260224075803ChangeAvailableClassesSettingToJsonType.php
│ ├── M260224095600ChangeChargeNumberToPartieNr.php
│ ├── M260225074054AddSortingJobDtoHtmlTemplateSetting.php
│ ├── M260225125753AdjustDataStructureToEDBSRRequirements.php
│ ├── M260303062016AddEdbsFetchedColumnsToTables.php
│ ├── M260316075051RemoveAvailableClassesSetting.php
│ ├── M260318073638AddInfoFieldsToManualSortingTables.php
│ ├── M260407135243AddWgColumn.php
│ ├── M260408072100AddColumnArtikelGrpKurz.php
│ ├── M260408090047AddActiveClassesSetting.php
│ └── M260408110723AddAverageWeightPerPackageColumn.php
├── models/
│ ├── ManualSortingJob.php # Sortierauftrag (Job)
│ └── SortingJobPosition.php # Einzelne Sortierklasse pro Job
├── views/
│ ├── sorting/
│ │ ├── index.php # GridView Sortierergebnisse
│ │ ├── edit.php # Sortierauftrag bearbeiten
│ │ └── _positions.php # Positionen-Teilansicht (ExpandRow)
│ └── admin/
│ └── config/
│ └── classes.php # Admin: Sortierklassen-Konfiguration
├── modules/
│ └── rest/
│ ├── Module.php # REST-Submodul Bootstrap
│ ├── controllers/
│ │ └── SortingController.php # REST API Endpunkte
│ └── models/
│ └── dto/
│ └── SortingJobDTO.php # API-Serialisierung
└── Module.php # Modul-Bootstrap, Sidebar, i18nDatenmodell
manual_sorting_job
Enthält die Sortieraufträge. Jeder Auftrag referenziert eine Partie aus dem EDBS.
| Spalte | Typ | Beschreibung |
|---|---|---|
| id | int | Primary Key |
| sortNumber | string(45) | Eindeutige Sortiernummer (z.B. 260014) |
| partieNr | string(255) | Partienummer aus dem EDBS |
| sortDate | date | Datum der Sortierung |
| modified | datetime | Automatischer Timestamp bei Änderung |
| completed | boolean | 0 = offen, 1 = abgeschlossen |
| count | int | Gebindeanzahl |
| supplierNr | string | Lieferantennummer |
| supplierName | string | Lieferantenname |
| fruitInfo | string | Frucht/Artikelgruppe |
| comment | string | Sortierbemerkung |
| WarenGrpKurz | string(45) | Warengruppe Kurzbezeichnung |
| ArtikelGrpKurz | string(45) | Artikelgruppe Kurzbezeichnung |
| averageWeightPerPackage | decimal | Durchschnittliches Gewicht pro Gebinde |
| edbs_fetched_date | datetime | Zeitpunkt des EDBS-Datenabrufs |
sorting_job_position
Positionen eines Sortierauftrags. Pro aktiver Klasse der Warengruppe wird eine Position erstellt.
| Spalte | Typ | Beschreibung |
|---|---|---|
| id | int | Primary Key |
| sortingJobId | int | FK zu manual_sorting_job.id (CASCADE) |
| sortClass | string(255) | Name der Sortierklasse (z.B. "Klasse I 65-75") |
| edbsFruitClassID | int | Sortierklassen-ID aus dem EDBS |
| sortResult | decimal(10,2) | Erfasstes Ergebnis (NULL = noch nicht erfasst) |
| modified | datetime | Automatischer Timestamp bei Änderung |
Beziehungen
manual_sorting_job 1 ──── * sorting_job_position
FK: sortingJobId → id (CASCADE/CASCADE)Sortiernummer-Vergabe
Eine Sortiernummer ist eine eindeutige Nummer, welche einen manuellen Sortierauftrag beschreibt. Sortiernummern werden sequentiell vergeben und im Setting current_sort_number gespeichert. Das Format ist JJNNNN:
JJ= zweistelliges JahrNNNN= vierstellige, aufsteigende Sequenz
php
// ManualSortingJob::createNewSortNumber()
$sortNumber = Yii::$app->settings->getSetting('current_sort_number', 'manualSorting');
$seq = substr($sortNumber, 2);
$sortNumber = (new DateTime())->format('y') . str_pad((int)$seq + 1, 4, '0', STR_PAD_LEFT);
Yii::$app->settings->saveSetting($sortNumber, 'current_sort_number', 'manualSorting');Beispiel: 260001, 260002, ..., 269999.
Auftragserstellung
Die Erstellung eines Sortierauftrags erfolgt über ManualSortingJob::createNewJob($partieNr, $count):
- Die Warengruppe der Partie wird aus dem EDBS gelesen (
USWLager::findOne) - Alle Sortierklassen für diese Warengruppe werden aus dem EDBS geladen (
SortierKlasse::findEdbs($key)) - Die aktiven Klassen werden anhand des
activeClasses-Settings gefiltert (siehe Aktive Sortierklassen) - Dabei wird geprüft, ob die konfigurierten aktiven Klassen tatsächlich noch im EDBS existieren (Schnittmenge)
- Ein neuer
ManualSortingJobmit generiertersortNumberwird angelegt - Für jede aktive Klasse wird eine
SortingJobPositionerstellt
Wenn keine Klassen für die Warengruppe im EDBS existieren oder nach dem Filtern keine aktiven Klassen übrig bleiben, wird eine InvalidDataException geworfen.
Aktive Sortierklassen
Sortierklassen werden aus dem EDBS geladen (SortierKlasse::findEdbs). Da nicht immer alle EDBS-Klassen in iScan benötigt werden, kann pro Warengruppe konfiguriert werden, welche Klassen aktiv sind.
Setting activeClasses
| Key | Modul | Typ | Beschreibung |
|---|---|---|---|
activeClasses | manualSorting | json | Aktive Sortierklassen-IDs pro Warengruppe |
Die Struktur ist ein JSON-Objekt mit Warengruppen-Kurzbezeichnungen als Schlüssel und Arrays von sortierklasseID-Werten:
json
{
"01": ["1", "3", "5"],
"10": ["2", "4"],
"010": ["10", "12", "15"]
}Wichtig: Warengruppen-Nummern werden immer als Strings behandelt, da numerisch ähnliche Kombinationen (z.B. "10" und "010") unterschiedliche Warengruppen darstellen. json_decode konvertiert numerische String-Schlüssel zu Integers — beim Auslesen müssen die Schlüssel daher explizit zurück zu Strings gecastet werden (siehe ConfigController::getActiveClasses()).
Wenn für eine Warengruppe kein Eintrag im Setting existiert, werden alle EDBS-Klassen als aktiv betrachtet.
Admin-Konfigurationsseite
Erreichbar unter /manualSorting/admin/config/classes (nur für Benutzer mit admin-Rolle).
Ablauf:
- Warengruppe aus Dropdown auswählen (Daten aus
ProductGroups::findEdbs()) - Alle Sortierklassen der gewählten Warengruppe werden aus dem EDBS geladen
- Klassen werden als GridView mit Checkboxen angezeigt (aktiviert = in iScan sichtbar)
- Beim Speichern wird geprüft, ob die ausgewählten Klassen-IDs noch im EDBS vorhanden sind
- Nicht mehr existierende Klassen werden mit einer Fehlermeldung gemeldet und nicht gespeichert
Validierung bei Auftragserstellung
Beim Erstellen eines neuen Auftrags (createNewJob) werden die aktiven Klassen gegen die aktuellen EDBS-Klassen validiert. Nur Klassen, die sowohl im activeClasses-Setting als auch im EDBS vorhanden sind, werden als Positionen erstellt.
Settings
| Key | Modul | Typ | Beschreibung |
|---|---|---|---|
current_sort_number | manualSorting | string | Aktuelle Sortiernummer für Sequenz |
activeClasses | manualSorting | json | Aktive Sortierklassen-IDs pro Warengruppe |
REST API
Authentifizierung via Bearer Token. Alle Endpunkte erfordern einen authentifizierten Benutzer (@).
| Methode | Endpunkt | Beschreibung |
|---|---|---|
| GET | /manualSorting/rest/sorting/get-job?sortNumber= | Einzelnen Auftrag mit Positionen laden |
| GET | /manualSorting/rest/sorting/list-open-jobs | Alle offenen Aufträge auflisten |
| POST | /manualSorting/rest/sorting/create-new-job?partieNr= | Neuen Auftrag für Partie erstellen |
| PUT | /manualSorting/rest/sorting/update-positions | Positionen eines Auftrags aktualisieren |
| POST | /manualSorting/rest/sorting/complete-job?sortNumber= | Auftrag abschließen |
create-new-job
Erstellt einen neuen Auftrag basierend auf der Partienummer. Liest die Warengruppe aus dem EDBS, filtert die Sortierklassen anhand der activeClasses-Konfiguration und erstellt Positionen für alle aktiven Klassen. Gibt den Auftrag mit Positionen als SortingJobDTO zurück.
update-positions
Erwartet im Body ein Objekt mit id (Job-ID) und positions-Array. Jede Position muss id, sortingJobId und sortResult enthalten. Validiert, dass alle Positionen zum angegebenen Job gehören.
complete-job
Schließt einen Auftrag ab. Prüft vorher, ob alle Positionen ein Ergebnis haben (sortResult !== null). Gibt einen Fehler zurück, wenn Positionen ohne Ergebnis existieren.
Web-Controller
SortingController
| Action | Methode | Beschreibung |
|---|---|---|
actionIndex | GET | GridView mit allen Sortieraufträgen (mit Datumsbereichsfilter, Lieferanten-Select2, Fruchtsuche) und expandierbaren Positionen |
actionExpandDetail | POST (AJAX) | Positionen eines Auftrags als Teilansicht laden (für ExpandRowColumn) |
actionEdit | GET/POST | Sortierauftrag bearbeiten (Positionen, Kommentar, etc.) |
actionComplete | POST | Auftrag abschließen (mit Validierung), Redirect auf Index |
admin/ConfigController
| Action | Methode | Beschreibung |
|---|---|---|
actionClasses | GET | Konfigurationsseite für aktive Sortierklassen anzeigen |
actionClasses | POST | Aktive Sortierklassen für eine Warengruppe speichern (mit EDBS-Validierung) |
Sidebar-Integration
Das Modul registriert Sidebar-Einträge in Module::initSideBar():
Hauptnavigation (für alle Benutzer mit Lizenz):
- Gruppe "Manuelle Sortierung" (
m_manualsorting, Gewicht 650) - Kind "Sortierergebnisse" →
/manualSorting/sorting/index
Admin-Bereich (nur für admin-Rolle, unter "Modulkonfiguration"):
- Gruppe "Manuelle Sortierung" (
m_manualsorting_admin) - Kind "Sortierklassen" →
/manualSorting/admin/config/classes
Internationalisierung
Übersetzungskategorie: manualsorting Basispfad: @app/modules/manualSorting/messages Konfiguriert in Module::initConfig().
Lizenzierung
Das Modul ist lizenzbasiert. Die Lizenz manualSorting muss in der Tabelle licenses aktiviert sein. Sidebar-Einträge und Scanner-Modul werden nur angezeigt, wenn die Lizenz aktiv ist.