insertTagParser = $insertTagParser; $this->db = $connection; } protected function getResponse(Template $template, ModuleModel $model, Request $request): ?Response { global $objPage; Controller::loadDataContainer('tl_vr_wa_slot'); $GLOBALS['TL_CSS']['vr_wa'] = 'bundles/vonrotenbergweinanlieferung/css/frontend.scss|static'; $standortIds = StringUtil::deserialize($model->vr_wa_standortId); $arrData = $template->getData(); $arrOptions = []; // Add filter sql if (!empty($_GET['filter_kapazitaet'])) { $filterRaw = (string) $_GET['filter_kapazitaet']; $arrData['filter']['kapazitaet']['selected'] = $filterRaw; // Support unit-prefixed values like "u1-16" (unit-specific) and legacy "u-16". Both resolve to numeric capacity 16 for SQL. $filterNumeric = null; $filterUnitId = null; if (preg_match('/^u(\d+)-(\d+)$/', $filterRaw, $m)) { // New format: u- $filterUnitId = (int) $m[1]; $filterNumeric = (int) $m[2]; } elseif (preg_match('/^u-(\d+)$/', $filterRaw, $m)) { // Legacy format kept for backward compatibility: u- $filterNumeric = (int) $m[1]; } else { $filterNumeric = (int) $filterRaw; } // Capacity availability condition $arrOptions['column'][] = '(SELECT tl_vr_wa_slot.behaelter - IFNULL(SUM(tl_vr_wa_reservation.behaelter),0) FROM tl_vr_wa_reservation WHERE tl_vr_wa_reservation.pid = tl_vr_wa_slot.id) >= ?'; $arrOptions['value'][] = $filterNumeric; // If a specific unit was selected (new format), restrict slots to Standorte that have this unit enabled if ($filterUnitId !== null) { // units is a serialized array, match serialized occurrence of the unit id $arrOptions['column'][] = 'pid IN (SELECT id FROM tl_vr_wa_standort WHERE units REGEXP ?)'; $arrOptions['value'][] = 's:[0-9]+:"' . $filterUnitId . '"'; } } if (!empty($_GET['filter_standort'])) { $arrData['filter']['standort']['selected'] = $_GET['filter_standort']; $arrOptions['column'][] = 'pid = ?'; $arrOptions['value'][] = $_GET['filter_standort']; } if (!empty($_GET['filter_tag']) && ($Tag = new Date($_GET['filter_tag']))) { $arrData['filter']['tag']['selected'] = $_GET['filter_tag']; $arrOptions['column'][] = 'tl_vr_wa_slot.time BETWEEN ? and ?'; $arrOptions['value'][] = $Tag->dayBegin; $arrOptions['value'][] = $Tag->dayEnd; } if (!empty($_GET['filter_ernteart'])) { $arrData['filter']['ernteart']['selected'] = $_GET['filter_ernteart']; $arrOptions['column'][] = 'FIND_IN_SET (?,ernteart)'; $arrOptions['value'][] = $_GET['filter_ernteart']; } if (!empty($_GET['filter_sorte']) && !empty($_GET['filter_leseart'])) { $arrData['filter']['sorte']['selected'] = $_GET['filter_sorte']; $arrData['filter']['leseart']['selected'] = $_GET['filter_leseart']; $arrOptions['column'][] = "sorten REGEXP('\"sorte\";s:[0-9]+:\"" . intval($_GET['filter_sorte']) . "\"[^\}]+\"leseart\";s:[0-9]+:\"" . intval($_GET['filter_leseart']) . "\"')"; } else { if (!empty($_GET['filter_sorte'])) { $arrData['filter']['sorte']['selected'] = $_GET['filter_sorte']; $arrOptions['column'][] = "sorten REGEXP('\"sorte\";s:[0-9]+:\"" . intval($_GET['filter_sorte']) . "\"')"; } if (!empty($_GET['filter_leseart'])) { $arrData['filter']['leseart']['selected'] = $_GET['filter_leseart']; $arrOptions['column'][] = "sorten REGEXP('\"leseart\";s:[0-9]+:\"" . intval($_GET['filter_leseart']) . "\"')"; } } // Get available slots if (($slots = WeinanlieferungSlotsModel::findMultiplePublishedByPids($standortIds,$arrOptions)) !== null) { $slotIds = $slots->fetchEach('id'); /** @var WeinanlieferungSlotsModel $slot */ foreach ($slots as $slot) { $day = new Date($slot->date); $arrSorten = []; $arrErnteart = []; $intAvailableBehaelter = max(0, $slot->getAvailableBehaelter()); $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 : ''); } if ($slot->ernteart !== null) { foreach (explode(',', $slot->ernteart) as $ernteart) { $arrErnteart[] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart; } } $strStandort = ''; if (($Standort = $slot->getRelated('pid')) !== null) { $strStandort = $Standort->title; } $arrLage = []; if (($Lage = $slot->getRelated('lage')) !== null) { $arrLage = $Lage->fetchEach('title'); } $arrData['days'][$day->dayBegin][] = array_merge($slot->row(), [ 'anmerkungen' => $slot->anmerkungen ? StringUtil::substr(strip_tags($slot->anmerkungen), 110) : '', 'standort' => $strStandort, 'lage' => $arrLage, 'sorte' => $arrSorten, 'ernteart' => $arrErnteart, 'behaelterAvailable' => $intAvailableBehaelter, 'buchbar' => (boolean)$intAvailableBehaelter, 'gebucht' => (boolean)WeinanlieferungReservationModel::countBy(["uid = ?", "pid = ?"], [FrontendUser::getInstance()->id, $slot->id]) ]); } // Get filter values $result = $this->db->executeQuery("SELECT MAX(behaelter) as 'kapazitaet' FROM tl_vr_wa_slot WHERE id IN (" . implode(',', $slotIds) . ")"); $intMaxKapazitaet = (int)$result->fetchOne(); // Build capacity filter options as an ordered list where each item has a numeric value and a label. // Do not combine unit labels with numeric labels; append unit options at the bottom. $options = []; $max = max(1, $intMaxKapazitaet); // Plain numeric options first for ($i = 1; $i <= $max; $i++) { $options[] = [ 'value' => (string) $i, 'label' => (string) $i, ]; } // Determine which units are enabled on any of the shown standorts (locations) $allowedUnitIds = []; $shownStandortIds = []; if (!empty($slotIds)) { // Get unique Standort IDs from the shown slots $shownStandortIds = array_unique($slots->fetchEach('pid')); if (!empty($shownStandortIds)) { if (($ShownStandorte = WeinanlieferungStandortModel::findBy(["id IN (" . implode(',', $shownStandortIds) . ")"],null)) !== null) { foreach ($ShownStandorte as $ShownStandort) { $ids = \Contao\StringUtil::deserialize($ShownStandort->units, true); if (!empty($ids)) { foreach ($ids as $uid) { $allowedUnitIds[(int)$uid] = true; } } } } } } // Build a map of units enabled per shown Standort and compute per-unit max available capacity across shown slots $unitsPerStandort = []; $unitMaxAvailable = []; if (!empty($shownStandortIds)) { if (($ShownStandorte = WeinanlieferungStandortModel::findBy(["id IN (" . implode(',', $shownStandortIds) . ")"], null)) !== null) { foreach ($ShownStandorte as $ShownStandort) { $sid = (int) $ShownStandort->id; $ids = \Contao\StringUtil::deserialize($ShownStandort->units, true); if (!empty($ids)) { foreach ($ids as $uid) { $uid = (int) $uid; $unitsPerStandort[$sid][$uid] = true; // initialize unit max availability bucket if (!isset($unitMaxAvailable[$uid])) { $unitMaxAvailable[$uid] = 0; } } } } } } // Iterate the shown slots and, for each, update max available for the units enabled at that slot's Standort if (!empty($unitsPerStandort) && !empty($slotIds)) { foreach ($slots as $slot) { $sid = (int) $slot->pid; if (empty($unitsPerStandort[$sid])) { continue; } $available = max(0, (int) $slot->getAvailableBehaelter()); foreach ($unitsPerStandort[$sid] as $uid => $_true) { if ($available > ($unitMaxAvailable[$uid] ?? 0)) { $unitMaxAvailable[$uid] = $available; } } } } // Append unit options at the bottom, without exceeding per-unit max availability and only for allowed units if (!empty($allowedUnitIds) && ($Units = WeinanlieferungUnitsModel::findAll()) !== null) { foreach ($Units as $unit) { $unitId = (int) $unit->id; if (empty($allowedUnitIds[$unitId])) { continue; // this unit is not enabled on any shown standort } $unitContainers = (int) $unit->containers; if ($unitContainers <= 0) { continue; } $unitName = $unit->description ?: $unit->title; // visible unit name $unitCap = (int) ($unitMaxAvailable[$unitId] ?? 0); $multiplierMax = intdiv($unitCap, $unitContainers); if ($multiplierMax <= 0) { continue; // nothing available for this unit across shown slots } for ($m = 1; $m <= $multiplierMax; $m++) { $value = (string) ($m * $unitContainers); $label = $m . ' ' . $unitName; $options[] = [ 'value' => 'u' . $unitId . '-' . $value, 'label' => $label, ]; } } } $arrData['filter']['kapazitaet']['options'] = $options; if (($Standorte = WeinanlieferungStandortModel::findBy(["id IN (" . implode(',', $standortIds) . ")", "id IN (SELECT tl_vr_wa_slot.pid FROM tl_vr_wa_slot WHERE tl_vr_wa_slot.id IN (" . implode(',', $slotIds) . "))"], null, ['order' => 'title ASC'])) !== null) { $arrData['filter']['standort']['options'] = array_combine($Standorte->fetchEach('id'), $Standorte->fetchEach('title')); } // $slots->reset(); if (($slotsUnfiltered = WeinanlieferungSlotsModel::findMultiplePublishedByPids($standortIds)) !== null) { foreach ($slotsUnfiltered as $slot) { $Tag = new Date($slot->time); $arrData['filter']['tag']['options'][$Tag->dayBegin] = Date::parse(Date::getNumericDateFormat(), $Tag->dayBegin); } } /*if (isset($GLOBALS['TL_DCA']['tl_vr_wa_slot']['fields']['ernteart']['options'])) { foreach ($GLOBALS['TL_DCA']['tl_vr_wa_slot']['fields']['ernteart']['options'] as $ernteart) { $arrData['filter']['ernteart']['options'][$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart; } } if (($Sorten = WeinanlieferungRebsorteModel::findAll(['order'=>'title ASC'])) !== null) { $arrData['filter']['sorte']['options'] = array_combine($Sorten->fetchEach('id'),$Sorten->fetchEach('title')); } if (($Leseart = WeinanlieferungLeseartModel::findAll(['order'=>'title ASC'])) !== null) { $arrData['filter']['leseart']['options'] = array_combine($Leseart->fetchEach('id'),$Leseart->fetchEach('title')); }*/ } // Add page URL if ($objPage instanceof PageModel) { $arrData['pageUrl'] = $objPage->getFrontendUrl(); } $template->setData($arrData); // Handle ajax if ($request->headers->get('VR-Ajax') == 'WaSlotsModule') { throw new ResponseException(new Response($this->insertTagParser->replace($template->parse()))); } return $template->getResponse(); } }