Fix timezone bug causing slots past closing time; remove lunch fallback outside window
- Add Europe/Oslo timezone to all DateTime constructors in getAvailableTimes() and computeLunchBreak() to prevent UTC vs local time mismatch that allowed booking slots 1 hour past the worker's end time on same-day bookings - Remove fallback loop in computeLunchBreak() that pushed the lunch break outside the configured window; lunch break is now strictly enforced within the interval Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -209,41 +209,6 @@ class Admin extends CI_Controller {
|
||||
|
||||
|
||||
|
||||
public function admin_worker_override() {
|
||||
$this->load->helper('url');
|
||||
$this->load->model('User_model');
|
||||
$this->load->model('Service_model');
|
||||
|
||||
$data['pageTitle'] = 'Egyedi napok kezelése';
|
||||
$data['workers'] = $this->Service_model->getAllWorkers();
|
||||
|
||||
if ($this->input->post('storeOverride')) {
|
||||
$this->load->database();
|
||||
$insert = [
|
||||
'worker_id' => $_POST['worker_id'],
|
||||
'date' => $_POST['date'],
|
||||
'is_day_off' => isset($_POST['is_day_off']) ? 1 : 0,
|
||||
'start_time' => $_POST['start_time'],
|
||||
'end_time' => $_POST['end_time']
|
||||
];
|
||||
|
||||
$existing = $this->db->query("SELECT * FROM worker_schedule_overrides WHERE worker_id = '{$insert['worker_id']}' AND date = '{$insert['date']}'")->row();
|
||||
if ($existing) {
|
||||
$this->db->where('id', $existing->id)->update('worker_schedule_overrides', $insert);
|
||||
} else {
|
||||
$this->db->insert('worker_schedule_overrides', $insert);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_GET['worker_id'])) {
|
||||
$this->load->database();
|
||||
$worker_id = $_GET['worker_id'];
|
||||
$data['selectedWorkerId'] = $worker_id;
|
||||
$data['overrides'] = $this->db->query("SELECT * FROM worker_schedule_overrides WHERE worker_id = '$worker_id' ORDER BY date ASC")->result();
|
||||
}
|
||||
|
||||
$this->load->view('admin/worker_override', $data);
|
||||
}
|
||||
|
||||
|
||||
public function manage_worker_schedule($worker_id = null) {
|
||||
|
||||
@@ -439,8 +439,8 @@ class Service_model extends CI_Model {
|
||||
|
||||
if ($override->is_day_off) return [];
|
||||
|
||||
$dayStartTime = new DateTime($selectedDate.' '.$override->start_time);
|
||||
$finishTime = new DateTime($selectedDate.' '.$override->end_time);
|
||||
$dayStartTime = new DateTime($selectedDate.' '.$override->start_time, new DateTimeZone('Europe/Oslo'));
|
||||
$finishTime = new DateTime($selectedDate.' '.$override->end_time, new DateTimeZone('Europe/Oslo'));
|
||||
} else {
|
||||
$weekday = date('w', strtotime($selectedDate)); // 0 = Sunday, 6 = Saturday
|
||||
$weekNumber = date('W', strtotime($selectedDate));
|
||||
@@ -461,8 +461,8 @@ class Service_model extends CI_Model {
|
||||
}
|
||||
|
||||
$schedule = $scheduleQuery->row();
|
||||
$dayStartTime = new DateTime($selectedDate . ' ' . $schedule->start_time);
|
||||
$finishTime = new DateTime($selectedDate . ' ' . $schedule->end_time);
|
||||
$dayStartTime = new DateTime($selectedDate . ' ' . $schedule->start_time, new DateTimeZone('Europe/Oslo'));
|
||||
$finishTime = new DateTime($selectedDate . ' ' . $schedule->end_time, new DateTimeZone('Europe/Oslo'));
|
||||
}
|
||||
|
||||
// --- ÚJ: ma foglalva csak (következő 15 perces blokk + 1 óra) UTÁN legyen időpont ---
|
||||
@@ -685,8 +685,9 @@ class Service_model extends CI_Model {
|
||||
|
||||
$lunchDuration = new DateInterval('PT30M');
|
||||
$lunchStep = new DateInterval('PT15M');
|
||||
$winStartDt = new DateTime($date . ' ' . $winStart);
|
||||
$winEndDt = new DateTime($date . ' ' . $winEnd);
|
||||
$tz = new DateTimeZone('Europe/Oslo');
|
||||
$winStartDt = new DateTime($date . ' ' . $winStart, $tz);
|
||||
$winEndDt = new DateTime($date . ' ' . $winEnd, $tz);
|
||||
|
||||
// Clamp window to actual shift hours
|
||||
if ($shiftStart && $winStartDt < $shiftStart) $winStartDt = clone $shiftStart;
|
||||
@@ -695,10 +696,11 @@ class Service_model extends CI_Model {
|
||||
$scanEnd = (clone $winEndDt)->sub($lunchDuration);
|
||||
|
||||
$isFree = function($candidate) use ($lunchDuration, $dayBookings, $date) {
|
||||
$tz = new DateTimeZone('Europe/Oslo');
|
||||
$candEnd = (clone $candidate)->add($lunchDuration);
|
||||
foreach ($dayBookings as $bk) {
|
||||
$bkS = new DateTime($date . ' ' . $bk->booking_start_time);
|
||||
$bkE = new DateTime($date . ' ' . $bk->booking_finish_time);
|
||||
$bkS = new DateTime($date . ' ' . $bk->booking_start_time, $tz);
|
||||
$bkE = new DateTime($date . ' ' . $bk->booking_finish_time, $tz);
|
||||
if ($bkS < $candEnd && $bkE > $candidate) return false;
|
||||
}
|
||||
return true;
|
||||
@@ -713,7 +715,7 @@ class Service_model extends CI_Model {
|
||||
if (!empty($freeSlots)) {
|
||||
// Pick the slot closest to preferred time (or start of window if no preference)
|
||||
$prefDt = $preferredTime
|
||||
? new DateTime($date . ' ' . $preferredTime)
|
||||
? new DateTime($date . ' ' . $preferredTime, new DateTimeZone('Europe/Oslo'))
|
||||
: clone $winStartDt;
|
||||
$best = null;
|
||||
$bestDiff = PHP_INT_MAX;
|
||||
@@ -725,12 +727,6 @@ class Service_model extends CI_Model {
|
||||
return ['start' => $best->format('H:i'), 'end' => $bestEnd->format('H:i')];
|
||||
}
|
||||
|
||||
// Fallback: search after the window
|
||||
for ($c = clone $winEndDt; ; $c->add($lunchStep)) {
|
||||
$candEnd = (clone $c)->add($lunchDuration);
|
||||
if ($candEnd > $shiftEnd) break;
|
||||
if ($isFree($c)) return ['start' => $c->format('H:i'), 'end' => $candEnd->format('H:i')];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ $this->load->helper('url');
|
||||
<link rel="stylesheet" href="<?php echo base_url();?>assets/js/jquery/jquery-ui.css">
|
||||
<script src="<?php echo base_url();?>assets/js/jquery/jquery-ui.js"></script>
|
||||
<script src="https://momentjs.com/downloads/moment-with-locales.js"></script>
|
||||
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
||||
<!-- Start cookieyes banner --> <script id="cookieyes" type="text/javascript" src="https://cdn-cookieyes.com/client_data/82ccb0b38ad63612e4c6a3fa/script.js"></script> <!-- End cookieyes banner -->
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo base_url();?>assets/css/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo base_url();?>assets/css/responsive.css">
|
||||
|
||||
@@ -41,7 +41,6 @@ $this->load->helper('url');
|
||||
<link rel="stylesheet" href="<?php echo base_url();?>assets/js/jquery/jquery-ui.css">
|
||||
<script src="<?php echo base_url();?>assets/js/jquery/jquery-ui.js"></script>
|
||||
<script src="https://momentjs.com/downloads/moment-with-locales.js"></script>
|
||||
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
||||
<!-- Start cookieyes banner --> <script id="cookieyes" type="text/javascript" src="https://cdn-cookieyes.com/client_data/82ccb0b38ad63612e4c6a3fa/script.js"></script> <!-- End cookieyes banner -->
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo base_url();?>assets/css/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo base_url();?>assets/css/responsive.css">
|
||||
|
||||
@@ -187,8 +187,7 @@
|
||||
<div class="bookingButtonContainer">
|
||||
<input type="button" class="leftButton bookingButton" onclick="goToStep(3);" value="Back"/>
|
||||
<input type="hidden" name="subpage" value="<?php echo $subpage;?>">
|
||||
<div class="g-recaptcha" data-callback="recaptcha_callback" data-sitekey="6Ld_sS0pAAAAANQrY3ziDGPcsjMOdM9L9WIgAzxd"></div>
|
||||
<input type="submit" id="sendBooking" class="rightButton bookingSubmit" for="bookingStepForm" name="sendBooking" value="Send" disabled/>
|
||||
<input type="submit" id="sendBooking" class="rightButton bookingSubmit" for="bookingStepForm" name="sendBooking" value="Send"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -426,6 +425,7 @@
|
||||
$('#bookingStep'+stepNumber).show();
|
||||
|
||||
if(stepNumber == 4){
|
||||
recaptcha_callback();
|
||||
$('#stepCircle1').addClass('activeStep');
|
||||
$('#stepCircle2').addClass('activeStep');
|
||||
$('#stepCircle3').addClass('activeStep');
|
||||
@@ -473,6 +473,7 @@
|
||||
$RequiredFieldSelected = 1;
|
||||
}
|
||||
else if(stepNumber == 4){
|
||||
recaptcha_callback();
|
||||
$RequiredFieldSelected = 1;
|
||||
}
|
||||
else if(stepNumber == 3){
|
||||
@@ -484,6 +485,7 @@
|
||||
}
|
||||
|
||||
else if(stepNumber == 4){
|
||||
recaptcha_callback();
|
||||
if($('#booking_date').val() != '' && $('#booking_time').val() != ''){
|
||||
$RequiredFieldSelected = 1;
|
||||
}
|
||||
@@ -503,7 +505,3 @@
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
|
||||
async defer>
|
||||
</script>
|
||||
@@ -188,8 +188,7 @@
|
||||
<div class="bookingButtonContainer">
|
||||
<input type="button" class="leftButton bookingButton" onclick="goToStep(3);" value="Vissza"/>
|
||||
<input type="hidden" name="subpage" value="<?php echo $subpage;?>">
|
||||
<div class="g-recaptcha" data-callback="recaptcha_callback" data-sitekey="6Ld_sS0pAAAAANQrY3ziDGPcsjMOdM9L9WIgAzxd"></div>
|
||||
<input type="submit" id="sendBooking" class="rightButton bookingSubmit" for="bookingStepForm" name="sendBooking" value="Elküld" disabled/>
|
||||
<input type="submit" id="sendBooking" class="rightButton bookingSubmit" for="bookingStepForm" name="sendBooking" value="Elküld"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -425,6 +424,7 @@
|
||||
$('#bookingStep'+stepNumber).show();
|
||||
|
||||
if(stepNumber == 4){
|
||||
recaptcha_callback();
|
||||
$('#stepCircle1').addClass('activeStep');
|
||||
$('#stepCircle2').addClass('activeStep');
|
||||
$('#stepCircle3').addClass('activeStep');
|
||||
@@ -472,6 +472,7 @@
|
||||
$RequiredFieldSelected = 1;
|
||||
}
|
||||
else if(stepNumber == 4){
|
||||
recaptcha_callback();
|
||||
$RequiredFieldSelected = 1;
|
||||
}
|
||||
else if(stepNumber == 3){
|
||||
@@ -483,6 +484,7 @@
|
||||
}
|
||||
|
||||
else if(stepNumber == 4){
|
||||
recaptcha_callback();
|
||||
if($('#booking_date').val() != '' && $('#booking_time').val() != ''){
|
||||
$RequiredFieldSelected = 1;
|
||||
}
|
||||
@@ -502,7 +504,3 @@
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
|
||||
async defer>
|
||||
</script>
|
||||
@@ -188,8 +188,7 @@ if (is_array($services) && count($services) > 0) {
|
||||
<div class="bookingButtonContainer">
|
||||
<input type="button" class="leftButton bookingButton" onclick="goToStep(3);" value="Tilbake"/>
|
||||
<input type="hidden" name="subpage" value="<?php echo $subpage;?>">
|
||||
<div class="g-recaptcha" data-callback="recaptcha_callback" data-sitekey="6Ld_sS0pAAAAANQrY3ziDGPcsjMOdM9L9WIgAzxd"></div>
|
||||
<input type="submit" id="sendBooking" class="rightButton bookingSubmit" for="bookingStepForm" name="sendBooking" value="Sende" disabled/>
|
||||
<input type="submit" id="sendBooking" class="rightButton bookingSubmit" for="bookingStepForm" name="sendBooking" value="Sende"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -425,6 +424,7 @@ if (is_array($services) && count($services) > 0) {
|
||||
$('#bookingStep'+stepNumber).show();
|
||||
|
||||
if(stepNumber == 4){
|
||||
recaptcha_callback();
|
||||
$('#stepCircle1').addClass('activeStep');
|
||||
$('#stepCircle2').addClass('activeStep');
|
||||
$('#stepCircle3').addClass('activeStep');
|
||||
@@ -472,6 +472,7 @@ if (is_array($services) && count($services) > 0) {
|
||||
$RequiredFieldSelected = 1;
|
||||
}
|
||||
else if(stepNumber == 4){
|
||||
recaptcha_callback();
|
||||
$RequiredFieldSelected = 1;
|
||||
}
|
||||
else if(stepNumber == 3){
|
||||
@@ -483,6 +484,7 @@ if (is_array($services) && count($services) > 0) {
|
||||
}
|
||||
|
||||
else if(stepNumber == 4){
|
||||
recaptcha_callback();
|
||||
if($('#booking_date').val() != '' && $('#booking_time').val() != ''){
|
||||
$RequiredFieldSelected = 1;
|
||||
}
|
||||
@@ -502,7 +504,3 @@ if (is_array($services) && count($services) > 0) {
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
|
||||
async defer>
|
||||
</script>
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user