| ... | ... |
@@ -8,6 +8,11 @@ services: |
| 8 | 8 |
resource: ../src |
| 9 | 9 |
exclude: ../src/{VonrotenbergWeinanlieferungBundle.php,ContaoManager,Entity,Migrations,Model,Resources,Tests,Widget}
|
| 10 | 10 |
|
| 11 |
+ vonRotenberg\WeinanlieferungBundle\EventListener\CheckInCompletedListener: |
|
| 12 |
+ arguments: |
|
| 13 |
+ $logger: '@logger' |
|
| 14 |
+ $projectDir: '%kernel.project_dir%' |
|
| 15 |
+ |
|
| 11 | 16 |
vonRotenberg\WeinanlieferungBundle\Migration\RebsortenMigration: |
| 12 | 17 |
tags: |
| 13 | 18 |
- { name: contao.migration, priority: 0 }
|
| ... | ... |
@@ -27,10 +27,12 @@ use Contao\Input; |
| 27 | 27 |
use Contao\StringUtil; |
| 28 | 28 |
use Contao\System; |
| 29 | 29 |
use Doctrine\DBAL\Connection; |
| 30 |
+use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
|
| 30 | 31 |
use Symfony\Component\HttpFoundation\Request; |
| 31 | 32 |
use Symfony\Component\HttpFoundation\Response; |
| 32 | 33 |
use Symfony\Component\Routing\Annotation\Route; |
| 33 | 34 |
use Symfony\Contracts\Translation\TranslatorInterface; |
| 35 |
+use vonRotenberg\WeinanlieferungBundle\Event\CheckInCompletedEvent; |
|
| 34 | 36 |
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLageModel; |
| 35 | 37 |
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel; |
| 36 | 38 |
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel; |
| ... | ... |
@@ -45,12 +47,19 @@ class SlotAjaxController extends AbstractController |
| 45 | 47 |
private $tokenChecker; |
| 46 | 48 |
private $translator; |
| 47 | 49 |
private $framework; |
| 48 |
- |
|
| 49 |
- public function __construct(ContaoFramework $framework, TokenChecker $tokenChecker, TranslatorInterface $translator) |
|
| 50 |
+ private $eventDispatcher; |
|
| 51 |
+ |
|
| 52 |
+ public function __construct( |
|
| 53 |
+ ContaoFramework $framework, |
|
| 54 |
+ TokenChecker $tokenChecker, |
|
| 55 |
+ TranslatorInterface $translator, |
|
| 56 |
+ EventDispatcherInterface $eventDispatcher |
|
| 57 |
+ ) |
|
| 50 | 58 |
{
|
| 51 | 59 |
$this->framework = $framework; |
| 52 | 60 |
$this->tokenChecker = $tokenChecker; |
| 53 | 61 |
$this->translator = $translator; |
| 62 |
+ $this->eventDispatcher = $eventDispatcher; |
|
| 54 | 63 |
} |
| 55 | 64 |
|
| 56 | 65 |
public function __invoke(Request $request) |
| ... | ... |
@@ -801,6 +810,13 @@ class SlotAjaxController extends AbstractController |
| 801 | 810 |
$Booking->tstamp = $time; |
| 802 | 811 |
$Booking->save(); |
| 803 | 812 |
|
| 813 |
+ // Create reservation data array for the event |
|
| 814 |
+ $reservationData = $Booking->row(); |
|
| 815 |
+ |
|
| 816 |
+ // Dispatch the check-in completed event |
|
| 817 |
+ $event = new CheckInCompletedEvent($reservationData, $Booking); |
|
| 818 |
+ $this->eventDispatcher->dispatch($event, CheckInCompletedEvent::NAME); |
|
| 819 |
+ |
|
| 804 | 820 |
return new Response('<div class="toast toast--success mx-0"><p>Check-in erfolgreich durchgeführt</p></div>', 200, ['HX-Trigger'=> 'updateWaBooking']);
|
| 805 | 821 |
} |
| 806 | 822 |
|
| 807 | 823 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,59 @@ |
| 1 |
+<?php |
|
| 2 |
+ |
|
| 3 |
+namespace vonRotenberg\WeinanlieferungBundle\Event; |
|
| 4 |
+ |
|
| 5 |
+use Symfony\Contracts\EventDispatcher\Event; |
|
| 6 |
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel; |
|
| 7 |
+ |
|
| 8 |
+/** |
|
| 9 |
+ * Event that is triggered when a check-in is completed. |
|
| 10 |
+ */ |
|
| 11 |
+class CheckInCompletedEvent extends Event |
|
| 12 |
+{
|
|
| 13 |
+ /** |
|
| 14 |
+ * Event name |
|
| 15 |
+ */ |
|
| 16 |
+ public const NAME = 'vonrotenberg.weinanlieferung.checkin_completed'; |
|
| 17 |
+ |
|
| 18 |
+ /** |
|
| 19 |
+ * @var array The reservation data |
|
| 20 |
+ */ |
|
| 21 |
+ private $reservationData; |
|
| 22 |
+ |
|
| 23 |
+ /** |
|
| 24 |
+ * @var WeinanlieferungReservationModel The reservation model |
|
| 25 |
+ */ |
|
| 26 |
+ private $reservationModel; |
|
| 27 |
+ |
|
| 28 |
+ /** |
|
| 29 |
+ * CheckInCompletedEvent constructor. |
|
| 30 |
+ * |
|
| 31 |
+ * @param array $reservationData The reservation data as array |
|
| 32 |
+ * @param WeinanlieferungReservationModel $reservationModel The reservation model |
|
| 33 |
+ */ |
|
| 34 |
+ public function __construct(array $reservationData, WeinanlieferungReservationModel $reservationModel) |
|
| 35 |
+ {
|
|
| 36 |
+ $this->reservationData = $reservationData; |
|
| 37 |
+ $this->reservationModel = $reservationModel; |
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 40 |
+ /** |
|
| 41 |
+ * Get the reservation data. |
|
| 42 |
+ * |
|
| 43 |
+ * @return array |
|
| 44 |
+ */ |
|
| 45 |
+ public function getReservationData(): array |
|
| 46 |
+ {
|
|
| 47 |
+ return $this->reservationData; |
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ /** |
|
| 51 |
+ * Get the reservation model. |
|
| 52 |
+ * |
|
| 53 |
+ * @return WeinanlieferungReservationModel |
|
| 54 |
+ */ |
|
| 55 |
+ public function getReservationModel(): WeinanlieferungReservationModel |
|
| 56 |
+ {
|
|
| 57 |
+ return $this->reservationModel; |
|
| 58 |
+ } |
|
| 59 |
+} |
| 0 | 60 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,231 @@ |
| 1 |
+<?php |
|
| 2 |
+ |
|
| 3 |
+namespace vonRotenberg\WeinanlieferungBundle\EventListener; |
|
| 4 |
+ |
|
| 5 |
+use Psr\Log\LoggerInterface; |
|
| 6 |
+use Psr\Log\LogLevel; |
|
| 7 |
+use Contao\CoreBundle\Monolog\ContaoContext; |
|
| 8 |
+use Contao\MemberModel; |
|
| 9 |
+use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
|
| 10 |
+use Symfony\Component\Filesystem\Filesystem; |
|
| 11 |
+use vonRotenberg\WeinanlieferungBundle\Event\CheckInCompletedEvent; |
|
| 12 |
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLageModel; |
|
| 13 |
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel; |
|
| 14 |
+use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel; |
|
| 15 |
+ |
|
| 16 |
+/** |
|
| 17 |
+ * Listener for the CheckInCompletedEvent. |
|
| 18 |
+ * Generates a CSV export file for each checked-in booking. |
|
| 19 |
+ */ |
|
| 20 |
+class CheckInCompletedListener implements EventSubscriberInterface |
|
| 21 |
+{
|
|
| 22 |
+ /** |
|
| 23 |
+ * @var LoggerInterface |
|
| 24 |
+ */ |
|
| 25 |
+ private $logger; |
|
| 26 |
+ |
|
| 27 |
+ /** |
|
| 28 |
+ * @var string |
|
| 29 |
+ */ |
|
| 30 |
+ private $projectDir; |
|
| 31 |
+ |
|
| 32 |
+ /** |
|
| 33 |
+ * Constructor. |
|
| 34 |
+ * |
|
| 35 |
+ * @param LoggerInterface $logger |
|
| 36 |
+ * @param string $projectDir |
|
| 37 |
+ */ |
|
| 38 |
+ public function __construct(LoggerInterface $logger, string $projectDir) |
|
| 39 |
+ {
|
|
| 40 |
+ $this->logger = $logger; |
|
| 41 |
+ $this->projectDir = $projectDir; |
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ /** |
|
| 45 |
+ * Returns an array of event names this subscriber wants to listen to. |
|
| 46 |
+ * |
|
| 47 |
+ * @return array The event names to listen to |
|
| 48 |
+ */ |
|
| 49 |
+ public static function getSubscribedEvents() |
|
| 50 |
+ {
|
|
| 51 |
+ return [ |
|
| 52 |
+ CheckInCompletedEvent::NAME => 'onCheckInCompleted', |
|
| 53 |
+ ]; |
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 56 |
+ /** |
|
| 57 |
+ * Handle the check-in completed event. |
|
| 58 |
+ * |
|
| 59 |
+ * @param CheckInCompletedEvent $event |
|
| 60 |
+ */ |
|
| 61 |
+ public function onCheckInCompleted(CheckInCompletedEvent $event) |
|
| 62 |
+ {
|
|
| 63 |
+ // Get the reservation data from the event |
|
| 64 |
+ $reservationData = $event->getReservationData(); |
|
| 65 |
+ $reservationModel = $event->getReservationModel(); |
|
| 66 |
+ |
|
| 67 |
+ // Log the event for testing purposes |
|
| 68 |
+ $this->logger->log( |
|
| 69 |
+ LogLevel::INFO, |
|
| 70 |
+ sprintf('Check-in completed for reservation ID: %s', $reservationData['id']),
|
|
| 71 |
+ ['contao' => new ContaoContext(__METHOD__, 'CHECK_IN')] |
|
| 72 |
+ ); |
|
| 73 |
+ |
|
| 74 |
+ // Generate and save the CSV export |
|
| 75 |
+ $this->generateCsvExport($reservationData, $reservationModel); |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ /** |
|
| 79 |
+ * Generate and save the CSV export file. |
|
| 80 |
+ * |
|
| 81 |
+ * @param array $reservationData |
|
| 82 |
+ * @param object $reservationModel |
|
| 83 |
+ */ |
|
| 84 |
+ private function generateCsvExport(array $reservationData, $reservationModel) |
|
| 85 |
+ {
|
|
| 86 |
+ // Create export directory if it doesn't exist |
|
| 87 |
+ $exportDir = $this->projectDir . '/export/check_in'; |
|
| 88 |
+ $filesystem = new Filesystem(); |
|
| 89 |
+ |
|
| 90 |
+ if (!$filesystem->exists($exportDir)) {
|
|
| 91 |
+ $filesystem->mkdir($exportDir, 0755); |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ // Get member data |
|
| 95 |
+ $memberId = $reservationData['uid']; |
|
| 96 |
+ $memberModel = MemberModel::findById($memberId); |
|
| 97 |
+ |
|
| 98 |
+ if (null === $memberModel) {
|
|
| 99 |
+ $this->logger->log( |
|
| 100 |
+ LogLevel::ERROR, |
|
| 101 |
+ sprintf('Could not find member with ID: %s', $memberId),
|
|
| 102 |
+ ['contao' => new ContaoContext(__METHOD__, 'CHECK_IN_CSV_EXPORT')] |
|
| 103 |
+ ); |
|
| 104 |
+ return; |
|
| 105 |
+ } |
|
| 106 |
+ |
|
| 107 |
+ // Get behaelter numbers |
|
| 108 |
+ $behaelterNumbers = json_decode($reservationData['behaelter_numbers'], true); |
|
| 109 |
+ if (!is_array($behaelterNumbers) || empty($behaelterNumbers)) {
|
|
| 110 |
+ $this->logger->log( |
|
| 111 |
+ LogLevel::ERROR, |
|
| 112 |
+ sprintf('No behaelter numbers found for reservation ID: %s', $reservationData['id']),
|
|
| 113 |
+ ['contao' => new ContaoContext(__METHOD__, 'CHECK_IN_CSV_EXPORT')] |
|
| 114 |
+ ); |
|
| 115 |
+ return; |
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 118 |
+ // Format check-in date |
|
| 119 |
+ $checkInDate = date('d.m.Y', $reservationData['checked_in_on']);
|
|
| 120 |
+ |
|
| 121 |
+ // Get sorten (grape varieties and harvest types) |
|
| 122 |
+ $sortenData = []; |
|
| 123 |
+ $sortenLeseartPairs = explode(';', $reservationData['sorten']);
|
|
| 124 |
+ foreach ($sortenLeseartPairs as $pair) {
|
|
| 125 |
+ if (empty($pair)) {
|
|
| 126 |
+ continue; |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ list($sorteId, $leseartId) = explode(',', $pair);
|
|
| 130 |
+ |
|
| 131 |
+ $rebsorteModel = WeinanlieferungRebsorteModel::findById($sorteId); |
|
| 132 |
+ $leseartModel = WeinanlieferungLeseartModel::findById($leseartId); |
|
| 133 |
+ |
|
| 134 |
+ if (null !== $rebsorteModel && null !== $leseartModel) {
|
|
| 135 |
+ $sortenData[] = [ |
|
| 136 |
+ 'rebsorte_id' => $rebsorteModel->ident_string, |
|
| 137 |
+ 'rebsorte_title' => $rebsorteModel->title, |
|
| 138 |
+ 'leseart_id' => $leseartModel->ident_string, |
|
| 139 |
+ 'leseart_title' => $leseartModel->title |
|
| 140 |
+ ]; |
|
| 141 |
+ } |
|
| 142 |
+ } |
|
| 143 |
+ |
|
| 144 |
+ // Get lagen (locations) |
|
| 145 |
+ $lagenData = []; |
|
| 146 |
+ $lagenIds = explode(',', $reservationData['lage']);
|
|
| 147 |
+ foreach ($lagenIds as $lageId) {
|
|
| 148 |
+ if (empty($lageId)) {
|
|
| 149 |
+ continue; |
|
| 150 |
+ } |
|
| 151 |
+ |
|
| 152 |
+ $lageModel = WeinanlieferungLageModel::findById($lageId); |
|
| 153 |
+ |
|
| 154 |
+ if (null !== $lageModel) {
|
|
| 155 |
+ $lagenData[] = [ |
|
| 156 |
+ 'lage_id' => $lageModel->ident_string, |
|
| 157 |
+ 'lage_title' => $lageModel->title |
|
| 158 |
+ ]; |
|
| 159 |
+ } |
|
| 160 |
+ } |
|
| 161 |
+ |
|
| 162 |
+ // Get ernteart (harvest method) |
|
| 163 |
+ $ernteartValues = explode(',', $reservationData['ernteart']);
|
|
| 164 |
+ $ernteartMapping = [ |
|
| 165 |
+ 'handlese' => 'H', |
|
| 166 |
+ 'vollernter' => 'V' |
|
| 167 |
+ ]; |
|
| 168 |
+ |
|
| 169 |
+ // Prepare CSV data |
|
| 170 |
+ $csvData = []; |
|
| 171 |
+ |
|
| 172 |
+ // For each behaelter, create a line in the CSV |
|
| 173 |
+ foreach ($behaelterNumbers as $behaelterNumber) {
|
|
| 174 |
+ // Skip special value 9999 if needed |
|
| 175 |
+ if ($behaelterNumber === '9999') {
|
|
| 176 |
+ continue; |
|
| 177 |
+ } |
|
| 178 |
+ |
|
| 179 |
+ // Use the first sorte, leseart, and lage if available |
|
| 180 |
+ $rebsorteId = !empty($sortenData) ? $sortenData[0]['rebsorte_id'] : ''; |
|
| 181 |
+ $rebsorteTitle = !empty($sortenData) ? $sortenData[0]['rebsorte_title'] : ''; |
|
| 182 |
+ $leseartId = !empty($sortenData) ? $sortenData[0]['leseart_id'] : ''; |
|
| 183 |
+ $leseartTitle = !empty($sortenData) ? $sortenData[0]['leseart_title'] : ''; |
|
| 184 |
+ $lageId = !empty($lagenData) ? $lagenData[0]['lage_id'] : ''; |
|
| 185 |
+ $lageTitle = !empty($lagenData) ? $lagenData[0]['lage_title'] : ''; |
|
| 186 |
+ |
|
| 187 |
+ // Map ernteart values to H/V |
|
| 188 |
+ $ernteart = ''; |
|
| 189 |
+ foreach ($ernteartValues as $value) {
|
|
| 190 |
+ if (isset($ernteartMapping[$value])) {
|
|
| 191 |
+ $ernteart = $ernteartMapping[$value]; |
|
| 192 |
+ break; |
|
| 193 |
+ } |
|
| 194 |
+ } |
|
| 195 |
+ |
|
| 196 |
+ // Create CSV line |
|
| 197 |
+ $csvData[] = [ |
|
| 198 |
+ $memberModel->memberno, // member id |
|
| 199 |
+ $memberModel->firstname . ' ' . $memberModel->lastname, // member first and lastname |
|
| 200 |
+ $rebsorteId, // rebsorte identifikator |
|
| 201 |
+ $rebsorteTitle, // rebsorte title |
|
| 202 |
+ $lageId, // lage identifikator |
|
| 203 |
+ $lageTitle, // lage title |
|
| 204 |
+ $leseartId, // leseart identifikator |
|
| 205 |
+ $leseartTitle, // leseart title |
|
| 206 |
+ $ernteart, // ernteart (H for handlese, V for vollernter) |
|
| 207 |
+ $behaelterNumber, // behaelter number |
|
| 208 |
+ $checkInDate, // check-in date |
|
| 209 |
+ $reservationData['behaelter'] // behaelter amount for the whole checked-in booking |
|
| 210 |
+ ]; |
|
| 211 |
+ } |
|
| 212 |
+ |
|
| 213 |
+ // Generate CSV file |
|
| 214 |
+ $filename = $reservationData['id'] . '_' . $memberModel->memberno . '.csv'; |
|
| 215 |
+ $filepath = $exportDir . '/' . $filename; |
|
| 216 |
+ |
|
| 217 |
+ $file = fopen($filepath, 'w'); |
|
| 218 |
+ |
|
| 219 |
+ foreach ($csvData as $line) {
|
|
| 220 |
+ fputcsv($file, $line, ';'); |
|
| 221 |
+ } |
|
| 222 |
+ |
|
| 223 |
+ fclose($file); |
|
| 224 |
+ |
|
| 225 |
+ $this->logger->log( |
|
| 226 |
+ LogLevel::INFO, |
|
| 227 |
+ sprintf('CSV export created for reservation ID: %s at %s', $reservationData['id'], $filepath),
|
|
| 228 |
+ ['contao' => new ContaoContext(__METHOD__, 'CHECK_IN_CSV_EXPORT')] |
|
| 229 |
+ ); |
|
| 230 |
+ } |
|
| 231 |
+} |