Browse code

Update

Benjamin Roth authored on30/08/2023 22:08:09
Showing16 changed files
... ...
@@ -97,7 +97,7 @@ $GLOBALS['TL_DCA']['tl_vr_wa_reservation'] = array
97 97
     'palettes' => array
98 98
     (
99 99
         '__selector__' => array(),
100
-        'default' => 'pid,uid,lage,behaelter,sorten,ernteart,upload'
100
+        'default' => 'pid,uid,behaelter,sorten,lage,ernteart,upload'
101 101
     ),
102 102
 
103 103
     // Subpalettes
... ...
@@ -140,6 +140,7 @@ $GLOBALS['TL_DCA']['tl_vr_wa_reservation'] = array
140 140
             'inputType'               => 'checkbox',
141 141
             'eval'  => array('multiple'=>true, 'csv'=>',','tl_class'=>'w50'),
142 142
             'sql'       => "blob NULL",
143
+            'relation'   => array('type' => 'hasMany', 'load' => 'lazy', 'table'=>'tl_vr_wa_lage')
143 144
         ),
144 145
         'behaelter'         => array
145 146
         (
... ...
@@ -76,7 +76,7 @@ $GLOBALS['TL_DCA']['tl_vr_wa_slot'] = array
76 76
             ),
77 77
             'toggle' => array
78 78
             (
79
-                'href'                => 'act=toggle&field=buchbar',
79
+                'href'                => 'act=toggle&field=published',
80 80
                 'icon'                => 'visible.svg',
81 81
                 'showInHeader'        => true
82 82
             ),
... ...
@@ -91,7 +91,7 @@ $GLOBALS['TL_DCA']['tl_vr_wa_slot'] = array
91 91
     'palettes' => array
92 92
     (
93 93
         '__selector__' => array('addEnclosure'),
94
-        'default' => '{time_legend},date,time;{type_legend},lage,behaelter,sorten,ernteart;{info_legend},anmerkungen,addEnclosure;{booking_legend},buchbar,buchbar_bis'
94
+        'default' => '{time_legend},date,time;{type_legend},behaelter,sorten,lage,ernteart;{info_legend},anmerkungen,addEnclosure;{booking_legend},published,buchbar_ab,buchbar_bis'
95 95
     ),
96 96
 
97 97
     // Subpalettes
... ...
@@ -140,11 +140,11 @@ $GLOBALS['TL_DCA']['tl_vr_wa_slot'] = array
140 140
         'lage'   => array
141 141
         (
142 142
             'exclude'   => true,
143
-            'inputType' => 'select',
143
+            'inputType' => 'checkbox',
144 144
             'foreignKey'   => 'tl_vr_wa_lage.title',
145
-            'eval'      => array('mandatory' => false, 'includeBlankOption' => true, 'chosen'=>true,'tl_class' => 'w50'),
146
-            'sql'       => "smallint(3) unsigned NOT NULL default 0",
147
-            'relation'   => array('type' => 'hasOne', 'load' => 'lazy')
145
+            'eval'      => array('mandatory' => false, 'multiple' => true, 'csv'=>',','tl_class' => 'w50'),
146
+            'sql'       => "blob NULL",
147
+            'relation'   => array('type' => 'hasMany', 'load' => 'lazy')
148 148
         ),
149 149
         'behaelter'   => array
150 150
         (
... ...
@@ -193,7 +193,7 @@ $GLOBALS['TL_DCA']['tl_vr_wa_slot'] = array
193 193
             'exclude'                 => true,
194 194
             'inputType'               => 'checkbox',
195 195
             'options'                 => array('handlese','vollernter'),
196
-            'eval'                    => array('mandatory'=>true,'multiple'=>true,'csv'=>','),
196
+            'eval'                    => array('mandatory'=>true,'multiple'=>true,'csv'=>',', 'tl_class'=>'w50'),
197 197
             'reference'               => $GLOBALS['TL_LANG']['REF']['wa_ernteart'],
198 198
             'sql'                     => "blob NULL"
199 199
         ),
... ...
@@ -219,7 +219,7 @@ $GLOBALS['TL_DCA']['tl_vr_wa_slot'] = array
219 219
             'eval'                    => array('multiple'=>true, 'fieldType'=>'checkbox', 'filesOnly'=>true, 'isDownloads'=>true, 'extensions'=>Config::get('allowedDownload'), 'mandatory'=>true, 'isSortable'=>true),
220 220
             'sql'                     => "blob NULL"
221 221
         ),
222
-        'buchbar'     => array
222
+        'published'     => array
223 223
         (
224 224
             'exclude'   => true,
225 225
             'toggle'    => true,
... ...
@@ -230,11 +230,18 @@ $GLOBALS['TL_DCA']['tl_vr_wa_slot'] = array
230 230
             'eval'      => array('doNotCopy' => true),
231 231
             'sql'       => "char(1) NOT NULL default ''"
232 232
         ),
233
+        'buchbar_ab' => array
234
+        (
235
+            'exclude'   => true,
236
+            'inputType' => 'text',
237
+            'eval'      => array('rgxp' => 'datim', 'datepicker' => true, 'tl_class' => 'clr w50 wizard'),
238
+            'sql'       => "int(10) unsigned NULL"
239
+        ),
233 240
         'buchbar_bis' => array
234 241
         (
235 242
             'exclude'   => true,
236 243
             'inputType' => 'text',
237
-            'eval'      => array('rgxp' => 'datim', 'mandatory' => true, 'datepicker' => true, 'tl_class' => 'clr w50 wizard'),
244
+            'eval'      => array('rgxp' => 'datim', 'mandatory' => true, 'datepicker' => true, 'tl_class' => 'w50 wizard'),
238 245
             'sql'       => "int(10) unsigned NULL"
239 246
         ),
240 247
     )
... ...
@@ -14,6 +14,8 @@ $GLOBALS['TL_LANG']['tl_vr_wa_reservation']['behaelter'][0] = 'Behälteranzahl';
14 14
 $GLOBALS['TL_LANG']['tl_vr_wa_reservation']['behaelter'][1] = 'Die Anzahl an Behälter, die der Winzer anliefert.';
15 15
 $GLOBALS['TL_LANG']['tl_vr_wa_reservation']['sorten'][0] = 'Sorten';
16 16
 $GLOBALS['TL_LANG']['tl_vr_wa_reservation']['sorten'][1] = 'Die Sorten, die der Winzer anliefert.';
17
+$GLOBALS['TL_LANG']['tl_vr_wa_reservation']['lage'][0] = 'Lage';
18
+$GLOBALS['TL_LANG']['tl_vr_wa_reservation']['lage'][1] = 'Lage der angelieferten Reben.';
17 19
 $GLOBALS['TL_LANG']['tl_vr_wa_reservation']['ernteart'][0] = 'Ernteart';
18 20
 $GLOBALS['TL_LANG']['tl_vr_wa_reservation']['ernteart'][1] = 'Die Ernteart, der angelieferten Reben.';
19 21
 $GLOBALS['TL_LANG']['tl_vr_wa_reservation']['uid'][0] = 'Winzer';
... ...
@@ -28,8 +28,10 @@ $GLOBALS['TL_LANG']['tl_vr_wa_slot']['addEnclosure'][0] = 'Anhänge';
28 28
 $GLOBALS['TL_LANG']['tl_vr_wa_slot']['addEnclosure'][1] = 'Dateianhänge zum Download.';
29 29
 $GLOBALS['TL_LANG']['tl_vr_wa_slot']['enclosure'][0] = 'Dateien';
30 30
 $GLOBALS['TL_LANG']['tl_vr_wa_slot']['enclosure'][1] = 'Bitte die downloadbaren Dateien auswählen.';
31
-$GLOBALS['TL_LANG']['tl_vr_wa_slot']['buchbar'][0] = 'Buchbar';
32
-$GLOBALS['TL_LANG']['tl_vr_wa_slot']['buchbar'][1] = 'Steuert die Buchbarkeit dieses Zeitslots.';
31
+$GLOBALS['TL_LANG']['tl_vr_wa_slot']['published'][0] = 'Veröffentlicht';
32
+$GLOBALS['TL_LANG']['tl_vr_wa_slot']['published'][1] = 'Legt fest ob der Slot im Frontend angezeigt wird oder nicht.';
33
+$GLOBALS['TL_LANG']['tl_vr_wa_slot']['buchbar_ab'][0] = 'Buchbar ab';
34
+$GLOBALS['TL_LANG']['tl_vr_wa_slot']['buchbar_ab'][1] = 'Zeitpunkt, ab wann der Slot gebucht werden kann.';
33 35
 $GLOBALS['TL_LANG']['tl_vr_wa_slot']['buchbar_bis'][0] = 'Buchbar bis';
34 36
 $GLOBALS['TL_LANG']['tl_vr_wa_slot']['buchbar_bis'][1] = 'Zeitpunkt, bis wann der Slot spätestens gebucht werden kann.';
35 37
 
... ...
@@ -1,10 +1,10 @@
1 1
 {% block content %}
2 2
     {% if modal %}<div class="modal-content">{% endif %}
3
-    <div id="wa-booking-{{ id }}" class="frame">
3
+    <div id="wa-booking-{{ id }}" hx-target="this" hx-swap="outerHTML" class="frame">
4 4
         <div class="frame__header">
5 5
             <h3>Reservierung</h3>
6
-            <div class="row">
7
-                <div class="col-md-6">
6
+            <div class="grid-md u-gap-2">
7
+                <div class="grid-c-6 mb-2 mb-0-md">
8 8
                     <div class="u-flex u-items-center u-gap-1">
9 9
                         <i class="icon-uhr-outline"></i>
10 10
                         <div class="t-label">Tag/Urzeit</div>
... ...
@@ -12,52 +12,37 @@
12 12
                     <div class="">{{ slot.time|date('d.m.Y H:i') }}</div>
13 13
                 </div>
14 14
 
15
-                <div class="col-md-6">
15
+                <div class="grid-c-6 mb-2 mb-0-md">
16 16
                     <div class="u-flex u-items-center u-gap-1">
17 17
                         <i class="icon-standort-outline"></i>
18 18
                         <div class="t-label">Standort</div>
19 19
                     </div>
20 20
                     <div class="">{{ standort.title }}</div>
21 21
                 </div>
22
-            </div>
23
-
24
-            <div class="row">
25
-                <div class="col-md-6">
26
-                    <div class="u-flex u-items-center u-gap-1">
27
-                        <i class="icon-behaelter-outline"></i>
28
-                        <div class="t-label">Verfügbare Behälterkapazität</div>
29
-                    </div>
30
-                    <div class="">{{ slot.behaelterAvailable }}</div>
31
-                </div>
32 22
 
33
-                <div class="col-md-6">
34
-                    <div class="u-flex u-items-center u-gap-1">
35
-                        <i class="icon-reben-outline"></i>
36
-                        <div class="t-label">Verarbeitete Sorten</div>
23
+                {% if slot.anmerkungen %}
24
+                    <div class="grid-c-12 mb-2 mb-0-md">
25
+                        <div class="u-flex u-items-center u-gap-1">
26
+                            <i class="icon-info-outline"></i>
27
+                            <div class="t-label">Anmerkungen</div>
28
+                        </div>
29
+                        {{ slot.anmerkungen|raw }}
37 30
                     </div>
38
-                    <div class="">{{ slot.sorte|join(', ') }}</div>
39
-                </div>
31
+                {% endif %}
40 32
             </div>
41
-
42
-            {% if slot.anmerkungen %}
43
-                <div>
44
-                    <div class="u-flex u-items-center u-gap-1">
45
-                        <i class="icon-info-outline"></i>
46
-                        <div class="t-label">Anmerkungen</div>
47
-                    </div>
48
-                    {{ slot.anmerkungen|raw }}
49
-                </div>
50
-            {% endif %}
51 33
         </div>
52 34
 
53 35
         <div class="divider m-0 mb-2"></div>
54 36
 
55 37
         <div class="frame__body">
56 38
             <h3>Reservierung ändern</h3>
39
+            {% if toast is defined %}
40
+                {{ toast|raw }}
41
+            {% endif %}
57 42
             <form hx-post="/_ajax/vr_wa/v1/slot?do=updateReservation" enctype="multipart/form-data">
58 43
                 <input type="hidden" name="id" value="{{ id }}">
59 44
                 <fieldset>
60
-                    <label for="res-behaelter">Liefernde Behältermenge</label>
45
+                    <label for="res-behaelter"><strong>Liefernde Behältermenge</strong><sup class="text-danger">*</sup></label>
61 46
                     <select id="res-behaelter" name="behaelter" required>
62 47
                         <option value="">-</option>
63 48
                         {% for option in buchen.behaelter %}
... ...
@@ -66,21 +51,33 @@
66 51
                     </select>
67 52
                 </fieldset>
68 53
                 <fieldset>
69
-                  <legend>Anliefernde Rebsorte(n)</legend>
54
+                  <legend>Anliefernde Rebsorte(n)<sup class="text-danger">*</sup></legend>
70 55
                     {% for value,label in buchen.sorten %}
71 56
                         <label><input type="checkbox" name="sorten[]" value="{{ value }}"{{ value in buchung.sorten|keys ? ' checked' : '' }}> <span class="checkable">{{ label }}</span></label><br>
72 57
                     {% endfor %}
73 58
                 </fieldset>
59
+                <fieldset>
60
+                    <legend>Ernteart(en)<sup class="text-danger">*</sup></legend>
61
+                    {% for value,label in buchen.ernteart %}
62
+                        <label><input type="checkbox" name="ernteart[]" value="{{ value }}"{{ value in buchung.ernteart ? ' checked' : '' }}> <span class="checkable">{{ label }}</span></label><br>
63
+                    {% endfor %}
64
+                </fieldset>
65
+                <fieldset>
66
+                    <legend>Lage(n)</legend>
67
+                    {% for value,label in buchen.lage %}
68
+                        <label><input type="checkbox" name="lage[]" value="{{ value }}"{{ value in buchung.lage ? ' checked' : '' }}> <span class="checkable">{{ label }}</span></label><br>
69
+                    {% endfor %}
70
+                </fieldset>
74 71
                 {% if file is defined %}
75 72
                     <fieldset>
76 73
                         <div class="t-label">Aktuelle Datei</div>
77 74
                         <div class="u-flex u-items-center u-gap-1"><a href="{{ file.link }}">{{ file.name }}</a> <a href="javascript:;" hx-get="/_ajax/vr_wa/v1/slot?do=booking&id={{ id }}&modal=false&deleteFile=true" hx-target="closest .frame" hx-swap="outerHTML" class="tag tag__close-btn tag--danger tag--xs"></a></div>
78 75
                     </fieldset>
79 76
                     <fieldset>
80
-                        <label for="res-upload">Datei überschreiben</label>
77
+                        <label for="res-upload"><strong>Datei überschreiben</strong></label>
81 78
                 {% else %}
82 79
                     <fieldset>
83
-                        <label for="res-upload">Datei hochladen</label>
80
+                        <label for="res-upload"><strong>Datei hochladen</strong></label>
84 81
                 {% endif %}
85 82
                     <input type="file" id="res-upload" name="upload">
86 83
                 </fieldset>
... ...
@@ -31,7 +31,7 @@
31 31
                     <div class="grid-c-6 mb-2 mb-0-md">
32 32
                         <div class="u-flex u-items-center u-gap-1">
33 33
                             <i class="icon-schere-outline"></i>
34
-                            <div class="t-label">Ernteart</div>
34
+                            <div class="t-label">Erntearten</div>
35 35
                         </div>
36 36
                         <div class="">{{ ernteart|join(', ') }}</div>
37 37
                     </div>
... ...
@@ -47,9 +47,9 @@
47 47
                     <div class="grid-c-6 mb-2 mb-0-md">
48 48
                         <div class="u-flex u-items-center u-gap-1">
49 49
                             <i class="icon-lage-outline"></i>
50
-                            <div class="t-label">Lage</div>
50
+                            <div class="t-label">Lagen</div>
51 51
                         </div>
52
-                        <div class="">{{ lage.title }}</div>
52
+                        <div class="">{{ lage|join(', ') }}</div>
53 53
                     </div>
54 54
 
55 55
                     {% if slot.anmerkungen %}
... ...
@@ -31,7 +31,7 @@
31 31
                     <div class="grid-c-6 mb-2 mb-0-md">
32 32
                         <div class="u-flex u-items-center u-gap-1">
33 33
                             <i class="icon-schere-outline"></i>
34
-                            <div class="t-label">Ernteart</div>
34
+                            <div class="t-label">Erntearten</div>
35 35
                         </div>
36 36
                         <div class="">{{ ernteart|join(', ') }}</div>
37 37
                     </div>
... ...
@@ -47,9 +47,9 @@
47 47
                     <div class="grid-c-6 mb-2 mb-0-md">
48 48
                         <div class="u-flex u-items-center u-gap-1">
49 49
                             <i class="icon-lage-outline"></i>
50
-                            <div class="t-label">Lage</div>
50
+                            <div class="t-label">Lagen</div>
51 51
                         </div>
52
-                        <div class="">{{ lage.title }}</div>
52
+                        <div class="">{{ lage|join(', ') }}</div>
53 53
                     </div>
54 54
 
55 55
                     {% if slot.anmerkungen %}
... ...
@@ -105,7 +105,7 @@
105 105
                     <form hx-post="/_ajax/vr_wa/v1/slot?do=reservate" enctype="multipart/form-data">
106 106
                         <input type="hidden" name="id" value="{{ id }}">
107 107
                         <fieldset>
108
-                            <label for="res-behaelter">Liefernde Behältermenge</label>
108
+                            <label for="res-behaelter"><strong>Liefernde Behältermenge<sup class="text-danger">*</sup></strong></label>
109 109
                             <select id="res-behaelter" name="behaelter" required>
110 110
                                 <option value="">-</option>
111 111
                                 {% for option in buchen.behaelter %}
... ...
@@ -114,13 +114,25 @@
114 114
                             </select>
115 115
                         </fieldset>
116 116
                         <fieldset>
117
-                          <legend>Anliefernde Rebsorte(n)</legend>
117
+                          <legend>Anliefernde Rebsorte(n)<sup class="text-danger">*</sup></legend>
118 118
                             {% for value,label in buchen.sorten %}
119 119
                                 <label><input type="checkbox" name="sorten[]" value="{{ value }}"> <span class="checkable">{{ label }}</span></label><br>
120 120
                             {% endfor %}
121 121
                         </fieldset>
122 122
                         <fieldset>
123
-                            <label for="res-upload">Dateianhang</label>
123
+                            <legend>Ernteart(en)<sup class="text-danger">*</sup></legend>
124
+                            {% for value,label in buchen.ernteart %}
125
+                                <label><input type="checkbox" name="ernteart[]" value="{{ value }}"> <span class="checkable">{{ label }}</span></label><br>
126
+                            {% endfor %}
127
+                        </fieldset>
128
+                        <fieldset>
129
+                            <legend>Lage(n)</legend>
130
+                            {% for value,label in buchen.lage %}
131
+                                <label><input type="checkbox" name="lage[]" value="{{ value }}"> <span class="checkable">{{ label }}</span></label><br>
132
+                            {% endfor %}
133
+                        </fieldset>
134
+                        <fieldset>
135
+                            <label for="res-upload"><strong>Dateianhang</strong></label>
124 136
                             <input type="file" id="res-upload" name="upload">
125 137
                         </fieldset>
126 138
                         <fieldset>
... ...
@@ -32,18 +32,24 @@
32 32
                 </h3>
33 33
               <div class="bookings u-row-striped">
34 34
                 {% for booking in bookings.items %}
35
-                  <div class="row u-items-center ml-2">
36
-                    <div class="col-2 time icon-uhr-outline">
35
+                  <div class="row u-items-center">
36
+                    <div class="col-1 time icon-uhr-outline">
37 37
                       <div class="t-label">Uhrzeit</div>
38 38
                       {{ booking.slot.time|date('H:i') }}
39 39
                     </div>
40 40
                     <div class="col-3 behaelter icon-behaelter-outline">
41 41
                       <div class="t-label">Gebuchte Behälterkapazität</div>
42 42
                       {{ booking.behaelter }}
43
-                      <div class="t-label">Anliefernde Sorten</div>
44
-                      {{ booking.sorte|join(', ') }}
43
+                      <div class="t-label">Ernteart</div>
44
+                      {{ booking.ernteart|join(', ') }}
45 45
                     </div>
46
-                    <div class="col-5 rebsorten icon-reben-outline">
46
+                      <div class="col-3 behaelter icon-behaelter-outline">
47
+                          <div class="t-label">Anliefernde Sorten</div>
48
+                          {{ booking.sorte|join(', ') }}
49
+                          <div class="t-label">Lage</div>
50
+                          {{ booking.lage|join(', ') }}
51
+                      </div>
52
+                    <div class="col-3 rebsorten icon-reben-outline">
47 53
                       <div class="t-label">Mitglied</div>
48 54
                         {% if booking.member is not null %}
49 55
                         {% if booking.member.memberno is defined %}<div>{{booking.member.memberno }}</div>{% endif %}
... ...
@@ -52,7 +58,7 @@
52 58
                             {% if booking.member.email is defined %}<div><a href="mailto:{{ booking.member.email }}">{{ booking.member.email }}</a></div>{% endif %}
53 59
                         {% endif %}
54 60
                     </div>
55
-                    <div class="col u-text-right action">
61
+                    <div class="col-1 u-text-right action">
56 62
                       <a
57 63
                         href="/contao?do=weinanlieferung&table=tl_vr_wa_reservation&act=edit&id={{ booking.id }}&rt={{ request_token }}&ref={{ ref }}"
58 64
 {#                        onclick="Backend.openModalIframe({'title':'Quellelement ID {{ booking.id }} bearbeiten','url':this.href});return false"#}
... ...
@@ -8,39 +8,60 @@
8 8
                     <div class="accordion-dis">
9 9
                         <div class="bookings">
10 10
                             {% for booking in bookings %}
11
-                                <div class="row u-flex-nowrap-md u-items-center">
11
+                                <div class="row py-2 u-flex-nowrap-md u-items-center">
12 12
                                     <div class="col-12">
13
-                                        <div class="row">
14
-                                            <div class="col-3 time pl-0">
15
-                                                <div class="icon-uhr-outline u-flex u-items-center u-gap-1">
13
+                                        <div class="grid-md u-gap-1">
14
+                                            <div class="grid-c-3 time bg-white p-1">
15
+                                                <div class="u-flex u-items-center u-gap-1">
16
+                                                    <i class="icon-uhr-outline"></i>
16 17
                                                     <span class="t-label">Uhrzeit</span>
17 18
                                                     {{ booking.slot.time|date('H:i') }}
18 19
                                                 </div>
19
-                                                <div class="icon-standort-outline u-flex u-items-center u-gap-1">
20
-                                                    <span class="t-label">Standort</span>
21
-                                                    {{ booking.standort }}
22
-                                                </div>
23 20
                                             </div>
24
-                                            <div class="col-3 behaelter">
25
-                                                <div class="icon-behaelter-outline u-flex u-items-center u-gap-1">
21
+                                            <div class="grid-c-3 behaelter bg-white p-1">
22
+                                                <div class="u-flex u-flex-wrap u-gap-1">
23
+                                                    <i class="icon-behaelter-outline"></i>
26 24
                                                     <span class="t-label">Gebuchte Behälterkapazität</span>
27 25
                                                     {{ booking.behaelter }}
28 26
                                                 </div>
29 27
                                             </div>
30
-                                            <div class="col-6 rebsorten pr-0">
31
-                                                <div class="icon-reben-outline u-flex u-items-center u-gap-1">
28
+                                            <div class="grid-c-6 rebsorten bg-white p-1">
29
+                                                <div class="u-flex u-flex-wrap u-gap-1">
30
+                                                    <i class="icon-reben-outline"></i>
32 31
                                                     <div class="t-label">Anliefernde Sorten</div>
33 32
                                                     &nbsp;
34 33
                                                 </div>
35 34
                                                 {{ booking.sorte|join(', ') }}
36 35
                                             </div>
36
+                                            <div class="grid-c-3 standort bg-white p-1">
37
+                                                <div class="u-flex u-flex-wrap u-gap-1">
38
+                                                    <i class="icon-standort-outline"></i>
39
+                                                    <span class="t-label">Standort</span>
40
+                                                    {{ booking.standort }}
41
+                                                </div>
42
+                                            </div>
43
+                                            <div class="grid-c-3 ernteart bg-white p-1">
44
+                                                <div class="u-flex u-flex-wrap u-gap-1">
45
+                                                    <i class="icon-schere-outline"></i>
46
+                                                    <div class="t-label">Erntearten</div>
47
+                                                    {{ booking.ernteart|join(', ') }}
48
+                                                </div>
49
+                                            </div>
50
+                                            <div class="grid-c-6 lage bg-white p-1">
51
+                                                <div class="u-flex u-flex-wrap u-gap-1">
52
+                                                    <i class="icon-lage-outline"></i>
53
+                                                    <span class="t-label">Lagen</span>
54
+                                                    {{ booking.lage|join(', ') }}
55
+                                                </div>
56
+                                            </div>
37 57
                                             {% if booking.slot.anmerkungen is defined and booking.slot.anmerkungen %}
38
-                                                <div class="col-12 px-0">
39
-                                                    <div class="icon-info-outline u-flex u-items-center u-gap-1">
58
+                                                <div class="grid-c-12 bg-white p-1">
59
+                                                    <div class="u-flex u-flex-wrap u-gap-1">
60
+                                                        <i class="icon-info-outline"></i>
40 61
                                                         <span class="t-label">Anmerkungen</span>
62
+                                                        {{ booking.slot.anmerkungen|raw }}
63
+                                                        <a hx-get="/_ajax/vr_wa/v1/slot?do=annotation&id={{ booking.slot.id }}" hx-target="body" hx-swap="beforeend" href="javascript:;">mehr lesen</a>
41 64
                                                     </div>
42
-                                                    {{ booking.slot.anmerkungen|raw }}
43
-                                                    <a hx-get="/_ajax/vr_wa/v1/slot?do=annotation&id={{ booking.slot.id }}" hx-target="body" hx-swap="beforeend" href="javascript:;">mehr lesen</a>
44 65
                                                 </div>
45 66
                                             {% endif %}
46 67
                                         </div>
... ...
@@ -83,15 +83,15 @@
83 83
                                             <div class="grid-c-3 ernteart bg-white p-1">
84 84
                                                 <div class="u-flex u-flex-wrap u-gap-1">
85 85
                                                     <i class="icon-schere-outline"></i>
86
-                                                    <div class="t-label">Ernteart</div>
86
+                                                    <div class="t-label">Erntearten</div>
87 87
                                                     {{ slot.ernteart|join(', ') }}
88 88
                                                 </div>
89 89
                                             </div>
90 90
                                             <div class="grid-c-6 lage bg-white p-1">
91 91
                                                 <div class="u-flex u-flex-wrap u-gap-1">
92 92
                                                     <i class="icon-lage-outline"></i>
93
-                                                    <span class="t-label">Lage</span>
94
-                                                    {{ slot.lage }}
93
+                                                    <span class="t-label">Lagen</span>
94
+                                                    {{ slot.lage|join(', ') }}
95 95
                                                 </div>
96 96
                                             </div>
97 97
                                             {% if slot.anmerkungen is defined and slot.anmerkungen %}
... ...
@@ -62,6 +62,7 @@ class WeinanlieferungBookingsController extends AbstractController
62 62
             'request_token' => $this->tokenManager->getDefaultTokenValue(),
63 63
             'ref' => $this->request->attributes->get('_contao_referer_id')
64 64
         ];
65
+        System::loadLanguageFile('default');
65 66
 
66 67
         // Get bookings
67 68
         if (($bookings = WeinanlieferungReservationModel::findAllFuture(['order' => "(SELECT tl_vr_wa_slot.time FROM tl_vr_wa_slot WHERE tl_vr_wa_slot.id=tl_vr_wa_reservation.pid) ASC"])) !== null)
... ...
@@ -74,6 +75,8 @@ class WeinanlieferungBookingsController extends AbstractController
74 75
                 {
75 76
                     $day = new Date($Slot->date);
76 77
                     $arrSorten = [];
78
+                    $arrErnteart = [];
79
+                    $arrLagen = [];
77 80
 
78 81
                     if (!isset($arrData['days'][$day->dayBegin][$Slot->time]))
79 82
                     {
... ...
@@ -103,8 +106,26 @@ class WeinanlieferungBookingsController extends AbstractController
103 106
                         }
104 107
                     }
105 108
 
109
+                    if ($booking->ernteart !== null)
110
+                    {
111
+                        foreach (explode(',', $booking->ernteart) as $ernteart)
112
+                        {
113
+                            $arrErnteart[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
114
+                        }
115
+                    }
116
+
117
+                    if (($Lagen = $booking->getRelated('lage')) !== null)
118
+                    {
119
+                        foreach ($Lagen as $lage)
120
+                        {
121
+                            $arrLagen[$lage->id] = $lage->title;
122
+                        }
123
+                    }
124
+
106 125
                     $arrData['days'][$day->dayBegin][$Slot->time]['items'][] = array_merge($booking->row(), [
107 126
                         'sorte'              => $arrSorten,
127
+                        'ernteart' => $arrErnteart,
128
+                        'lage' => $arrLagen,
108 129
                         'slot'  => $Slot->row(),
109 130
                         'member' => $booking->getRelated('uid') !== null ? $booking->getRelated('uid')->row() : null
110 131
                     ]);
... ...
@@ -31,6 +31,7 @@ use Symfony\Component\HttpFoundation\Request;
31 31
 use Symfony\Component\HttpFoundation\Response;
32 32
 use Symfony\Component\Routing\Annotation\Route;
33 33
 use Symfony\Contracts\Translation\TranslatorInterface;
34
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLageModel;
34 35
 use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel;
35 36
 use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel;
36 37
 use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel;
... ...
@@ -154,10 +155,16 @@ class SlotAjaxController extends AbstractController
154 155
         {
155 156
             foreach (explode(',', $Slot->ernteart) as $ernteart)
156 157
             {
157
-                $arrErnteart[] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
158
+                $arrErnteart[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
158 159
             }
159 160
         }
160 161
 
162
+        $arrLage = [];
163
+        if (($Lage = $Slot->getRelated('lage')) !== null)
164
+        {
165
+            $arrLage = array_combine($Lage->fetchEach('id'),$Lage->fetchEach('title'));
166
+        }
167
+
161 168
         $intAvailableBehaelter = $Slot->getAvailableBehaelter();
162 169
 
163 170
         $arrData = [
... ...
@@ -168,12 +175,14 @@ class SlotAjaxController extends AbstractController
168 175
                 'behaelterAvailable' => $intAvailableBehaelter
169 176
             ]),
170 177
             'standort' => $Slot->getRelated('pid'),
171
-            'lage' => $Slot->getRelated('lage'),
178
+            'lage' => $arrLage,
172 179
             'ernteart' => $arrErnteart,
173 180
             'buchen' => [
174 181
                 'buchbar' => (boolean) $intAvailableBehaelter,
175 182
                 'behaelter' => range(min($intAvailableBehaelter,1),$intAvailableBehaelter),
176
-                'sorten' => $arrSorten
183
+                'sorten' => $arrSorten,
184
+                'lage' => $arrLage,
185
+                'ernteart' => $arrErnteart,
177 186
             ],
178 187
             'reservations' => $arrReservations
179 188
         ];
... ...
@@ -217,6 +226,12 @@ class SlotAjaxController extends AbstractController
217 226
             }
218 227
         }
219 228
 
229
+        $arrLage = [];
230
+        if (($Lage = $Slot->getRelated('lage')) !== null)
231
+        {
232
+            $arrLage = $Lage->fetchEach('title');
233
+        }
234
+
220 235
         $intAvailableBehaelter = $Slot->getAvailableBehaelter();
221 236
 
222 237
         $arrData = [
... ...
@@ -227,7 +242,7 @@ class SlotAjaxController extends AbstractController
227 242
                 'behaelterAvailable' => $intAvailableBehaelter
228 243
             ]),
229 244
             'standort' => $Slot->getRelated('pid'),
230
-            'lage' => $Slot->getRelated('lage'),
245
+            'lage' => $arrLage,
231 246
             'ernteart' => $arrErnteart,
232 247
             'buchen' => [
233 248
                 'buchbar' => (boolean) $intAvailableBehaelter,
... ...
@@ -239,7 +254,7 @@ class SlotAjaxController extends AbstractController
239 254
         return $this->render('@Contao/modal_slot_annotation.html.twig',$arrData);
240 255
     }
241 256
 
242
-    protected function renderBooking(bool $blnModal=true)
257
+    protected function renderBooking(bool $blnModal=true,string $error=null)
243 258
     {
244 259
         $arrData = [];
245 260
 
... ...
@@ -315,6 +330,34 @@ class SlotAjaxController extends AbstractController
315 330
             $arrSortenBooked[$objSorte->id.','.$objLeseart->id] = ($objSorte !== null  ? $objSorte->title : '') . ' ' . ($objLeseart !== null  ? $objLeseart->title : '');
316 331
         }
317 332
 
333
+        $arrErnteartAvailable = [];
334
+        if ($Slot->ernteart !== null)
335
+        {
336
+            foreach (explode(',', $Slot->ernteart) as $ernteart)
337
+            {
338
+                $arrErnteartAvailable[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
339
+            }
340
+        }
341
+        $arrErnteartBooked = [];
342
+        if ($Booking->ernteart !== null)
343
+        {
344
+            $arrErnteartBooked = explode(',', $Booking->ernteart);
345
+        }
346
+
347
+        $arrLagenAvailable = [];
348
+        if (($Lagen = $Slot->getRelated('lage')) !== null)
349
+        {
350
+            foreach ($Lagen as $lage)
351
+            {
352
+                $arrLagenAvailable[$lage->id] = $lage->title;
353
+            }
354
+        }
355
+        $arrLagenBooked = [];
356
+        if ($Booking->lage !== null)
357
+        {
358
+            $arrLagenBooked = explode(',', $Booking->lage);
359
+        }
360
+
318 361
         $intAvailableBehaelter = $Slot->getAvailableBehaelter();
319 362
 
320 363
         $arrData = array_merge($arrData,[
... ...
@@ -322,19 +365,28 @@ class SlotAjaxController extends AbstractController
322 365
             'id'       => $Booking->id,
323 366
             'slot'     => array_merge($Slot->row(),[
324 367
                 'sorte' => $arrSortenAvailable,
325
-                'behaelterAvailable' => $intAvailableBehaelter
368
+                'behaelterAvailable' => $intAvailableBehaelter,
326 369
             ]),
327 370
             'buchung' => array_merge($Booking->row(),[
328
-                'sorten' => $arrSortenBooked
371
+                'sorten' => $arrSortenBooked,
372
+                'ernteart' => $arrErnteartBooked,
373
+                'lage' => $arrLagenBooked,
329 374
             ]),
330 375
             'standort' => $Slot->getRelated('pid'),
331 376
             'buchen' => [
332 377
                 'buchbar' => (boolean) $intAvailableBehaelter,
333 378
                 'behaelter' => range(min($intAvailableBehaelter,1),$intAvailableBehaelter+$Booking->behaelter),
334
-                'sorten' => $arrSortenAvailable
379
+                'sorten' => $arrSortenAvailable,
380
+                'ernteart' => $arrErnteartAvailable,
381
+                'lage' => $arrLagenAvailable,
335 382
             ]
336 383
         ]);
337 384
 
385
+        if (!empty($error))
386
+        {
387
+            $arrData['toast'] = $error;
388
+        }
389
+
338 390
         return $this->render('@Contao/modal_booking_details.html.twig',$arrData);
339 391
     }
340 392
 
... ...
@@ -364,11 +416,33 @@ class SlotAjaxController extends AbstractController
364 416
             }
365 417
         }
366 418
 
367
-        if (empty($_REQUEST['id']) || empty(Input::post('behaelter')) || empty(Input::post('sorten')))
419
+        if (empty($_REQUEST['id']))
368 420
         {
369 421
             return new Response('Missing parameter',412);
370 422
         }
371 423
 
424
+        // Form validation
425
+        if (($Slot = WeinanlieferungSlotsModel::findByPk($_REQUEST['id'])) !== null)
426
+        {
427
+            if (Input::post('behaelter') > $Slot->getAvailableBehaelter())
428
+            {
429
+                return $this->renderDetails(false,sprintf('<div class="toast toast--danger mx-0">Fehler: Es sind mittlerweile nur noch %s Behälter verfügbar.</div>',$Slot->getAvailableBehaelter()));
430
+            }
431
+        }
432
+        $arrError = [];
433
+        foreach (['behaelter','sorten','ernteart'] as $field)
434
+        {
435
+            if (empty(Input::post($field)))
436
+            {
437
+                $arrError = [$field];
438
+            }
439
+        }
440
+
441
+        if (count($arrError))
442
+        {
443
+            return $this->renderDetails(false,'<div class="toast toast--danger mx-0">Bitte geben Sie alle Pflichtangaben (mit * marierte Felder) an</div>');
444
+        }
445
+
372 446
         $arrSorten = [];
373 447
         if (!is_array(Input::post('sorten')))
374 448
         {
... ...
@@ -377,13 +451,31 @@ class SlotAjaxController extends AbstractController
377 451
             $arrSorten = implode(';', Input::post('sorten'));
378 452
         }
379 453
 
454
+        $arrErnteart = [];
455
+        if (!is_array(Input::post('ernteart')))
456
+        {
457
+            $arrErnteart[] = Input::post('ernteart');
458
+        } else {
459
+            $arrErnteart = implode(',', Input::post('ernteart'));
460
+        }
461
+
462
+        $arrLage = [];
463
+        if (!is_array(Input::post('lage')))
464
+        {
465
+            $arrLage[] = Input::post('lage');
466
+        } else {
467
+            $arrLage = implode(',', Input::post('lage'));
468
+        }
469
+
380 470
         $Reservation = new WeinanlieferungReservationModel();
381 471
         $arrData = array_merge($arrData,[
382 472
             'pid' => $_REQUEST['id'],
383 473
             'tstamp' => time(),
384 474
             'uid' => FrontendUser::getInstance()->id,
385 475
             'behaelter' => Input::post('behaelter'),
386
-            'sorten' => $arrSorten
476
+            'sorten' => $arrSorten,
477
+            'ernteart' => $arrErnteart,
478
+            'lage' => $arrLage
387 479
         ]);
388 480
         $Reservation->setRow($arrData);
389 481
 
... ...
@@ -397,7 +489,7 @@ class SlotAjaxController extends AbstractController
397 489
     {
398 490
         Controller::loadDataContainer('tl_vr_wa_reservation');
399 491
 
400
-        if (empty($_REQUEST['id']) || empty(Input::post('behaelter')) || empty(Input::post('sorten')))
492
+        if (empty($_REQUEST['id']))
401 493
         {
402 494
             return new Response('Missing parameter',412);
403 495
         }
... ...
@@ -418,7 +510,7 @@ class SlotAjaxController extends AbstractController
418 510
 
419 511
             if ($File->hasErrors())
420 512
             {
421
-                return $this->renderDetails(false,'<div class="toast toast--danger mx-0">' . $File->getErrorAsHTML() . '</div>');
513
+                return $this->renderBooking(false,'<div class="toast toast--danger mx-0">' . $File->getErrorAsHTML() . '</div>');
422 514
             }
423 515
 
424 516
             if (!empty($_SESSION['FILES'][$File->name]))
... ...
@@ -428,6 +520,28 @@ class SlotAjaxController extends AbstractController
428 520
             }
429 521
         }
430 522
 
523
+        // Form validation
524
+        if (($Slot = $Reservation->getRelated('pid')) !== null)
525
+        {
526
+            if (Input::post('behaelter') > $Slot->getAvailableBehaelter()+$Reservation->behaelter)
527
+            {
528
+                return $this->renderBooking(false,sprintf('<div class="toast toast--danger mx-0">Fehler: Es sind mittlerweile nur noch %s Behälter verfügbar.</div>',$Slot->getAvailableBehaelter()+$Reservation->behaelter));
529
+            }
530
+        }
531
+        $arrError = [];
532
+        foreach (['behaelter','sorten','ernteart'] as $field)
533
+        {
534
+            if (empty(Input::post($field)))
535
+            {
536
+                $arrError = [$field];
537
+            }
538
+        }
539
+
540
+        if (count($arrError))
541
+        {
542
+            return $this->renderBooking(false,'<div class="toast toast--danger mx-0">Bitte geben Sie alle Pflichtangaben (mit * marierte Felder) an</div>');
543
+        }
544
+
431 545
         $arrSorten = [];
432 546
         if (!is_array(Input::post('sorten')))
433 547
         {
... ...
@@ -436,9 +550,27 @@ class SlotAjaxController extends AbstractController
436 550
             $arrSorten = implode(';', Input::post('sorten'));
437 551
         }
438 552
 
553
+        $arrErnteart = [];
554
+        if (!is_array(Input::post('ernteart')))
555
+        {
556
+            $arrErnteart[] = Input::post('ernteart');
557
+        } else {
558
+            $arrErnteart = implode(',', Input::post('ernteart'));
559
+        }
560
+
561
+        $arrLage = [];
562
+        if (!is_array(Input::post('lage')))
563
+        {
564
+            $arrLage[] = Input::post('lage');
565
+        } else {
566
+            $arrLage = implode(',', Input::post('lage'));
567
+        }
568
+
439 569
         $Reservation->tstamp = time();
440 570
         $Reservation->behaelter = Input::post('behaelter');
441 571
         $Reservation->sorten = $arrSorten;
572
+        $Reservation->ernteart = $arrErnteart;
573
+        $Reservation->lage = $arrLage;
442 574
 
443 575
         $Reservation->save();
444 576
 
... ...
@@ -76,12 +76,28 @@ class WeinanlieferungBookedListModuleController extends AbstractFrontendModuleCo
76 76
                         $strStandort = $Standort->title;
77 77
                     }
78 78
 
79
+                    if ($booking->ernteart !== null)
80
+                    {
81
+                        foreach (explode(',', $booking->ernteart) as $ernteart)
82
+                        {
83
+                            $arrErnteart[] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
84
+                        }
85
+                    }
86
+
87
+                    $arrLage = [];
88
+                    if (($Lage = $booking->getRelated('lage')) !== null)
89
+                    {
90
+                        $arrLage = $Lage->fetchEach('title');
91
+                    }
92
+
79 93
                     $arrData['days'][$day->dayBegin][] = array_merge($booking->row(), [
80 94
                         'sorte'              => $arrSorten,
81 95
                         'slot'  => array_merge($Slot->row(),[
82 96
                             'anmerkungen' => $Slot->anmerkungen ? StringUtil::substr(strip_tags($Slot->anmerkungen),110) : '',
83 97
                         ]),
84 98
                         'standort' => $strStandort,
99
+                        'ernteart' => $arrErnteart,
100
+                        'lage' => $arrLage,
85 101
                     ]);
86 102
                 }
87 103
             }
... ...
@@ -135,16 +135,16 @@ class WeinanlieferungSlotsListModuleController extends AbstractFrontendModuleCon
135 135
                     $strStandort = $Standort->title;
136 136
                 }
137 137
 
138
-                $strLage = '';
138
+                $arrLage = [];
139 139
                 if (($Lage = $slot->getRelated('lage')) !== null)
140 140
                 {
141
-                    $strLage = $Lage->title;
141
+                    $arrLage = $Lage->fetchEach('title');
142 142
                 }
143 143
 
144 144
                 $arrData['days'][$day->dayBegin][] = array_merge($slot->row(),[
145 145
                     'anmerkungen' => $slot->anmerkungen ? StringUtil::substr(strip_tags($slot->anmerkungen),110) : '',
146 146
                     'standort' => $strStandort,
147
-                    'lage' => $strLage,
147
+                    'lage' => $arrLage,
148 148
                     'sorte' => $arrSorten,
149 149
                     'ernteart' => $arrErnteart,
150 150
                     'behaelterAvailable' => $intAvailableBehaelter,
... ...
@@ -56,6 +56,26 @@ class WeinanlieferungReservationContainerListener
56 56
     }
57 57
 
58 58
     /**
59
+     * @Callback(table="tl_vr_wa_reservation", target="fields.lage.options")
60
+     */
61
+    public function onLageOptionsCallback(DataContainer $dc)
62
+    {
63
+        $arrLagen = [];
64
+
65
+        if (($Slot = WeinanlieferungSlotsModel::findByPk($dc->activeRecord->pid)) === null || $Slot->lage === null)
66
+        {
67
+            return $arrLagen;
68
+        }
69
+
70
+        if (($Lagen = $Slot->getRelated('lage')) !== null)
71
+        {
72
+            $arrLagen = array_combine($Lagen->fetchEach('id'),$Lagen->fetchEach('title'));
73
+        }
74
+
75
+        return $arrLagen;
76
+    }
77
+
78
+    /**
59 79
      * @Callback(table="tl_vr_wa_reservation", target="fields.ernteart.options")
60 80
      */
61 81
     public function onErnteartOptionsCallback(DataContainer $dc)
... ...
@@ -70,7 +90,7 @@ class WeinanlieferungReservationContainerListener
70 90
         $Ernteart = explode(',',$Slot->ernteart);
71 91
         foreach($Ernteart as $art)
72 92
         {
73
-            $arrErnteart[] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$art] ?? $art;
93
+            $arrErnteart[$art] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$art] ?? $art;
74 94
         }
75 95
 
76 96
         return $arrErnteart;
... ...
@@ -35,8 +35,9 @@ class WeinanlieferungSlotsModel extends Model
35 35
 
36 36
         // Skip unsaved elements (see #2708)
37 37
         $arrColumns[] = "$t.tstamp!=0";
38
+        $arrColumns[] = "$t.published='1'";
38 39
 
39
-        $arrColumns[] = "$t.buchbar='1' AND $t.buchbar_bis>$time";
40
+        $arrColumns[] = "($t.buchbar_ab<=$time OR $t.buchbar_ab = 0 OR $t.buchbar_ab IS NULL) AND $t.buchbar_bis>$time";
40 41
 
41 42
         if (!isset($arrOptions['order']))
42 43
         {
... ...
@@ -54,8 +55,9 @@ class WeinanlieferungSlotsModel extends Model
54 55
 
55 56
         // Skip unsaved elements (see #2708)
56 57
         $arrColumns[] = "$t.tstamp!=0";
58
+        $arrColumns[] = "$t.published='1'";
57 59
 
58
-        $arrColumns[] = "$t.buchbar='1' AND $t.buchbar_bis>$time";
60
+        $arrColumns[] = "($t.buchbar_ab<=$time OR $t.buchbar_ab = 0 OR $t.buchbar_ab IS NULL) AND $t.buchbar_bis>$time";
59 61
 
60 62
         if (!isset($arrOptions['order']))
61 63
         {
... ...
@@ -87,8 +89,9 @@ class WeinanlieferungSlotsModel extends Model
87 89
 
88 90
         // Skip unsaved elements (see #2708)
89 91
         $arrColumns[] = "$t.tstamp!=0";
92
+        $arrColumns[] = "$t.published='1'";
90 93
 
91
-        $arrColumns[] = "$t.buchbar='1' AND $t.buchbar_bis>$time";
94
+        $arrColumns[] = "($t.buchbar_ab<=$time OR $t.buchbar_ab = 0 OR $t.buchbar_ab IS NULL) AND $t.buchbar_bis>$time";
92 95
 
93 96
         if (!isset($arrOptions['order']))
94 97
         {
... ...
@@ -108,7 +111,7 @@ class WeinanlieferungSlotsModel extends Model
108 111
             $arrOptions['order'] = "$t.time ASC";
109 112
         }
110 113
 
111
-        return static::findBy(array("$t.time >= ?","$t.tstamp!=0","$t.buchbar='1' AND $t.buchbar_bis > ?"), [$time,$time], $arrOptions);
114
+        return static::findBy(array("$t.time >= ?","$t.tstamp!=0","$t.published='1' AND ($t.buchbar_ab<=$time OR $t.buchbar_ab = 0 OR $t.buchbar_ab IS NULL) AND $t.buchbar_bis > ?"), [$time,$time,$time], $arrOptions);
112 115
     }
113 116
 
114 117
     public function getAvailableBehaelter(?int $intOffset=null)