Added Shift model

This commit is contained in:
Igor Scheller 2023-01-03 22:19:03 +01:00 committed by GitHub
parent 1d1618836b
commit 3115870ec4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 1425 additions and 957 deletions

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models\Shifts;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class ShiftFactory extends Factory
{
/** @var string */
protected $model = Shift::class; // phpcs:ignore
public function definition(): array
{
$start = $this->faker->dateTimeThisMonth('2 weeks');
return [
'title' => $this->faker->unique()->text(15),
'description' => $this->faker->text(),
'url' => $this->faker->url(),
'start' => $start,
'end' => $this->faker->dateTimeInInterval($start, '+3 hours'),
'shift_type_id' => ShiftType::factory(),
'room_id' => Room::factory(),
'transaction_id' => $this->faker->optional()->uuid(),
'created_by' => User::factory(),
'updated_by' => $this->faker->optional(.3)->boolean() ? User::factory() : null,
];
}
}

View File

@ -0,0 +1,149 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Engelsystem\Helpers\Carbon;
use Illuminate\Database\Schema\Blueprint;
use stdClass;
class CreateShiftsTable extends Migration
{
use ChangesReferences;
use Reference;
/**
* Creates the new table, copies the data and drops the old one
*/
public function up(): void
{
$connection = $this->schema->getConnection();
$previous = $this->schema->hasTable('Shifts');
if ($previous) {
$this->schema->rename('Shifts', 'shifts_old');
}
$this->schema->create('shifts', function (Blueprint $table): void {
$table->increments('id');
$table->string('title');
$table->text('description')->default('');
$table->string('url')->default('');
$table->dateTime('start')->index();
$table->dateTime('end');
$this->references($table, 'shift_types');
$this->references($table, 'rooms');
$table->uuid('transaction_id')->nullable()->default(null)->index();
$this->references($table, 'users', 'created_by');
$this->references($table, 'users', 'updated_by')->nullable()->default(null);
$table->timestamps();
});
if (!$previous) {
return;
}
/** @var stdClass[] $records */
$records = $connection
->table('shifts_old')
->get();
foreach ($records as $record) {
$isUpdated = !empty($record->edited_at_timestamp)
&& $record->edited_at_timestamp != $record->created_at_timestamp;
$connection->table('shifts')->insert([
'id' => $record->SID,
'title' => (string) $record->title,
'description' => (string) $record->description,
'url' => (string) $record->URL,
'start' => Carbon::createFromTimestamp($record->start),
'end' => Carbon::createFromTimestamp($record->end),
'shift_type_id' => $record->shifttype_id,
'room_id' => $record->RID,
'transaction_id' => $record->transaction_id,
'created_by' => $record->created_by_user_id,
'updated_by' => $isUpdated ? $record->edited_by_user_id : null,
'created_at' => Carbon::createFromTimestamp($record->created_at_timestamp),
'updated_at' => $isUpdated ? Carbon::createFromTimestamp($record->edited_at_timestamp) : null,
]);
}
$this->changeReferences(
'shifts_old',
'SID',
'shifts',
'id'
);
$this->schema->drop('shifts_old');
}
/**
* Recreates the previous table, copies the data and drops the new one
*/
public function down(): void
{
$connection = $this->schema->getConnection();
$this->schema->rename('shifts', 'shifts_old');
$this->schema->create('Shifts', function (Blueprint $table): void {
$table->increments('SID');
$table->mediumText('title')->nullable()->default(null);
$this->references($table, 'shift_types', 'shifttype_id');
$table->text('description')->nullable()->default(null);
$table->integer('start')->index();
$table->integer('end');
$this->references($table, 'rooms', 'RID')->default(0);
$table->mediumText('URL')->nullable()->default(null);
$this->references($table, 'users', 'created_by_user_id')->nullable()->default(null);
$table->integer('created_at_timestamp');
$this->references($table, 'users', 'edited_by_user_id')->nullable()->default(null);
$table->integer('edited_at_timestamp');
$table->uuid('transaction_id')->nullable()->default(null)->index();
});
/** @var stdClass[] $records */
$records = $connection
->table('shifts_old')
->get();
$now = Carbon::now()->getTimestamp();
foreach ($records as $record) {
$createdAt = $record->created_at ? Carbon::make($record->created_at)->getTimestamp() : $now;
$updatedAt = $record->updated_at ? Carbon::make($record->updated_at)->getTimestamp() : $createdAt;
$connection->table('Shifts')->insert([
'SID' => $record->id,
'title' => $record->title,
'shifttype_id' => $record->shift_type_id,
'description' => $record->description ?: null,
'start' => Carbon::make($record->start)->getTimestamp(),
'end' => Carbon::make($record->end)->getTimestamp(),
'RID' => $record->room_id,
'URL' => $record->url ?: null,
'created_by_user_id' => $record->created_by,
'created_at_timestamp' => $createdAt,
'edited_by_user_id' => $record->updated_by,
'edited_at_timestamp' => $updatedAt,
'transaction_id' => $record->transaction_id,
]);
}
$this->changeReferences(
'shifts_old',
'id',
'Shifts',
'SID'
);
$this->schema->drop('shifts_old');
}
}

View File

@ -1,5 +1,6 @@
<?php <?php
use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Engelsystem\ShiftsFilter; use Engelsystem\ShiftsFilter;
@ -237,7 +238,7 @@ function angeltype_controller_shiftsFilterDays(AngelType $angeltype)
$all_shifts = Shifts_by_angeltype($angeltype); $all_shifts = Shifts_by_angeltype($angeltype);
$days = []; $days = [];
foreach ($all_shifts as $shift) { foreach ($all_shifts as $shift) {
$day = date('Y-m-d', $shift['start']); $day = Carbon::make($shift['start'])->format('Y-m-d');
if (!in_array($day, $days)) { if (!in_array($day, $days)) {
$days[] = $day; $days[] = $day;
} }

View File

@ -1,8 +1,7 @@
<?php <?php
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\ShiftsFilter; use Engelsystem\ShiftsFilter;
/** /**
@ -49,6 +48,7 @@ function public_dashboard_controller()
$free_shifts_source = Shifts_free(time(), time() + 12 * 60 * 60, $filter); $free_shifts_source = Shifts_free(time(), time() + 12 * 60 * 60, $filter);
$free_shifts = []; $free_shifts = [];
foreach ($free_shifts_source as $shift) { foreach ($free_shifts_source as $shift) {
$shift = Shift($shift);
$free_shift = public_dashboard_controller_free_shift($shift, $filter); $free_shift = public_dashboard_controller_free_shift($shift, $filter);
if (count($free_shift['needed_angels']) > 0) { if (count($free_shift['needed_angels']) > 0) {
$free_shifts[] = $free_shift; $free_shifts[] = $free_shift;
@ -64,32 +64,30 @@ function public_dashboard_controller()
/** /**
* Gathers information for free shifts to display. * Gathers information for free shifts to display.
* *
* @param array $shift * @param Shift $shift
* @param ShiftsFilter|null $filter * @param ShiftsFilter|null $filter
* *
* @return array * @return array
*/ */
function public_dashboard_controller_free_shift($shift, ShiftsFilter $filter = null) function public_dashboard_controller_free_shift(Shift $shift, ShiftsFilter $filter = null)
{ {
$shifttype = ShiftType::find($shift['shifttype_id']); // ToDo move to model and return one
$room = Room::find($shift['RID']);
$free_shift = [ $free_shift = [
'SID' => $shift['SID'], 'id' => $shift->id,
'style' => 'default', 'style' => 'default',
'start' => date('H:i', $shift['start']), 'start' => $shift->start->format('H:i'),
'end' => date('H:i', $shift['end']), 'end' => $shift->end->format('H:i'),
'duration' => round(($shift['end'] - $shift['start']) / 3600), 'duration' => round(($shift->end->timestamp - $shift->start->timestamp) / 3600),
'shifttype_name' => $shifttype->name, 'shifttype_name' => $shift->shiftType->name,
'title' => $shift['title'], 'title' => $shift->title,
'room_name' => $room->name, 'room_name' => $shift->room->name,
'needed_angels' => public_dashboard_needed_angels($shift['NeedAngels'], $filter), 'needed_angels' => public_dashboard_needed_angels($shift->neededAngels, $filter),
]; ];
if (time() + 3 * 60 * 60 > $shift['start']) { if (time() + 3 * 60 * 60 > $shift->start->timestamp) {
$free_shift['style'] = 'warning'; $free_shift['style'] = 'warning';
} }
if (time() > $shift['start']) { if (time() > $shift->start->timestamp) {
$free_shift['style'] = 'danger'; $free_shift['style'] = 'danger';
} }

View File

@ -23,10 +23,10 @@ function room_controller(): array
$request = request(); $request = request();
$room = load_room(); $room = load_room();
$all_shifts = Shifts_by_room($room); $all_shifts = $room->shifts->sortBy('start');
$days = []; $days = [];
foreach ($all_shifts as $shift) { foreach ($all_shifts as $shift) {
$day = date('Y-m-d', $shift['start']); $day = $shift->start->format('Y-m-d');
if (!in_array($day, $days)) { if (!in_array($day, $days)) {
$days[] = $day; $days[] = $day;
} }

View File

@ -1,7 +1,7 @@
<?php <?php
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Engelsystem\ShiftSignupState; use Engelsystem\ShiftSignupState;
@ -71,11 +71,11 @@ function shift_entry_create_controller(): array
* Sign up for a shift. * Sign up for a shift.
* Case: Admin * Case: Admin
* *
* @param array $shift * @param Shift $shift
* @param AngelType|null $angeltype * @param AngelType|null $angeltype
* @return array * @return array
*/ */
function shift_entry_create_controller_admin($shift, ?AngelType $angeltype): array function shift_entry_create_controller_admin(Shift $shift, ?AngelType $angeltype): array
{ {
$signup_user = auth()->user(); $signup_user = auth()->user();
$request = request(); $request = request();
@ -100,7 +100,7 @@ function shift_entry_create_controller_admin($shift, ?AngelType $angeltype): arr
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
ShiftEntry_create([ ShiftEntry_create([
'SID' => $shift['SID'], 'SID' => $shift->id,
'TID' => $angeltype->id, 'TID' => $angeltype->id,
'UID' => $signup_user->id, 'UID' => $signup_user->id,
'Comment' => '', 'Comment' => '',
@ -120,7 +120,7 @@ function shift_entry_create_controller_admin($shift, ?AngelType $angeltype): arr
} }
$angeltypes_select = $angeltypes->pluck('name', 'id')->toArray(); $angeltypes_select = $angeltypes->pluck('name', 'id')->toArray();
$room = Room::find($shift['RID']); $room = $shift->room;
return [ return [
ShiftEntry_create_title(), ShiftEntry_create_title(),
ShiftEntry_create_view_admin($shift, $room, $angeltype, $angeltypes_select, $signup_user, $users_select) ShiftEntry_create_view_admin($shift, $room, $angeltype, $angeltypes_select, $signup_user, $users_select)
@ -131,11 +131,11 @@ function shift_entry_create_controller_admin($shift, ?AngelType $angeltype): arr
* Sign up for a shift. * Sign up for a shift.
* Case: Supporter * Case: Supporter
* *
* @param array $shift * @param Shift $shift
* @param AngelType $angeltype * @param AngelType $angeltype
* @return array * @return array
*/ */
function shift_entry_create_controller_supporter($shift, AngelType $angeltype): array function shift_entry_create_controller_supporter(Shift $shift, AngelType $angeltype): array
{ {
$request = request(); $request = request();
$signup_user = auth()->user(); $signup_user = auth()->user();
@ -151,7 +151,7 @@ function shift_entry_create_controller_supporter($shift, AngelType $angeltype):
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
ShiftEntry_create([ ShiftEntry_create([
'SID' => $shift['SID'], 'SID' => $shift->id,
'TID' => $angeltype->id, 'TID' => $angeltype->id,
'UID' => $signup_user->id, 'UID' => $signup_user->id,
'Comment' => '', 'Comment' => '',
@ -169,7 +169,7 @@ function shift_entry_create_controller_supporter($shift, AngelType $angeltype):
$users_select[$u->id] = $u->name; $users_select[$u->id] = $u->name;
} }
$room = Room::find($shift['RID']); $room = $shift->room;
return [ return [
ShiftEntry_create_title(), ShiftEntry_create_title(),
ShiftEntry_create_view_supporter($shift, $room, $angeltype, $signup_user, $users_select) ShiftEntry_create_view_supporter($shift, $room, $angeltype, $signup_user, $users_select)
@ -204,17 +204,17 @@ function shift_entry_error_message(ShiftSignupState $shift_signup_state)
* Sign up for a shift. * Sign up for a shift.
* Case: User * Case: User
* *
* @param array $shift * @param Shift $shift
* @param AngelType $angeltype * @param AngelType $angeltype
* @return array * @return array
*/ */
function shift_entry_create_controller_user($shift, AngelType $angeltype): array function shift_entry_create_controller_user(Shift $shift, AngelType $angeltype): array
{ {
$request = request(); $request = request();
$signup_user = auth()->user(); $signup_user = auth()->user();
$needed_angeltype = (new AngelType())->forceFill(NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype)); $needed_angeltype = (new AngelType())->forceFill(NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype));
$shift_entries = ShiftEntries_by_shift_and_angeltype($shift['SID'], $angeltype->id); $shift_entries = ShiftEntries_by_shift_and_angeltype($shift->id, $angeltype->id);
$shift_signup_state = Shift_signup_allowed( $shift_signup_state = Shift_signup_allowed(
$signup_user, $signup_user,
$shift, $shift,
@ -233,7 +233,7 @@ function shift_entry_create_controller_user($shift, AngelType $angeltype): array
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
$comment = strip_request_item_nl('comment'); $comment = strip_request_item_nl('comment');
ShiftEntry_create([ ShiftEntry_create([
'SID' => $shift['SID'], 'SID' => $shift->id,
'TID' => $angeltype->id, 'TID' => $angeltype->id,
'UID' => $signup_user->id, 'UID' => $signup_user->id,
'Comment' => $comment, 'Comment' => $comment,
@ -255,7 +255,7 @@ function shift_entry_create_controller_user($shift, AngelType $angeltype): array
throw_redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
$room = Room::find($shift['RID']); $room = $shift->room;
return [ return [
ShiftEntry_create_title(), ShiftEntry_create_title(),
ShiftEntry_create_view_user($shift, $room, $angeltype, $comment) ShiftEntry_create_view_user($shift, $room, $angeltype, $comment)
@ -265,16 +265,16 @@ function shift_entry_create_controller_user($shift, AngelType $angeltype): array
/** /**
* Link to create a shift entry. * Link to create a shift entry.
* *
* @param array $shift * @param Shift $shift
* @param AngelType $angeltype * @param AngelType $angeltype
* @param array $params * @param array $params
* @return string URL * @return string URL
*/ */
function shift_entry_create_link($shift, AngelType $angeltype, $params = []) function shift_entry_create_link(Shift $shift, AngelType $angeltype, $params = [])
{ {
$params = array_merge([ $params = array_merge([
'action' => 'create', 'action' => 'create',
'shift_id' => $shift['SID'], 'shift_id' => $shift->id,
'angeltype_id' => $angeltype->id 'angeltype_id' => $angeltype->id
], $params); ], $params);
return page_link_to('shift_entries', $params); return page_link_to('shift_entries', $params);
@ -283,15 +283,15 @@ function shift_entry_create_link($shift, AngelType $angeltype, $params = [])
/** /**
* Link to create a shift entry as admin. * Link to create a shift entry as admin.
* *
* @param array $shift * @param Shift $shift
* @param array $params * @param array $params
* @return string URL * @return string URL
*/ */
function shift_entry_create_link_admin($shift, $params = []) function shift_entry_create_link_admin(Shift $shift, $params = [])
{ {
$params = array_merge([ $params = array_merge([
'action' => 'create', 'action' => 'create',
'shift_id' => $shift['SID'] 'shift_id' => $shift->id
], $params); ], $params);
return page_link_to('shift_entries', $params); return page_link_to('shift_entries', $params);
} }
@ -360,7 +360,7 @@ function shift_entry_delete_controller()
/** /**
* Link to delete a shift entry. * Link to delete a shift entry.
* *
* @param array $shiftEntry * @param array|Shift $shiftEntry
* @param array $params * @param array $params
* @return string URL * @return string URL
*/ */
@ -368,7 +368,7 @@ function shift_entry_delete_link($shiftEntry, $params = [])
{ {
$params = array_merge([ $params = array_merge([
'action' => 'delete', 'action' => 'delete',
'shift_entry_id' => $shiftEntry['id'] 'shift_entry_id' => $shiftEntry['shift_entry_id'] ?? $shiftEntry['id']
], $params); ], $params);
return page_link_to('shift_entries', $params); return page_link_to('shift_entries', $params);
} }

View File

@ -1,44 +1,45 @@
<?php <?php
use Engelsystem\Helpers\Carbon; use Carbon\CarbonTimeZone;
use Engelsystem\Http\Exceptions\HttpForbidden; use Engelsystem\Http\Exceptions\HttpForbidden;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\ShiftSignupState; use Engelsystem\ShiftSignupState;
use Illuminate\Support\Collection;
/** /**
* @param array $shift * @param array|Shift $shift
* @return string * @return string
*/ */
function shift_link($shift) function shift_link($shift)
{ {
$parameters = ['action' => 'view']; $parameters = ['action' => 'view'];
if (isset($shift['SID'])) { if (isset($shift['shift_id']) || isset($shift['id'])) {
$parameters['shift_id'] = $shift['SID']; $parameters['shift_id'] = $shift['shift_id'] ?? $shift['id'];
} }
return page_link_to('shifts', $parameters); return page_link_to('shifts', $parameters);
} }
/** /**
* @param array $shift * @param Shift $shift
* @return string * @return string
*/ */
function shift_delete_link($shift) function shift_delete_link(Shift $shift)
{ {
return page_link_to('user_shifts', ['delete_shift' => $shift['SID']]); return page_link_to('user_shifts', ['delete_shift' => $shift->id]);
} }
/** /**
* @param array $shift * @param Shift $shift
* @return string * @return string
*/ */
function shift_edit_link($shift) function shift_edit_link(Shift $shift)
{ {
return page_link_to('user_shifts', ['edit_shift' => $shift['SID']]); return page_link_to('user_shifts', ['edit_shift' => $shift->id]);
} }
/** /**
@ -60,8 +61,8 @@ function shift_edit_controller()
} }
$shift_id = $request->input('edit_shift'); $shift_id = $request->input('edit_shift');
$shift = Shift($shift_id); $shift = Shift(Shift::findOrFail($shift_id));
if (ScheduleShift::whereShiftId($shift['SID'])->first()) { if (ScheduleShift::whereShiftId($shift->id)->first()) {
warning(__( warning(__(
'This shift was imported from a schedule so some changes will be overwritten with the next import.' 'This shift was imported from a schedule so some changes will be overwritten with the next import.'
)); ));
@ -81,12 +82,12 @@ function shift_edit_controller()
} }
} }
$shifttype_id = $shift['shifttype_id']; $shifttype_id = $shift->shift_type_id;
$title = $shift['title']; $title = $shift->title;
$description = $shift['description']; $description = $shift->description;
$rid = $shift['RID']; $rid = $shift->room_id;
$start = $shift['start']; $start = $shift->start;
$end = $shift['end']; $end = $shift->end;
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
// Name/Bezeichnung der Schicht, darf leer sein // Name/Bezeichnung der Schicht, darf leer sein
@ -102,33 +103,33 @@ function shift_edit_controller()
$rid = $request->input('rid'); $rid = $request->input('rid');
} else { } else {
$valid = false; $valid = false;
error(__('Please select a room.'), true); error(__('Please select a room.'));
} }
if ($request->has('shifttype_id') && isset($shifttypes[$request->input('shifttype_id')])) { if ($request->has('shifttype_id') && isset($shifttypes[$request->input('shifttype_id')])) {
$shifttype_id = $request->input('shifttype_id'); $shifttype_id = $request->input('shifttype_id');
} else { } else {
$valid = false; $valid = false;
error(__('Please select a shifttype.'), true); error(__('Please select a shifttype.'));
} }
if ($request->has('start') && $tmp = parse_date('Y-m-d H:i', $request->input('start'))) { if ($request->has('start') && $tmp = DateTime::createFromFormat('Y-m-d H:i', $request->input('start'))) {
$start = $tmp; $start = $tmp;
} else { } else {
$valid = false; $valid = false;
error(__('Please enter a valid starting time for the shifts.'), true); error(__('Please enter a valid starting time for the shifts.'));
} }
if ($request->has('end') && $tmp = parse_date('Y-m-d H:i', $request->input('end'))) { if ($request->has('end') && $tmp = DateTime::createFromFormat('Y-m-d H:i', $request->input('end'))) {
$end = $tmp; $end = $tmp;
} else { } else {
$valid = false; $valid = false;
error(__('Please enter a valid ending time for the shifts.'), true); error(__('Please enter a valid ending time for the shifts.'));
} }
if ($start >= $end) { if ($start >= $end) {
$valid = false; $valid = false;
error(__('The ending time has to be after the starting time.'), true); error(__('The ending time has to be after the starting time.'));
} }
foreach ($needed_angel_types as $needed_angeltype_id => $count) { foreach ($needed_angel_types as $needed_angeltype_id => $count) {
@ -143,20 +144,28 @@ function shift_edit_controller()
error(sprintf( error(sprintf(
__('Please check your input for needed angels of type %s.'), __('Please check your input for needed angels of type %s.'),
$angeltypes[$needed_angeltype_id] $angeltypes[$needed_angeltype_id]
), true); ));
} }
} }
} }
if ($valid) { if ($valid) {
$shift['shifttype_id'] = $shifttype_id; $shift->shift_type_id = $shifttype_id;
$shift['title'] = $title; $shift->title = $title;
$shift['description'] = $description; $shift->description = $description;
$shift['RID'] = $rid; $shift->room_id = $rid;
$shift['start'] = $start; $shift->start = $start;
$shift['end'] = $end; $shift->end = $end;
$shift->updatedBy()->associate(auth()->user());
// Remove merged data as it is not really part of the model and thus can't be saved
unset($shift->shiftEntry);
unset($shift->neededAngels);
$shift->save();
mail_shift_change(Shift($shift->id), $shift);
Shift_update($shift);
NeededAngelTypes_delete_by_shift($shift_id); NeededAngelTypes_delete_by_shift($shift_id);
$needed_angel_types_info = []; $needed_angel_types_info = [];
foreach ($needed_angel_types as $type_id => $count) { foreach ($needed_angel_types as $type_id => $count) {
@ -168,16 +177,14 @@ function shift_edit_controller()
engelsystem_log( engelsystem_log(
'Updated shift \'' . $shifttypes[$shifttype_id] . ', ' . $title 'Updated shift \'' . $shifttypes[$shifttype_id] . ', ' . $title
. '\' from ' . date('Y-m-d H:i', $start) . '\' from ' . $start->format('Y-m-d H:i')
. ' to ' . date('Y-m-d H:i', $end) . ' to ' . $end->format('Y-m-d H:i')
. ' with angel types ' . join(', ', $needed_angel_types_info) . ' with angel types ' . join(', ', $needed_angel_types_info)
. ' and description ' . $description . ' and description ' . $description
); );
success(__('Shift updated.')); success(__('Shift updated.'));
throw_redirect(shift_link([ throw_redirect(shift_link($shift));
'SID' => $shift_id
]));
} }
} }
@ -201,8 +208,8 @@ function shift_edit_controller()
form_select('shifttype_id', __('Shifttype'), $shifttypes, $shifttype_id), form_select('shifttype_id', __('Shifttype'), $shifttypes, $shifttype_id),
form_text('title', __('Title'), $title), form_text('title', __('Title'), $title),
form_select('rid', __('Room:'), $rooms, $rid), form_select('rid', __('Room:'), $rooms, $rid),
form_text('start', __('Start:'), date('Y-m-d H:i', $start)), form_text('start', __('Start:'), $start->format('Y-m-d H:i')),
form_text('end', __('End:'), date('Y-m-d H:i', $end)), form_text('end', __('End:'), $end->format('Y-m-d H:i')),
form_textarea('description', __('Additional description'), $description), form_textarea('description', __('Additional description'), $description),
form_info('', __('This description is for single shifts, otherwise please use the description in shift type.')), form_info('', __('This description is for single shifts, otherwise please use the description in shift type.')),
'<h2>' . __('Needed angels') . '</h2>', '<h2>' . __('Needed angels') . '</h2>',
@ -237,27 +244,26 @@ function shift_delete_controller()
// Schicht löschen bestätigt // Schicht löschen bestätigt
if ($request->hasPostData('delete')) { if ($request->hasPostData('delete')) {
$room = Room::find($shift['RID']); foreach ($shift->shiftEntry as $entry) {
foreach ($shift['ShiftEntry'] as $entry) {
$type = AngelType::find($entry['TID']); $type = AngelType::find($entry['TID']);
event('shift.entry.deleting', [ event('shift.entry.deleting', [
'user' => User::find($entry['user_id']), 'user' => User::find($entry['user_id']),
'start' => Carbon::createFromTimestamp($shift['start']), 'start' => $shift->start,
'end' => Carbon::createFromTimestamp($shift['end']), 'end' => $shift->end,
'name' => $shift['name'], 'name' => $shift->shiftType->name,
'title' => $shift['title'], 'title' => $shift->title,
'type' => $type->name, 'type' => $type->name,
'room' => $room, 'room' => $shift->room,
'freeloaded' => (bool) $entry['freeloaded'], 'freeloaded' => (bool) $entry['freeloaded'],
]); ]);
} }
Shift_delete($shift_id); $shift->delete();
engelsystem_log( engelsystem_log(
'Deleted shift ' . $shift['name'] 'Deleted shift ' . $shift->title . ': ' . $shift->shiftType->name
. ' from ' . date('Y-m-d H:i', $shift['start']) . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . date('Y-m-d H:i', $shift['end']) . ' to ' . $shift->end->format('Y-m-d H:i')
); );
success(__('Shift deleted.')); success(__('Shift deleted.'));
throw_redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
@ -266,12 +272,12 @@ function shift_delete_controller()
return page_with_title(shifts_title(), [ return page_with_title(shifts_title(), [
error(sprintf( error(sprintf(
__('Do you want to delete the shift %s from %s to %s?'), __('Do you want to delete the shift %s from %s to %s?'),
$shift['name'], $shift->shiftType->name,
date('Y-m-d H:i', $shift['start']), $shift->start->format('Y-m-d H:i'),
date('H:i', $shift['end']) $shift->end->format('H:i')
), true), ), true),
form([ form([
form_hidden('delete_shift', $shift_id), form_hidden('delete_shift', $shift->id),
form_submit('delete', __('delete')), form_submit('delete', __('delete')),
]), ]),
]); ]);
@ -299,8 +305,9 @@ function shift_controller()
throw_redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
$shifttype = ShiftType::find($shift['shifttype_id']); $shifttype = $shift->shiftType;
$room = Room::find($shift['RID']); $room = $shift->room;
/** @var AngelType[] $angeltypes */
$angeltypes = AngelType::all(); $angeltypes = AngelType::all();
$user_shifts = Shifts_by_user($user->id); $user_shifts = Shifts_by_user($user->id);
@ -311,7 +318,7 @@ function shift_controller()
continue; continue;
} }
$shift_entries = ShiftEntries_by_shift_and_angeltype($shift['SID'], $angeltype->id); $shift_entries = ShiftEntries_by_shift_and_angeltype($shift->id, $angeltype->id);
$needed_angeltype = (new AngelType())->forceFill($needed_angeltype); $needed_angeltype = (new AngelType())->forceFill($needed_angeltype);
$angeltype_signup_state = Shift_signup_allowed( $angeltype_signup_state = Shift_signup_allowed(
@ -328,7 +335,7 @@ function shift_controller()
} }
return [ return [
$shift['name'], $shift->shiftType->name,
Shift_view($shift, $shifttype, $room, $angeltypes, $shift_signup_state) Shift_view($shift, $shifttype, $room, $angeltypes, $shift_signup_state)
]; ];
} }
@ -345,7 +352,7 @@ function shifts_controller()
return match ($request->input('action')) { return match ($request->input('action')) {
'view' => shift_controller(), 'view' => shift_controller(),
'next' => shift_next_controller(), // throw_redirect 'next' => shift_next_controller(), // throws redirect
default => throw_redirect(page_link_to('/')), default => throw_redirect(page_link_to('/')),
}; };
} }
@ -359,7 +366,7 @@ function shift_next_controller()
throw_redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
$upcoming_shifts = ShiftEntries_upcoming_for_user(auth()->user()->id); $upcoming_shifts = ShiftEntries_upcoming_for_user(auth()->user());
if (!empty($upcoming_shifts)) { if (!empty($upcoming_shifts)) {
throw_redirect(shift_link($upcoming_shifts[0])); throw_redirect(shift_link($upcoming_shifts[0]));
@ -390,19 +397,74 @@ function shifts_json_export_controller()
} }
$shifts = load_ical_shifts(); $shifts = load_ical_shifts();
foreach ($shifts as $row => $shift) { $shifts->sortBy('start_date');
$shifts[$row]['start_date'] = Carbon::createFromTimestamp($shift['start'])->toRfc3339String(); $timeZone = CarbonTimeZone::create(config('timezone'));
$shifts[$row]['end_date'] = Carbon::createFromTimestamp($shift['end'])->toRfc3339String();
$shiftsData = [];
foreach ($shifts as $shift) {
// Data required for the Fahrplan app integration https://github.com/johnjohndoe/engelsystem
// See engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/models/Shift.kt
$data = [
// Name of the shift (type)
'name' => $shift->shiftType->name,
// Shift / Talk title
'title' => $shift->title,
// Shift description
'description' => $shift->description,
// Users comment
'Comment' => $shift->Comment,
// Shift id
'SID' => $shift->id,
// Shift type id
'shifttype_id' => $shift->shift_type_id,
// Talk URL
'URL' => $shift->url,
// Room name
'Name' => $shift->room->name,
// Location map url
'map_url' => $shift->room->map_url,
// Start timestamp
/** @deprecated start_date should be used */
'start' => $shift->start->timestamp,
// Start date
'start_date' => $shift->start->toRfc3339String(),
// End timestamp
/** @deprecated end_date should be used */
'end' => $shift->end->timestamp,
// End date
'end_date' => $shift->end->toRfc3339String(),
// Timezone offset like "+01:00"
/** @deprecated should be retrieved from start_date or end_date */
'timezone' => $timeZone->toOffsetName(),
// The events timezone like "Europe/Berlin"
'event_timezone' => $timeZone->getName(),
];
$shiftsData[] = [
// Model data
...$shift->toArray(),
// legacy fields (ignoring created / updated (at/by) data)
'RID' => $shift->room_id,
// Fahrplan app required data
...$data
];
} }
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
raw_output(json_encode($shifts)); raw_output(json_encode($shiftsData));
} }
/** /**
* Returns users shifts to export. * Returns users shifts to export.
* *
* @return array * @return Shift[]|Collection
*/ */
function load_ical_shifts() function load_ical_shifts()
{ {

View File

@ -204,9 +204,9 @@ function user_controller()
} }
$shifts = Shifts_by_user($user_source->id, auth()->can('user_shifts_admin')); $shifts = Shifts_by_user($user_source->id, auth()->can('user_shifts_admin'));
foreach ($shifts as &$shift) { foreach ($shifts as $shift) {
// TODO: Move queries to model // TODO: Move queries to model
$shift['needed_angeltypes'] = Db::select( $shift->needed_angeltypes = Db::select(
' '
SELECT DISTINCT `angel_types`.* SELECT DISTINCT `angel_types`.*
FROM `ShiftEntry` FROM `ShiftEntry`
@ -214,9 +214,10 @@ function user_controller()
WHERE `ShiftEntry`.`SID` = ? WHERE `ShiftEntry`.`SID` = ?
ORDER BY `angel_types`.`name` ORDER BY `angel_types`.`name`
', ',
[$shift['SID']] [$shift->id]
); );
foreach ($shift['needed_angeltypes'] as &$needed_angeltype) { $neededAngeltypes = $shift->needed_angeltypes;
foreach ($neededAngeltypes as &$needed_angeltype) {
$needed_angeltype['users'] = Db::select( $needed_angeltype['users'] = Db::select(
' '
SELECT `ShiftEntry`.`freeloaded`, `users`.* SELECT `ShiftEntry`.`freeloaded`, `users`.*
@ -225,9 +226,10 @@ function user_controller()
WHERE `ShiftEntry`.`SID` = ? WHERE `ShiftEntry`.`SID` = ?
AND `ShiftEntry`.`TID` = ? AND `ShiftEntry`.`TID` = ?
', ',
[$shift['SID'], $needed_angeltype['id']] [$shift->id, $needed_angeltype['id']]
); );
} }
$shift->needed_angeltypes = $neededAngeltypes;
} }
if (empty($user_source->api_key)) { if (empty($user_source->api_key)) {
@ -360,8 +362,8 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
$needed_angeltypes = []; $needed_angeltypes = [];
$shift_entries = []; $shift_entries = [];
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$needed_angeltypes[$shift['SID']] = []; $needed_angeltypes[$shift->id] = [];
$shift_entries[$shift['SID']] = []; $shift_entries[$shift->id] = [];
} }
foreach ($shift_entries_source as $shift_entry) { foreach ($shift_entries_source as $shift_entry) {
@ -371,8 +373,8 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
} }
foreach ($needed_angeltypes_source as $needed_angeltype) { foreach ($needed_angeltypes_source as $needed_angeltype) {
if (isset($needed_angeltypes[$needed_angeltype['SID']])) { if (isset($needed_angeltypes[$needed_angeltype['shift_id']])) {
$needed_angeltypes[$needed_angeltype['SID']][] = $needed_angeltype; $needed_angeltypes[$needed_angeltype['shift_id']][] = $needed_angeltype;
} }
} }
@ -389,7 +391,7 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
$filtered_shifts = []; $filtered_shifts = [];
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$needed_angels_count = 0; $needed_angels_count = 0;
foreach ($needed_angeltypes[$shift['SID']] as $needed_angeltype) { foreach ($needed_angeltypes[$shift->id] as $needed_angeltype) {
$taken = 0; $taken = 0;
if ( if (
@ -399,7 +401,7 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
continue; continue;
} }
foreach ($shift_entries[$shift['SID']] as $shift_entry) { foreach ($shift_entries[$shift->id] as $shift_entry) {
if ( if (
$needed_angeltype['angel_type_id'] == $shift_entry['TID'] $needed_angeltype['angel_type_id'] == $shift_entry['TID']
&& $shift_entry['freeloaded'] == 0 && $shift_entry['freeloaded'] == 0

View File

@ -2,7 +2,7 @@
namespace Engelsystem\Events\Listener; namespace Engelsystem\Events\Listener;
use Engelsystem\Helpers\Carbon; use Carbon\Carbon;
use Engelsystem\Helpers\Shifts; use Engelsystem\Helpers\Shifts;
use Engelsystem\Mail\EngelsystemMailer; use Engelsystem\Mail\EngelsystemMailer;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;

View File

@ -1,52 +1,53 @@
<?php <?php
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
/** function mail_shift_change(Shift $old_shift, Shift $new_shift)
* @param array $old_shift
* @param array $new_shift
*/
function mail_shift_change($old_shift, $new_shift)
{ {
$users = ShiftEntries_by_shift($old_shift['SID']); $users = ShiftEntries_by_shift($old_shift->id);
$old_room = Room::find($old_shift['RID']); $old_room = $old_shift->room;
$new_room = Room::find($new_shift['RID']); $new_room = $new_shift->room;
$noticeable_changes = false; $noticeable_changes = false;
$message = __('A Shift you are registered on has changed:'); $message = __('A Shift you are registered on has changed:');
$message .= "\n"; $message .= "\n";
if ($old_shift['name'] != $new_shift['name']) { if ($old_shift->shift_type_id != $new_shift->shift_type_id) {
$message .= sprintf(__('* Shift type changed from %s to %s'), $old_shift['name'], $new_shift['name']) . "\n"; $message .= sprintf(
__('* Shift type changed from %s to %s'),
$old_shift->shiftType->name,
$new_shift->shiftType->name
) . "\n";
$noticeable_changes = true; $noticeable_changes = true;
} }
if ($old_shift['title'] != $new_shift['title']) { if ($old_shift->title != $new_shift->title) {
$message .= sprintf(__('* Shift title changed from %s to %s'), $old_shift['title'], $new_shift['title']) . "\n"; $message .= sprintf(__('* Shift title changed from %s to %s'), $old_shift->title, $new_shift->title) . "\n";
$noticeable_changes = true; $noticeable_changes = true;
} }
if ($old_shift['start'] != $new_shift['start']) { if ($old_shift->start->timestamp != $new_shift->start->timestamp) {
$message .= sprintf( $message .= sprintf(
__('* Shift Start changed from %s to %s'), __('* Shift Start changed from %s to %s'),
date('Y-m-d H:i', $old_shift['start']), $old_shift->start->format('Y-m-d H:i'),
date('Y-m-d H:i', $new_shift['start']) $new_shift->start->format('Y-m-d H:i')
) . "\n"; ) . "\n";
$noticeable_changes = true; $noticeable_changes = true;
} }
if ($old_shift['end'] != $new_shift['end']) { if ($old_shift->end->timestamp != $new_shift->end->timestamp) {
$message .= sprintf( $message .= sprintf(
__('* Shift End changed from %s to %s'), __('* Shift End changed from %s to %s'),
date('Y-m-d H:i', $old_shift['end']), $old_shift->end->format('Y-m-d H:i'),
date('Y-m-d H:i', $new_shift['end']) $new_shift->end->format('Y-m-d H:i')
) . "\n"; ) . "\n";
$noticeable_changes = true; $noticeable_changes = true;
} }
if ($old_shift['RID'] != $new_shift['RID']) { if ($old_shift->room_id != $new_shift->room_id) {
$message .= sprintf(__('* Shift Location changed from %s to %s'), $old_room->name, $new_room->name) . "\n"; $message .= sprintf(__('* Shift Location changed from %s to %s'), $old_room->name, $new_room->name) . "\n";
$noticeable_changes = true; $noticeable_changes = true;
} }
@ -59,11 +60,11 @@ function mail_shift_change($old_shift, $new_shift)
$message .= "\n"; $message .= "\n";
$message .= __('The updated Shift:') . "\n"; $message .= __('The updated Shift:') . "\n";
$message .= $new_shift['name'] . "\n"; $message .= $new_shift->shiftType->name . "\n";
$message .= $new_shift['title'] . "\n"; $message .= $new_shift->title . "\n";
$message .= date('Y-m-d H:i', $new_shift['start']) . ' - ' . date('H:i', $new_shift['end']) . "\n"; $message .= $new_shift->start->format('Y-m-d H:i') . ' - ' . $new_shift->end->format('H:i') . "\n";
$message .= $new_room->name . "\n\n"; $message .= $new_room->name . "\n\n";
$message .= url('/shifts', ['action' => 'view', 'shift_id' => $new_shift['SID']]) . "\n"; $message .= url('/shifts', ['action' => 'view', 'shift_id' => $new_shift->id]) . "\n";
foreach ($users as $user) { foreach ($users as $user) {
$user = (new User())->forceFill($user); $user = (new User())->forceFill($user);
@ -78,44 +79,36 @@ function mail_shift_change($old_shift, $new_shift)
} }
} }
/** function mail_shift_assign(User $user, Shift $shift)
* @param User $user
* @param array $shift
*/
function mail_shift_assign($user, $shift)
{ {
if (!$user->settings->email_shiftinfo) { if (!$user->settings->email_shiftinfo) {
return; return;
} }
$room = Room::find($shift['RID']); $room = $shift->room;
$message = __('You have been assigned to a Shift:') . "\n"; $message = __('You have been assigned to a Shift:') . "\n";
$message .= $shift['name'] . "\n"; $message .= $shift->shiftType->name . "\n";
$message .= $shift['title'] . "\n"; $message .= $shift->title . "\n";
$message .= date('Y-m-d H:i', $shift['start']) . ' - ' . date('H:i', $shift['end']) . "\n"; $message .= $shift->start->format('Y-m-d H:i') . ' - ' . $shift->end->format('H:i') . "\n";
$message .= $room->name . "\n\n"; $message .= $room->name . "\n\n";
$message .= url('/shifts', ['action' => 'view', 'shift_id' => $shift['SID']]) . "\n"; $message .= url('/shifts', ['action' => 'view', 'shift_id' => $shift->id]) . "\n";
engelsystem_email_to_user($user, __('Assigned to Shift'), $message, true); engelsystem_email_to_user($user, __('Assigned to Shift'), $message, true);
} }
/** function mail_shift_removed(User $user, Shift $shift)
* @param User $user
* @param array $shift
*/
function mail_shift_removed($user, $shift)
{ {
if (!$user->settings->email_shiftinfo) { if (!$user->settings->email_shiftinfo) {
return; return;
} }
$room = Room::find($shift['RID']); $room = $shift->room;
$message = __('You have been removed from a Shift:') . "\n"; $message = __('You have been removed from a Shift:') . "\n";
$message .= $shift['name'] . "\n"; $message .= $shift->shiftType->name . "\n";
$message .= $shift['title'] . "\n"; $message .= $shift->title . "\n";
$message .= date('Y-m-d H:i', $shift['start']) . ' - ' . date('H:i', $shift['end']) . "\n"; $message .= $shift->start->format('Y-m-d H:i') . ' - ' . $shift->end->format('H:i') . "\n";
$message .= $room->name . "\n"; $message .= $room->name . "\n";
engelsystem_email_to_user($user, __('Removed from Shift'), $message, true); engelsystem_email_to_user($user, __('Removed from Shift'), $message, true);

View File

@ -101,8 +101,8 @@ function NeededAngelTypes_by_shift($shiftId)
SELECT `NeededAngelTypes`.*, `angel_types`.`name`, `angel_types`.`restricted` SELECT `NeededAngelTypes`.*, `angel_types`.`name`, `angel_types`.`restricted`
FROM `NeededAngelTypes` FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id` = `NeededAngelTypes`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id` = `NeededAngelTypes`.`angel_type_id`
JOIN `Shifts` ON `Shifts`.`RID` = `NeededAngelTypes`.`room_id` JOIN `shifts` ON `shifts`.`room_id` = `NeededAngelTypes`.`room_id`
WHERE `Shifts`.`SID` = ? WHERE `shifts`.`id` = ?
AND `count` > 0 AND `count` > 0
ORDER BY `room_id` DESC ORDER BY `room_id` DESC
', [$shiftId]); ', [$shiftId]);

View File

@ -3,8 +3,6 @@
use Carbon\Carbon; use Carbon\Carbon;
use Engelsystem\Database\Db; use Engelsystem\Database\Db;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
/** /**
@ -60,8 +58,8 @@ function ShiftEntry_create($shift_entry)
{ {
$user = User::find($shift_entry['UID']); $user = User::find($shift_entry['UID']);
$shift = Shift($shift_entry['SID']); $shift = Shift($shift_entry['SID']);
$shifttype = ShiftType::find($shift['shifttype_id']); $shifttype = $shift->shiftType;
$room = Room::find($shift['RID']); $room = $shift->room;
$angeltype = AngelType::find($shift_entry['TID']); $angeltype = AngelType::find($shift_entry['TID']);
$result = Db::insert( $result = Db::insert(
' '
@ -86,11 +84,11 @@ function ShiftEntry_create($shift_entry)
); );
engelsystem_log( engelsystem_log(
'User ' . User_Nick_render($user, true) 'User ' . User_Nick_render($user, true)
. ' signed up for shift ' . $shift['name'] . ' signed up for shift ' . $shift->title
. ' (' . $shifttype->name . ')' . ' (' . $shifttype->name . ')'
. ' at ' . $room->name . ' at ' . $room->name
. ' from ' . date('Y-m-d H:i', $shift['start']) . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . date('Y-m-d H:i', $shift['end']) . ' to ' . $shift->end->format('Y-m-d H:i')
. ' as ' . $angeltype->name . ' as ' . $angeltype->name
); );
mail_shift_assign($user, $shift); mail_shift_assign($user, $shift);
@ -147,44 +145,43 @@ function ShiftEntry_delete($shiftEntry)
$signout_user = User::find($shiftEntry['UID']); $signout_user = User::find($shiftEntry['UID']);
$shift = Shift($shiftEntry['SID']); $shift = Shift($shiftEntry['SID']);
$shifttype = ShiftType::find($shift['shifttype_id']); $shifttype = $shift->shiftType;
$room = Room::find($shift['RID']); $room = $shift->room;
$angeltype = AngelType::find($shiftEntry['TID']); $angeltype = AngelType::find($shiftEntry['TID']);
engelsystem_log( engelsystem_log(
'Shift signout: ' . User_Nick_render($signout_user, true) 'Shift signout: ' . User_Nick_render($signout_user, true)
. ' from shift ' . $shift['name'] . ' from shift ' . $shift->title
. ' (' . $shifttype->name . ')' . ' (' . $shifttype->name . ')'
. ' at ' . $room->name . ' at ' . $room->name
. ' from ' . date('Y-m-d H:i', $shift['start']) . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . date('Y-m-d H:i', $shift['end']) . ' to ' . $shift->end->format('Y-m-d H:i')
. ' as ' . $angeltype->name . ' as ' . $angeltype->name
); );
mail_shift_removed(User::find($shiftEntry['UID']), Shift($shiftEntry['SID'])); mail_shift_removed(User::find($shiftEntry['UID']), $shift);
} }
/** /**
* Returns next (or current) shifts of given user. * Returns next (or current) shifts of given user.
* *
* @param int $userId * @param User $user
* @return array * @return array
*/ */
function ShiftEntries_upcoming_for_user($userId) function ShiftEntries_upcoming_for_user(User $user)
{ {
return Db::select( return Db::select(
' '
SELECT * SELECT *, shifts.id as shift_id
FROM `ShiftEntry` FROM `ShiftEntry`
JOIN `Shifts` ON (`Shifts`.`SID` = `ShiftEntry`.`SID`) JOIN `shifts` ON (`shifts`.`id` = `ShiftEntry`.`SID`)
JOIN `shift_types` ON `shift_types`.`id` = `Shifts`.`shifttype_id` JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id`
WHERE `ShiftEntry`.`UID` = ? WHERE `ShiftEntry`.`UID` = ?
AND `Shifts`.`end` > ? AND `shifts`.`end` > NOW()
ORDER BY `Shifts`.`end` ORDER BY `shifts`.`end`
', ',
[ [
$userId, $user->id
time(),
] ]
); );
} }
@ -192,27 +189,26 @@ function ShiftEntries_upcoming_for_user($userId)
/** /**
* Returns shifts completed by the given user. * Returns shifts completed by the given user.
* *
* @param int $userId * @param User $user
* @param Carbon|null $sinceTime * @param Carbon|null $sinceTime
* @return array * @return array
*/ */
function ShiftEntries_finished_by_user($userId, Carbon $sinceTime = null) function ShiftEntries_finished_by_user(User $user, Carbon $sinceTime = null)
{ {
return Db::select( return Db::select(
' '
SELECT * SELECT *
FROM `ShiftEntry` FROM `ShiftEntry`
JOIN `Shifts` ON (`Shifts`.`SID` = `ShiftEntry`.`SID`) JOIN `shifts` ON (`shifts`.`id` = `ShiftEntry`.`SID`)
JOIN `shift_types` ON `shift_types`.`id` = `Shifts`.`shifttype_id` JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id`
WHERE `ShiftEntry`.`UID` = ? WHERE `ShiftEntry`.`UID` = ?
AND `Shifts`.`end` < ? AND `shifts`.`end` < NOW()
AND `ShiftEntry`.`freeloaded` = 0 AND `ShiftEntry`.`freeloaded` = 0
' . ($sinceTime ? 'AND Shifts.start >= ' . $sinceTime->getTimestamp() : '') . ' ' . ($sinceTime ? 'AND shifts.start >= "' . $sinceTime->toString() . '"' : '') . '
ORDER BY `Shifts`.`end` desc ORDER BY `shifts`.`end` desc
', ',
[ [
$userId, $user->id,
time(),
] ]
); );
} }

View File

@ -2,6 +2,8 @@
namespace Engelsystem; namespace Engelsystem;
use Engelsystem\Helpers\Carbon;
/** /**
* BO Class that stores all parameters used to filter shifts for users. * BO Class that stores all parameters used to filter shifts for users.
* *
@ -86,6 +88,14 @@ class ShiftsFilter
$this->endTime = $data['endTime']; $this->endTime = $data['endTime'];
} }
/**
* @return Carbon
*/
public function getStart()
{
return Carbon::createFromTimestamp($this->startTime);
}
/** /**
* @return int unix timestamp * @return int unix timestamp
*/ */
@ -102,6 +112,14 @@ class ShiftsFilter
$this->startTime = $startTime; $this->startTime = $startTime;
} }
/**
* @return Carbon
*/
public function getEnd()
{
return Carbon::createFromTimestamp($this->endTime);
}
/** /**
* @return int unix timestamp * @return int unix timestamp
*/ */

View File

@ -1,13 +1,14 @@
<?php <?php
use Engelsystem\Database\Db; use Engelsystem\Database\Db;
use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Engelsystem\ShiftsFilter; use Engelsystem\ShiftsFilter;
use Engelsystem\ShiftSignupState; use Engelsystem\ShiftSignupState;
use Illuminate\Support\Collection;
/** /**
* @param AngelType $angeltype * @param AngelType $angeltype
@ -16,18 +17,18 @@ use Engelsystem\ShiftSignupState;
function Shifts_by_angeltype(AngelType $angeltype) function Shifts_by_angeltype(AngelType $angeltype)
{ {
return Db::select(' return Db::select('
SELECT DISTINCT `Shifts`.* FROM `Shifts` SELECT DISTINCT `shifts`.* FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `Shifts`.`SID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `shifts`.`id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `NeededAngelTypes`.`angel_type_id` = ? WHERE `NeededAngelTypes`.`angel_type_id` = ?
AND `NeededAngelTypes`.`count` > 0 AND `NeededAngelTypes`.`count` > 0
AND s.shift_id IS NULL AND s.shift_id IS NULL
UNION UNION
SELECT DISTINCT `Shifts`.* FROM `Shifts` SELECT DISTINCT `shifts`.* FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id` = `Shifts`.`RID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id` = `shifts`.`room_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `NeededAngelTypes`.`angel_type_id` = ? WHERE `NeededAngelTypes`.`angel_type_id` = ?
AND `NeededAngelTypes`.`count` > 0 AND `NeededAngelTypes`.`count` > 0
AND NOT s.shift_id IS NULL AND NOT s.shift_id IS NULL
@ -41,31 +42,35 @@ function Shifts_by_angeltype(AngelType $angeltype)
* @param int $end timestamp * @param int $end timestamp
* @param ShiftsFilter|null $filter * @param ShiftsFilter|null $filter
* *
* @return array * @return Collection|Shift[]
*/ */
function Shifts_free($start, $end, ShiftsFilter $filter = null) function Shifts_free($start, $end, ShiftsFilter $filter = null)
{ {
$start = Carbon::createFromTimestamp($start);
$end = Carbon::createFromTimestamp($end);
$shifts = Db::select(' $shifts = Db::select('
SELECT * FROM (
SELECT * SELECT *
FROM `Shifts` FROM (
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id SELECT id, start
FROM `shifts`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE (`end` > ? AND `start` < ?) WHERE (`end` > ? AND `start` < ?)
AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`shifts`.`id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
> (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0' . ($filter ? ' AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') > (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`shifts`.`id` AND `freeloaded`=0' . ($filter ? ' AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
AND s.shift_id IS NULL AND s.shift_id IS NULL
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
UNION UNION
SELECT * SELECT id, start
FROM `Shifts` FROM `shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE (`end` > ? AND `start` < ?) WHERE (`end` > ? AND `start` < ?)
AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`Shifts`.`RID`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`shifts`.`room_id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
> (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0' . ($filter ? ' AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') > (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`shifts`.`id` AND `freeloaded`=0' . ($filter ? ' AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
AND NOT s.shift_id IS NULL AND NOT s.shift_id IS NULL
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
) AS `tmp` ) AS `tmp`
ORDER BY `tmp`.`start` ORDER BY `tmp`.`start`
', [ ', [
@ -74,40 +79,29 @@ function Shifts_free($start, $end, ShiftsFilter $filter = null)
$start, $start,
$end $end
]); ]);
$free_shifts = [];
foreach ($shifts as $shift) {
$free_shifts[] = Shift($shift['SID']);
}
return $free_shifts;
}
/** $shifts = collect($shifts);
* @param Room $room
* @return array[] return Shift::query()
*/ ->whereIn('id', $shifts->pluck('id')->toArray())
function Shifts_by_room(Room $room) ->get();
{
return Db::select(
'SELECT * FROM `Shifts` WHERE `RID`=? ORDER BY `start`',
[$room->id]
);
} }
/** /**
* @param ShiftsFilter $shiftsFilter * @param ShiftsFilter $shiftsFilter
* @return array[] * @return Shift[]|Collection
*/ */
function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter) function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
{ {
$sql = ' $sql = '
SELECT * FROM ( SELECT * FROM (
SELECT DISTINCT `Shifts`.*, `shift_types`.`name`, `rooms`.`name` AS `room_name` SELECT DISTINCT `shifts`.*, `shift_types`.`name`, `rooms`.`name` AS `room_name`
FROM `Shifts` FROM `shifts`
JOIN `rooms` ON `Shifts`.`RID` = `rooms`.`id` JOIN `rooms` ON `shifts`.`room_id` = `rooms`.`id`
JOIN `shift_types` ON `shift_types`.`id` = `Shifts`.`shifttype_id` JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `Shifts`.`SID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `shifts`.`id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ') WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ? AND `start` BETWEEN ? AND ?
AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ') AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ')
AND `NeededAngelTypes`.`count` > 0 AND `NeededAngelTypes`.`count` > 0
@ -115,13 +109,13 @@ function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
UNION UNION
SELECT DISTINCT `Shifts`.*, `shift_types`.`name`, `rooms`.`name` AS `room_name` SELECT DISTINCT `shifts`.*, `shift_types`.`name`, `rooms`.`name` AS `room_name`
FROM `Shifts` FROM `shifts`
JOIN `rooms` ON `Shifts`.`RID` = `rooms`.`id` JOIN `rooms` ON `shifts`.`room_id` = `rooms`.`id`
JOIN `shift_types` ON `shift_types`.`id` = `Shifts`.`shifttype_id` JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ') WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ? AND `start` BETWEEN ? AND ?
AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ') AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ')
AND `NeededAngelTypes`.`count` > 0 AND `NeededAngelTypes`.`count` > 0
@ -131,15 +125,22 @@ function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
ORDER BY `room_name`, `start` ORDER BY `room_name`, `start`
'; ';
return Db::select( $shiftsData = Db::select(
$sql, $sql,
[ [
$shiftsFilter->getStartTime(), $shiftsFilter->getStart(),
$shiftsFilter->getEndTime(), $shiftsFilter->getEnd(),
$shiftsFilter->getStartTime(), $shiftsFilter->getStart(),
$shiftsFilter->getEndTime(), $shiftsFilter->getEnd(),
] ]
); );
$shifts = [];
foreach ($shiftsData as $shift) {
$shifts[] = (new Shift())->forceFill($shift);
}
return collect($shifts);
} }
/** /**
@ -151,69 +152,69 @@ function NeededAngeltypes_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
$sql = ' $sql = '
SELECT SELECT
`NeededAngelTypes`.*, `NeededAngelTypes`.*,
`Shifts`.`SID`, `shifts`.`id` AS shift_id,
`angel_types`.`id`, `angel_types`.`id`,
`angel_types`.`name`, `angel_types`.`name`,
`angel_types`.`restricted`, `angel_types`.`restricted`,
`angel_types`.`no_self_signup` `angel_types`.`no_self_signup`
FROM `Shifts` FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`Shifts`.`SID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`shifts`.`id`
JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ') WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ? AND shifts.`start` BETWEEN ? AND ?
AND s.shift_id IS NULL AND s.shift_id IS NULL
UNION UNION
SELECT SELECT
`NeededAngelTypes`.*, `NeededAngelTypes`.*,
`Shifts`.`SID`, `shifts`.`id` AS shift_id,
`angel_types`.`id`, `angel_types`.`id`,
`angel_types`.`name`, `angel_types`.`name`,
`angel_types`.`restricted`, `angel_types`.`restricted`,
`angel_types`.`no_self_signup` `angel_types`.`no_self_signup`
FROM `Shifts` FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ') WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ? AND shifts.`start` BETWEEN ? AND ?
AND NOT s.shift_id IS NULL AND NOT s.shift_id IS NULL
'; ';
return Db::select( return Db::select(
$sql, $sql,
[ [
$shiftsFilter->getStartTime(), $shiftsFilter->getStart(),
$shiftsFilter->getEndTime(), $shiftsFilter->getEnd(),
$shiftsFilter->getStartTime(), $shiftsFilter->getStart(),
$shiftsFilter->getEndTime(), $shiftsFilter->getEnd(),
] ]
); );
} }
/** /**
* @param array $shift * @param Shift $shift
* @param AngelType $angeltype * @param AngelType $angeltype
* @return array|null * @return array|null
*/ */
function NeededAngeltype_by_Shift_and_Angeltype($shift, AngelType $angeltype) function NeededAngeltype_by_Shift_and_Angeltype(Shift $shift, AngelType $angeltype)
{ {
return Db::selectOne( return Db::selectOne(
' '
SELECT SELECT
`NeededAngelTypes`.*, `NeededAngelTypes`.*,
`Shifts`.`SID`, `shifts`.`id` AS shift_id,
`angel_types`.`id`, `angel_types`.`id`,
`angel_types`.`name`, `angel_types`.`name`,
`angel_types`.`restricted`, `angel_types`.`restricted`,
`angel_types`.`no_self_signup` `angel_types`.`no_self_signup`
FROM `Shifts` FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`Shifts`.`SID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`shifts`.`id`
JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `Shifts`.`SID`=? WHERE `shifts`.`id`=?
AND `angel_types`.`id`=? AND `angel_types`.`id`=?
AND s.shift_id IS NULL AND s.shift_id IS NULL
@ -221,23 +222,23 @@ function NeededAngeltype_by_Shift_and_Angeltype($shift, AngelType $angeltype)
SELECT SELECT
`NeededAngelTypes`.*, `NeededAngelTypes`.*,
`Shifts`.`SID`, `shifts`.`id` AS shift_id,
`angel_types`.`id`, `angel_types`.`id`,
`angel_types`.`name`, `angel_types`.`name`,
`angel_types`.`restricted`, `angel_types`.`restricted`,
`angel_types`.`no_self_signup` `angel_types`.`no_self_signup`
FROM `Shifts` FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `Shifts`.`SID`=? WHERE `shifts`.`id`=?
AND `angel_types`.`id`=? AND `angel_types`.`id`=?
AND NOT s.shift_id IS NULL AND NOT s.shift_id IS NULL
', ',
[ [
$shift['SID'], $shift->id,
$angeltype->id, $angeltype->id,
$shift['SID'], $shift->id,
$angeltype->id $angeltype->id
] ]
); );
@ -258,20 +259,20 @@ function ShiftEntries_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
`ShiftEntry`.`SID`, `ShiftEntry`.`SID`,
`ShiftEntry`.`Comment`, `ShiftEntry`.`Comment`,
`ShiftEntry`.`freeloaded` `ShiftEntry`.`freeloaded`
FROM `Shifts` FROM `shifts`
JOIN `ShiftEntry` ON `ShiftEntry`.`SID`=`Shifts`.`SID` JOIN `ShiftEntry` ON `ShiftEntry`.`SID`=`shifts`.`id`
JOIN `users` ON `ShiftEntry`.`UID`=`users`.`id` JOIN `users` ON `ShiftEntry`.`UID`=`users`.`id`
WHERE `Shifts`.`RID` IN (%s) WHERE `shifts`.`room_id` IN (%s)
AND `start` BETWEEN ? AND ? AND `start` BETWEEN ? AND ?
ORDER BY `Shifts`.`start` ORDER BY `shifts`.`start`
', ',
implode(',', $shiftsFilter->getRooms()) implode(',', $shiftsFilter->getRooms())
); );
return Db::select( return Db::select(
$sql, $sql,
[ [
$shiftsFilter->getStartTime(), $shiftsFilter->getStart(),
$shiftsFilter->getEndTime(), $shiftsFilter->getEnd(),
] ]
); );
} }
@ -279,15 +280,20 @@ function ShiftEntries_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
/** /**
* Check if a shift collides with other shifts (in time). * Check if a shift collides with other shifts (in time).
* *
* @param array $shift * @param Shift $shift
* @param array $shifts * @param Shift[]|Collection $shifts
* @return bool * @return bool
*/ */
function Shift_collides($shift, $shifts) function Shift_collides(Shift $shift, $shifts)
{ {
foreach ($shifts as $other_shift) { foreach ($shifts as $other_shift) {
if ($shift['SID'] != $other_shift['SID']) { if ($shift->id != $other_shift->id) {
if (!($shift['start'] >= $other_shift['end'] || $shift['end'] <= $other_shift['start'])) { if (
!(
$shift->start->timestamp >= $other_shift->end->timestamp
|| $shift->end->timestamp <= $other_shift->start->timestamp
)
) {
return true; return true;
} }
} }
@ -319,17 +325,17 @@ function Shift_free_entries(AngelType $needed_angeltype, $shift_entries)
* Check if shift signup is allowed from the end users point of view (no admin like privileges) * Check if shift signup is allowed from the end users point of view (no admin like privileges)
* *
* @param User $user * @param User $user
* @param array $shift The shift * @param Shift $shift The shift
* @param AngelType $angeltype The angeltype to which the user wants to sign up * @param AngelType $angeltype The angeltype to which the user wants to sign up
* @param array|null $user_angeltype * @param array|null $user_angeltype
* @param array|null $user_shifts List of the users shifts * @param SHift[]|Collection|null $user_shifts List of the users shifts
* @param AngelType $needed_angeltype * @param AngelType $needed_angeltype
* @param array[] $shift_entries * @param array[] $shift_entries
* @return ShiftSignupState * @return ShiftSignupState
*/ */
function Shift_signup_allowed_angel( function Shift_signup_allowed_angel(
$user, $user,
$shift, Shift $shift,
AngelType $angeltype, AngelType $angeltype,
$user_angeltype, $user_angeltype,
$user_shifts, $user_shifts,
@ -342,7 +348,7 @@ function Shift_signup_allowed_angel(
return new ShiftSignupState(ShiftSignupState::NOT_ARRIVED, $free_entries); return new ShiftSignupState(ShiftSignupState::NOT_ARRIVED, $free_entries);
} }
if (config('signup_advance_hours') && $shift['start'] > time() + config('signup_advance_hours') * 3600) { if (config('signup_advance_hours') && $shift->start->timestamp > time() + config('signup_advance_hours') * 3600) {
return new ShiftSignupState(ShiftSignupState::NOT_YET, $free_entries); return new ShiftSignupState(ShiftSignupState::NOT_YET, $free_entries);
} }
@ -352,7 +358,7 @@ function Shift_signup_allowed_angel(
$signed_up = false; $signed_up = false;
foreach ($user_shifts as $user_shift) { foreach ($user_shifts as $user_shift) {
if ($user_shift['SID'] == $shift['SID']) { if ($user_shift->id == $shift->id) {
$signed_up = true; $signed_up = true;
break; break;
} }
@ -364,10 +370,10 @@ function Shift_signup_allowed_angel(
} }
$shift_post_signup_total_allowed_seconds = $shift_post_signup_total_allowed_seconds =
(config('signup_post_fraction') * ($shift['end'] - $shift['start'])) (config('signup_post_fraction') * ($shift->end->timestamp - $shift->start->timestamp))
+ (config('signup_post_minutes') * 60); + (config('signup_post_minutes') * 60);
if (time() > $shift['start'] + $shift_post_signup_total_allowed_seconds) { if (time() > $shift->start->timestamp + $shift_post_signup_total_allowed_seconds) {
// you can only join if the shift is in future // you can only join if the shift is in future
return new ShiftSignupState(ShiftSignupState::SHIFT_ENDED, $free_entries); return new ShiftSignupState(ShiftSignupState::SHIFT_ENDED, $free_entries);
} }
@ -393,7 +399,7 @@ function Shift_signup_allowed_angel(
} }
if (Shift_collides($shift, $user_shifts)) { if (Shift_collides($shift, $user_shifts)) {
// you cannot join if user alread joined a parallel or this shift // you cannot join if user already joined a parallel of this shift
return new ShiftSignupState(ShiftSignupState::COLLIDES, $free_entries); return new ShiftSignupState(ShiftSignupState::COLLIDES, $free_entries);
} }
@ -438,14 +444,14 @@ function Shift_signup_allowed_admin(AngelType $needed_angeltype, $shift_entries)
} }
/** /**
* Check if an angel can signout from a shift. * Check if an angel can sign out from a shift.
* *
* @param array $shift The shift * @param Shift $shift The shift
* @param AngelType $angeltype The angeltype * @param AngelType $angeltype The angeltype
* @param int $signout_user_id The user that was signed up for the shift * @param int $signout_user_id The user that was signed up for the shift
* @return bool * @return bool
*/ */
function Shift_signout_allowed($shift, AngelType $angeltype, $signout_user_id) function Shift_signout_allowed(Shift $shift, AngelType $angeltype, $signout_user_id)
{ {
$user = auth()->user(); $user = auth()->user();
@ -462,7 +468,7 @@ function Shift_signout_allowed($shift, AngelType $angeltype, $signout_user_id)
return true; return true;
} }
if ($signout_user_id == $user->id && $shift['start'] > time() + config('last_unsubscribe') * 3600) { if ($signout_user_id == $user->id && $shift->start->timestamp > time() + config('last_unsubscribe') * 3600) {
return true; return true;
} }
@ -473,17 +479,17 @@ function Shift_signout_allowed($shift, AngelType $angeltype, $signout_user_id)
* Check if an angel can sign up for given shift. * Check if an angel can sign up for given shift.
* *
* @param User $signup_user * @param User $signup_user
* @param array $shift The shift * @param Shift $shift The shift
* @param AngelType $angeltype The angeltype to which the user wants to sign up * @param AngelType $angeltype The angeltype to which the user wants to sign up
* @param array|null $user_angeltype * @param array|null $user_angeltype
* @param array|null $user_shifts List of the users shifts * @param Shift[]|Collection|null $user_shifts List of the users shifts
* @param AngelType $needed_angeltype * @param AngelType $needed_angeltype
* @param array[] $shift_entries * @param array[] $shift_entries
* @return ShiftSignupState * @return ShiftSignupState
*/ */
function Shift_signup_allowed( function Shift_signup_allowed(
$signup_user, $signup_user,
$shift, Shift $shift,
AngelType $angeltype, AngelType $angeltype,
$user_angeltype, $user_angeltype,
$user_shifts, $user_shifts,
@ -512,179 +518,85 @@ function Shift_signup_allowed(
); );
} }
/**
* Delete a shift.
*
* @param int $shift_id
*/
function Shift_delete($shift_id)
{
Db::delete('DELETE FROM `Shifts` WHERE `SID`=?', [$shift_id]);
}
/**
* Update a shift.
*
* @param array $shift
* @return int Updated row count
*/
function Shift_update($shift)
{
$user = auth()->user();
$shift['name'] = ShiftType::find($shift['shifttype_id'])->name;
mail_shift_change(Shift($shift['SID']), $shift);
return Db::update(
'
UPDATE `Shifts` SET
`shifttype_id` = ?,
`start` = ?,
`end` = ?,
`RID` = ?,
`title` = ?,
`description` = ?,
`URL` = ?,
`edited_by_user_id` = ?,
`edited_at_timestamp` = ?
WHERE `SID` = ?
',
[
$shift['shifttype_id'],
$shift['start'],
$shift['end'],
$shift['RID'],
$shift['title'],
$shift['description'],
$shift['URL'],
$user->id,
time(),
$shift['SID']
]
);
}
/**
* Create a new shift.
*
* @param array $shift
* @param int $transactionId
* @return int|false ID of the new created shift
*/
function Shift_create($shift, $transactionId = null)
{
Db::insert(
'
INSERT INTO `Shifts` (
`shifttype_id`,
`start`,
`end`,
`RID`,
`title`,
`description`,
`URL`,
`transaction_id`,
`created_by_user_id`,
`edited_at_timestamp`,
`created_at_timestamp`
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
',
[
$shift['shifttype_id'],
$shift['start'],
$shift['end'],
$shift['RID'],
$shift['title'],
$shift['description'],
$shift['URL'],
$transactionId,
auth()->user()->id,
time(),
time(),
]
);
return Db::getPdo()->lastInsertId();
}
/** /**
* Return users shifts. * Return users shifts.
* *
* @param int $userId * @param int $userId
* @param bool $include_freeload_comments * @param bool $include_freeload_comments
* @return array[] * @return Collection|Shift[]
*/ */
function Shifts_by_user($userId, $include_freeload_comments = false) function Shifts_by_user($userId, $include_freeload_comments = false)
{ {
return Db::select( $shiftsData = Db::select(
' '
SELECT SELECT
`rooms`.*, `rooms`.*,
`rooms`.name AS Name, `rooms`.name AS Name,
`shift_types`.`id` AS `shifttype_id`, `shift_types`.`id` AS `shifttype_id`,
`shift_types`.`name`, `shift_types`.`name`,
`ShiftEntry`.`id`, `ShiftEntry`.`id` as shift_entry_id,
`ShiftEntry`.`SID`, `ShiftEntry`.`SID`,
`ShiftEntry`.`TID`, `ShiftEntry`.`TID`,
`ShiftEntry`.`UID`, `ShiftEntry`.`UID`,
`ShiftEntry`.`freeloaded`, `ShiftEntry`.`freeloaded`,
`ShiftEntry`.`Comment`, `ShiftEntry`.`Comment`,
' . ($include_freeload_comments ? '`ShiftEntry`.`freeload_comment`, ' : '') . ' ' . ($include_freeload_comments ? '`ShiftEntry`.`freeload_comment`, ' : '') . '
`Shifts`.*, `shifts`.*
@@session.time_zone AS timezone,
? AS event_timezone
FROM `ShiftEntry` FROM `ShiftEntry`
JOIN `Shifts` ON (`ShiftEntry`.`SID` = `Shifts`.`SID`) JOIN `shifts` ON (`ShiftEntry`.`SID` = `shifts`.`id`)
JOIN `shift_types` ON (`shift_types`.`id` = `Shifts`.`shifttype_id`) JOIN `shift_types` ON (`shift_types`.`id` = `shifts`.`shift_type_id`)
JOIN `rooms` ON (`Shifts`.`RID` = `rooms`.`id`) JOIN `rooms` ON (`shifts`.`room_id` = `rooms`.`id`)
WHERE `UID` = ? WHERE ShiftEntry.`UID` = ?
ORDER BY `start` ORDER BY `start`
', ',
[ [
config('timezone'),
$userId, $userId,
] ]
); );
$shifts = [];
foreach ($shiftsData as $data) {
$shifts[] = (new Shift())->forceFill($data);
}
return collect($shifts);
} }
/** /**
* Returns Shift by id. * Returns Shift by id or extends existing Shift
* *
* @param int $shift_id Shift ID * @param int|Shift $shift Shift ID or shift model
* @return array|null * @return Shift|null
*/ */
function Shift($shift_id) function Shift($shift)
{ {
$result = Db::selectOne(' if (!$shift instanceof Shift) {
SELECT `Shifts`.*, `shift_types`.`name` $shift = Shift::find($shift);
FROM `Shifts` }
JOIN `shift_types` ON (`shift_types`.`id` = `Shifts`.`shifttype_id`)
WHERE `SID`=?', [$shift_id]);
if (empty($result)) { if (!$shift) {
return null; return null;
} }
$shiftsEntry_source = Db::select(' $shift->shiftEntry = Db::select('
SELECT SELECT
`ShiftEntry`.`id`, `ShiftEntry`.`TID` , `ShiftEntry`.`UID` , `ShiftEntry`.`freeloaded`, `ShiftEntry`.`id`, `ShiftEntry`.`TID` , `ShiftEntry`.`UID` , `ShiftEntry`.`freeloaded`,
`users`.`name` AS `username`, `users`.`id` AS `user_id` `users`.`name` AS `username`, `users`.`id` AS `user_id`
FROM `ShiftEntry` FROM `ShiftEntry`
LEFT JOIN `users` ON (`users`.`id` = `ShiftEntry`.`UID`) LEFT JOIN `users` ON (`users`.`id` = `ShiftEntry`.`UID`)
WHERE `SID`=?', [$shift_id]); WHERE `SID`=?', [$shift->id]);
$result['ShiftEntry'] = $shiftsEntry_source; $neededAngels = [];
$result['NeedAngels'] = []; $angelTypes = NeededAngelTypes_by_shift($shift->id);
$angelTypes = NeededAngelTypes_by_shift($shift_id);
foreach ($angelTypes as $type) { foreach ($angelTypes as $type) {
$result['NeedAngels'][] = [ $neededAngels[] = [
'TID' => $type['angel_type_id'], 'TID' => $type['angel_type_id'],
'count' => $type['count'], 'count' => $type['count'],
'restricted' => $type['restricted'], 'restricted' => $type['restricted'],
'taken' => $type['taken'] 'taken' => $type['taken']
]; ];
} }
$shift->neededAngels = $neededAngels;
return $result; return $shift;
} }

View File

@ -17,13 +17,13 @@ function stats_currently_working(ShiftsFilter $filter = null)
SELECT SUM(( SELECT SUM((
SELECT COUNT(*) SELECT COUNT(*)
FROM `ShiftEntry` FROM `ShiftEntry`
WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` WHERE `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0 AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
)) AS `count` )) AS `count`
FROM `Shifts` FROM `shifts`
WHERE (`end` >= UNIX_TIMESTAMP() AND `start` <= UNIX_TIMESTAMP()) WHERE (`end` >= NOW() AND `start` <= NOW())
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '')
); );
return $result['count'] ?: '-'; return $result['count'] ?: '-';
@ -42,24 +42,24 @@ function stats_hours_to_work(ShiftsFilter $filter = null)
' '
SELECT ROUND(SUM(`count`)) AS `count` FROM ( SELECT ROUND(SUM(`count`)) AS `count` FROM (
SELECT SELECT
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`shifts`.`id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
* (`Shifts`.`end` - `Shifts`.`start`)/3600 AS `count` * TIMESTAMPDIFF(MINUTE, `shifts`.`start`, `shifts`.`end`) / 60 AS `count`
FROM `Shifts` FROM `shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `end` >= UNIX_TIMESTAMP() WHERE `shifts`.`end` >= NOW()
AND s.shift_id IS NULL AND s.shift_id IS NULL
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
UNION ALL UNION ALL
SELECT SELECT
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`Shifts`.`RID`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`shifts`.`room_id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
* (`Shifts`.`end` - `Shifts`.`start`)/3600 AS `count` * TIMESTAMPDIFF(MINUTE, `shifts`.`start`, `shifts`.`end`) / 60 AS `count`
FROM `Shifts` FROM `shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `end` >= UNIX_TIMESTAMP() WHERE shifts.`end` >= NOW()
AND NOT s.shift_id IS NULL AND NOT s.shift_id IS NULL
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
) AS `tmp` ) AS `tmp`
' '
); );
@ -86,23 +86,23 @@ function stats_angels_needed_three_hours(ShiftsFilter $filter = null)
FROM `NeededAngelTypes` FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `NeededAngelTypes`.`shift_id`=`Shifts`.`SID` AND `NeededAngelTypes`.`shift_id`=`shifts`.`id`
' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) - ( ) - (
SELECT COUNT(*) FROM `ShiftEntry` SELECT COUNT(*) FROM `ShiftEntry`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID` JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`Shifts`.`SID` AND `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0 AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) )
) )
AS `count` AS `count`
FROM `Shifts` FROM `shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `end` > UNIX_TIMESTAMP() AND `start` < ? WHERE shifts.`end` > NOW() AND shifts.`start` < ?
AND s.shift_id IS NULL AND s.shift_id IS NULL
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
UNION ALL UNION ALL
@ -113,23 +113,23 @@ function stats_angels_needed_three_hours(ShiftsFilter $filter = null)
FROM `NeededAngelTypes` FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `NeededAngelTypes`.`room_id`=`Shifts`.`RID` AND `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) - ( ) - (
SELECT COUNT(*) FROM `ShiftEntry` SELECT COUNT(*) FROM `ShiftEntry`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID` JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`Shifts`.`SID` AND `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0 AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) )
) )
AS `count` AS `count`
FROM `Shifts` FROM `shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `end` > UNIX_TIMESTAMP() AND `start` < ? WHERE `end` > NOW() AND `start` < ?
AND NOT s.shift_id IS NULL AND NOT s.shift_id IS NULL
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
) AS `tmp`', [ ) AS `tmp`', [
$in3hours, $in3hours,
$in3hours $in3hours
@ -165,23 +165,23 @@ function stats_angels_needed_for_nightshifts(ShiftsFilter $filter = null)
FROM `NeededAngelTypes` FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `NeededAngelTypes`.`shift_id`=`Shifts`.`SID` AND `NeededAngelTypes`.`shift_id`=`shifts`.`id`
' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) - ( ) - (
SELECT COUNT(*) FROM `ShiftEntry` SELECT COUNT(*) FROM `ShiftEntry`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID` JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`Shifts`.`SID` AND `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0 AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) )
) )
AS `count` AS `count`
FROM `Shifts` FROM `shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `end` > ? AND `start` < ? WHERE shifts.`end` > ? AND shifts.`start` < ?
AND s.shift_id IS NULL AND s.shift_id IS NULL
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
UNION ALL UNION ALL
@ -192,23 +192,23 @@ function stats_angels_needed_for_nightshifts(ShiftsFilter $filter = null)
FROM `NeededAngelTypes` FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `NeededAngelTypes`.`room_id`=`Shifts`.`RID` AND `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
' . ($filter ? 'AND angel_types.id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND angel_types.id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) - ( ) - (
SELECT COUNT(*) FROM `ShiftEntry` SELECT COUNT(*) FROM `ShiftEntry`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID` JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`Shifts`.`SID` AND `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0 AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) )
) )
AS `count` AS `count`
FROM `Shifts` FROM `shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `end` > ? AND `start` < ? WHERE `end` > ? AND `start` < ?
AND NOT s.shift_id IS NULL AND NOT s.shift_id IS NULL
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
) AS `tmp`', [ ) AS `tmp`', [
$night_start, $night_start,
$night_end, $night_end,

View File

@ -26,13 +26,12 @@ function User_tshirt_score($userId)
$result_shifts = Db::selectOne(sprintf(' $result_shifts = Db::selectOne(sprintf('
SELECT ROUND((%s) / 3600, 2) AS `tshirt_score` SELECT ROUND((%s) / 3600, 2) AS `tshirt_score`
FROM `users` LEFT JOIN `ShiftEntry` ON `users`.`id` = `ShiftEntry`.`UID` FROM `users` LEFT JOIN `ShiftEntry` ON `users`.`id` = `ShiftEntry`.`UID`
LEFT JOIN `Shifts` ON `ShiftEntry`.`SID` = `Shifts`.`SID` LEFT JOIN `shifts` ON `ShiftEntry`.`SID` = `shifts`.`id`
WHERE `users`.`id` = ? WHERE `users`.`id` = ?
AND `Shifts`.`end` < ? AND `shifts`.`end` < NOW()
GROUP BY `users`.`id` GROUP BY `users`.`id`
', $shift_sum_formula), [ ', $shift_sum_formula), [
$userId, $userId
time()
]); ]);
if (!isset($result_shifts['tshirt_score'])) { if (!isset($result_shifts['tshirt_score'])) {
$result_shifts = ['tshirt_score' => 0]; $result_shifts = ['tshirt_score' => 0];
@ -197,7 +196,7 @@ function User_get_eligable_voucher_count($user)
? Carbon::createFromFormat('Y-m-d', $voucher_settings['voucher_start'])->setTime(0, 0) ? Carbon::createFromFormat('Y-m-d', $voucher_settings['voucher_start'])->setTime(0, 0)
: null; : null;
$shifts = ShiftEntries_finished_by_user($user->id, $start); $shifts = ShiftEntries_finished_by_user($user, $start);
$worklog = UserWorkLogsForUser($user->id, $start); $worklog = UserWorkLogsForUser($user->id, $start);
$shifts_done = $shifts_done =
count($shifts) count($shifts)
@ -205,7 +204,7 @@ function User_get_eligable_voucher_count($user)
$shiftsTime = 0; $shiftsTime = 0;
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$shiftsTime += ($shift['end'] - $shift['start']) / 60 / 60; $shiftsTime += (Carbon::make($shift['end'])->timestamp - Carbon::make($shift['start'])->timestamp) / 60 / 60;
} }
foreach ($worklog as $entry) { foreach ($worklog as $entry) {
$shiftsTime += $entry->hours; $shiftsTime += $entry->hours;
@ -237,18 +236,18 @@ function User_get_shifts_sum_query()
{ {
$nightShifts = config('night_shifts'); $nightShifts = config('night_shifts');
if (!$nightShifts['enabled']) { if (!$nightShifts['enabled']) {
return 'COALESCE(SUM(`end` - `start`), 0)'; return 'COALESCE(SUM(UNIX_TIMESTAMP(shifts.end) - UNIX_TIMESTAMP(shifts.start)), 0)';
} }
return sprintf( return sprintf(
' '
COALESCE(SUM( COALESCE(SUM(
(1 + ( (1 + (
(HOUR(FROM_UNIXTIME(`Shifts`.`end`)) > %1$d AND HOUR(FROM_UNIXTIME(`Shifts`.`end`)) < %2$d) (HOUR(shifts.end) > %1$d AND HOUR(shifts.end) < %2$d)
OR (HOUR(FROM_UNIXTIME(`Shifts`.`start`)) > %1$d AND HOUR(FROM_UNIXTIME(`Shifts`.`start`)) < %2$d) OR (HOUR(shifts.start) > %1$d AND HOUR(shifts.start) < %2$d)
OR (HOUR(FROM_UNIXTIME(`Shifts`.`start`)) <= %1$d AND HOUR(FROM_UNIXTIME(`Shifts`.`end`)) >= %2$d) OR (HOUR(shifts.start) <= %1$d AND HOUR(shifts.end) >= %2$d)
)) ))
* (`Shifts`.`end` - `Shifts`.`start`) * (UNIX_TIMESTAMP(shifts.end) - UNIX_TIMESTAMP(shifts.start))
* (1 - (%3$d + 1) * `ShiftEntry`.`freeloaded`) * (1 - (%3$d + 1) * `ShiftEntry`.`freeloaded`)
), 0) ), 0)
', ',

View File

@ -1,5 +1,6 @@
<?php <?php
use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\User\State; use Engelsystem\Models\User\State;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\Builder;
@ -70,7 +71,7 @@ function admin_active()
) )
) )
->leftJoin('ShiftEntry', 'users.id', '=', 'ShiftEntry.UID') ->leftJoin('ShiftEntry', 'users.id', '=', 'ShiftEntry.UID')
->leftJoin('Shifts', 'ShiftEntry.SID', '=', 'Shifts.SID') ->leftJoin('shifts', 'ShiftEntry.SID', '=', 'shifts.id')
->leftJoin('users_state', 'users.id', '=', 'users_state.user_id') ->leftJoin('users_state', 'users.id', '=', 'users_state.user_id')
->where('users_state.arrived', '=', true) ->where('users_state.arrived', '=', true)
->groupBy('users.id') ->groupBy('users.id')
@ -162,14 +163,14 @@ function admin_active()
) )
) )
->leftJoin('ShiftEntry', 'users.id', '=', 'ShiftEntry.UID') ->leftJoin('ShiftEntry', 'users.id', '=', 'ShiftEntry.UID')
->leftJoin('Shifts', function ($join) use ($show_all_shifts) { ->leftJoin('shifts', function ($join) use ($show_all_shifts) {
/** @var JoinClause $join */ /** @var JoinClause $join */
$join->on('ShiftEntry.SID', '=', 'Shifts.SID'); $join->on('ShiftEntry.SID', '=', 'shifts.id');
if (!$show_all_shifts) { if (!$show_all_shifts) {
$join->where(function ($query) { $join->where(function ($query) {
/** @var Builder $query */ /** @var Builder $query */
$query->where('Shifts.end', '<', time()) $query->where('shifts.end', '<', Carbon::now())
->orWhereNull('Shifts.end'); ->orWhereNull('shifts.end');
}); });
} }
}) })

View File

@ -1,5 +1,6 @@
<?php <?php
use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
@ -41,14 +42,14 @@ function admin_free()
->select('users.*') ->select('users.*')
->leftJoin('ShiftEntry', 'users.id', 'ShiftEntry.UID') ->leftJoin('ShiftEntry', 'users.id', 'ShiftEntry.UID')
->leftJoin('users_state', 'users.id', 'users_state.user_id') ->leftJoin('users_state', 'users.id', 'users_state.user_id')
->leftJoin('Shifts', function ($join) { ->leftJoin('shifts', function ($join) {
/** @var JoinClause $join */ /** @var JoinClause $join */
$join->on('ShiftEntry.SID', '=', 'Shifts.SID') $join->on('ShiftEntry.SID', '=', 'shifts.id')
->where('Shifts.start', '<', time()) ->where('shifts.start', '<', Carbon::now())
->where('Shifts.end', '>', time()); ->where('shifts.end', '>', Carbon::now());
}) })
->where('users_state.arrived', '=', 1) ->where('users_state.arrived', '=', 1)
->whereNull('Shifts.SID') ->whereNull('shifts.id')
->orderBy('users.name') ->orderBy('users.name')
->groupBy('users.id'); ->groupBy('users.id');

View File

@ -1,6 +1,5 @@
<?php <?php
use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
@ -187,17 +186,17 @@ function admin_rooms()
} elseif ($request->input('show') == 'delete') { } elseif ($request->input('show') == 'delete') {
if ($request->hasPostData('ack')) { if ($request->hasPostData('ack')) {
$room = Room::find($room_id); $room = Room::find($room_id);
$shifts = Shifts_by_room($room); $shifts = $room->shifts;
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$shift = Shift($shift['SID']); $shift = Shift($shift);
foreach ($shift['ShiftEntry'] as $entry) { foreach ($shift->shiftEntry as $entry) {
$type = AngelType::find($entry['TID']); $type = AngelType::find($entry['TID']);
event('shift.entry.deleting', [ event('shift.entry.deleting', [
'user' => User::find($entry['user_id']), 'user' => User::find($entry['user_id']),
'start' => Carbon::createFromTimestamp($shift['start']), 'start' => $shift->start,
'end' => Carbon::createFromTimestamp($shift['end']), 'end' => $shift->end,
'name' => $shift['name'], 'name' => $shift->shiftType->name,
'title' => $shift['title'], 'title' => $shift->title,
'type' => $type->name, 'type' => $type->name,
'room' => $room, 'room' => $room,
'freeloaded' => (bool) $entry['freeloaded'], 'freeloaded' => (bool) $entry['freeloaded'],

View File

@ -5,6 +5,7 @@ use Engelsystem\Helpers\Carbon;
use Engelsystem\Http\Exceptions\HttpForbidden; use Engelsystem\Http\Exceptions\HttpForbidden;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -27,7 +28,7 @@ function admin_shifts()
$valid = true; $valid = true;
$request = request(); $request = request();
$session = session(); $session = session();
$start = Carbon::createTimestampFromDatetime(date('Y-m-d') . 'T00:00'); $start = Carbon::createFromDateTime(date('Y-m-d') . 'T00:00');
$end = $start; $end = $start;
$mode = 'multi'; $mode = 'multi';
$angelmode = 'manually'; $angelmode = 'manually';
@ -46,7 +47,7 @@ function admin_shifts()
$room_array[$room->id] = $room->name; $room_array[$room->id] = $room->name;
} }
// Engeltypen laden // Load angeltypes
$types = AngelType::all(); $types = AngelType::all();
$needed_angel_types = []; $needed_angel_types = [];
foreach ($types as $type) { foreach ($types as $type) {
@ -93,14 +94,14 @@ function admin_shifts()
error(__('Please select a location.')); error(__('Please select a location.'));
} }
if ($request->has('start') && $tmp = Carbon::createTimestampFromDatetime($request->input('start'))) { if ($request->has('start') && $tmp = Carbon::createFromDateTime($request->input('start'))) {
$start = $tmp; $start = $tmp;
} else { } else {
$valid = false; $valid = false;
error(__('Please select a start time.')); error(__('Please select a start time.'));
} }
if ($request->has('end') && $tmp = Carbon::createTimestampFromDatetime($request->input('end'))) { if ($request->has('end') && $tmp = Carbon::createFromDateTime($request->input('end'))) {
$end = $tmp; $end = $tmp;
} else { } else {
$valid = false; $valid = false;
@ -201,15 +202,15 @@ function admin_shifts()
$shifts[] = [ $shifts[] = [
'start' => $start, 'start' => $start,
'end' => $end, 'end' => $end,
'RID' => $rid, 'room_id' => $rid,
'title' => $title, 'title' => $title,
'shifttype_id' => $shifttype_id, 'shift_type_id' => $shifttype_id,
'description' => $description, 'description' => $description,
]; ];
} elseif ($mode == 'multi') { } elseif ($mode == 'multi') {
$shift_start = (int) $start; $shift_start = $start;
do { do {
$shift_end = $shift_start + (int) $length * 60; $shift_end = (clone $shift_start)->addSeconds((int) $length * 60);
if ($shift_end > $end) { if ($shift_end > $end) {
$shift_end = $end; $shift_end = $end;
@ -221,9 +222,9 @@ function admin_shifts()
$shifts[] = [ $shifts[] = [
'start' => $shift_start, 'start' => $shift_start,
'end' => $shift_end, 'end' => $shift_end,
'RID' => $rid, 'room_id' => $rid,
'title' => $title, 'title' => $title,
'shifttype_id' => $shifttype_id, 'shift_type_id' => $shifttype_id,
'description' => $description, 'description' => $description,
]; ];
@ -238,8 +239,8 @@ function admin_shifts()
}); });
// Alle Tage durchgehen // Alle Tage durchgehen
$end_day = Carbon::createTimestampFromDatetime(date('Y-m-d', $end) . ' 00:00'); $end_day = Carbon::createFromDatetime($end->format('Y-m-d') . ' 00:00');
$day = Carbon::createTimestampFromDatetime(date('Y-m-d', $start) . ' 00:00'); $day = Carbon::createFromDatetime($start->format('Y-m-d') . ' 00:00');
do { do {
// Alle Schichtwechselstunden durchgehen // Alle Schichtwechselstunden durchgehen
for ($i = 0; $i < count($change_hours); $i++) { for ($i = 0; $i < count($change_hours); $i++) {
@ -248,20 +249,21 @@ function admin_shifts()
// Normales Intervall zwischen zwei Schichtwechselstunden // Normales Intervall zwischen zwei Schichtwechselstunden
$end_hour = $change_hours[$i + 1]; $end_hour = $change_hours[$i + 1];
} elseif ($shift_over_midnight) { } elseif ($shift_over_midnight) {
// Letzte Schichtwechselstunde: Wenn eine 24h Abdeckung gewünscht ist, hier die erste Schichtwechselstunde als Ende ensetzen // Letzte Schichtwechselstunde: Wenn eine 24h Abdeckung gewünscht ist,
// hier die erste Schichtwechselstunde als Ende einsetzen
$end_hour = $change_hours[0]; $end_hour = $change_hours[0];
} else { } else {
// Letzte Schichtwechselstunde: Keine Schicht erstellen // Letzte Schichtwechselstunde: Keine Schicht erstellen
break; break;
} }
$interval_start = Carbon::createTimestampFromDatetime(date('Y-m-d', $day) . ' ' . $start_hour); $interval_start = Carbon::createFromDatetime($day->format('Y-m-d') . ' ' . $start_hour);
if (str_replace(':', '', $end_hour) < str_replace(':', '', $start_hour)) { if (str_replace(':', '', $end_hour) < str_replace(':', '', $start_hour)) {
// Endstunde kleiner Startstunde? Dann sind wir im nächsten Tag gelandet // Endstunde kleiner Startstunde? Dann sind wir im nächsten Tag gelandet
$interval_end = Carbon::createTimestampFromDatetime(date('Y-m-d', $day + 36 * 60 * 60) . ' ' . $end_hour); $interval_end = Carbon::createFromDatetime(date('Y-m-d', $day->timestamp + 36 * 60 * 60) . ' ' . $end_hour);
} else { } else {
// Endstunde ist noch im selben Tag // Endstunde ist noch im selben Tag
$interval_end = Carbon::createTimestampFromDatetime(date('Y-m-d', $day) . ' ' . $end_hour); $interval_end = Carbon::createFromDatetime($day->format('Y-m-d', $day) . ' ' . $end_hour);
} }
// Liegt das Intervall vor dem Startzeitpunkt -> Überspringen // Liegt das Intervall vor dem Startzeitpunkt -> Überspringen
@ -288,14 +290,14 @@ function admin_shifts()
$shifts[] = [ $shifts[] = [
'start' => $interval_start, 'start' => $interval_start,
'end' => $interval_end, 'end' => $interval_end,
'RID' => $rid, 'room_id' => $rid,
'title' => $title, 'title' => $title,
'shifttype_id' => $shifttype_id, 'shift_type_id' => $shifttype_id,
'description' => $description 'description' => $description
]; ];
} }
$day = Carbon::createTimestampFromDatetime(date('Y-m-d', $day + 36 * 60 * 60) . ' 00:00'); $day = Carbon::createFromDatetime(date('Y-m-d', $day->timestamp + 36 * 60 * 60) . ' 00:00');
} while ($day <= $end_day); } while ($day <= $end_day);
usort($shifts, function ($a, $b) { usort($shifts, function ($a, $b) {
@ -308,11 +310,11 @@ function admin_shifts()
$shifts_table_entry = [ $shifts_table_entry = [
'timeslot' => 'timeslot' =>
icon('clock-history') . ' ' icon('clock-history') . ' '
. date('Y-m-d H:i', $shift['start']) . $shift['start']->format('Y-m-d H:i')
. ' - ' . ' - '
. date('H:i', $shift['end']) . $shift['end']->format('H:i')
. '<br />' . '<br />'
. Room_name_render(Room::find($shift['RID'])), . Room_name_render(Room::find($shift['room_id'])),
'title' => 'title' =>
ShiftType_name_render(ShiftType::find($shifttype_id)) ShiftType_name_render(ShiftType::find($shifttype_id))
. ($shift['title'] ? '<br />' . $shift['title'] : ''), . ($shift['title'] ? '<br />' . $shift['title'] : ''),
@ -342,8 +344,8 @@ function admin_shifts()
form_hidden('description', $description), form_hidden('description', $description),
form_hidden('title', $title), form_hidden('title', $title),
form_hidden('rid', $rid), form_hidden('rid', $rid),
form_hidden('start', date('Y-m-d H:i', $start)), form_hidden('start', $start->format('Y-m-d H:i')),
form_hidden('end', date('Y-m-d H:i', $end)), form_hidden('end', $end->format('Y-m-d H:i')),
form_hidden('mode', $mode), form_hidden('mode', $mode),
form_hidden('length', $length), form_hidden('length', $length),
form_hidden('change_hours', implode(', ', $change_hours)), form_hidden('change_hours', implode(', ', $change_hours)),
@ -369,15 +371,19 @@ function admin_shifts()
$transactionId = Str::uuid(); $transactionId = Str::uuid();
foreach ($session->get('admin_shifts_shifts', []) as $shift) { foreach ($session->get('admin_shifts_shifts', []) as $shift) {
$shift['URL'] = null; $shift = new Shift($shift);
$shift_id = Shift_create($shift, $transactionId); $shift->url = '';
$shift->transaction_id = $transactionId;
$shift->createdBy()->associate(auth()->user());
$shift->save();
$shift_id = $shift->id;
engelsystem_log( engelsystem_log(
'Shift created: ' . $shifttypes[$shift['shifttype_id']] 'Shift created: ' . $shifttypes[$shift->shift_type_id]
. ' with title ' . $shift['title'] . ' with title ' . $shift->title
. ' with description ' . $shift['description'] . ' with description ' . $shift->description
. ' from ' . date('Y-m-d H:i', $shift['start']) . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . date('Y-m-d H:i', $shift['end']) . ' to ' . $shift->end->format('Y-m-d H:i')
. ', transaction: ' . $transactionId . ', transaction: ' . $transactionId
); );
@ -405,7 +411,7 @@ function admin_shifts()
engelsystem_log('Shift needs following angel types: ' . join(', ', $needed_angel_types_info)); engelsystem_log('Shift needs following angel types: ' . join(', ', $needed_angel_types_info));
} }
success('Schichten angelegt.'); success('Shifts created.');
throw_redirect(page_link_to('admin_shifts')); throw_redirect(page_link_to('admin_shifts'));
} else { } else {
$session->remove('admin_shifts_shifts'); $session->remove('admin_shifts_shifts');
@ -550,37 +556,32 @@ function admin_shifts_history(): string
$request = request(); $request = request();
$transactionId = $request->postData('transaction_id'); $transactionId = $request->postData('transaction_id');
if ($request->hasPostData('delete') && $transactionId) { if ($request->hasPostData('delete') && $transactionId) {
$shifts = Db::select(' $shifts = Shift::whereTransactionId($transactionId);
SELECT SID
FROM Shifts
WHERE transaction_id = ?
', [$transactionId]);
engelsystem_log('Deleting ' . count($shifts) . ' shifts (transaction id ' . $transactionId . ')'); engelsystem_log('Deleting ' . count($shifts) . ' shifts (transaction id ' . $transactionId . ')');
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$shift = Shift($shift['SID']); $shift = Shift($shift);
$room = Room::find($shift['RID']); foreach ($shift->shiftEntry as $entry) {
foreach ($shift['ShiftEntry'] as $entry) {
$type = AngelType::find($entry['TID']); $type = AngelType::find($entry['TID']);
event('shift.entry.deleting', [ event('shift.entry.deleting', [
'user' => User::find($entry['user_id']), 'user' => User::find($entry['user_id']),
'start' => Carbon::createFromTimestamp($shift['start']), 'start' => $shift->start,
'end' => Carbon::createFromTimestamp($shift['end']), 'end' => $shift->end,
'name' => $shift['name'], 'name' => $shift->shiftType->name,
'title' => $shift['title'], 'title' => $shift->title,
'type' => $type->name, 'type' => $type->name,
'room' => $room, 'room' => $shift->room,
'freeloaded' => (bool) $entry['freeloaded'], 'freeloaded' => (bool) $entry['freeloaded'],
]); ]);
} }
shift_delete($shift['SID']); $shift->delete();
engelsystem_log( engelsystem_log(
'Deleted shift ' . $shift['name'] 'Deleted shift ' . $shift->title . ' / ' . $shift->shiftType->name
. ' from ' . date('Y-m-d H:i', $shift['start']) . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . date('Y-m-d H:i', $shift['end']) . ' to ' . $shift->end->format('Y-m-d H:i')
); );
} }
@ -588,28 +589,28 @@ function admin_shifts_history(): string
throw_redirect(page_link_to('admin_shifts_history')); throw_redirect(page_link_to('admin_shifts_history'));
} }
$shifts = Db::select(' $shiftsData = Db::select('
SELECT SELECT
transaction_id, transaction_id,
title, title,
COUNT(SID) AS count, COUNT(id) AS count,
MIN(start) AS start, MIN(start) AS start,
MAX(end) AS end, MAX(end) AS end,
created_by_user_id AS user_id, created_by AS user_id,
created_at_timestamp AS created_at MAX(created_at) AS created_at
FROM Shifts FROM shifts
WHERE transaction_id IS NOT NULL WHERE transaction_id IS NOT NULL
GROUP BY transaction_id GROUP BY transaction_id
ORDER BY transaction_id DESC ORDER BY created_at DESC
'); ');
foreach ($shifts as &$shift) { foreach ($shiftsData as &$shiftData) {
$shift['user'] = User_Nick_render(User::find($shift['user_id'])); $shiftData['user'] = User_Nick_render(User::find($shiftData['user_id']));
$shift['start'] = Carbon::createFromTimestamp($shift['start'])->format(__('Y-m-d H:i')); $shiftData['start'] = Carbon::make($shiftData['start'])->format(__('Y-m-d H:i'));
$shift['end'] = Carbon::createFromTimestamp($shift['end'])->format(__('Y-m-d H:i')); $shiftData['end'] = Carbon::make($shiftData['end'])->format(__('Y-m-d H:i'));
$shift['created_at'] = Carbon::createFromTimestamp($shift['created_at'])->format(__('Y-m-d H:i')); $shiftData['created_at'] = Carbon::make($shiftData['created_at'])->format(__('Y-m-d H:i'));
$shift['actions'] = form([ $shiftData['actions'] = form([
form_hidden('transaction_id', $shift['transaction_id']), form_hidden('transaction_id', $shiftData['transaction_id']),
form_submit('delete', icon('trash') . __('delete all'), 'btn-sm', true, 'danger'), form_submit('delete', icon('trash') . __('delete all'), 'btn-sm', true, 'danger'),
]); ]);
} }
@ -625,6 +626,6 @@ function admin_shifts_history(): string
'user' => __('User'), 'user' => __('User'),
'created_at' => __('Created'), 'created_at' => __('Created'),
'actions' => '' 'actions' => ''
], $shifts) ], $shiftsData)
], true); ], true);
} }

View File

@ -17,6 +17,7 @@ use Engelsystem\Http\Response;
use Engelsystem\Models\Room as RoomModel; use Engelsystem\Models\Room as RoomModel;
use Engelsystem\Models\Shifts\Schedule as ScheduleUrl; use Engelsystem\Models\Shifts\Schedule as ScheduleUrl;
use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use ErrorException; use ErrorException;
@ -27,7 +28,6 @@ use Illuminate\Database\Eloquent\Builder as QueryBuilder;
use Illuminate\Database\Eloquent\Collection as DatabaseCollection; use Illuminate\Database\Eloquent\Collection as DatabaseCollection;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use stdClass;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
class ImportSchedule extends BaseController class ImportSchedule extends BaseController
@ -267,22 +267,22 @@ class ImportSchedule extends BaseController
$shiftEntries = $this->db $shiftEntries = $this->db
->table('ShiftEntry') ->table('ShiftEntry')
->select([ ->select([
'shift_types.name', 'Shifts.title', 'angel_types.name AS type', 'rooms.id AS room_id', 'shift_types.name', 'shifts.title', 'angel_types.name AS type', 'rooms.id AS room_id',
'Shifts.start', 'Shifts.end', 'ShiftEntry.UID as user_id', 'ShiftEntry.freeloaded' 'shifts.start', 'shifts.end', 'ShiftEntry.UID as user_id', 'ShiftEntry.freeloaded'
]) ])
->join('Shifts', 'Shifts.SID', 'ShiftEntry.SID') ->join('shifts', 'shifts.id', 'ShiftEntry.SID')
->join('schedule_shift', 'Shifts.SID', 'schedule_shift.shift_id') ->join('schedule_shift', 'shifts.id', 'schedule_shift.shift_id')
->join('rooms', 'rooms.id', 'Shifts.RID') ->join('rooms', 'rooms.id', 'shifts.room_id')
->join('angel_types', 'angel_types.id', 'ShiftEntry.TID') ->join('angel_types', 'angel_types.id', 'ShiftEntry.TID')
->join('shift_types', 'shift_types.id', 'Shifts.shifttype_id') ->join('shift_types', 'shift_types.id', 'shifts.shift_type_id')
->where('schedule_shift.guid', $event->getGuid()) ->where('schedule_shift.guid', $event->getGuid())
->get(); ->get();
foreach ($shiftEntries as $shiftEntry) { foreach ($shiftEntries as $shiftEntry) {
event('shift.entry.deleting', [ event('shift.entry.deleting', [
'user' => User::find($shiftEntry->user_id), 'user' => User::find($shiftEntry->user_id),
'start' => Carbon::createFromTimestamp($shiftEntry->start), 'start' => Carbon::make($shiftEntry->start),
'end' => Carbon::createFromTimestamp($shiftEntry->end), 'end' => Carbon::make($shiftEntry->end),
'name' => $shiftEntry->name, 'name' => $shiftEntry->name,
'title' => $shiftEntry->title, 'title' => $shiftEntry->title,
'type' => $shiftEntry->type, 'type' => $shiftEntry->type,
@ -292,93 +292,82 @@ class ImportSchedule extends BaseController
} }
} }
protected function createEvent(Event $shift, int $shiftTypeId, RoomModel $room, ScheduleUrl $scheduleUrl): void protected function createEvent(Event $event, int $shiftTypeId, RoomModel $room, ScheduleUrl $scheduleUrl): void
{ {
$user = auth()->user(); $user = auth()->user();
$eventTimeZone = Carbon::now()->timezone;
$this->db $shift = new Shift();
->table('Shifts') $shift->title = $event->getTitle();
->insert( $shift->shift_type_id = $shiftTypeId;
[ $shift->start = $event->getDate()->copy()->timezone($eventTimeZone);
'title' => $shift->getTitle(), $shift->end = $event->getEndDate()->copy()->timezone($eventTimeZone);
'shifttype_id' => $shiftTypeId, $shift->room()->associate($room);
'start' => $shift->getDate()->unix(), $shift->url = $event->getUrl() ?? '';
'end' => $shift->getEndDate()->unix(), $shift->createdBy()->associate($user);
'RID' => $room->id, $shift->save();
'URL' => $shift->getUrl(),
'created_by_user_id' => $user->id,
'created_at_timestamp' => time(),
'edited_by_user_id' => null,
'edited_at_timestamp' => 0,
]
);
$shiftId = $this->db->getDoctrineConnection()->lastInsertId(); $scheduleShift = new ScheduleShift(['guid' => $event->getGuid()]);
$scheduleShift = new ScheduleShift(['shift_id' => $shiftId, 'guid' => $shift->getGuid()]);
$scheduleShift->schedule()->associate($scheduleUrl); $scheduleShift->schedule()->associate($scheduleUrl);
$scheduleShift->shift()->associate($shift);
$scheduleShift->save(); $scheduleShift->save();
$this->log( $this->log(
'Created schedule shift "{shift}" in "{room}" ({from} {to}, {guid})', 'Created schedule shift "{shift}" in "{room}" ({from} {to}, {guid})',
[ [
'shift' => $shift->getTitle(), 'shift' => $shift->title,
'room' => $room->name, 'room' => $shift->room->name,
'from' => $shift->getDate()->format(DateTimeInterface::RFC3339), 'from' => $shift->start->format(DateTimeInterface::RFC3339),
'to' => $shift->getEndDate()->format(DateTimeInterface::RFC3339), 'to' => $shift->end->format(DateTimeInterface::RFC3339),
'guid' => $shift->getGuid(), 'guid' => $scheduleShift->guid,
] ]
); );
} }
protected function updateEvent(Event $shift, int $shiftTypeId, RoomModel $room): void protected function updateEvent(Event $event, int $shiftTypeId, RoomModel $room): void
{ {
$user = auth()->user(); $user = auth()->user();
$eventTimeZone = Carbon::now()->timezone;
$this->db /** @var ScheduleShift $scheduleShift */
->table('Shifts') $scheduleShift = ScheduleShift::whereGuid($event->getGuid())->first();
->join('schedule_shift', 'Shifts.SID', 'schedule_shift.shift_id') $shift = $scheduleShift->shift;
->where('schedule_shift.guid', $shift->getGuid()) $shift->title = $event->getTitle();
->update( $shift->shift_type_id = $shiftTypeId;
[ $shift->start = $event->getDate()->copy()->timezone($eventTimeZone);
'title' => $shift->getTitle(), $shift->end = $event->getEndDate()->copy()->timezone($eventTimeZone);
'shifttype_id' => $shiftTypeId, $shift->room()->associate($room);
'start' => $shift->getDate()->unix(), $shift->url = $event->getUrl() ?? '';
'end' => $shift->getEndDate()->unix(), $shift->updatedBy()->associate($user);
'RID' => $room->id, $shift->save();
'URL' => $shift->getUrl(),
'edited_by_user_id' => $user->id,
'edited_at_timestamp' => time(),
]
);
$this->log( $this->log(
'Updated schedule shift "{shift}" in "{room}" ({from} {to}, {guid})', 'Updated schedule shift "{shift}" in "{room}" ({from} {to}, {guid})',
[ [
'shift' => $shift->getTitle(), 'shift' => $shift->title,
'room' => $room->name, 'room' => $shift->room->name,
'from' => $shift->getDate()->format(DateTimeInterface::RFC3339), 'from' => $shift->start->format(DateTimeInterface::RFC3339),
'to' => $shift->getEndDate()->format(DateTimeInterface::RFC3339), 'to' => $shift->end->format(DateTimeInterface::RFC3339),
'guid' => $shift->getGuid(), 'guid' => $scheduleShift->guid,
] ]
); );
} }
protected function deleteEvent(Event $shift): void protected function deleteEvent(Event $event): void
{ {
$this->db /** @var ScheduleShift $scheduleShift */
->table('Shifts') $scheduleShift = ScheduleShift::whereGuid($event->getGuid())->first();
->join('schedule_shift', 'Shifts.SID', 'schedule_shift.shift_id') $shift = $scheduleShift->shift;
->where('schedule_shift.guid', $shift->getGuid()) $shift->delete();
->delete();
$this->log( $this->log(
'Deleted schedule shift "{shift}" ({from} {to}, {guid})', 'Deleted schedule shift "{shift}" in {room} ({from} {to}, {guid})',
[ [
'shift' => $shift->getTitle(), 'shift' => $shift->title,
'from' => $shift->getDate()->format(DateTimeInterface::RFC3339), 'room' => $shift->room->name,
'to' => $shift->getEndDate()->format(DateTimeInterface::RFC3339), 'from' => $shift->start->format(DateTimeInterface::RFC3339),
'guid' => $shift->getGuid(), 'to' => $shift->end->format(DateTimeInterface::RFC3339),
'guid' => $scheduleShift->guid,
] ]
); );
} }
@ -465,15 +454,20 @@ class ImportSchedule extends BaseController
/** @var Event[] $deleteEvents */ /** @var Event[] $deleteEvents */
$deleteEvents = []; $deleteEvents = [];
$rooms = $this->getAllRooms(); $rooms = $this->getAllRooms();
$eventTimeZone = Carbon::now()->timezone;
foreach ($schedule->getDay() as $day) { foreach ($schedule->getDay() as $day) {
foreach ($day->getRoom() as $room) { foreach ($day->getRoom() as $room) {
foreach ($room->getEvent() as $event) { foreach ($room->getEvent() as $event) {
$scheduleEvents[$event->getGuid()] = $event; $scheduleEvents[$event->getGuid()] = $event;
$event->getDate()->subMinutes($minutesBefore); $event->getDate()->timezone($eventTimeZone)->subMinutes($minutesBefore);
$event->getEndDate()->addMinutes($minutesAfter); $event->getEndDate()->timezone($eventTimeZone)->addMinutes($minutesAfter);
$event->setTitle(sprintf('%s [%s]', $event->getTitle(), $event->getLanguage())); $event->setTitle(
$event->getLanguage()
? sprintf('%s [%s]', $event->getTitle(), $event->getLanguage())
: $event->getTitle()
);
} }
} }
} }
@ -482,17 +476,18 @@ class ImportSchedule extends BaseController
$existingShifts = $this->getScheduleShiftsByGuid($scheduleUrl, $scheduleEventsGuidList); $existingShifts = $this->getScheduleShiftsByGuid($scheduleUrl, $scheduleEventsGuidList);
foreach ($existingShifts as $shift) { foreach ($existingShifts as $shift) {
$guid = $shift->guid; $guid = $shift->guid;
$shift = $this->loadShift($shift->shift_id); /** @var Shift $shift */
$shift = Shift::with('room')->find($shift->shift_id);
$event = $scheduleEvents[$guid]; $event = $scheduleEvents[$guid];
$room = $rooms->where('name', $event->getRoom()->getName())->first(); $room = $rooms->where('name', $event->getRoom()->getName())->first();
if ( if (
$shift->title != $event->getTitle() $shift->title != $event->getTitle()
|| $shift->shift_type_id != $shiftType || $shift->shift_type_id != $shiftType
|| Carbon::createFromTimestamp($shift->start) != $event->getDate() || $shift->start != $event->getDate()
|| Carbon::createFromTimestamp($shift->end) != $event->getEndDate() || $shift->end != $event->getEndDate()
|| $shift->room_id != ($room->id ?? '') || $shift->room_id != ($room->id ?? '')
|| $shift->url != $event->getUrl() || $shift->url != ($event->getUrl() ?? '')
) { ) {
$changeEvents[$guid] = $event; $changeEvents[$guid] = $event;
} }
@ -515,20 +510,19 @@ class ImportSchedule extends BaseController
protected function eventFromScheduleShift(ScheduleShift $scheduleShift): Event protected function eventFromScheduleShift(ScheduleShift $scheduleShift): Event
{ {
$shift = $this->loadShift($scheduleShift->shift_id); /** @var Shift $shift */
$start = Carbon::createFromTimestamp($shift->start); $shift = Shift::with('room')->find($scheduleShift->shift_id);
$end = Carbon::createFromTimestamp($shift->end); $duration = $shift->start->diff($shift->end);
$duration = $start->diff($end);
return new Event( return new Event(
$scheduleShift->guid, $scheduleShift->guid,
0, 0,
new Room($shift->room_name), new Room($shift->room->name),
$shift->title, $shift->title,
'', '',
'n/a', 'n/a',
Carbon::createFromTimestamp($shift->start), $shift->start,
$start->format('H:i'), $shift->start->format('H:i'),
$duration->format('%H:%I'), $duration->format('%H:%I'),
'', '',
'', '',
@ -570,31 +564,6 @@ class ImportSchedule extends BaseController
->get(); ->get();
} }
/**
* @param $id
* @return stdClass|null
*/
protected function loadShift($id): ?stdClass
{
return $this->db->selectOne(
'
SELECT
s.SID AS id,
s.title,
s.start,
s.end,
s.shifttype_id AS shift_type_id,
s.RID AS room_id,
r.Name AS room_name,
s.URL as url
FROM Shifts AS s
LEFT JOIN rooms r on s.RID = r.id
WHERE SID = ?
',
[$id]
);
}
/** /**
* @param string $message * @param string $message
* @param array $context * @param array $context

View File

@ -1,7 +1,8 @@
<?php <?php
use Carbon\Carbon;
use Engelsystem\Http\Exceptions\HttpForbidden; use Engelsystem\Http\Exceptions\HttpForbidden;
use Engelsystem\Models\Shifts\Shift;
use Illuminate\Support\Collection;
/** /**
* Controller for ical output of users own shifts or any user_shifts filter. * Controller for ical output of users own shifts or any user_shifts filter.
@ -31,7 +32,7 @@ function user_ical()
/** /**
* Renders an ical calendar from given shifts array. * Renders an ical calendar from given shifts array.
* *
* @param array $shifts Shift * @param Shift[]|Collection $shifts Shift
*/ */
function send_ical_from_shifts($shifts) function send_ical_from_shifts($shifts)
{ {
@ -50,25 +51,22 @@ function send_ical_from_shifts($shifts)
/** /**
* Renders an ical vevent from given shift. * Renders an ical vevent from given shift.
* *
* @param array $shift * @param Shift $shift
* @return string * @return string
*/ */
function make_ical_entry_from_shift($shift) function make_ical_entry_from_shift(Shift $shift)
{ {
$start = Carbon::createFromTimestamp($shift['start']);
$end = Carbon::createFromTimestamp($shift['end']);
$output = "BEGIN:VEVENT\r\n"; $output = "BEGIN:VEVENT\r\n";
$output .= 'UID:' . md5($shift['start'] . $shift['end'] . $shift['name']) . "\r\n"; $output .= 'UID:' . md5($shift->start->timestamp . $shift->end->timestamp . $shift->shiftType->name) . "\r\n";
$output .= 'SUMMARY:' . str_replace("\n", "\\n", $shift['name']) $output .= 'SUMMARY:' . str_replace("\n", "\\n", $shift->shiftType->name)
. ' (' . str_replace("\n", "\\n", $shift['title']) . ")\r\n"; . ' (' . str_replace("\n", "\\n", $shift->title) . ")\r\n";
if (isset($shift['Comment'])) { if (isset($shift->Comment)) {
$output .= 'DESCRIPTION:' . str_replace("\n", "\\n", $shift['Comment']) . "\r\n"; $output .= 'DESCRIPTION:' . str_replace("\n", "\\n", $shift->Comment) . "\r\n";
} }
$output .= 'DTSTAMP:' . $start->utc()->format('Ymd\THis\Z') . "\r\n"; $output .= 'DTSTAMP:' . $shift->start->utc()->format('Ymd\THis\Z') . "\r\n";
$output .= 'DTSTART:' . $start->utc()->format('Ymd\THis\Z') . "\r\n"; $output .= 'DTSTART:' . $shift->start->utc()->format('Ymd\THis\Z') . "\r\n";
$output .= 'DTEND:' . $end->utc()->format('Ymd\THis\Z') . "\r\n"; $output .= 'DTEND:' . $shift->end->utc()->format('Ymd\THis\Z') . "\r\n";
$output .= 'LOCATION:' . $shift['Name'] . "\r\n"; $output .= 'LOCATION:' . $shift->room->name . "\r\n";
$output .= "END:VEVENT\r\n"; $output .= "END:VEVENT\r\n";
return $output; return $output;
} }

View File

@ -1,6 +1,7 @@
<?php <?php
use Engelsystem\Database\Db; use Engelsystem\Database\Db;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
/** /**
@ -56,14 +57,12 @@ function user_myshifts()
`ShiftEntry`.`Comment`, `ShiftEntry`.`Comment`,
`ShiftEntry`.`UID`, `ShiftEntry`.`UID`,
`shift_types`.`name`, `shift_types`.`name`,
`Shifts`.*, `shifts`.*,
`rooms`.`name` as room_name,
`angel_types`.`name` AS `angel_type` `angel_types`.`name` AS `angel_type`
FROM `ShiftEntry` FROM `ShiftEntry`
JOIN `angel_types` ON (`ShiftEntry`.`TID` = `angel_types`.`id`) JOIN `angel_types` ON (`ShiftEntry`.`TID` = `angel_types`.`id`)
JOIN `Shifts` ON (`ShiftEntry`.`SID` = `Shifts`.`SID`) JOIN `shifts` ON (`ShiftEntry`.`SID` = `shifts`.`id`)
JOIN `shift_types` ON (`shift_types`.`id` = `Shifts`.`shifttype_id`) JOIN `shift_types` ON (`shift_types`.`id` = `shifts`.`shift_type_id`)
JOIN `rooms` ON (`Shifts`.`RID` = `rooms`.`id`)
WHERE `ShiftEntry`.`id`=? WHERE `ShiftEntry`.`id`=?
AND `UID`=? AND `UID`=?
LIMIT 1 LIMIT 1
@ -74,8 +73,11 @@ function user_myshifts()
] ]
); );
if (!empty($shift)) { if (!empty($shift)) {
$freeloaded = $shift['freeloaded']; /** @var Shift $shift */
$freeload_comment = $shift['freeload_comment']; $shift = (new Shift())->forceFill($shift);
$freeloaded = $shift->freeloaded;
$freeload_comment = $shift->freeloaded_comment;
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
$valid = true; $valid = true;
@ -88,8 +90,8 @@ function user_myshifts()
} }
} }
$comment = $shift['Comment']; $comment = $shift->Comment;
$user_source = User::find($shift['UID']); $user_source = User::find($shift->UID);
if (auth()->user()->id == $user_source->id) { if (auth()->user()->id == $user_source->id) {
$comment = strip_request_item_nl('comment'); $comment = strip_request_item_nl('comment');
} }
@ -103,9 +105,10 @@ function user_myshifts()
]); ]);
engelsystem_log( engelsystem_log(
'Updated ' . User_Nick_render($user_source, true) . '\'s shift ' . $shift['name'] 'Updated ' . User_Nick_render($user_source, true) . '\'s shift '
. ' from ' . date('Y-m-d H:i', $shift['start']) . $shift->title . ' / ' . $shift->shiftType->name
. ' to ' . date('Y-m-d H:i', $shift['end']) . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . $shift->end->format('Y-m-d H:i')
. ' with comment ' . $comment . ' with comment ' . $comment
. '. Freeloaded: ' . ($freeloaded ? 'YES Comment: ' . $freeload_comment : 'NO') . '. Freeloaded: ' . ($freeloaded ? 'YES Comment: ' . $freeload_comment : 'NO')
); );
@ -116,13 +119,13 @@ function user_myshifts()
return ShiftEntry_edit_view( return ShiftEntry_edit_view(
$shifts_user, $shifts_user,
date('Y-m-d H:i', $shift['start']) . ', ' . shift_length($shift), $shift->start->format('Y-m-d H:i') . ', ' . shift_length($shift),
$shift['room_name'], $shift->room->name,
$shift['name'], $shift->shiftType->name,
$shift['angel_type'], $shift->angel_type,
$shift['Comment'], $shift->Comment,
$shift['freeloaded'], $shift->freeloaded,
$shift['freeload_comment'], $shift->freeload_comment,
auth()->can('user_shifts_admin') auth()->can('user_shifts_admin')
); );
} else { } else {

View File

@ -131,8 +131,8 @@ function load_days()
{ {
$days = (new Collection(Db::select( $days = (new Collection(Db::select(
' '
SELECT DISTINCT DATE(FROM_UNIXTIME(`start`)) AS `id`, DATE(FROM_UNIXTIME(`start`)) AS `name` SELECT DISTINCT DATE(`start`) AS `id`, DATE(`start`) AS `name`
FROM `Shifts` FROM `shifts`
ORDER BY `id`, `name` ORDER BY `id`, `name`
' '
))) )))
@ -253,10 +253,10 @@ function view_user_shifts()
'name' => __('free') 'name' => __('free')
] ]
]; ];
$start_day = date('Y-m-d', $shiftsFilter->getStartTime()); $start_day = $shiftsFilter->getStart()->format('Y-m-d');
$start_time = date('H:i', $shiftsFilter->getStartTime()); $start_time = $shiftsFilter->getStart()->format('H:i');
$end_day = date('Y-m-d', $shiftsFilter->getEndTime()); $end_day = $shiftsFilter->getEnd()->format('Y-m-d');
$end_time = date('H:i', $shiftsFilter->getEndTime()); $end_time = $shiftsFilter->getEnd()->format('H:i');
if (config('signup_requires_arrival') && !$user->state->arrived) { if (config('signup_requires_arrival') && !$user->state->arrived) {
info(render_user_arrived_hint()); info(render_user_arrived_hint());

View File

@ -2,6 +2,7 @@
namespace Engelsystem; namespace Engelsystem;
use Engelsystem\Models\Shifts\Shift;
use Exception; use Exception;
/** /**
@ -9,17 +10,15 @@ use Exception;
*/ */
class ShiftCalendarLane class ShiftCalendarLane
{ {
/** @var array[] */ /** @var Shift[] */
private $shifts = []; private $shifts = [];
/** /**
* ShiftCalendarLane constructor. * ShiftCalendarLane constructor.
* *
* @param string $header * @param string $header
* @param int $firstBlockStartTime Unix timestamp
* @param int $blockCount
*/ */
public function __construct(private $header, private $firstBlockStartTime, private $blockCount) public function __construct(private $header)
{ {
} }
@ -27,10 +26,10 @@ class ShiftCalendarLane
* Adds a shift to the lane, but only if it fits. * Adds a shift to the lane, but only if it fits.
* Returns true on success. * Returns true on success.
* *
* @param array $shift The shift to add * @param Shift $shift The shift to add
* @throws Exception if the shift doesn't fit into the lane. * @throws Exception if the shift doesn't fit into the lane.
*/ */
public function addShift($shift) public function addShift(Shift $shift)
{ {
if ($this->shiftFits($shift)) { if ($this->shiftFits($shift)) {
$this->shifts[] = $shift; $this->shifts[] = $shift;
@ -43,14 +42,14 @@ class ShiftCalendarLane
/** /**
* Returns true if given shift fits into this lane. * Returns true if given shift fits into this lane.
* *
* @param array $newShift * @param Shift $newShift
* @return bool * @return bool
* @internal param array $shift The shift to fit into this lane * @internal param array $shift The shift to fit into this lane
*/ */
public function shiftFits($newShift) public function shiftFits(Shift $newShift)
{ {
foreach ($this->shifts as $laneShift) { foreach ($this->shifts as $laneShift) {
if (!($newShift['start'] >= $laneShift['end'] || $newShift['end'] <= $laneShift['start'])) { if (!($newShift->start >= $laneShift->end || $newShift->end <= $laneShift->start)) {
return false; return false;
} }
} }
@ -67,7 +66,7 @@ class ShiftCalendarLane
} }
/** /**
* @return array[] * @return Shift[]
*/ */
public function getShifts() public function getShifts()
{ {

View File

@ -2,7 +2,7 @@
namespace Engelsystem; namespace Engelsystem;
use Engelsystem\Models\Room; use Engelsystem\Models\Shifts\Shift;
class ShiftCalendarRenderer class ShiftCalendarRenderer
{ {
@ -45,7 +45,7 @@ class ShiftCalendarRenderer
/** /**
* ShiftCalendarRenderer constructor. * ShiftCalendarRenderer constructor.
* *
* @param array[] $shifts * @param Shift[] $shifts
* @param array[] $needed_angeltypes * @param array[] $needed_angeltypes
* @param array[] $shift_entries * @param array[] $shift_entries
* @param ShiftsFilter $shiftsFilter * @param ShiftsFilter $shiftsFilter
@ -61,7 +61,7 @@ class ShiftCalendarRenderer
/** /**
* Assigns the shifts to different lanes per room if they collide * Assigns the shifts to different lanes per room if they collide
* *
* @param array[] $shifts The shifts to assign * @param Shift[] $shifts The shifts to assign
* @return array Returns an array that assigns a room_id to an array of ShiftCalendarLane containing the shifts * @return array Returns an array that assigns a room_id to an array of ShiftCalendarLane containing the shifts
*/ */
private function assignShiftsToLanes($shifts) private function assignShiftsToLanes($shifts)
@ -70,20 +70,17 @@ class ShiftCalendarRenderer
$lanes = []; $lanes = [];
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$room_id = $shift['RID']; $room = $shift->room;
$room = new Room();
$room->name = $shift['room_name'];
$room->setAttribute('id', $room_id);
$header = Room_name_render($room); $header = Room_name_render($room);
if (!isset($lanes[$room_id])) { if (!isset($lanes[$room->id])) {
// initialize room with one lane // initialize room with one lane
$lanes[$room_id] = [ $lanes[$room->id] = [
new ShiftCalendarLane($header, $this->getFirstBlockStartTime(), $this->getBlocksPerSlot()) new ShiftCalendarLane($header)
]; ];
} }
// Try to add the shift to the existing lanes for this room // Try to add the shift to the existing lanes for this room
$shift_added = false; $shift_added = false;
foreach ($lanes[$room_id] as $lane) { foreach ($lanes[$room->id] as $lane) {
/** @var ShiftCalendarLane $lane */ /** @var ShiftCalendarLane $lane */
if ($lane->shiftFits($shift)) { if ($lane->shiftFits($shift)) {
$lane->addShift($shift); $lane->addShift($shift);
@ -93,9 +90,9 @@ class ShiftCalendarRenderer
} }
// If all lanes for this room are busy, create a new lane and add shift to it // If all lanes for this room are busy, create a new lane and add shift to it
if (!$shift_added) { if (!$shift_added) {
$newLane = new ShiftCalendarLane($header, $this->getFirstBlockStartTime(), $this->getBlocksPerSlot()); $newLane = new ShiftCalendarLane($header);
$newLane->addShift($shift); $newLane->addShift($shift);
$lanes[$room_id][] = $newLane; $lanes[$room->id][] = $newLane;
} }
} }
@ -176,15 +173,15 @@ class ShiftCalendarRenderer
$rendered_until = $this->getFirstBlockStartTime(); $rendered_until = $this->getFirstBlockStartTime();
foreach ($lane->getShifts() as $shift) { foreach ($lane->getShifts() as $shift) {
while ($rendered_until + ShiftCalendarRenderer::SECONDS_PER_ROW <= $shift['start']) { while ($rendered_until + ShiftCalendarRenderer::SECONDS_PER_ROW <= $shift->start->timestamp) {
$html .= $this->renderTick($rendered_until); $html .= $this->renderTick($rendered_until);
$rendered_until += ShiftCalendarRenderer::SECONDS_PER_ROW; $rendered_until += ShiftCalendarRenderer::SECONDS_PER_ROW;
} }
list ($shift_height, $shift_html) = $shift_renderer->render( list ($shift_height, $shift_html) = $shift_renderer->render(
$shift, $shift,
$this->needed_angeltypes[$shift['SID']], $this->needed_angeltypes[$shift->id],
$this->shift_entries[$shift['SID']], $this->shift_entries[$shift->id],
auth()->user() auth()->user()
); );
$html .= $shift_html; $html .= $shift_html;
@ -256,15 +253,15 @@ class ShiftCalendarRenderer
} }
/** /**
* @param array[] $shifts * @param Shift[] $shifts
* @return int * @return int
*/ */
private function calcFirstBlockStartTime($shifts) private function calcFirstBlockStartTime($shifts)
{ {
$start_time = $this->shiftsFilter->getEndTime(); $start_time = $this->shiftsFilter->getEndTime();
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
if ($shift['start'] < $start_time) { if ($shift->start->timestamp < $start_time) {
$start_time = $shift['start']; $start_time = $shift->start->timestamp;
} }
} }
return ShiftCalendarRenderer::SECONDS_PER_ROW * floor( return ShiftCalendarRenderer::SECONDS_PER_ROW * floor(
@ -274,15 +271,15 @@ class ShiftCalendarRenderer
} }
/** /**
* @param array[] $shifts * @param Shift[] $shifts
* @return int * @return int
*/ */
private function calcLastBlockEndTime($shifts) private function calcLastBlockEndTime($shifts)
{ {
$end_time = $this->shiftsFilter->getStartTime(); $end_time = $this->shiftsFilter->getStartTime();
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
if ($shift['end'] > $end_time) { if ($shift->end->timestamp > $end_time) {
$end_time = $shift['end']; $end_time = $shift->end->timestamp;
} }
} }

View File

@ -3,7 +3,7 @@
namespace Engelsystem; namespace Engelsystem;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use function theme_type; use function theme_type;
@ -16,17 +16,17 @@ class ShiftCalendarShiftRenderer
/** /**
* Renders a shift * Renders a shift
* *
* @param array $shift The shift to render * @param Shift $shift The shift to render
* @param array[] $needed_angeltypes * @param array[] $needed_angeltypes
* @param array $shift_entries * @param array $shift_entries
* @param User $user The user who is viewing the shift calendar * @param User $user The user who is viewing the shift calendar
* @return array * @return array
*/ */
public function render($shift, $needed_angeltypes, $shift_entries, $user) public function render(Shift $shift, $needed_angeltypes, $shift_entries, $user)
{ {
$info_text = ''; $info_text = '';
if ($shift['title'] != '') { if ($shift->title != '') {
$info_text = icon('info-circle') . $shift['title'] . '<br>'; $info_text = icon('info-circle') . $shift->title . '<br>';
} }
list($shift_signup_state, $shifts_row) = $this->renderShiftNeededAngeltypes( list($shift_signup_state, $shifts_row) = $this->renderShiftNeededAngeltypes(
$shift, $shift,
@ -37,12 +37,10 @@ class ShiftCalendarShiftRenderer
$class = $this->classForSignupState($shift_signup_state); $class = $this->classForSignupState($shift_signup_state);
$blocks = ceil(($shift['end'] - $shift['start']) / ShiftCalendarRenderer::SECONDS_PER_ROW); $blocks = ceil(($shift->end->timestamp - $shift->start->timestamp) / ShiftCalendarRenderer::SECONDS_PER_ROW);
$blocks = max(1, $blocks); $blocks = max(1, $blocks);
$room = new Room(); $room = $shift->room;
$room->name = $shift['room_name'];
$room->setAttribute('id', $shift['RID']);
return [ return [
$blocks, $blocks,
@ -82,13 +80,13 @@ class ShiftCalendarShiftRenderer
} }
/** /**
* @param array $shift * @param Shift $shift
* @param array[] $needed_angeltypes * @param array[] $needed_angeltypes
* @param array[] $shift_entries * @param array[] $shift_entries
* @param User $user * @param User $user
* @return array * @return array
*/ */
private function renderShiftNeededAngeltypes($shift, $needed_angeltypes, $shift_entries, $user) private function renderShiftNeededAngeltypes(Shift $shift, $needed_angeltypes, $shift_entries, $user)
{ {
$shift_entries_filtered = []; $shift_entries_filtered = [];
foreach ($needed_angeltypes as $needed_angeltype) { foreach ($needed_angeltypes as $needed_angeltype) {
@ -146,14 +144,14 @@ class ShiftCalendarShiftRenderer
/** /**
* Renders a list entry containing the needed angels for an angeltype * Renders a list entry containing the needed angels for an angeltype
* *
* @param array $shift The shift which is rendered * @param Shift $shift The shift which is rendered
* @param array[] $shift_entries * @param array[] $shift_entries
* @param array $angeltype The angeltype, containing information about needed angeltypes * @param array $angeltype The angeltype, containing information about needed angeltypes
* and already signed up angels * and already signed up angels
* @param User $user The user who is viewing the shift calendar * @param User $user The user who is viewing the shift calendar
* @return array * @return array
*/ */
private function renderShiftNeededAngeltype($shift, $shift_entries, $angeltype, $user) private function renderShiftNeededAngeltype(Shift $shift, $shift_entries, $angeltype, $user)
{ {
$angeltype = (new AngelType())->forceFill($angeltype); $angeltype = (new AngelType())->forceFill($angeltype);
$entry_list = []; $entry_list = [];
@ -259,30 +257,30 @@ class ShiftCalendarShiftRenderer
/** /**
* Renders the shift header * Renders the shift header
* *
* @param array $shift The shift * @param Shift $shift The shift
* @param string $class The shift state class * @param string $class The shift state class
* @return string * @return string
*/ */
private function renderShiftHead($shift, $class, $needed_angeltypes_count) private function renderShiftHead(Shift $shift, $class, $needed_angeltypes_count)
{ {
$header_buttons = ''; $header_buttons = '';
if (auth()->can('admin_shifts')) { if (auth()->can('admin_shifts')) {
$header_buttons = '<div class="ms-auto d-print-none">' . table_buttons([ $header_buttons = '<div class="ms-auto d-print-none">' . table_buttons([
button( button(
page_link_to('user_shifts', ['edit_shift' => $shift['SID']]), page_link_to('user_shifts', ['edit_shift' => $shift->id]),
icon('pencil'), icon('pencil'),
"btn-$class btn-sm border-light text-white" "btn-$class btn-sm border-light text-white"
), ),
button( button(
page_link_to('user_shifts', ['delete_shift' => $shift['SID']]), page_link_to('user_shifts', ['delete_shift' => $shift->id]),
icon('trash'), icon('trash'),
"btn-$class btn-sm border-light text-white" "btn-$class btn-sm border-light text-white"
) )
]) . '</div>'; ]) . '</div>';
} }
$shift_heading = date('H:i', $shift['start']) . ' &dash; ' $shift_heading = $shift->start->format('H:i') . ' &dash; '
. date('H:i', $shift['end']) . ' &mdash; ' . $shift->end->format('H:i') . ' &mdash; '
. $shift['name']; . $shift->shiftType->name;
if ($needed_angeltypes_count > 0) { if ($needed_angeltypes_count > 0) {
$shift_heading = '<span class="badge bg-light text-danger me-1">' . $needed_angeltypes_count . '</span> ' . $shift_heading; $shift_heading = '<span class="badge bg-light text-danger me-1">' . $needed_angeltypes_count . '</span> ' . $shift_heading;

View File

@ -1,28 +1,28 @@
<?php <?php
use Carbon\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
/** /**
* Sign off from a user from a shift with admin permissions, asking for ack. * Sign off from a user from a shift with admin permissions, asking for ack.
* *
* @param array $shift * @param Shift $shift
* @param AngelType $angeltype * @param AngelType $angeltype
* @param User $signoff_user * @param User $signoff_user
* *
* @return string HTML * @return string HTML
*/ */
function ShiftEntry_delete_view_admin($shift, AngelType $angeltype, $signoff_user) function ShiftEntry_delete_view_admin(Shift $shift, AngelType $angeltype, User $signoff_user)
{ {
return page_with_title(ShiftEntry_delete_title(), [ return page_with_title(ShiftEntry_delete_title(), [
info(sprintf( info(sprintf(
__('Do you want to sign off %s from shift %s from %s to %s as %s?'), __('Do you want to sign off %s from shift %s from %s to %s as %s?'),
User_Nick_render($signoff_user), User_Nick_render($signoff_user),
$shift['name'], $shift->shiftType->name,
date('Y-m-d H:i', $shift['start']), $shift->start->format('Y-m-d H:i'),
date('Y-m-d H:i', $shift['end']), $shift->end->format('Y-m-d H:i'),
$angeltype->name $angeltype->name
), true), ), true),
form([ form([
@ -37,20 +37,20 @@ function ShiftEntry_delete_view_admin($shift, AngelType $angeltype, $signoff_use
/** /**
* Sign off from a shift, asking for ack. * Sign off from a shift, asking for ack.
* *
* @param array $shift * @param Shift $shift
* @param AngelType $angeltype * @param AngelType $angeltype
* @param int $signoff_user_id * @param int $signoff_user_id
* *
* @return string HTML * @return string HTML
*/ */
function ShiftEntry_delete_view($shift, AngelType $angeltype, $signoff_user_id) function ShiftEntry_delete_view(Shift $shift, AngelType $angeltype, $signoff_user_id)
{ {
return page_with_title(ShiftEntry_delete_title(), [ return page_with_title(ShiftEntry_delete_title(), [
info(sprintf( info(sprintf(
__('Do you want to sign off from your shift %s from %s to %s as %s?'), __('Do you want to sign off from your shift %s from %s to %s as %s?'),
$shift['name'], $shift->shiftType->name,
date('Y-m-d H:i', $shift['start']), $shift->start->format('Y-m-d H:i'),
date('Y-m-d H:i', $shift['end']), $shift->end->format('Y-m-d H:i'),
$angeltype->name $angeltype->name
), true), ), true),
@ -74,7 +74,7 @@ function ShiftEntry_delete_title()
/** /**
* Admin puts user into shift. * Admin puts user into shift.
* *
* @param array $shift * @param Shift $shift
* @param Room $room * @param Room $room
* @param AngelType $angeltype * @param AngelType $angeltype
* @param array $angeltypes_select * @param array $angeltypes_select
@ -83,17 +83,17 @@ function ShiftEntry_delete_title()
* @return string * @return string
*/ */
function ShiftEntry_create_view_admin( function ShiftEntry_create_view_admin(
$shift, Shift $shift,
Room $room, Room $room,
AngelType $angeltype, AngelType $angeltype,
$angeltypes_select, $angeltypes_select,
$signup_user, $signup_user,
$users_select $users_select
) { ) {
$start = Carbon::createFromTimestamp($shift['start'])->format(__('Y-m-d H:i')); $start = $shift->start->format(__('Y-m-d H:i'));
return page_with_title( return page_with_title(
ShiftEntry_create_title() . ': ' . $shift['name'] ShiftEntry_create_title() . ': ' . $shift->shiftType->name
. ' <small title="' . $start . '" data-countdown-ts="' . $shift['start'] . '">%c</small>', . ' <small title="' . $start . '" data-countdown-ts="' . $shift->start->timestamp . '">%c</small>',
[ [
Shift_view_header($shift, $room), Shift_view_header($shift, $room),
info(__('Do you want to sign up the following user for this shift?'), true), info(__('Do you want to sign up the following user for this shift?'), true),
@ -109,19 +109,19 @@ function ShiftEntry_create_view_admin(
/** /**
* Supporter puts user into shift. * Supporter puts user into shift.
* *
* @param array $shift * @param Shift $shift
* @param Room $room * @param Room $room
* @param AngelType $angeltype * @param AngelType $angeltype
* @param User $signup_user * @param User $signup_user
* @param array $users_select * @param array $users_select
* @return string * @return string
*/ */
function ShiftEntry_create_view_supporter($shift, Room $room, AngelType $angeltype, $signup_user, $users_select) function ShiftEntry_create_view_supporter(Shift $shift, Room $room, AngelType $angeltype, $signup_user, $users_select)
{ {
$start = Carbon::createFromTimestamp($shift['start'])->format(__('Y-m-d H:i')); $start = $shift->start->format(__('Y-m-d H:i'));
return page_with_title( return page_with_title(
ShiftEntry_create_title() . ': ' . $shift['name'] ShiftEntry_create_title() . ': ' . $shift->shiftType->name
. ' <small title="' . $start . '" data-countdown-ts="' . $shift['start'] . '">%c</small>', . ' <small title="' . $start . '" data-countdown-ts="' . $shift->start->timestamp . '">%c</small>',
[ [
Shift_view_header($shift, $room), Shift_view_header($shift, $room),
info(sprintf( info(sprintf(
@ -139,18 +139,18 @@ function ShiftEntry_create_view_supporter($shift, Room $room, AngelType $angelty
/** /**
* User joining a shift. * User joining a shift.
* *
* @param array $shift * @param Shift $shift
* @param Room $room * @param Room $room
* @param AngelType $angeltype * @param AngelType $angeltype
* @param string $comment * @param string $comment
* @return string * @return string
*/ */
function ShiftEntry_create_view_user($shift, Room $room, AngelType $angeltype, $comment) function ShiftEntry_create_view_user(Shift $shift, Room $room, AngelType $angeltype, $comment)
{ {
$start = Carbon::createFromTimestamp($shift['start'])->format(__('Y-m-d H:i')); $start = $shift->start->format(__('Y-m-d H:i'));
return page_with_title( return page_with_title(
ShiftEntry_create_title() . ': ' . $shift['name'] ShiftEntry_create_title() . ': ' . $shift->shiftType->name
. ' <small title="' . $start . '" data-countdown-ts="' . $shift['start'] . '">%c</small>', . ' <small title="' . $start . '" data-countdown-ts="' . $shift->start->timestamp . '">%c</small>',
[ [
Shift_view_header($shift, $room), Shift_view_header($shift, $room),
info(sprintf(__('Do you want to sign up for this shift as %s?'), AngelType_name_render($angeltype)), true), info(sprintf(__('Do you want to sign up for this shift as %s?'), AngelType_name_render($angeltype)), true),

View File

@ -1,8 +1,8 @@
<?php <?php
use Carbon\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
@ -12,35 +12,35 @@ use Illuminate\Support\Collection;
/** /**
* Renders the basic shift view header. * Renders the basic shift view header.
* *
* @param array $shift * @param Shift $shift
* @param Room $room * @param Room $room
* @return string HTML * @return string HTML
*/ */
function Shift_view_header($shift, Room $room) function Shift_view_header(Shift $shift, Room $room)
{ {
return div('row', [ return div('row', [
div('col-sm-3 col-xs-6', [ div('col-sm-3 col-xs-6', [
'<h4>' . __('Title') . '</h4>', '<h4>' . __('Title') . '</h4>',
'<p class="lead">' '<p class="lead">'
. ($shift['URL'] != '' . ($shift->url != ''
? '<a href="' . $shift['URL'] . '">' . $shift['title'] . '</a>' ? '<a href="' . $shift->url . '">' . $shift->title . '</a>'
: $shift['title']) : $shift->title)
. '</p>' . '</p>'
]), ]),
div('col-sm-3 col-xs-6', [ div('col-sm-3 col-xs-6', [
'<h4>' . __('Start') . '</h4>', '<h4>' . __('Start') . '</h4>',
'<p class="lead' . (time() >= $shift['start'] ? ' text-success' : '') . '">', '<p class="lead' . (time() >= $shift->start->timestamp ? ' text-success' : '') . '">',
icon('calendar-event') . date(__('Y-m-d'), $shift['start']), icon('calendar-event') . $shift->start->format(__('Y-m-d')),
'<br />', '<br />',
icon('clock') . date('H:i', $shift['start']), icon('clock') . $shift->start->format('H:i'),
'</p>' '</p>'
]), ]),
div('col-sm-3 col-xs-6', [ div('col-sm-3 col-xs-6', [
'<h4>' . __('End') . '</h4>', '<h4>' . __('End') . '</h4>',
'<p class="lead' . (time() >= $shift['end'] ? ' text-success' : '') . '">', '<p class="lead' . (time() >= $shift->end->timestamp ? ' text-success' : '') . '">',
icon('calendar-event') . date(__('Y-m-d'), $shift['end']), icon('calendar-event') . $shift->end->format(__('Y-m-d')),
'<br />', '<br />',
icon('clock') . date('H:i', $shift['end']), icon('clock') . $shift->end->format('H:i'),
'</p>' '</p>'
]), ]),
div('col-sm-3 col-xs-6', [ div('col-sm-3 col-xs-6', [
@ -51,35 +51,35 @@ function Shift_view_header($shift, Room $room)
} }
/** /**
* @param array $shift * @param Shift $shift
* @return string * @return string
*/ */
function Shift_editor_info_render($shift) function Shift_editor_info_render(Shift $shift)
{ {
$info = []; $info = [];
if (!empty($shift['created_by_user_id'])) { if (!empty($shift->created_by)) {
$info[] = sprintf( $info[] = sprintf(
icon('plus-lg') . __('created at %s by %s'), icon('plus-lg') . __('created at %s by %s'),
date('Y-m-d H:i', $shift['created_at_timestamp']), $shift->created_at->format('Y-m-d H:i'),
User_Nick_render(User::find($shift['created_by_user_id'])) User_Nick_render($shift->createdBy)
); );
} }
if (!empty($shift['edited_by_user_id'])) { if (!empty($shift->updated_by)) {
$info[] = sprintf( $info[] = sprintf(
icon('pencil') . __('edited at %s by %s'), icon('pencil') . __('edited at %s by %s'),
date('Y-m-d H:i', $shift['edited_at_timestamp']), $shift->updated_at->format('Y-m-d H:i'),
User_Nick_render(User::find($shift['edited_by_user_id'])) User_Nick_render($shift->updatedBy)
); );
} }
return join('<br />', $info); return join('<br />', $info);
} }
/** /**
* @param array $shift * @param Shift $shift
* @param AngelType $angeltype * @param AngelType $angeltype
* @return string * @return string
*/ */
function Shift_signup_button_render($shift, AngelType $angeltype) function Shift_signup_button_render(Shift $shift, AngelType $angeltype)
{ {
/** @var UserAngelType|null $user_angeltype */ /** @var UserAngelType|null $user_angeltype */
$user_angeltype = UserAngelType::whereUserId(auth()->user()->id) $user_angeltype = UserAngelType::whereUserId(auth()->user()->id)
@ -109,14 +109,14 @@ function Shift_signup_button_render($shift, AngelType $angeltype)
} }
/** /**
* @param array $shift * @param Shift $shift
* @param ShiftType $shifttype * @param ShiftType $shifttype
* @param Room $room * @param Room $room
* @param AngelType[]|Collection $angeltypes_source * @param AngelType[]|Collection $angeltypes_source
* @param ShiftSignupState $shift_signup_state * @param ShiftSignupState $shift_signup_state
* @return string * @return string
*/ */
function Shift_view($shift, ShiftType $shifttype, Room $room, $angeltypes_source, ShiftSignupState $shift_signup_state) function Shift_view(Shift $shift, ShiftType $shifttype, Room $room, $angeltypes_source, ShiftSignupState $shift_signup_state)
{ {
$shift_admin = auth()->can('admin_shifts'); $shift_admin = auth()->can('admin_shifts');
$user_shift_admin = auth()->can('user_shifts_admin'); $user_shift_admin = auth()->can('user_shifts_admin');
@ -131,12 +131,12 @@ function Shift_view($shift, ShiftType $shifttype, Room $room, $angeltypes_source
} }
$needed_angels = ''; $needed_angels = '';
$neededAngels = new Collection($shift['NeedAngels']); $neededAngels = new Collection($shift->neededAngels);
foreach ($neededAngels as $needed_angeltype) { foreach ($neededAngels as $needed_angeltype) {
$needed_angels .= Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shift, $user_shift_admin); $needed_angels .= Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shift, $user_shift_admin);
} }
$shiftEntry = new Collection($shift['ShiftEntry']); $shiftEntry = new Collection($shift->shiftEntry);
foreach ($shiftEntry->groupBy('TID') as $angelTypes) { foreach ($shiftEntry->groupBy('TID') as $angelTypes) {
/** @var Collection $angelTypes */ /** @var Collection $angelTypes */
$type = $angelTypes->first()['TID']; $type = $angelTypes->first()['TID'];
@ -160,10 +160,10 @@ function Shift_view($shift, ShiftType $shifttype, Room $room, $angeltypes_source
$content[] = info(__('You are signed up for this shift.'), true); $content[] = info(__('You are signed up for this shift.'), true);
} }
if (config('signup_advance_hours') && $shift['start'] > time() + config('signup_advance_hours') * 3600) { if (config('signup_advance_hours') && $shift->start->timestamp > time() + config('signup_advance_hours') * 3600) {
$content[] = info(sprintf( $content[] = info(sprintf(
__('This shift is in the far future and becomes available for signup at %s.'), __('This shift is in the far future and becomes available for signup at %s.'),
date(__('Y-m-d') . ' H:i', $shift['start'] - config('signup_advance_hours') * 3600) date(__('Y-m-d') . ' H:i', $shift->start->timestamp - config('signup_advance_hours') * 3600)
), true); ), true);
} }
@ -188,7 +188,7 @@ function Shift_view($shift, ShiftType $shifttype, Room $room, $angeltypes_source
div('col-sm-6', [ div('col-sm-6', [
'<h2>' . __('Description') . '</h2>', '<h2>' . __('Description') . '</h2>',
$parsedown->parse($shifttype->description), $parsedown->parse($shifttype->description),
$parsedown->parse((string) $shift['description']), $parsedown->parse($shift->description),
]) ])
]); ]);
@ -196,10 +196,10 @@ function Shift_view($shift, ShiftType $shifttype, Room $room, $angeltypes_source
$content[] = Shift_editor_info_render($shift); $content[] = Shift_editor_info_render($shift);
} }
$start = Carbon::createFromTimestamp($shift['start'])->format(__('Y-m-d H:i')); $start = $shift->start->format(__('Y-m-d H:i'));
return page_with_title( return page_with_title(
$shift['name'] . ' <small title="' . $start . '" data-countdown-ts="' . $shift['start'] . '">%c</small>', $shift->shiftType->name . ' <small title="' . $start . '" data-countdown-ts="' . $shift->start->timestamp . '">%c</small>',
$content $content
); );
} }
@ -207,11 +207,11 @@ function Shift_view($shift, ShiftType $shifttype, Room $room, $angeltypes_source
/** /**
* @param array $needed_angeltype * @param array $needed_angeltype
* @param AngelType[]|Collection $angeltypes * @param AngelType[]|Collection $angeltypes
* @param array[] $shift * @param Shift $shift
* @param bool $user_shift_admin * @param bool $user_shift_admin
* @return string * @return string
*/ */
function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shift, $user_shift_admin) function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, Shift $shift, $user_shift_admin)
{ {
$angeltype = $angeltypes[$needed_angeltype['TID']]; $angeltype = $angeltypes[$needed_angeltype['TID']];
$angeltype_supporter = auth()->user()->isAngelTypeSupporter($angeltype) $angeltype_supporter = auth()->user()->isAngelTypeSupporter($angeltype)
@ -242,7 +242,7 @@ function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shi
); );
$angels = []; $angels = [];
foreach ($shift['ShiftEntry'] as $shift_entry) { foreach ($shift->shiftEntry as $shift_entry) {
if ($shift_entry['TID'] == $needed_angeltype['TID']) { if ($shift_entry['TID'] == $needed_angeltype['TID']) {
$angels[] = Shift_view_render_shift_entry($shift_entry, $user_shift_admin, $angeltype_supporter, $shift); $angels[] = Shift_view_render_shift_entry($shift_entry, $user_shift_admin, $angeltype_supporter, $shift);
} }
@ -258,10 +258,10 @@ function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shi
* @param array $shift_entry * @param array $shift_entry
* @param bool $user_shift_admin * @param bool $user_shift_admin
* @param bool $angeltype_supporter * @param bool $angeltype_supporter
* @param array $shift * @param Shift $shift
* @return string * @return string
*/ */
function Shift_view_render_shift_entry($shift_entry, $user_shift_admin, $angeltype_supporter, $shift) function Shift_view_render_shift_entry($shift_entry, $user_shift_admin, $angeltype_supporter, Shift $shift)
{ {
$entry = User_Nick_render(User::find($shift_entry['UID'])); $entry = User_Nick_render(User::find($shift_entry['UID']));
if ($shift_entry['freeloaded']) { if ($shift_entry['freeloaded']) {
@ -288,14 +288,14 @@ function Shift_view_render_shift_entry($shift_entry, $user_shift_admin, $angelty
/** /**
* Calc shift length in format 12:23h. * Calc shift length in format 12:23h.
* *
* @param array $shift * @param Shift $shift
* @return string * @return string
*/ */
function shift_length($shift) function shift_length(Shift $shift)
{ {
$length = floor(($shift['end'] - $shift['start']) / (60 * 60)) . ':'; $length = floor(($shift->end->timestamp - $shift->start->timestamp) / (60 * 60)) . ':';
$length .= str_pad( $length .= str_pad(
(($shift['end'] - $shift['start']) % (60 * 60)) / 60, (($shift->end->timestamp - $shift->start->timestamp) % (60 * 60)) / 60,
2, 2,
'0', '0',
STR_PAD_LEFT STR_PAD_LEFT

View File

@ -3,7 +3,7 @@
use Carbon\Carbon; use Carbon\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Group; use Engelsystem\Models\Group;
use Engelsystem\Models\Room; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Models\Worklog; use Engelsystem\Models\Worklog;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -196,35 +196,39 @@ function User_shift_state_render($user)
return ''; return '';
} }
$upcoming_shifts = ShiftEntries_upcoming_for_user($user->id); $upcoming_shifts = ShiftEntries_upcoming_for_user($user);
if (empty($upcoming_shifts)) { if (empty($upcoming_shifts)) {
return '<span class="text-success">' . __('Free') . '</span>'; return '<span class="text-success">' . __('Free') . '</span>';
} }
$nextShift = array_shift($upcoming_shifts); $nextShift = array_shift($upcoming_shifts);
$start = Carbon::createFromTimestamp($nextShift['start'])->format(__('Y-m-d H:i')); $start = Carbon::make($nextShift['start']);
$end = Carbon::make($nextShift['end']);
$startFormat = $start->format(__('Y-m-d H:i'));
$endFormat = $end->format(__('Y-m-d H:i'));
$startTimestamp = $start->timestamp;
$endTimestamp = $end->timestamp;
if ($nextShift['start'] > time()) { if ($startTimestamp > time()) {
if ($nextShift['start'] - time() > 3600) { if ($startTimestamp - time() > 3600) {
return '<span class="text-success" title="' . $start . '" data-countdown-ts="' . $nextShift['start'] . '">' return '<span class="text-success" title="' . $startFormat . '" data-countdown-ts="' . $startTimestamp . '">'
. __('Next shift %c') . __('Next shift %c')
. '</span>'; . '</span>';
} }
return '<span class="text-warning" title="' . $start . '" data-countdown-ts="' . $nextShift['start'] . '">' return '<span class="text-warning" title="' . $startFormat . '" data-countdown-ts="' . $startTimestamp . '">'
. __('Next shift %c') . __('Next shift %c')
. '</span>'; . '</span>';
} }
$halfway = ($nextShift['start'] + $nextShift['end']) / 2;
$halfway = ($startTimestamp + $endTimestamp) / 2;
if (time() < $halfway) { if (time() < $halfway) {
return '<span class="text-danger" title="' . $start . '" data-countdown-ts="' . $nextShift['start'] . '">' return '<span class="text-danger" title="' . $startFormat . '" data-countdown-ts="' . $startTimestamp . '">'
. __('Shift started %c') . __('Shift started %c')
. '</span>'; . '</span>';
} }
$end = Carbon::createFromTimestamp($nextShift['end'])->format(__('Y-m-d H:i')); return '<span class="text-danger" title="' . $endFormat . '" data-countdown-ts="' . $endTimestamp . '">'
return '<span class="text-danger" title="' . $end . '" data-countdown-ts="' . $nextShift['end'] . '">'
. __('Shift ends %c') . __('Shift ends %c')
. '</span>'; . '</span>';
} }
@ -235,15 +239,15 @@ function User_last_shift_render($user)
return ''; return '';
} }
$last_shifts = ShiftEntries_finished_by_user($user->id); $last_shifts = ShiftEntries_finished_by_user($user);
if (empty($last_shifts)) { if (empty($last_shifts)) {
return ''; return '';
} }
$lastShift = array_shift($last_shifts); $lastShift = array_shift($last_shifts);
$end = Carbon::createFromTimestamp($lastShift['end'])->format(__('Y-m-d H:i')); $end = Carbon::make($lastShift['end']);
return '<span title="' . $end . '" data-countdown-ts="' . $lastShift['end'] . '">' return '<span title="' . $end->format(__('Y-m-d H:i')) . '" data-countdown-ts="' . $end->timestamp . '">'
. __('Shift ended %c') . __('Shift ended %c')
. '</span>'; . '</span>';
} }
@ -275,44 +279,44 @@ function User_view_shiftentries($needed_angel_type)
/** /**
* Helper that renders a shift line for user view * Helper that renders a shift line for user view
* *
* @param array $shift * @param Shift $shift
* @param User $user_source * @param User $user_source
* @param bool $its_me * @param bool $its_me
* @return array * @return array
*/ */
function User_view_myshift($shift, $user_source, $its_me) function User_view_myshift(Shift $shift, $user_source, $its_me)
{ {
$shift_info = '<a href="' . shift_link($shift) . '">' . $shift['name'] . '</a>'; $shift_info = '<a href="' . shift_link($shift) . '">' . $shift->shiftType->name . '</a>';
if ($shift['title']) { if ($shift->title) {
$shift_info .= '<br /><a href="' . shift_link($shift) . '">' . $shift['title'] . '</a>'; $shift_info .= '<br /><a href="' . shift_link($shift) . '">' . $shift->title . '</a>';
} }
foreach ($shift['needed_angeltypes'] as $needed_angel_type) { foreach ($shift->needed_angeltypes as $needed_angel_type) {
$shift_info .= User_view_shiftentries($needed_angel_type); $shift_info .= User_view_shiftentries($needed_angel_type);
} }
$myshift = [ $myshift = [
'date' => icon('calendar-event') 'date' => icon('calendar-event')
. date('Y-m-d', $shift['start']) . '<br>' . $shift->start->format('Y-m-d') . '<br>'
. icon('clock-history') . date('H:i', $shift['start']) . icon('clock-history') . $shift->start->format('H:i')
. ' - ' . ' - '
. date('H:i', $shift['end']), . $shift->end->format('H:i'),
'duration' => sprintf('%.2f', ($shift['end'] - $shift['start']) / 3600) . '&nbsp;h', 'duration' => sprintf('%.2f', ($shift->end->timestamp - $shift->start->timestamp) / 3600) . '&nbsp;h',
'room' => Room_name_render(Room::find($shift['RID'])), 'room' => Room_name_render($shift->room),
'shift_info' => $shift_info, 'shift_info' => $shift_info,
'comment' => '' 'comment' => ''
]; ];
if ($its_me) { if ($its_me) {
$myshift['comment'] = $shift['Comment']; $myshift['comment'] = $shift->Comment;
} }
if ($shift['freeloaded']) { if ($shift->freeloaded) {
$myshift['duration'] = '<p class="text-danger">' $myshift['duration'] = '<p class="text-danger">'
. sprintf('%.2f', -($shift['end'] - $shift['start']) / 3600 * 2) . '&nbsp;h' . sprintf('%.2f', -($shift->end->timestamp - $shift->start->timestamp) / 3600 * 2) . '&nbsp;h'
. '</p>'; . '</p>';
if (auth()->can('user_shifts_admin')) { if (auth()->can('user_shifts_admin')) {
$myshift['comment'] .= '<br />' $myshift['comment'] .= '<br />'
. '<p class="text-danger">' . __('Freeloaded') . ': ' . $shift['freeload_comment'] . '</p>'; . '<p class="text-danger">' . __('Freeloaded') . ': ' . $shift->freeload_comment . '</p>';
} else { } else {
$myshift['comment'] .= '<br /><p class="text-danger">' . __('Freeloaded') . '</p>'; $myshift['comment'] .= '<br /><p class="text-danger">' . __('Freeloaded') . '</p>';
} }
@ -323,12 +327,12 @@ function User_view_myshift($shift, $user_source, $its_me)
]; ];
if ($its_me || auth()->can('user_shifts_admin')) { if ($its_me || auth()->can('user_shifts_admin')) {
$myshift['actions'][] = button( $myshift['actions'][] = button(
page_link_to('user_myshifts', ['edit' => $shift['id'], 'id' => $user_source->id]), page_link_to('user_myshifts', ['edit' => $shift->shift_entry_id, 'id' => $user_source->id]),
icon('pencil') . __('edit'), icon('pencil') . __('edit'),
'btn-sm' 'btn-sm'
); );
} }
if (Shift_signout_allowed($shift, (new AngelType())->forceFill(['id' => $shift['TID']]), $user_source->id)) { if (Shift_signout_allowed($shift, (new AngelType())->forceFill(['id' => $shift->TID]), $user_source->id)) {
$myshift['actions'][] = button( $myshift['actions'][] = button(
shift_entry_delete_link($shift), shift_entry_delete_link($shift),
icon('trash') . __('sign off'), icon('trash') . __('sign off'),
@ -343,7 +347,7 @@ function User_view_myshift($shift, $user_source, $its_me)
/** /**
* Helper that prepares the shift table for user view * Helper that prepares the shift table for user view
* *
* @param array[] $shifts * @param Shift[]|Collection $shifts
* @param User $user_source * @param User $user_source
* @param bool $its_me * @param bool $its_me
* @param int $tshirt_score * @param int $tshirt_score
@ -365,11 +369,11 @@ function User_view_myshifts(
$myshifts_table = []; $myshifts_table = [];
$timeSum = 0; $timeSum = 0;
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$key = $shift['start'] . '-shift-' . $shift['SID'] . '-' . $shift['id']; $key = $shift->start->timestamp . '-shift-' . $shift->shift_entry_id . $shift->id;
$myshifts_table[$key] = User_view_myshift($shift, $user_source, $its_me); $myshifts_table[$key] = User_view_myshift($shift, $user_source, $its_me);
if (!$shift['freeloaded']) { if (!$shift->freeloaded) {
$timeSum += ($shift['end'] - $shift['start']); $timeSum += ($shift->end->timestamp - $shift->start->timestamp);
} }
} }
@ -453,7 +457,7 @@ function User_view_worklog(Worklog $worklog, $admin_user_worklog_privilege)
* @param bool $freeloader * @param bool $freeloader
* @param AngelType[] $user_angeltypes * @param AngelType[] $user_angeltypes
* @param Group[] $user_groups * @param Group[] $user_groups
* @param array[] $shifts * @param Shift[]|Collection $shifts
* @param bool $its_me * @param bool $its_me
* @param int $tshirt_score * @param int $tshirt_score
* @param bool $tshirt_admin * @param bool $tshirt_admin

View File

@ -15,6 +15,7 @@ use Engelsystem\Models\NewsComment;
use Engelsystem\Models\OAuth; use Engelsystem\Models\OAuth;
use Engelsystem\Models\Question; use Engelsystem\Models\Question;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\License; use Engelsystem\Models\User\License;
use Engelsystem\Models\User\PasswordReset; use Engelsystem\Models\User\PasswordReset;
use Engelsystem\Models\User\PersonalData; use Engelsystem\Models\User\PersonalData;
@ -109,9 +110,9 @@ class Stats
{ {
$query = User::query() $query = User::query()
->join('ShiftEntry', 'ShiftEntry.UID', '=', 'users.id') ->join('ShiftEntry', 'ShiftEntry.UID', '=', 'users.id')
->join('Shifts', 'Shifts.SID', '=', 'ShiftEntry.SID') ->join('shifts', 'shifts.id', '=', 'ShiftEntry.SID')
->where('Shifts.start', '<=', time()) ->where('shifts.start', '<=', Carbon::now())
->where('Shifts.end', '>', time()); ->where('shifts.end', '>', Carbon::now());
if (!is_null($freeloaded)) { if (!is_null($freeloaded)) {
$query->where('ShiftEntry.freeloaded', '=', $freeloaded); $query->where('ShiftEntry.freeloaded', '=', $freeloaded);
@ -207,14 +208,14 @@ class Stats
{ {
$query = $this $query = $this
->getQuery('ShiftEntry') ->getQuery('ShiftEntry')
->join('Shifts', 'Shifts.SID', '=', 'ShiftEntry.SID'); ->join('shifts', 'shifts.id', '=', 'ShiftEntry.SID');
if (!is_null($freeloaded)) { if (!is_null($freeloaded)) {
$query->where('freeloaded', '=', $freeloaded); $query->where('freeloaded', '=', $freeloaded);
} }
if (!is_null($done)) { if (!is_null($done)) {
$query->where('end', ($done == true ? '<' : '>='), time()); $query->where('end', ($done ? '<' : '>='), Carbon::now());
} }
return $query; return $query;
@ -309,14 +310,9 @@ class Stats
->count(); ->count();
} }
/**
* @codeCoverageIgnore
*/
public function shifts(): int public function shifts(): int
{ {
return $this return Shift::count();
->getQuery('Shifts')
->count();
} }
/** /**

View File

@ -5,7 +5,10 @@ declare(strict_types=1);
namespace Engelsystem\Models; namespace Engelsystem\Models;
use Carbon\Carbon; use Carbon\Carbon;
use Engelsystem\Models\Shifts\Shift;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Database\Query\Builder as QueryBuilder;
/** /**
@ -17,6 +20,8 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
* @property Carbon|null $created_at * @property Carbon|null $created_at
* @property Carbon|null $updated_at * @property Carbon|null $updated_at
* *
* @property-read Collection|Shift[] $shifts
*
* @method static QueryBuilder|Room[] whereId($value) * @method static QueryBuilder|Room[] whereId($value)
* @method static QueryBuilder|Room[] whereName($value) * @method static QueryBuilder|Room[] whereName($value)
* @method static QueryBuilder|Room[] whereMapUrl($value) * @method static QueryBuilder|Room[] whereMapUrl($value)
@ -39,4 +44,9 @@ class Room extends BaseModel
'map_url', 'map_url',
'description', 'description',
]; ];
public function shifts(): HasMany
{
return $this->hasMany(Shift::class);
}
} }

View File

@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Database\Query\Builder as QueryBuilder;
/** /**
@ -20,6 +21,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
* @property Carbon $created_at * @property Carbon $created_at
* @property Carbon $updated_at * @property Carbon $updated_at
* *
* @property-read QueryBuilder|Collection|Shift[] $shifts
* @property-read QueryBuilder|Collection|ScheduleShift[] $scheduleShifts * @property-read QueryBuilder|Collection|ScheduleShift[] $scheduleShifts
* @property-read QueryBuilder|ShiftType $shiftType * @property-read QueryBuilder|ShiftType $shiftType
* *
@ -60,6 +62,11 @@ class Schedule extends BaseModel
return $this->hasMany(ScheduleShift::class); return $this->hasMany(ScheduleShift::class);
} }
public function shifts(): HasManyThrough
{
return $this->hasManyThrough(Shift::class, ScheduleShift::class, 'schedule_id', 'id');
}
public function shiftType(): BelongsTo public function shiftType(): BelongsTo
{ {
return $this->belongsTo(ShiftType::class, 'shift_type', 'id'); return $this->belongsTo(ShiftType::class, 'shift_type', 'id');

View File

@ -12,6 +12,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
* @property string $guid * @property string $guid
* *
* @property-read QueryBuilder|Schedule $schedule * @property-read QueryBuilder|Schedule $schedule
* @property-read QueryBuilder|Shift $shift
* *
* @method static QueryBuilder|ScheduleShift[] whereShiftId($value) * @method static QueryBuilder|ScheduleShift[] whereShiftId($value)
* @method static QueryBuilder|ScheduleShift[] whereScheduleId($value) * @method static QueryBuilder|ScheduleShift[] whereScheduleId($value)
@ -38,4 +39,9 @@ class ScheduleShift extends BaseModel
{ {
return $this->belongsTo(Schedule::class); return $this->belongsTo(Schedule::class);
} }
public function shift(): BelongsTo
{
return $this->belongsTo(Shift::class);
}
} }

110
src/Models/Shifts/Shift.php Normal file
View File

@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Models\Shifts;
use Carbon\Carbon;
use Engelsystem\Models\BaseModel;
use Engelsystem\Models\Room;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Query\Builder as QueryBuilder;
/**
* @property int $id
* @property string $title
* @property string $description
* @property string $url
* @property Carbon $start
* @property Carbon $end
* @property int $shift_type_id
* @property int $room_id
* @property string $transaction_id
* @property int $created_by
* @property int|null $updated_by
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
*
* @property-read QueryBuilder|Schedule $schedule
* @property-read QueryBuilder|ShiftType $shiftType
* @property-read QueryBuilder|Room $room
* @property-read QueryBuilder|User $createdBy
* @property-read QueryBuilder|User|null $updatedBy
*
* @method static QueryBuilder|Shift[] whereId($value)
* @method static QueryBuilder|Shift[] whereTitle($value)
* @method static QueryBuilder|Shift[] whereDescription($value)
* @method static QueryBuilder|Shift[] whereUrl($value)
* @method static QueryBuilder|Shift[] whereStart($value)
* @method static QueryBuilder|Shift[] whereEnd($value)
* @method static QueryBuilder|Shift[] whereShiftTypeId($value)
* @method static QueryBuilder|Shift[] whereRoomId($value)
* @method static QueryBuilder|Shift[] whereTransactionId($value)
* @method static QueryBuilder|Shift[] whereCreatedBy($value)
* @method static QueryBuilder|Shift[] whereUpdatedBy($value)
* @method static QueryBuilder|Shift[] whereCreatedAt($value)
* @method static QueryBuilder|Shift[] whereUpdatedAt($value)
*/
class Shift extends BaseModel
{
use HasFactory;
/** @var bool enable timestamps */
public $timestamps = true; // phpcs:ignore
/** @var array<string, string> */
protected $casts = [ // phpcs:ignore
'shift_type_id' => 'integer',
'room_id' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
];
/** @var array<string> Values that are mass assignable */
protected $fillable = [ // phpcs:ignore
'title',
'description',
'url',
'start',
'end',
'shift_type_id',
'room_id',
'transaction_id',
'created_by',
'updated_by',
];
/** @var array<string> Values that are DateTimes */
protected $dates = [ // phpcs:ignore
'start',
'end',
];
public function schedule(): HasOneThrough
{
return $this->hasOneThrough(Schedule::class, ScheduleShift::class, null, 'id', null, 'schedule_id');
}
public function shiftType(): BelongsTo
{
return $this->belongsTo(ShiftType::class);
}
public function room(): BelongsTo
{
return $this->belongsTo(Room::class);
}
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
public function updatedBy(): BelongsTo
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@ -16,6 +16,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
* @property string $description * @property string $description
* *
* @property-read Collection|Schedule[] $schedules * @property-read Collection|Schedule[] $schedules
* @property-read Collection|Shift[] $shifts
* *
* @method static QueryBuilder|ShiftType[] whereId($value) * @method static QueryBuilder|ShiftType[] whereId($value)
* @method static QueryBuilder|ShiftType[] whereName($value) * @method static QueryBuilder|ShiftType[] whereName($value)
@ -35,4 +36,9 @@ class ShiftType extends BaseModel
{ {
return $this->hasMany(Schedule::class, 'shift_type'); return $this->hasMany(Schedule::class, 'shift_type');
} }
public function shifts(): HasMany
{
return $this->hasMany(Shift::class);
}
} }

View File

@ -12,6 +12,7 @@ use Engelsystem\Models\NewsComment;
use Engelsystem\Models\OAuth; use Engelsystem\Models\OAuth;
use Engelsystem\Models\Privilege; use Engelsystem\Models\Privilege;
use Engelsystem\Models\Question; use Engelsystem\Models\Question;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Engelsystem\Models\Worklog; use Engelsystem\Models\Worklog;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
@ -53,6 +54,8 @@ use Illuminate\Support\Collection as SupportCollection;
* @property-read Collection|Message[] $messagesReceived * @property-read Collection|Message[] $messagesReceived
* @property-read Collection|Message[] $messagesSent * @property-read Collection|Message[] $messagesSent
* @property-read Collection|Message[] $messages * @property-read Collection|Message[] $messages
* @property-read Collection|Shift[] $shiftsCreated
* @property-read Collection|Shift[] $shiftsUpdated
* *
* @method static QueryBuilder|User[] whereId($value) * @method static QueryBuilder|User[] whereId($value)
* @method static QueryBuilder|User[] whereName($value) * @method static QueryBuilder|User[] whereName($value)
@ -215,9 +218,6 @@ class User extends BaseModel
->orderBy('id', 'DESC'); ->orderBy('id', 'DESC');
} }
/**
* @return HasMany|QueryBuilder
*/
public function messagesReceived(): HasMany public function messagesReceived(): HasMany
{ {
return $this->hasMany(Message::class, 'receiver_id') return $this->hasMany(Message::class, 'receiver_id')
@ -236,4 +236,14 @@ class User extends BaseModel
->orderBy('read') ->orderBy('read')
->orderBy('id', 'DESC'); ->orderBy('id', 'DESC');
} }
public function shiftsCreated(): HasMany
{
return $this->hasMany(Shift::class, 'created_by');
}
public function shiftsUpdated(): HasMany
{
return $this->hasMany(Shift::class, 'updated_by');
}
} }

View File

@ -12,6 +12,7 @@ use Engelsystem\Models\NewsComment;
use Engelsystem\Models\OAuth; use Engelsystem\Models\OAuth;
use Engelsystem\Models\Question; use Engelsystem\Models\Question;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\License; use Engelsystem\Models\User\License;
use Engelsystem\Models\User\PasswordReset; use Engelsystem\Models\User\PasswordReset;
use Engelsystem\Models\User\PersonalData; use Engelsystem\Models\User\PersonalData;
@ -176,6 +177,17 @@ class StatsTest extends TestCase
$this->assertEquals(4, $stats->rooms()); $this->assertEquals(4, $stats->rooms());
} }
/**
* @covers \Engelsystem\Controllers\Metrics\Stats::shifts
*/
public function testShifts(): void
{
Shift::factory(5)->create();
$stats = new Stats($this->database);
$this->assertEquals(5, $stats->shifts());
}
/** /**
* @covers \Engelsystem\Controllers\Metrics\Stats::announcements * @covers \Engelsystem\Controllers\Metrics\Stats::announcements
*/ */

View File

@ -9,6 +9,7 @@ use Engelsystem\Models\NewsComment;
use Engelsystem\Models\Question; use Engelsystem\Models\Question;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Schedule; use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\Contact; use Engelsystem\Models\User\Contact;
use Engelsystem\Models\User\License; use Engelsystem\Models\User\License;
use Engelsystem\Models\User\PasswordReset; use Engelsystem\Models\User\PasswordReset;
@ -23,27 +24,33 @@ class FactoriesTest extends TestCase
{ {
use HasDatabase; use HasDatabase;
/** @var string[] */ /**
protected array $models = [ * @return string[][]
Contact::class, */
Faq::class, public function factoriesProvider(): array
License::class, {
Message::class, return [
News::class, [Contact::class],
NewsComment::class, [Faq::class],
PasswordReset::class, [License::class],
PersonalData::class, [Message::class],
Question::class, [News::class],
Room::class, [NewsComment::class],
Schedule::class, [PasswordReset::class],
Settings::class, [PersonalData::class],
State::class, [Question::class],
User::class, [Room::class],
Worklog::class, [Schedule::class],
[Settings::class],
[Shift::class],
[State::class],
[User::class],
[Worklog::class],
]; ];
}
/** /**
* Test all existing model factories * Test all model factories
* *
* @covers \Database\Factories\Engelsystem\Models\User\ContactFactory * @covers \Database\Factories\Engelsystem\Models\User\ContactFactory
* @covers \Database\Factories\Engelsystem\Models\FaqFactory * @covers \Database\Factories\Engelsystem\Models\FaqFactory
@ -57,19 +64,20 @@ class FactoriesTest extends TestCase
* @covers \Database\Factories\Engelsystem\Models\RoomFactory * @covers \Database\Factories\Engelsystem\Models\RoomFactory
* @covers \Database\Factories\Engelsystem\Models\Shifts\ScheduleFactory * @covers \Database\Factories\Engelsystem\Models\Shifts\ScheduleFactory
* @covers \Database\Factories\Engelsystem\Models\User\SettingsFactory * @covers \Database\Factories\Engelsystem\Models\User\SettingsFactory
* @covers \Database\Factories\Engelsystem\Models\Shifts\ShiftFactory
* @covers \Database\Factories\Engelsystem\Models\User\StateFactory * @covers \Database\Factories\Engelsystem\Models\User\StateFactory
* @covers \Database\Factories\Engelsystem\Models\User\UserFactory * @covers \Database\Factories\Engelsystem\Models\User\UserFactory
* @covers \Database\Factories\Engelsystem\Models\WorklogFactory * @covers \Database\Factories\Engelsystem\Models\WorklogFactory
*
* @dataProvider factoriesProvider
*/ */
public function testFactories(): void public function testFactories(string $model): void
{ {
$this->initDatabase(); $this->initDatabase();
foreach ($this->models as $model) {
$instance = (new $model())->factory()->create(); $instance = (new $model())->factory()->create();
$this->assertInstanceOf(Model::class, $instance); $this->assertInstanceOf(Model::class, $instance);
} }
}
/** /**
* @covers \Database\Factories\Engelsystem\Models\User\StateFactory * @covers \Database\Factories\Engelsystem\Models\User\StateFactory

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Test\Unit\Models;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
class RoomTest extends ModelTest
{
/**
* @covers \Engelsystem\Models\Room::shifts
*/
public function testShifts(): void
{
$room = new Room(['name' => 'Test room']);
$room->save();
/** @var Shift $shift */
Shift::factory()->create(['room_id' => 1]);
$room = Room::find(1);
$this->assertCount(1, $room->shifts);
}
}

View File

@ -4,6 +4,7 @@ namespace Engelsystem\Test\Unit\Models\Shifts;
use Engelsystem\Models\Shifts\Schedule; use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Test\Unit\Models\ModelTest; use Engelsystem\Test\Unit\Models\ModelTest;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -11,6 +12,7 @@ class ScheduleShiftTest extends ModelTest
{ {
/** /**
* @covers \Engelsystem\Models\Shifts\ScheduleShift::schedule * @covers \Engelsystem\Models\Shifts\ScheduleShift::schedule
* @covers \Engelsystem\Models\Shifts\ScheduleShift::shift
*/ */
public function testScheduleShifts(): void public function testScheduleShifts(): void
{ {
@ -22,14 +24,19 @@ class ScheduleShiftTest extends ModelTest
'minutes_after' => 15, 'minutes_after' => 15,
]); ]);
$schedule->save(); $schedule->save();
/** @var Shift $shift */
$shift = Shift::factory()->create();
$scheduleShift = new ScheduleShift(['shift_id' => 1, 'guid' => 'a']); $scheduleShift = new ScheduleShift(['guid' => 'a']);
$scheduleShift->schedule()->associate($schedule); $scheduleShift->schedule()->associate($schedule);
$scheduleShift->shift()->associate($shift);
$scheduleShift->save(); $scheduleShift->save();
/** @var ScheduleShift $scheduleShift */ /** @var ScheduleShift $scheduleShift */
$scheduleShift = (new ScheduleShift())->find(1); $scheduleShift = (new ScheduleShift())->find(1);
$this->assertInstanceOf(BelongsTo::class, $scheduleShift->schedule()); $this->assertInstanceOf(BelongsTo::class, $scheduleShift->schedule());
$this->assertEquals($schedule->id, $scheduleShift->schedule->id); $this->assertEquals($schedule->id, $scheduleShift->schedule->id);
$this->assertInstanceOf(BelongsTo::class, $scheduleShift->shift());
$this->assertEquals($shift->id, $scheduleShift->shift->id);
} }
} }

View File

@ -4,8 +4,10 @@ namespace Engelsystem\Test\Unit\Models\Shifts;
use Engelsystem\Models\Shifts\Schedule; use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Test\Unit\Models\ModelTest; use Engelsystem\Test\Unit\Models\ModelTest;
use Illuminate\Database\Eloquent\Collection;
class ScheduleTest extends ModelTest class ScheduleTest extends ModelTest
{ {
@ -32,6 +34,24 @@ class ScheduleTest extends ModelTest
$this->assertCount(3, $schedule->scheduleShifts); $this->assertCount(3, $schedule->scheduleShifts);
} }
/**
* @covers \Engelsystem\Models\Shifts\Schedule::shifts
*/
public function testShifts(): void
{
$schedule = new Schedule($this->data);
$schedule->save();
/** @var Collection|Shift[] $shifts */
$shifts = Shift::factory(3)->create();
(new ScheduleShift(['shift_id' => $shifts[0]->id, 'schedule_id' => $schedule->id, 'guid' => 'a']))->save();
(new ScheduleShift(['shift_id' => $shifts[1]->id, 'schedule_id' => $schedule->id, 'guid' => 'b']))->save();
(new ScheduleShift(['shift_id' => $shifts[2]->id, 'schedule_id' => $schedule->id, 'guid' => 'c']))->save();
$this->assertCount(3, $schedule->shifts);
}
/** /**
* @covers \Engelsystem\Models\Shifts\Schedule::shiftType * @covers \Engelsystem\Models\Shifts\Schedule::shiftType
*/ */

View File

@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Test\Unit\Models\Shifts;
use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User;
use Engelsystem\Test\Unit\Models\ModelTest;
use Illuminate\Database\Eloquent\Collection;
class ShiftTest extends ModelTest
{
/**
* @covers \Engelsystem\Models\Shifts\Shift::shiftType
* @covers \Engelsystem\Models\Shifts\Shift::room
* @covers \Engelsystem\Models\Shifts\Shift::createdBy
* @covers \Engelsystem\Models\Shifts\Shift::updatedBy
*/
public function testShiftType(): void
{
/** @var User $user1 */
$user1 = User::factory()->create();
/** @var User $user2 */
$user2 = User::factory()->create();
/** @var ShiftType $shiftType */
$shiftType = ShiftType::factory()->create();
/** @var Room $room */
$room = Room::factory()->create();
$model = new Shift([
'title' => 'Test shift',
'description' => 'Some description',
'url' => 'https://foo.bar/map',
'start' => Carbon::now(),
'end' => Carbon::now(),
'shift_type_id' => $shiftType->id,
'room_id' => $room->id,
'transaction_id' => '',
'created_by' => $user1->id,
'updated_by' => $user2->id,
]);
$model->save();
$model = Shift::find(1);
$this->assertEquals($shiftType->id, $model->shiftType->id);
$this->assertEquals($room->id, $model->room->id);
$this->assertEquals($user1->id, $model->createdBy->id);
$this->assertEquals($user2->id, $model->updatedBy->id);
}
/**
* @covers \Engelsystem\Models\Shifts\Shift::schedule
*/
public function testSchedule(): void
{
/** @var Schedule $schedule */
$schedule = Schedule::factory()->create();
/** @var Collection|Shift[] $shifts */
$shifts = Shift::factory(3)->create();
(new ScheduleShift(['shift_id' => $shifts[0]->id, 'schedule_id' => $schedule->id, 'guid' => 'a']))->save();
(new ScheduleShift(['shift_id' => $shifts[1]->id, 'schedule_id' => $schedule->id, 'guid' => 'b']))->save();
(new ScheduleShift(['shift_id' => $shifts[2]->id, 'schedule_id' => $schedule->id, 'guid' => 'c']))->save();
$this->assertEquals(1, Shift::find(1)->schedule->id);
$this->assertEquals(1, Shift::find(2)->schedule->id);
$this->assertEquals(1, Shift::find(3)->schedule->id);
}
}

View File

@ -3,6 +3,7 @@
namespace Engelsystem\Test\Unit\Models\Shifts; namespace Engelsystem\Test\Unit\Models\Shifts;
use Engelsystem\Models\Shifts\Schedule; use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Test\Unit\Models\ModelTest; use Engelsystem\Test\Unit\Models\ModelTest;
@ -21,4 +22,17 @@ class ShiftTypeTest extends ModelTest
$this->assertCount(2, ShiftType::find(1)->schedules); $this->assertCount(2, ShiftType::find(1)->schedules);
} }
/**
* @covers \Engelsystem\Models\Shifts\ShiftType::shifts
*/
public function testShifts(): void
{
$shiftType = new ShiftType(['name' => 'Another type', 'description' => '']);
$shiftType->save();
Shift::factory()->create(['shift_type_id' => 1]);
$this->assertCount(1, ShiftType::find(1)->shifts);
}
} }

View File

@ -12,6 +12,7 @@ use Engelsystem\Models\NewsComment;
use Engelsystem\Models\OAuth; use Engelsystem\Models\OAuth;
use Engelsystem\Models\Privilege; use Engelsystem\Models\Privilege;
use Engelsystem\Models\Question; use Engelsystem\Models\Question;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\Contact; use Engelsystem\Models\User\Contact;
use Engelsystem\Models\User\HasUserModel; use Engelsystem\Models\User\HasUserModel;
use Engelsystem\Models\User\License; use Engelsystem\Models\User\License;
@ -103,8 +104,8 @@ class UserTest extends ModelTest
'text' => 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.', 'text' => 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.',
'is_meeting' => true, 'is_meeting' => true,
], ],
] ],
] ],
]; ];
} }
@ -415,4 +416,29 @@ class UserTest extends ModelTest
$this->assertContains($question1->id, $answers); $this->assertContains($question1->id, $answers);
$this->assertContains($question2->id, $answers); $this->assertContains($question2->id, $answers);
} }
/**
* @covers \Engelsystem\Models\User\User::shiftsCreated
* @covers \Engelsystem\Models\User\User::shiftsUpdated
*/
public function testShiftsCreatedAndUpdated(): void
{
($user1 = new User($this->data))->save();
($user2 = new User(array_merge($this->data, ['name' => 'foo', 'email' => 'someone@bar.batz'])))->save();
Shift::factory()->create();
Shift::factory()->create(['created_by' => $user1->id]);
Shift::factory()->create(['created_by' => $user2->id, 'updated_by' => $user1->id]);
Shift::factory()->create(['created_by' => $user1->id, 'updated_by' => $user2->id]);
Shift::factory()->create(['created_by' => $user2->id, 'updated_by' => $user2->id]);
Shift::factory()->create(['created_by' => $user1->id]);
$user1 = User::find(1);
$this->assertCount(3, $user1->shiftsCreated);
$this->assertCount(1, $user1->shiftsUpdated);
$user2 = User::find(2);
$this->assertCount(2, $user2->shiftsCreated);
$this->assertCount(2, $user2->shiftsUpdated);
}
} }