<?php
declare(strict_types=1);
/*
* This file is part of contao-weinanlieferung-bundle.
*
* (c) vonRotenberg
*
* @license commercial
*/
namespace vonRotenberg\WeinanlieferungBundle\Cron;
use Contao\Config;
use Contao\CoreBundle\Monolog\ContaoContext;
use Contao\CoreBundle\ServiceAnnotation\CronJob;
use Contao\Date;
use Contao\MemberModel;
use Contao\StringUtil;
use Contao\System;
use Doctrine\DBAL\Connection;
use NotificationCenter\Model\Notification;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLageModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungStandortModel;
use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungUnitModel;
/**
* @CronJob("* * * * *")
*/
class SendCheckInNotificationJob
{
/** @var LoggerInterface */
private $logger;
/** @var Connection */
private $db;
public function __construct(Connection $db, LoggerInterface $logger)
{
$this->logger = $logger;
$this->db = $db;
}
public function __invoke(string $scope)
{
System::loadLanguageFile('default', 'de');
list($admin_name, $admin_email) = StringUtil::splitFriendlyEmail(Config::get('adminEmail'));
$intNotificationsCount = 0;
// Get locations
$Locations = WeinanlieferungStandortModel::findBy('nc_enable', '1');
if ($Locations !== null) {
while ($Locations->next()) {
$Location = $Locations->current();
$time = Date::floorToMinute();
// Do we have check-ins that need notifications
// The key difference from SendBookingChangeNotificationJob is the WHERE clause:
// We look for r.checked_in = '1' AND r.checked_in_on > r.checked_in_nc_sent
$Bookings = $this->db->executeQuery(
"SELECT r.id, r.pid, r.uid, r.behaelter, r.sorten, r.lage, r.ernteart, r.upload, r.nc_sent, r.checked_in_nc_sent, r.unit_id, r.unit_amount,
s.date as slot_date, s.time as slot_time, s.behaelter as slot_behaelter,
s.sorten as slot_sorten, s.ernteart as slot_ernteart, s.lage as slot_lage,
s.anmerkungen as slot_anmerkungen, r.approved, r.approved_on, r.checked_in,
r.checked_in_on, r.behaelter_numbers
FROM tl_vr_wa_reservation r
INNER JOIN tl_vr_wa_slot s ON s.id = r.pid
WHERE s.pid = ? AND r.checked_in = '1' AND r.checked_in_on > r.checked_in_nc_sent AND s.published='1'",
[$Location->id]
);
// Load notification model if we have check-ins to notify
if ($Bookings->rowCount() && ($Notification = Notification::findByPk($Location->nc_notification_checkin)) !== null) {
foreach ($Bookings->iterateAssociative() as $Booking) {
// Get member
$Member = MemberModel::findOneBy(
["id = ?", "login='1' AND (start='' OR start<='$time') AND (stop='' OR stop>'" . ($time + 60) . "') AND disable=''"],
[$Booking['uid']]
);
// Get unit
$Unit = WeinanlieferungUnitModel::findByPk($Booking['unit_id']);
// Send notification to member
if ($Member !== null) {
$arrSortenAvailable = [];
$arrErnteartAvailable = [];
$arrLageAvailable = [];
$arrSortenBooked = [];
$arrErnteartBooked = [];
$arrLageBooked = [];
$SortenLeseart = StringUtil::deserialize($Booking['slot_sorten'], true);
foreach ($SortenLeseart as $sorteLeseart) {
$objSorte = WeinanlieferungRebsorteModel::findByPk($sorteLeseart['sorte']);
$objLeseart = WeinanlieferungLeseartModel::findByPk($sorteLeseart['leseart']);
if ($objSorte !== null && $objLeseart !== null) {
$arrSortenAvailable[$objSorte->id . ',' . $objLeseart->id] =
$objSorte->title . ' ' . $objLeseart->title;
}
}
if (!empty($Booking['sorten'])) {
$SortenLeseart = explode(';', $Booking['sorten']);
foreach ($SortenLeseart as $sorteLeseart) {
if (empty($sorteLeseart)) {
continue;
}
$parts = explode(',', $sorteLeseart);
if (count($parts) >= 2) {
$sorte = $parts[0];
$leseart = $parts[1];
$objSorte = WeinanlieferungRebsorteModel::findByPk($sorte);
$objLeseart = WeinanlieferungLeseartModel::findByPk($leseart);
if ($objSorte !== null && $objLeseart !== null) {
$arrSortenBooked[$objSorte->id . ',' . $objLeseart->id] =
$objSorte->title . ' ' . $objLeseart->title;
}
}
}
}
if (!empty($Booking['ernteart'])) {
foreach (explode(',', $Booking['ernteart']) as $ernteart) {
if (empty($ernteart)) {
continue;
}
$arrErnteartBooked[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
}
}
if (!empty($Booking['slot_ernteart'])) {
foreach (explode(',', $Booking['slot_ernteart']) as $ernteart) {
if (empty($ernteart)) {
continue;
}
$arrErnteartAvailable[$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart;
}
}
if (!empty($Booking['slot_lage'])) {
foreach (explode(',', $Booking['slot_lage']) as $lage) {
if (empty($lage)) {
continue;
}
if (($Lage = WeinanlieferungLageModel::findByPk($lage)) !== null) {
$arrLageAvailable[$Lage->id] = $Lage->title;
}
}
}
if (!empty($Booking['lage'])) {
foreach (explode(',', $Booking['lage']) as $lage) {
if (empty($lage)) {
continue;
}
if (($Lage = WeinanlieferungLageModel::findByPk($lage)) !== null) {
$arrLageBooked[$Lage->id] = $Lage->title;
}
}
}
// Behaelter numbers handling
$blnIsIncomplete = false;
$arrBehaelterNumbers = [];
$behaelterNumbers = json_decode($Booking['behaelter_numbers'] ?? '[]', true);
$isNewFormat = isset($behaelterNumbers[0]) && is_array($behaelterNumbers[0]) && isset($behaelterNumbers[0]['behaelter']);
// If it's the old format, convert it to the new format for compatibility
if (!$isNewFormat) {
$oldFormat = $behaelterNumbers;
$memberModel = MemberModel::findById($Booking['uid']);
$behaelterNumbers = [];
foreach ($oldFormat as $number) {
$behaelterNumbers[] = [
'behaelter' => $number,
'member' => $memberModel->memberno
];
}
}
foreach ($behaelterNumbers as $behaelterNumber) {
if ($behaelterNumber['behaelter'] === '9999') {
$blnIsIncomplete = true;
}
$arrBehaelterNumbers[] = $behaelterNumber['behaelter'] . ' [Mitglied: ' . $behaelterNumber['member'] . ']';
}
// Send notification with all booking data
// We include the check-in specific fields: checked_in, checked_in_on, behaelter_numbers
$Notification->send(
[
'member_email' => $Member->email . ($Member->nc_news_additionalEmail ? ',' . $Member->nc_news_additionalEmail : ''),
'member_firstname' => $Member->firstname,
'member_lastname' => $Member->lastname,
'member_memberno' => $Member->memberno,
'slot_date' => Date::parse(Date::getNumericDateFormat(), $Booking['slot_date']),
'slot_time' => Date::parse(Date::getNumericTimeFormat(), $Booking['slot_time']),
'slot_standort' => $Location->title,
'slot_behaelter' => $Booking['slot_behaelter'],
'slot_sorten' => implode(', ', $arrSortenAvailable),
'slot_ernteart' => implode(', ', $arrErnteartAvailable),
'slot_lage' => implode(', ', $arrLageAvailable),
'slot_anmerkungen' => $Booking['slot_anmerkungen'],
'booking_ncsent' => $Booking['nc_sent'],
'booking_behaelter' => $Booking['behaelter'],
'booking_unit_name' => $Unit !== null ? $Unit->title : 'Behälter',
'booking_unit_amount' => $Booking['unit_amount'],
'booking_sorten' => implode(', ', $arrSortenBooked),
'booking_ernteart' => implode(', ', $arrErnteartBooked),
'booking_lage' => implode(', ', $arrLageBooked),
'booking_approved' => $Booking['approved'],
'booking_approved_on'=> $Booking['approved_on'],
'booking_checked_in' => $Booking['checked_in'],
'booking_checked_in_on' => $Booking['checked_in_on'],
'booking_checked_in_ncsent' => $Booking['checked_in_nc_sent'],
'booking_checkin_incomplete' => $blnIsIncomplete,
'booking_behaelter_numbers' => \implode(', ', $arrBehaelterNumbers),
'admin_email' => $admin_email,
],
$GLOBALS['TL_LANGUAGE']
);
// Update the checked_in_nc_sent timestamp to mark this notification as sent
$intNotificationsCount++;
$this->db->executeStatement(
"UPDATE tl_vr_wa_reservation SET checked_in_nc_sent = ? WHERE id = ?",
[time(), $Booking['id']]
);
}
}
}
}
// Log notifications sent
if ($intNotificationsCount) {
$this->logger->log(
LogLevel::INFO,
sprintf('WA check-in notifications(%s) has been sent', $intNotificationsCount),
['contao' => new ContaoContext(__METHOD__, 'CRON')]
);
}
}
}
}