<?php declare(strict_types=1); /* * This file is part of contao-weinanlieferung-bundle. * * (c) vonRotenberg * * @license commercial */ namespace vonRotenberg\WeinanlieferungBundle\EventListener\DataContainer; use Contao\CoreBundle\ServiceAnnotation\Callback; use Contao\DataContainer; use Contao\Date; use Contao\StringUtil; use Doctrine\DBAL\Connection; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use vonRotenberg\WeinanlieferungBundle\Event\CheckInCompletedEvent; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungSlotsModel; class WeinanlieferungReservationContainerListener { /** @var Connection */ protected $db; private $eventDispatcher; public function __construct(Connection $db, EventDispatcherInterface $eventDispatcher) { $this->db = $db; $this->eventDispatcher = $eventDispatcher; } /** * @Callback(table="tl_vr_wa_reservation", target="fields.sorten.options") */ public function onSortenOptionsCallback(DataContainer $dc) { $arrSorten = []; if (($Slot = WeinanlieferungSlotsModel::findByPk($dc->activeRecord->pid)) === null) { return $arrSorten; } $Sorten = StringUtil::deserialize($Slot->sorten,true); foreach($Sorten as $sorte) { $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte['sorte']); $objLeseart = WeinanlieferungLeseartModel::findByPk($sorte['leseart']); $arrSorten[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null ? $objSorte->title : '') . ' ' . ($objLeseart !== null ? $objLeseart->title : ''); } return $arrSorten; } /** * @Callback(table="tl_vr_wa_reservation", target="fields.lage.options") */ public function onLageOptionsCallback(DataContainer $dc) { $arrLagen = []; if (($Slot = WeinanlieferungSlotsModel::findByPk($dc->activeRecord->pid)) === null || $Slot->lage === null) { return $arrLagen; } if (($Lagen = $Slot->getRelated('lage')) !== null) { $arrLagen = array_combine($Lagen->fetchEach('id'),$Lagen->fetchEach('title')); } return $arrLagen; } /** * @Callback(table="tl_vr_wa_reservation", target="fields.ernteart.options") */ public function onErnteartOptionsCallback(DataContainer $dc) { $arrErnteart = []; if (($Slot = WeinanlieferungSlotsModel::findByPk($dc->activeRecord->pid)) === null || $Slot->ernteart === null) { return $arrErnteart; } $Ernteart = explode(',',$Slot->ernteart); foreach($Ernteart as $art) { $arrErnteart[$art] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$art] ?? $art; } return $arrErnteart; } /** * @Callback(table="tl_vr_wa_reservation", target="fields.pid.options") */ public function onPidOptionsCallback(DataContainer $dc) { $arrData = []; if (($CurrentSlot = WeinanlieferungSlotsModel::findByPk($dc->activeRecord->pid)) !== null) { $arrSorten = []; $intAvailableBehaelter = $CurrentSlot->getAvailableBehaelter(); $Standort = $CurrentSlot->getRelated('pid'); $Sorten = StringUtil::deserialize($CurrentSlot->sorten,true); foreach($Sorten as $sorte) { $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte['sorte']); $objLeseart = WeinanlieferungLeseartModel::findByPk($sorte['leseart']); $arrSorten[] = ($objSorte !== null ? $objSorte->title : '') . ' ' . ($objLeseart !== null ? $objLeseart->title : ''); } $arrData[$CurrentSlot->id] = Date::parse(Date::getNumericDatimFormat(),$CurrentSlot->time) . ' - ' . ($Standort !== null ? $Standort->title : $CurrentSlot->pid) . ' - (' . implode(', ',$arrSorten) . ') [Verfügbare Behälter: ' . $intAvailableBehaelter . '/' . $CurrentSlot->behaelter . ']'; } if (($Slots = WeinanlieferungSlotsModel::findAllFuturePublished()) === null) { return $arrData; } foreach ($Slots as $slot) { $arrSorten = []; $intAvailableBehaelter = $slot->getAvailableBehaelter(); $Standort = $slot->getRelated('pid'); $Sorten = StringUtil::deserialize($slot->sorten,true); foreach($Sorten as $sorte) { $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte['sorte']); $objLeseart = WeinanlieferungLeseartModel::findByPk($sorte['leseart']); $arrSorten[] = ($objSorte !== null ? $objSorte->title : '') . ' ' . ($objLeseart !== null ? $objLeseart->title : ''); } $arrData[$slot->id] = Date::parse(Date::getNumericDatimFormat(),$slot->time) . ' - ' . ($Standort !== null ? $Standort->title : $slot->pid) . ' - (' . implode(', ',$arrSorten) . ') [Verfügbare Behälter: ' . $intAvailableBehaelter . '/' . $slot->behaelter . ']'; } return $arrData; } /** * @Callback(table="tl_vr_wa_reservation", target="fields.uid.options") */ public function onUidOptionsCallback(DataContainer $dc) { if (($Members = \MemberModel::findAll()) === null) { return []; } $arrData = []; foreach ($Members as $member) { $arrData[$member->id] = ($member->memberno !== null && $member->memberno ? $member->memberno.' ' : '') . $member->firstname . ' ' . $member->lastname . ' [' . $member->email . ']'; } return $arrData; } /** * @Callback(table="tl_vr_wa_reservation", target="fields.approved.save") */ public function onApprovedSaveCallback($varValue, DataContainer $dc) { if (($Current = WeinanlieferungReservationModel::findByPk($dc->activeRecord->id)) !== null) { if ($Current->approved === '1' && $varValue !== '1') { $Current->approved_on = 0; $Current->save(); } elseif ($Current->approved !== '1' && $varValue === '1') { $Current->approved_on = time(); $Current->save(); } } return $varValue; } /** * @Callback(table="tl_vr_wa_reservation", target="fields.behaelter_numbers.load") */ public function onBehaelterNumbersLoadCallback($varValue, DataContainer $dc) { if (!empty($varValue)) { $decodedValue = \json_decode($varValue, true); // Check if we have the new format (array of objects with behaelter and member) // or the old format (simple array of behaelter numbers) $isNewFormat = isset($decodedValue[0]) && is_array($decodedValue[0]) && isset($decodedValue[0]['behaelter']); if ($isNewFormat) { // The data is already in the correct format for the multiColumnWizard return $decodedValue; } else { // Convert old format to new format $result = []; // Get the member associated with this reservation as fallback $reservation = \vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel::findByPk($dc->id); $memberId = $reservation ? $reservation->uid : 0; $memberModel = \Contao\MemberModel::findById($memberId); $defaultMemberNo = $memberModel ? $memberModel->memberno : ''; foreach ($decodedValue as $behaelterNumber) { $result[] = [ 'behaelter' => $behaelterNumber, 'member' => $defaultMemberNo ]; } return $result; } } return $varValue; } /** * @Callback(table="tl_vr_wa_reservation", target="fields.behaelter_numbers.save") */ public function onBehaelterNumbersSaveCallback($varValue, DataContainer $dc) { $varValue = StringUtil::deserialize($varValue, true); if (!empty($varValue) && is_array($varValue)) { // Get the reservation model $reservation = \vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel::findByPk($dc->id); if ($reservation === null) { throw new \Exception('Reservation not found'); } // Get the slot model $slot = $reservation->getRelated('pid'); if ($slot === null) { throw new \Exception('Slot not found'); } // Check if the booking is in the past $isInPast = $slot->time < time(); // Extract behaelter numbers from the multiColumnWizard data $behaelterNumbers = array_map(function($item) { return $item['behaelter']; }, $varValue); // Only perform validation if the booking is not in the past if (!$isInPast) { // Check if the number of container numbers matches the number of booked containers if (count($behaelterNumbers) != $reservation->behaelter) { throw new \Exception('Die Anzahl der Behälternummern muss mit der Anzahl der gebuchten Behälter übereinstimmen.'); } // Filter out the special value 9999 ("Nummer nicht bekannt") for duplicate check $numbersForDuplicateCheck = array_filter($behaelterNumbers, function($number) { return $number !== '9999'; }); // Check for duplicate numbers (excluding the special value 9999) if (count(array_unique($numbersForDuplicateCheck)) != count($numbersForDuplicateCheck)) { throw new \Exception('Jede Nummer kann nur einmal verwendet werden.'); } // Get the standort to access the number_ranges $standort = $slot->getRelated('pid'); if ($standort === null) { throw new \Exception('Standort not found'); } // Get all used numbers from current bookings (excluding past bookings) $usedNumbers = []; $currentTime = time(); // Get the database connection $sql = "SELECT r.behaelter_numbers FROM tl_vr_wa_reservation r JOIN tl_vr_wa_slot s ON r.pid = s.id WHERE r.behaelter_numbers != '' AND s.time >= ? AND r.id != ?"; $stmt = $this->db->prepare($sql); $stmt->bindValue(1, $currentTime); $stmt->bindValue(2, $dc->id); $result = $stmt->executeQuery(); while ($row = $result->fetchAssociative()) { $numbers = json_decode($row['behaelter_numbers'], true); if (is_array($numbers)) { // Check if we have the new format (array of objects with behaelter and member) // or the old format (simple array of behaelter numbers) $isNewFormat = isset($numbers[0]) && is_array($numbers[0]) && isset($numbers[0]['behaelter']); if ($isNewFormat) { // Extract just the behaelter numbers from the new format foreach ($numbers as $item) { $usedNumbers[] = $item['behaelter']; } } else { // Old format - simple array of behaelter numbers foreach ($numbers as $number) { $usedNumbers[] = $number; } } } } // Get available numbers directly, excluding used ones $availableNumbers = $standort->extractNumbersFromRanges($usedNumbers); // Add the special option "9999" which is always valid $availableNumbers[] = '9999'; // Check if all numbers are valid foreach ($numbersForDuplicateCheck as $number) { if (!in_array($number, $availableNumbers)) { throw new \Exception('Die Behälternummer "' . $number . '" ist nicht gültig oder wird bereits verwendet.'); } } } // Get the member associated with this reservation as fallback $memberId = $reservation->uid; $memberModel = \Contao\MemberModel::findById($memberId); $defaultMemberNo = $memberModel ? $memberModel->memberno : ''; // Process the data from the multiColumnWizard $processedData = []; foreach ($varValue as $item) { $behaelterNumber = $item['behaelter']; // If member number is empty, use the default member number $memberNumber = !empty($item['member']) ? $item['member'] : $defaultMemberNo; $processedData[] = [ 'behaelter' => $behaelterNumber, 'member' => $memberNumber ]; } // Return the processed data as JSON return json_encode($processedData); } return $varValue; } /** * @Callback(table="tl_vr_wa_reservation", target="config.onsubmit") */ public function onSubmitCallback(DataContainer $dc) { if (($reservation = WeinanlieferungReservationModel::findByPk($dc->id)) !== null && $reservation->checked_in) { $time = time(); $reservation->checked_in = '1'; $reservation->checked_in_on = $time; $reservation->save(); $event = new CheckInCompletedEvent($reservation->row(), $reservation); $this->eventDispatcher->dispatch($event, CheckInCompletedEvent::NAME); } } /** * @Callback (table="tl_vr_wa_reservation", target="config.onload") */ public function onLoadCallback(DataContainer $dc) { if ($_GET['act'] == 'edit' && ($reservation = WeinanlieferungReservationModel::findByPk($dc->id)) !== null && $reservation->checked_in_export_generated) { foreach ($GLOBALS['TL_DCA'][$dc->table]['fields'] as &$field) { $field['eval']['disabled'] = true; $field['eval']['readonly'] = true; if (!empty($field['eval']['chosen'])) { $field['eval']['chosen'] = false; } } } } }