... | ... |
@@ -28,3 +28,8 @@ $GLOBALS['TL_LANG']['MSC']['wa_approval_status'] = [ |
28 | 28 |
'canceled' => 'abgelehnt', |
29 | 29 |
'approved' => 'angenommen', |
30 | 30 |
]; |
31 |
+ |
|
32 |
+$GLOBALS['TL_LANG']['MSC']['wa_checkin_status'] = [ |
|
33 |
+ 'pending' => 'Check-In ausstehend', |
|
34 |
+ 'checked_in' => 'Check-In erfolgt', |
|
35 |
+]; |
... | ... |
@@ -137,6 +137,14 @@ |
137 | 137 |
</div>{% endif %} |
138 | 138 |
{% endif %} |
139 | 139 |
</div> |
140 |
+ {% if booking.checked_in %} |
|
141 |
+ <div class="col-1"> |
|
142 |
+ </div> |
|
143 |
+ <div class="col-10 behaelter_nummern"> |
|
144 |
+ <div class="t-label">Eingecheckte Behälternummern</div> |
|
145 |
+ {{ booking.behaelter_numbers|join(', ') }} |
|
146 |
+ </div> |
|
147 |
+ {% endif %} |
|
140 | 148 |
<div class="col px-1 u-text-right action"> |
141 | 149 |
<a |
142 | 150 |
href="/contao?do=weinanlieferung&table=tl_vr_wa_reservation&act=edit&id={{ booking.id }}&rt={{ request_token }}&ref={{ ref }}" |
... | ... |
@@ -30,7 +30,12 @@ |
30 | 30 |
{% else %} |
31 | 31 |
{% set status = 'pending' %} |
32 | 32 |
{% endif %} |
33 |
- <div class="row py-2 mb-1 u-flex-nowrap-md u-items-center status status--{{ status }}"> |
|
33 |
+ {% if booking.checked_in == '1' %} |
|
34 |
+ {% set checkin_state = 'checked_in' %} |
|
35 |
+ {% else %} |
|
36 |
+ {% set checkin_state = 'pending' %} |
|
37 |
+ {% endif %} |
|
38 |
+ <div class="row py-2 mb-1 u-flex-nowrap-md u-items-center status status--{{ status }} checkin--{{ checkin_state }}"> |
|
34 | 39 |
{#<div class="col-1"> |
35 | 40 |
<div class="u-flex u-items-center u-justify-center"> |
36 | 41 |
<i class="status-icon" title="{{ ('MSC.wa_approval_status.'~status)|trans([], 'contao_default') }}"></i> |
... | ... |
@@ -38,13 +43,20 @@ |
38 | 43 |
</div>#} |
39 | 44 |
<div class="col-11"> |
40 | 45 |
<div class="grid-md u-gap-1"> |
41 |
- <div class="grid-c-12 bg-white p-1"> |
|
46 |
+ <div class="grid-c-6 bg-white p-1"> |
|
42 | 47 |
<div class="u-flex u-flex-wrap u-gap-1"> |
43 | 48 |
<i class="status-icon"></i> |
44 | 49 |
{{ ('MSC.wa_approval_status.'~status)|trans([], 'contao_default') }} |
45 | 50 |
</div> |
46 | 51 |
</div> |
47 | 52 |
|
53 |
+ <div class="grid-c-6 bg-white p-1"> |
|
54 |
+ <div class="u-flex u-flex-wrap u-gap-1"> |
|
55 |
+ <i class="checkin-icon"></i> |
|
56 |
+ {{ ('MSC.wa_checkin_status.'~checkin_state)|trans([], 'contao_default') }} |
|
57 |
+ </div> |
|
58 |
+ </div> |
|
59 |
+ |
|
48 | 60 |
<div class="grid-c-3 time bg-white p-1"> |
49 | 61 |
<div class="u-flex u-items-center u-gap-1"> |
50 | 62 |
<i class="icon-uhr-outline"></i> |
... | ... |
@@ -216,7 +216,8 @@ ul { |
216 | 216 |
|
217 | 217 |
.status { |
218 | 218 |
|
219 |
- .status-icon { |
|
219 |
+ .status-icon, |
|
220 |
+ .checkin-icon { |
|
220 | 221 |
visibility: hidden; |
221 | 222 |
position: relative; |
222 | 223 |
width: 24px; |
... | ... |
@@ -237,7 +238,8 @@ ul { |
237 | 238 |
|
238 | 239 |
} |
239 | 240 |
} |
240 |
-.status-icon { |
|
241 |
+.status-icon, |
|
242 |
+.checkin-icon { |
|
241 | 243 |
visibility: hidden; |
242 | 244 |
position: relative; |
243 | 245 |
display: inline-block; |
... | ... |
@@ -308,3 +310,24 @@ ul { |
308 | 310 |
} |
309 | 311 |
} |
310 | 312 |
} |
313 |
+ |
|
314 |
+.status, |
|
315 |
+.checkin-icon { |
|
316 |
+ &.checkin--checked_in { |
|
317 |
+ &:not(.status), |
|
318 |
+ .checkin-icon { |
|
319 |
+ &:after { |
|
320 |
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='icon icon-tabler icon-tabler-thumb-up' width='100%25' height='100%25' viewBox='0 0 24 24' stroke-width='1.5' stroke='%2300b341' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3' /%3E%3C/svg%3E"); |
|
321 |
+ } |
|
322 |
+ } |
|
323 |
+ } |
|
324 |
+ |
|
325 |
+ &.checkin--pending { |
|
326 |
+ &:not(.status), |
|
327 |
+ .checkin-icon { |
|
328 |
+ &:after { |
|
329 |
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='icon icon-tabler icon-tabler-hourglass-high' width='100%25' height='100%25' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23ff9300' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M6.5 7h11' /%3E%3Cpath d='M6 20v-2a6 6 0 1 1 12 0v2a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1 -1z' /%3E%3Cpath d='M6 4v2a6 6 0 1 0 12 0v-2a1 1 0 0 0 -1 -1h-10a1 1 0 0 0 -1 1z' /%3E%3C/svg%3E"); |
|
330 |
+ } |
|
331 |
+ } |
|
332 |
+ } |
|
333 |
+} |
... | ... |
@@ -279,7 +279,8 @@ class WeinanlieferungBookingsController extends AbstractController |
279 | 279 |
'lage' => $arrLagen, |
280 | 280 |
'slot' => $Slot->row(), |
281 | 281 |
'standort' => $strStandort, |
282 |
- 'member' => $booking->getRelated('uid') !== null ? $booking->getRelated('uid')->row() : null |
|
282 |
+ 'member' => $booking->getRelated('uid') !== null ? $booking->getRelated('uid')->row() : null, |
|
283 |
+ 'behaelter_numbers' => \json_decode($booking->behaelter_numbers) |
|
283 | 284 |
]); |
284 | 285 |
} |
285 | 286 |
} |
... | ... |
@@ -167,4 +167,108 @@ class WeinanlieferungReservationContainerListener |
167 | 167 |
|
168 | 168 |
return $varValue; |
169 | 169 |
} |
170 |
+ |
|
171 |
+ /** |
|
172 |
+ * @Callback(table="tl_vr_wa_reservation", target="fields.behaelter_numbers.load") |
|
173 |
+ */ |
|
174 |
+ public function onBehaelterNumbersLoadCallback($varValue, DataContainer $dc) |
|
175 |
+ { |
|
176 |
+ if (!empty($varValue)) |
|
177 |
+ { |
|
178 |
+ $varValue = implode(', ',\json_decode($varValue)); |
|
179 |
+ } |
|
180 |
+ return $varValue; |
|
181 |
+ } |
|
182 |
+ |
|
183 |
+ /** |
|
184 |
+ * @Callback(table="tl_vr_wa_reservation", target="fields.behaelter_numbers.save") |
|
185 |
+ */ |
|
186 |
+ public function onBehaelterNumbersSaveCallback($varValue, DataContainer $dc) |
|
187 |
+ { |
|
188 |
+ if (!empty($varValue)) |
|
189 |
+ { |
|
190 |
+ $values = array_map('trim', explode(',', $varValue)); |
|
191 |
+ |
|
192 |
+ // Get the reservation model |
|
193 |
+ $reservation = \vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel::findByPk($dc->id); |
|
194 |
+ if ($reservation === null) { |
|
195 |
+ throw new \Exception('Reservation not found'); |
|
196 |
+ } |
|
197 |
+ |
|
198 |
+ // Get the slot model |
|
199 |
+ $slot = $reservation->getRelated('pid'); |
|
200 |
+ if ($slot === null) { |
|
201 |
+ throw new \Exception('Slot not found'); |
|
202 |
+ } |
|
203 |
+ |
|
204 |
+ // Check if the booking is in the past |
|
205 |
+ $isInPast = $slot->time < time(); |
|
206 |
+ |
|
207 |
+ // Only perform validation if the booking is not in the past |
|
208 |
+ if (!$isInPast) { |
|
209 |
+ // Check if the number of container numbers matches the number of booked containers |
|
210 |
+ if (count($values) != $reservation->behaelter) { |
|
211 |
+ throw new \Exception('Die Anzahl der Behälternummern muss mit der Anzahl der gebuchten Behälter übereinstimmen.'); |
|
212 |
+ } |
|
213 |
+ |
|
214 |
+ // Filter out the special value 9999 ("Nummer nicht bekannt") for duplicate check |
|
215 |
+ $numbersForDuplicateCheck = array_filter($values, function($number) { |
|
216 |
+ return $number !== '9999'; |
|
217 |
+ }); |
|
218 |
+ |
|
219 |
+ // Check for duplicate numbers (excluding the special value 9999) |
|
220 |
+ if (count(array_unique($numbersForDuplicateCheck)) != count($numbersForDuplicateCheck)) { |
|
221 |
+ throw new \Exception('Jede Nummer kann nur einmal verwendet werden.'); |
|
222 |
+ } |
|
223 |
+ |
|
224 |
+ // Get the standort to access the number_ranges |
|
225 |
+ $standort = $slot->getRelated('pid'); |
|
226 |
+ if ($standort === null) { |
|
227 |
+ throw new \Exception('Standort not found'); |
|
228 |
+ } |
|
229 |
+ |
|
230 |
+ // Get all used numbers from current bookings (excluding past bookings) |
|
231 |
+ $usedNumbers = []; |
|
232 |
+ $currentTime = time(); |
|
233 |
+ |
|
234 |
+ // Get the database connection |
|
235 |
+ $sql = "SELECT r.behaelter_numbers |
|
236 |
+ FROM tl_vr_wa_reservation r |
|
237 |
+ JOIN tl_vr_wa_slot s ON r.pid = s.id |
|
238 |
+ WHERE r.behaelter_numbers != '' |
|
239 |
+ AND s.time >= ? |
|
240 |
+ AND r.id != ?"; |
|
241 |
+ |
|
242 |
+ $stmt = $this->db->prepare($sql); |
|
243 |
+ $stmt->bindValue(1, $currentTime); |
|
244 |
+ $stmt->bindValue(2, $dc->id); |
|
245 |
+ $result = $stmt->executeQuery(); |
|
246 |
+ |
|
247 |
+ while ($row = $result->fetchAssociative()) { |
|
248 |
+ $numbers = json_decode($row['behaelter_numbers'], true); |
|
249 |
+ if (is_array($numbers)) { |
|
250 |
+ foreach ($numbers as $number) { |
|
251 |
+ $usedNumbers[] = $number; |
|
252 |
+ } |
|
253 |
+ } |
|
254 |
+ } |
|
255 |
+ |
|
256 |
+ // Get available numbers directly, excluding used ones |
|
257 |
+ $availableNumbers = $standort->extractNumbersFromRanges($usedNumbers); |
|
258 |
+ |
|
259 |
+ // Add the special option "9999" which is always valid |
|
260 |
+ $availableNumbers[] = '9999'; |
|
261 |
+ |
|
262 |
+ // Check if all numbers are valid |
|
263 |
+ foreach ($numbersForDuplicateCheck as $number) { |
|
264 |
+ if (!in_array($number, $availableNumbers)) { |
|
265 |
+ throw new \Exception('Die Behälternummer "' . $number . '" ist nicht gültig oder wird bereits verwendet.'); |
|
266 |
+ } |
|
267 |
+ } |
|
268 |
+ } |
|
269 |
+ |
|
270 |
+ $varValue = json_encode($values); |
|
271 |
+ } |
|
272 |
+ return $varValue; |
|
273 |
+ } |
|
170 | 274 |
} |