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
use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\UserAngelType;
use Engelsystem\ShiftsFilter;
@ -237,7 +238,7 @@ function angeltype_controller_shiftsFilterDays(AngelType $angeltype)
$all_shifts = Shifts_by_angeltype($angeltype);
$days = [];
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)) {
$days[] = $day;
}

View File

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

View File

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

View File

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

View File

@ -1,44 +1,45 @@
<?php
use Engelsystem\Helpers\Carbon;
use Carbon\CarbonTimeZone;
use Engelsystem\Http\Exceptions\HttpForbidden;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User;
use Engelsystem\ShiftSignupState;
use Illuminate\Support\Collection;
/**
* @param array $shift
* @param array|Shift $shift
* @return string
*/
function shift_link($shift)
{
$parameters = ['action' => 'view'];
if (isset($shift['SID'])) {
$parameters['shift_id'] = $shift['SID'];
if (isset($shift['shift_id']) || isset($shift['id'])) {
$parameters['shift_id'] = $shift['shift_id'] ?? $shift['id'];
}
return page_link_to('shifts', $parameters);
}
/**
* @param array $shift
* @param Shift $shift
* @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
*/
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 = Shift($shift_id);
if (ScheduleShift::whereShiftId($shift['SID'])->first()) {
$shift = Shift(Shift::findOrFail($shift_id));
if (ScheduleShift::whereShiftId($shift->id)->first()) {
warning(__(
'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'];
$title = $shift['title'];
$description = $shift['description'];
$rid = $shift['RID'];
$start = $shift['start'];
$end = $shift['end'];
$shifttype_id = $shift->shift_type_id;
$title = $shift->title;
$description = $shift->description;
$rid = $shift->room_id;
$start = $shift->start;
$end = $shift->end;
if ($request->hasPostData('submit')) {
// Name/Bezeichnung der Schicht, darf leer sein
@ -102,33 +103,33 @@ function shift_edit_controller()
$rid = $request->input('rid');
} else {
$valid = false;
error(__('Please select a room.'), true);
error(__('Please select a room.'));
}
if ($request->has('shifttype_id') && isset($shifttypes[$request->input('shifttype_id')])) {
$shifttype_id = $request->input('shifttype_id');
} else {
$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;
} else {
$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;
} else {
$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) {
$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) {
@ -143,20 +144,28 @@ function shift_edit_controller()
error(sprintf(
__('Please check your input for needed angels of type %s.'),
$angeltypes[$needed_angeltype_id]
), true);
));
}
}
}
if ($valid) {
$shift['shifttype_id'] = $shifttype_id;
$shift['title'] = $title;
$shift['description'] = $description;
$shift['RID'] = $rid;
$shift['start'] = $start;
$shift['end'] = $end;
$shift->shift_type_id = $shifttype_id;
$shift->title = $title;
$shift->description = $description;
$shift->room_id = $rid;
$shift->start = $start;
$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);
$needed_angel_types_info = [];
foreach ($needed_angel_types as $type_id => $count) {
@ -168,16 +177,14 @@ function shift_edit_controller()
engelsystem_log(
'Updated shift \'' . $shifttypes[$shifttype_id] . ', ' . $title
. '\' from ' . date('Y-m-d H:i', $start)
. ' to ' . date('Y-m-d H:i', $end)
. '\' from ' . $start->format('Y-m-d H:i')
. ' to ' . $end->format('Y-m-d H:i')
. ' with angel types ' . join(', ', $needed_angel_types_info)
. ' and description ' . $description
);
success(__('Shift updated.'));
throw_redirect(shift_link([
'SID' => $shift_id
]));
throw_redirect(shift_link($shift));
}
}
@ -201,8 +208,8 @@ function shift_edit_controller()
form_select('shifttype_id', __('Shifttype'), $shifttypes, $shifttype_id),
form_text('title', __('Title'), $title),
form_select('rid', __('Room:'), $rooms, $rid),
form_text('start', __('Start:'), date('Y-m-d H:i', $start)),
form_text('end', __('End:'), date('Y-m-d H:i', $end)),
form_text('start', __('Start:'), $start->format('Y-m-d H:i')),
form_text('end', __('End:'), $end->format('Y-m-d H:i')),
form_textarea('description', __('Additional description'), $description),
form_info('', __('This description is for single shifts, otherwise please use the description in shift type.')),
'<h2>' . __('Needed angels') . '</h2>',
@ -237,27 +244,26 @@ function shift_delete_controller()
// Schicht löschen bestätigt
if ($request->hasPostData('delete')) {
$room = Room::find($shift['RID']);
foreach ($shift['ShiftEntry'] as $entry) {
foreach ($shift->shiftEntry as $entry) {
$type = AngelType::find($entry['TID']);
event('shift.entry.deleting', [
'user' => User::find($entry['user_id']),
'start' => Carbon::createFromTimestamp($shift['start']),
'end' => Carbon::createFromTimestamp($shift['end']),
'name' => $shift['name'],
'title' => $shift['title'],
'start' => $shift->start,
'end' => $shift->end,
'name' => $shift->shiftType->name,
'title' => $shift->title,
'type' => $type->name,
'room' => $room,
'room' => $shift->room,
'freeloaded' => (bool) $entry['freeloaded'],
]);
}
Shift_delete($shift_id);
$shift->delete();
engelsystem_log(
'Deleted shift ' . $shift['name']
. ' from ' . date('Y-m-d H:i', $shift['start'])
. ' to ' . date('Y-m-d H:i', $shift['end'])
'Deleted shift ' . $shift->title . ': ' . $shift->shiftType->name
. ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . $shift->end->format('Y-m-d H:i')
);
success(__('Shift deleted.'));
throw_redirect(page_link_to('user_shifts'));
@ -266,12 +272,12 @@ function shift_delete_controller()
return page_with_title(shifts_title(), [
error(sprintf(
__('Do you want to delete the shift %s from %s to %s?'),
$shift['name'],
date('Y-m-d H:i', $shift['start']),
date('H:i', $shift['end'])
$shift->shiftType->name,
$shift->start->format('Y-m-d H:i'),
$shift->end->format('H:i')
), true),
form([
form_hidden('delete_shift', $shift_id),
form_hidden('delete_shift', $shift->id),
form_submit('delete', __('delete')),
]),
]);
@ -299,8 +305,9 @@ function shift_controller()
throw_redirect(page_link_to('user_shifts'));
}
$shifttype = ShiftType::find($shift['shifttype_id']);
$room = Room::find($shift['RID']);
$shifttype = $shift->shiftType;
$room = $shift->room;
/** @var AngelType[] $angeltypes */
$angeltypes = AngelType::all();
$user_shifts = Shifts_by_user($user->id);
@ -311,7 +318,7 @@ function shift_controller()
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);
$angeltype_signup_state = Shift_signup_allowed(
@ -328,7 +335,7 @@ function shift_controller()
}
return [
$shift['name'],
$shift->shiftType->name,
Shift_view($shift, $shifttype, $room, $angeltypes, $shift_signup_state)
];
}
@ -345,7 +352,7 @@ function shifts_controller()
return match ($request->input('action')) {
'view' => shift_controller(),
'next' => shift_next_controller(), // throw_redirect
'next' => shift_next_controller(), // throws redirect
default => throw_redirect(page_link_to('/')),
};
}
@ -359,7 +366,7 @@ function shift_next_controller()
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)) {
throw_redirect(shift_link($upcoming_shifts[0]));
@ -390,19 +397,74 @@ function shifts_json_export_controller()
}
$shifts = load_ical_shifts();
foreach ($shifts as $row => $shift) {
$shifts[$row]['start_date'] = Carbon::createFromTimestamp($shift['start'])->toRfc3339String();
$shifts[$row]['end_date'] = Carbon::createFromTimestamp($shift['end'])->toRfc3339String();
$shifts->sortBy('start_date');
$timeZone = CarbonTimeZone::create(config('timezone'));
$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');
raw_output(json_encode($shifts));
raw_output(json_encode($shiftsData));
}
/**
* Returns users shifts to export.
*
* @return array
* @return Shift[]|Collection
*/
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'));
foreach ($shifts as &$shift) {
foreach ($shifts as $shift) {
// TODO: Move queries to model
$shift['needed_angeltypes'] = Db::select(
$shift->needed_angeltypes = Db::select(
'
SELECT DISTINCT `angel_types`.*
FROM `ShiftEntry`
@ -214,9 +214,10 @@ function user_controller()
WHERE `ShiftEntry`.`SID` = ?
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(
'
SELECT `ShiftEntry`.`freeloaded`, `users`.*
@ -225,9 +226,10 @@ function user_controller()
WHERE `ShiftEntry`.`SID` = ?
AND `ShiftEntry`.`TID` = ?
',
[$shift['SID'], $needed_angeltype['id']]
[$shift->id, $needed_angeltype['id']]
);
}
$shift->needed_angeltypes = $neededAngeltypes;
}
if (empty($user_source->api_key)) {
@ -360,8 +362,8 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
$needed_angeltypes = [];
$shift_entries = [];
foreach ($shifts as $shift) {
$needed_angeltypes[$shift['SID']] = [];
$shift_entries[$shift['SID']] = [];
$needed_angeltypes[$shift->id] = [];
$shift_entries[$shift->id] = [];
}
foreach ($shift_entries_source as $shift_entry) {
@ -371,8 +373,8 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
}
foreach ($needed_angeltypes_source as $needed_angeltype) {
if (isset($needed_angeltypes[$needed_angeltype['SID']])) {
$needed_angeltypes[$needed_angeltype['SID']][] = $needed_angeltype;
if (isset($needed_angeltypes[$needed_angeltype['shift_id']])) {
$needed_angeltypes[$needed_angeltype['shift_id']][] = $needed_angeltype;
}
}
@ -389,7 +391,7 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
$filtered_shifts = [];
foreach ($shifts as $shift) {
$needed_angels_count = 0;
foreach ($needed_angeltypes[$shift['SID']] as $needed_angeltype) {
foreach ($needed_angeltypes[$shift->id] as $needed_angeltype) {
$taken = 0;
if (
@ -399,7 +401,7 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
continue;
}
foreach ($shift_entries[$shift['SID']] as $shift_entry) {
foreach ($shift_entries[$shift->id] as $shift_entry) {
if (
$needed_angeltype['angel_type_id'] == $shift_entry['TID']
&& $shift_entry['freeloaded'] == 0

View File

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

View File

@ -1,52 +1,53 @@
<?php
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User;
/**
* @param array $old_shift
* @param array $new_shift
*/
function mail_shift_change($old_shift, $new_shift)
function mail_shift_change(Shift $old_shift, Shift $new_shift)
{
$users = ShiftEntries_by_shift($old_shift['SID']);
$old_room = Room::find($old_shift['RID']);
$new_room = Room::find($new_shift['RID']);
$users = ShiftEntries_by_shift($old_shift->id);
$old_room = $old_shift->room;
$new_room = $new_shift->room;
$noticeable_changes = false;
$message = __('A Shift you are registered on has changed:');
$message .= "\n";
if ($old_shift['name'] != $new_shift['name']) {
$message .= sprintf(__('* Shift type changed from %s to %s'), $old_shift['name'], $new_shift['name']) . "\n";
if ($old_shift->shift_type_id != $new_shift->shift_type_id) {
$message .= sprintf(
__('* Shift type changed from %s to %s'),
$old_shift->shiftType->name,
$new_shift->shiftType->name
) . "\n";
$noticeable_changes = true;
}
if ($old_shift['title'] != $new_shift['title']) {
$message .= sprintf(__('* Shift title changed from %s to %s'), $old_shift['title'], $new_shift['title']) . "\n";
if ($old_shift->title != $new_shift->title) {
$message .= sprintf(__('* Shift title changed from %s to %s'), $old_shift->title, $new_shift->title) . "\n";
$noticeable_changes = true;
}
if ($old_shift['start'] != $new_shift['start']) {
if ($old_shift->start->timestamp != $new_shift->start->timestamp) {
$message .= sprintf(
__('* Shift Start changed from %s to %s'),
date('Y-m-d H:i', $old_shift['start']),
date('Y-m-d H:i', $new_shift['start'])
$old_shift->start->format('Y-m-d H:i'),
$new_shift->start->format('Y-m-d H:i')
) . "\n";
$noticeable_changes = true;
}
if ($old_shift['end'] != $new_shift['end']) {
if ($old_shift->end->timestamp != $new_shift->end->timestamp) {
$message .= sprintf(
__('* Shift End changed from %s to %s'),
date('Y-m-d H:i', $old_shift['end']),
date('Y-m-d H:i', $new_shift['end'])
$old_shift->end->format('Y-m-d H:i'),
$new_shift->end->format('Y-m-d H:i')
) . "\n";
$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";
$noticeable_changes = true;
}
@ -59,11 +60,11 @@ function mail_shift_change($old_shift, $new_shift)
$message .= "\n";
$message .= __('The updated Shift:') . "\n";
$message .= $new_shift['name'] . "\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->shiftType->name . "\n";
$message .= $new_shift->title . "\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 .= 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) {
$user = (new User())->forceFill($user);
@ -78,44 +79,36 @@ function mail_shift_change($old_shift, $new_shift)
}
}
/**
* @param User $user
* @param array $shift
*/
function mail_shift_assign($user, $shift)
function mail_shift_assign(User $user, Shift $shift)
{
if (!$user->settings->email_shiftinfo) {
return;
}
$room = Room::find($shift['RID']);
$room = $shift->room;
$message = __('You have been assigned to a Shift:') . "\n";
$message .= $shift['name'] . "\n";
$message .= $shift['title'] . "\n";
$message .= date('Y-m-d H:i', $shift['start']) . ' - ' . date('H:i', $shift['end']) . "\n";
$message .= $shift->shiftType->name . "\n";
$message .= $shift->title . "\n";
$message .= $shift->start->format('Y-m-d H:i') . ' - ' . $shift->end->format('H:i') . "\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);
}
/**
* @param User $user
* @param array $shift
*/
function mail_shift_removed($user, $shift)
function mail_shift_removed(User $user, Shift $shift)
{
if (!$user->settings->email_shiftinfo) {
return;
}
$room = Room::find($shift['RID']);
$room = $shift->room;
$message = __('You have been removed from a Shift:') . "\n";
$message .= $shift['name'] . "\n";
$message .= $shift['title'] . "\n";
$message .= date('Y-m-d H:i', $shift['start']) . ' - ' . date('H:i', $shift['end']) . "\n";
$message .= $shift->shiftType->name . "\n";
$message .= $shift->title . "\n";
$message .= $shift->start->format('Y-m-d H:i') . ' - ' . $shift->end->format('H:i') . "\n";
$message .= $room->name . "\n";
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`
FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id` = `NeededAngelTypes`.`angel_type_id`
JOIN `Shifts` ON `Shifts`.`RID` = `NeededAngelTypes`.`room_id`
WHERE `Shifts`.`SID` = ?
JOIN `shifts` ON `shifts`.`room_id` = `NeededAngelTypes`.`room_id`
WHERE `shifts`.`id` = ?
AND `count` > 0
ORDER BY `room_id` DESC
', [$shiftId]);

View File

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

View File

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

View File

@ -1,13 +1,14 @@
<?php
use Engelsystem\Database\Db;
use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType;
use Engelsystem\ShiftsFilter;
use Engelsystem\ShiftSignupState;
use Illuminate\Support\Collection;
/**
* @param AngelType $angeltype
@ -16,18 +17,18 @@ use Engelsystem\ShiftSignupState;
function Shifts_by_angeltype(AngelType $angeltype)
{
return Db::select('
SELECT DISTINCT `Shifts`.* FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `Shifts`.`SID`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
SELECT DISTINCT `shifts`.* FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `shifts`.`id`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `NeededAngelTypes`.`angel_type_id` = ?
AND `NeededAngelTypes`.`count` > 0
AND s.shift_id IS NULL
UNION
SELECT DISTINCT `Shifts`.* FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id` = `Shifts`.`RID`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
SELECT DISTINCT `shifts`.* FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id` = `shifts`.`room_id`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `NeededAngelTypes`.`angel_type_id` = ?
AND `NeededAngelTypes`.`count` > 0
AND NOT s.shift_id IS NULL
@ -41,31 +42,35 @@ function Shifts_by_angeltype(AngelType $angeltype)
* @param int $end timestamp
* @param ShiftsFilter|null $filter
*
* @return array
* @return Collection|Shift[]
*/
function Shifts_free($start, $end, ShiftsFilter $filter = null)
{
$start = Carbon::createFromTimestamp($start);
$end = Carbon::createFromTimestamp($end);
$shifts = Db::select('
SELECT * FROM (
SELECT *
FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
FROM (
SELECT id, start
FROM `shifts`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
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()) . ')' : '') . ')
> (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0' . ($filter ? ' AND ShiftEntry.TID 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`.`id` AND `freeloaded`=0' . ($filter ? ' AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
AND s.shift_id IS NULL
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
UNION
SELECT *
FROM `Shifts`
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` < ?)
AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`Shifts`.`RID`' . ($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()) . ')' : '') . ')
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`.`id` AND `freeloaded`=0' . ($filter ? ' AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
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`
ORDER BY `tmp`.`start`
', [
@ -74,40 +79,29 @@ function Shifts_free($start, $end, ShiftsFilter $filter = null)
$start,
$end
]);
$free_shifts = [];
foreach ($shifts as $shift) {
$free_shifts[] = Shift($shift['SID']);
}
return $free_shifts;
}
/**
* @param Room $room
* @return array[]
*/
function Shifts_by_room(Room $room)
{
return Db::select(
'SELECT * FROM `Shifts` WHERE `RID`=? ORDER BY `start`',
[$room->id]
);
$shifts = collect($shifts);
return Shift::query()
->whereIn('id', $shifts->pluck('id')->toArray())
->get();
}
/**
* @param ShiftsFilter $shiftsFilter
* @return array[]
* @return Shift[]|Collection
*/
function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
{
$sql = '
SELECT * FROM (
SELECT DISTINCT `Shifts`.*, `shift_types`.`name`, `rooms`.`name` AS `room_name`
FROM `Shifts`
JOIN `rooms` ON `Shifts`.`RID` = `rooms`.`id`
JOIN `shift_types` ON `shift_types`.`id` = `Shifts`.`shifttype_id`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `Shifts`.`SID`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
SELECT DISTINCT `shifts`.*, `shift_types`.`name`, `rooms`.`name` AS `room_name`
FROM `shifts`
JOIN `rooms` ON `shifts`.`room_id` = `rooms`.`id`
JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `shifts`.`id`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ?
AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ')
AND `NeededAngelTypes`.`count` > 0
@ -115,13 +109,13 @@ function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
UNION
SELECT DISTINCT `Shifts`.*, `shift_types`.`name`, `rooms`.`name` AS `room_name`
FROM `Shifts`
JOIN `rooms` ON `Shifts`.`RID` = `rooms`.`id`
JOIN `shift_types` ON `shift_types`.`id` = `Shifts`.`shifttype_id`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
SELECT DISTINCT `shifts`.*, `shift_types`.`name`, `rooms`.`name` AS `room_name`
FROM `shifts`
JOIN `rooms` ON `shifts`.`room_id` = `rooms`.`id`
JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ?
AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ')
AND `NeededAngelTypes`.`count` > 0
@ -131,15 +125,22 @@ function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
ORDER BY `room_name`, `start`
';
return Db::select(
$shiftsData = Db::select(
$sql,
[
$shiftsFilter->getStartTime(),
$shiftsFilter->getEndTime(),
$shiftsFilter->getStartTime(),
$shiftsFilter->getEndTime(),
$shiftsFilter->getStart(),
$shiftsFilter->getEnd(),
$shiftsFilter->getStart(),
$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 = '
SELECT
`NeededAngelTypes`.*,
`Shifts`.`SID`,
`shifts`.`id` AS shift_id,
`angel_types`.`id`,
`angel_types`.`name`,
`angel_types`.`restricted`,
`angel_types`.`no_self_signup`
FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`
FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`shifts`.`id`
JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ?
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND shifts.`start` BETWEEN ? AND ?
AND s.shift_id IS NULL
UNION
SELECT
`NeededAngelTypes`.*,
`Shifts`.`SID`,
`shifts`.`id` AS shift_id,
`angel_types`.`id`,
`angel_types`.`name`,
`angel_types`.`restricted`,
`angel_types`.`no_self_signup`
FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID`
FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ?
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND shifts.`start` BETWEEN ? AND ?
AND NOT s.shift_id IS NULL
';
return Db::select(
$sql,
[
$shiftsFilter->getStartTime(),
$shiftsFilter->getEndTime(),
$shiftsFilter->getStartTime(),
$shiftsFilter->getEndTime(),
$shiftsFilter->getStart(),
$shiftsFilter->getEnd(),
$shiftsFilter->getStart(),
$shiftsFilter->getEnd(),
]
);
}
/**
* @param array $shift
* @param Shift $shift
* @param AngelType $angeltype
* @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(
'
SELECT
`NeededAngelTypes`.*,
`Shifts`.`SID`,
`shifts`.`id` AS shift_id,
`angel_types`.`id`,
`angel_types`.`name`,
`angel_types`.`restricted`,
`angel_types`.`no_self_signup`
FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`
FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`shifts`.`id`
JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`SID`=?
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `shifts`.`id`=?
AND `angel_types`.`id`=?
AND s.shift_id IS NULL
@ -221,23 +222,23 @@ function NeededAngeltype_by_Shift_and_Angeltype($shift, AngelType $angeltype)
SELECT
`NeededAngelTypes`.*,
`Shifts`.`SID`,
`shifts`.`id` AS shift_id,
`angel_types`.`id`,
`angel_types`.`name`,
`angel_types`.`restricted`,
`angel_types`.`no_self_signup`
FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID`
FROM `shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`SID`=?
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `shifts`.`id`=?
AND `angel_types`.`id`=?
AND NOT s.shift_id IS NULL
',
[
$shift['SID'],
$shift->id,
$angeltype->id,
$shift['SID'],
$shift->id,
$angeltype->id
]
);
@ -258,20 +259,20 @@ function ShiftEntries_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
`ShiftEntry`.`SID`,
`ShiftEntry`.`Comment`,
`ShiftEntry`.`freeloaded`
FROM `Shifts`
JOIN `ShiftEntry` ON `ShiftEntry`.`SID`=`Shifts`.`SID`
FROM `shifts`
JOIN `ShiftEntry` ON `ShiftEntry`.`SID`=`shifts`.`id`
JOIN `users` ON `ShiftEntry`.`UID`=`users`.`id`
WHERE `Shifts`.`RID` IN (%s)
WHERE `shifts`.`room_id` IN (%s)
AND `start` BETWEEN ? AND ?
ORDER BY `Shifts`.`start`
ORDER BY `shifts`.`start`
',
implode(',', $shiftsFilter->getRooms())
);
return Db::select(
$sql,
[
$shiftsFilter->getStartTime(),
$shiftsFilter->getEndTime(),
$shiftsFilter->getStart(),
$shiftsFilter->getEnd(),
]
);
}
@ -279,15 +280,20 @@ function ShiftEntries_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
/**
* Check if a shift collides with other shifts (in time).
*
* @param array $shift
* @param array $shifts
* @param Shift $shift
* @param Shift[]|Collection $shifts
* @return bool
*/
function Shift_collides($shift, $shifts)
function Shift_collides(Shift $shift, $shifts)
{
foreach ($shifts as $other_shift) {
if ($shift['SID'] != $other_shift['SID']) {
if (!($shift['start'] >= $other_shift['end'] || $shift['end'] <= $other_shift['start'])) {
if ($shift->id != $other_shift->id) {
if (
!(
$shift->start->timestamp >= $other_shift->end->timestamp
|| $shift->end->timestamp <= $other_shift->start->timestamp
)
) {
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)
*
* @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 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 array[] $shift_entries
* @return ShiftSignupState
*/
function Shift_signup_allowed_angel(
$user,
$shift,
Shift $shift,
AngelType $angeltype,
$user_angeltype,
$user_shifts,
@ -342,7 +348,7 @@ function Shift_signup_allowed_angel(
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);
}
@ -352,7 +358,7 @@ function Shift_signup_allowed_angel(
$signed_up = false;
foreach ($user_shifts as $user_shift) {
if ($user_shift['SID'] == $shift['SID']) {
if ($user_shift->id == $shift->id) {
$signed_up = true;
break;
}
@ -364,10 +370,10 @@ function Shift_signup_allowed_angel(
}
$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);
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
return new ShiftSignupState(ShiftSignupState::SHIFT_ENDED, $free_entries);
}
@ -393,7 +399,7 @@ function Shift_signup_allowed_angel(
}
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);
}
@ -440,12 +446,12 @@ function Shift_signup_allowed_admin(AngelType $needed_angeltype, $shift_entries)
/**
* 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 int $signout_user_id The user that was signed up for the shift
* @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();
@ -462,7 +468,7 @@ function Shift_signout_allowed($shift, AngelType $angeltype, $signout_user_id)
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;
}
@ -473,17 +479,17 @@ function Shift_signout_allowed($shift, AngelType $angeltype, $signout_user_id)
* Check if an angel can sign up for given shift.
*
* @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 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 array[] $shift_entries
* @return ShiftSignupState
*/
function Shift_signup_allowed(
$signup_user,
$shift,
Shift $shift,
AngelType $angeltype,
$user_angeltype,
$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.
*
* @param int $userId
* @param bool $include_freeload_comments
* @return array[]
* @return Collection|Shift[]
*/
function Shifts_by_user($userId, $include_freeload_comments = false)
{
return Db::select(
$shiftsData = Db::select(
'
SELECT
`rooms`.*,
`rooms`.name AS Name,
`shift_types`.`id` AS `shifttype_id`,
`shift_types`.`name`,
`ShiftEntry`.`id`,
`ShiftEntry`.`id` as shift_entry_id,
`ShiftEntry`.`SID`,
`ShiftEntry`.`TID`,
`ShiftEntry`.`UID`,
`ShiftEntry`.`freeloaded`,
`ShiftEntry`.`Comment`,
' . ($include_freeload_comments ? '`ShiftEntry`.`freeload_comment`, ' : '') . '
`Shifts`.*,
@@session.time_zone AS timezone,
? AS event_timezone
`shifts`.*
FROM `ShiftEntry`
JOIN `Shifts` ON (`ShiftEntry`.`SID` = `Shifts`.`SID`)
JOIN `shift_types` ON (`shift_types`.`id` = `Shifts`.`shifttype_id`)
JOIN `rooms` ON (`Shifts`.`RID` = `rooms`.`id`)
WHERE `UID` = ?
JOIN `shifts` ON (`ShiftEntry`.`SID` = `shifts`.`id`)
JOIN `shift_types` ON (`shift_types`.`id` = `shifts`.`shift_type_id`)
JOIN `rooms` ON (`shifts`.`room_id` = `rooms`.`id`)
WHERE ShiftEntry.`UID` = ?
ORDER BY `start`
',
[
config('timezone'),
$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
* @return array|null
* @param int|Shift $shift Shift ID or shift model
* @return Shift|null
*/
function Shift($shift_id)
function Shift($shift)
{
$result = Db::selectOne('
SELECT `Shifts`.*, `shift_types`.`name`
FROM `Shifts`
JOIN `shift_types` ON (`shift_types`.`id` = `Shifts`.`shifttype_id`)
WHERE `SID`=?', [$shift_id]);
if (!$shift instanceof Shift) {
$shift = Shift::find($shift);
}
if (empty($result)) {
if (!$shift) {
return null;
}
$shiftsEntry_source = Db::select('
$shift->shiftEntry = Db::select('
SELECT
`ShiftEntry`.`id`, `ShiftEntry`.`TID` , `ShiftEntry`.`UID` , `ShiftEntry`.`freeloaded`,
`users`.`name` AS `username`, `users`.`id` AS `user_id`
FROM `ShiftEntry`
LEFT JOIN `users` ON (`users`.`id` = `ShiftEntry`.`UID`)
WHERE `SID`=?', [$shift_id]);
WHERE `SID`=?', [$shift->id]);
$result['ShiftEntry'] = $shiftsEntry_source;
$result['NeedAngels'] = [];
$angelTypes = NeededAngelTypes_by_shift($shift_id);
$neededAngels = [];
$angelTypes = NeededAngelTypes_by_shift($shift->id);
foreach ($angelTypes as $type) {
$result['NeedAngels'][] = [
$neededAngels[] = [
'TID' => $type['angel_type_id'],
'count' => $type['count'],
'restricted' => $type['restricted'],
'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 COUNT(*)
FROM `ShiftEntry`
WHERE `ShiftEntry`.`SID`=`Shifts`.`SID`
WHERE `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
)) AS `count`
FROM `Shifts`
WHERE (`end` >= UNIX_TIMESTAMP() AND `start` <= UNIX_TIMESTAMP())
' . ($filter ? 'AND Shifts.RID IN (' . implode(',', $filter->getRooms()) . ')' : '')
FROM `shifts`
WHERE (`end` >= NOW() AND `start` <= NOW())
' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '')
);
return $result['count'] ?: '-';
@ -42,24 +42,24 @@ function stats_hours_to_work(ShiftsFilter $filter = null)
'
SELECT ROUND(SUM(`count`)) AS `count` FROM (
SELECT
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
* (`Shifts`.`end` - `Shifts`.`start`)/3600 AS `count`
FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` >= UNIX_TIMESTAMP()
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`shifts`.`id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
* TIMESTAMPDIFF(MINUTE, `shifts`.`start`, `shifts`.`end`) / 60 AS `count`
FROM `shifts`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `shifts`.`end` >= NOW()
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
SELECT
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`Shifts`.`RID`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
* (`Shifts`.`end` - `Shifts`.`start`)/3600 AS `count`
FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` >= UNIX_TIMESTAMP()
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`shifts`.`room_id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
* TIMESTAMPDIFF(MINUTE, `shifts`.`start`, `shifts`.`end`) / 60 AS `count`
FROM `shifts`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE shifts.`end` >= NOW()
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`
'
);
@ -86,23 +86,23 @@ function stats_angels_needed_three_hours(ShiftsFilter $filter = null)
FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id`
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()) . ')' : '') . '
) - (
SELECT COUNT(*) FROM `ShiftEntry`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID`
WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`Shifts`.`SID`
AND `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
)
)
AS `count`
FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` > UNIX_TIMESTAMP() AND `start` < ?
FROM `shifts`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE shifts.`end` > NOW() AND shifts.`start` < ?
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
@ -113,23 +113,23 @@ function stats_angels_needed_three_hours(ShiftsFilter $filter = null)
FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id`
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()) . ')' : '') . '
) - (
SELECT COUNT(*) FROM `ShiftEntry`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID`
WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`Shifts`.`SID`
AND `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
)
)
AS `count`
FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` > UNIX_TIMESTAMP() AND `start` < ?
FROM `shifts`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `end` > NOW() AND `start` < ?
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`', [
$in3hours,
$in3hours
@ -165,23 +165,23 @@ function stats_angels_needed_for_nightshifts(ShiftsFilter $filter = null)
FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id`
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()) . ')' : '') . '
) - (
SELECT COUNT(*) FROM `ShiftEntry`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID`
WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`Shifts`.`SID`
AND `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
)
)
AS `count`
FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` > ? AND `start` < ?
FROM `shifts`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE shifts.`end` > ? AND shifts.`start` < ?
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
@ -192,23 +192,23 @@ function stats_angels_needed_for_nightshifts(ShiftsFilter $filter = null)
FROM `NeededAngelTypes`
JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id`
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()) . ')' : '') . '
) - (
SELECT COUNT(*) FROM `ShiftEntry`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID`
WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`Shifts`.`SID`
AND `ShiftEntry`.`SID`=`shifts`.`id`
AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
)
)
AS `count`
FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
FROM `shifts`
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE `end` > ? AND `start` < ?
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`', [
$night_start,
$night_end,

View File

@ -26,13 +26,12 @@ function User_tshirt_score($userId)
$result_shifts = Db::selectOne(sprintf('
SELECT ROUND((%s) / 3600, 2) AS `tshirt_score`
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` = ?
AND `Shifts`.`end` < ?
AND `shifts`.`end` < NOW()
GROUP BY `users`.`id`
', $shift_sum_formula), [
$userId,
time()
$userId
]);
if (!isset($result_shifts['tshirt_score'])) {
$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)
: null;
$shifts = ShiftEntries_finished_by_user($user->id, $start);
$shifts = ShiftEntries_finished_by_user($user, $start);
$worklog = UserWorkLogsForUser($user->id, $start);
$shifts_done =
count($shifts)
@ -205,7 +204,7 @@ function User_get_eligable_voucher_count($user)
$shiftsTime = 0;
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) {
$shiftsTime += $entry->hours;
@ -237,18 +236,18 @@ function User_get_shifts_sum_query()
{
$nightShifts = config('night_shifts');
if (!$nightShifts['enabled']) {
return 'COALESCE(SUM(`end` - `start`), 0)';
return 'COALESCE(SUM(UNIX_TIMESTAMP(shifts.end) - UNIX_TIMESTAMP(shifts.start)), 0)';
}
return sprintf(
'
COALESCE(SUM(
(1 + (
(HOUR(FROM_UNIXTIME(`Shifts`.`end`)) > %1$d AND HOUR(FROM_UNIXTIME(`Shifts`.`end`)) < %2$d)
OR (HOUR(FROM_UNIXTIME(`Shifts`.`start`)) > %1$d AND HOUR(FROM_UNIXTIME(`Shifts`.`start`)) < %2$d)
OR (HOUR(FROM_UNIXTIME(`Shifts`.`start`)) <= %1$d AND HOUR(FROM_UNIXTIME(`Shifts`.`end`)) >= %2$d)
(HOUR(shifts.end) > %1$d AND HOUR(shifts.end) < %2$d)
OR (HOUR(shifts.start) > %1$d AND HOUR(shifts.start) < %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`)
), 0)
',

View File

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

View File

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

View File

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

View File

@ -5,6 +5,7 @@ use Engelsystem\Helpers\Carbon;
use Engelsystem\Http\Exceptions\HttpForbidden;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User;
use Illuminate\Support\Str;
@ -27,7 +28,7 @@ function admin_shifts()
$valid = true;
$request = request();
$session = session();
$start = Carbon::createTimestampFromDatetime(date('Y-m-d') . 'T00:00');
$start = Carbon::createFromDateTime(date('Y-m-d') . 'T00:00');
$end = $start;
$mode = 'multi';
$angelmode = 'manually';
@ -46,7 +47,7 @@ function admin_shifts()
$room_array[$room->id] = $room->name;
}
// Engeltypen laden
// Load angeltypes
$types = AngelType::all();
$needed_angel_types = [];
foreach ($types as $type) {
@ -93,14 +94,14 @@ function admin_shifts()
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;
} else {
$valid = false;
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;
} else {
$valid = false;
@ -201,15 +202,15 @@ function admin_shifts()
$shifts[] = [
'start' => $start,
'end' => $end,
'RID' => $rid,
'room_id' => $rid,
'title' => $title,
'shifttype_id' => $shifttype_id,
'shift_type_id' => $shifttype_id,
'description' => $description,
];
} elseif ($mode == 'multi') {
$shift_start = (int) $start;
$shift_start = $start;
do {
$shift_end = $shift_start + (int) $length * 60;
$shift_end = (clone $shift_start)->addSeconds((int) $length * 60);
if ($shift_end > $end) {
$shift_end = $end;
@ -221,9 +222,9 @@ function admin_shifts()
$shifts[] = [
'start' => $shift_start,
'end' => $shift_end,
'RID' => $rid,
'room_id' => $rid,
'title' => $title,
'shifttype_id' => $shifttype_id,
'shift_type_id' => $shifttype_id,
'description' => $description,
];
@ -238,8 +239,8 @@ function admin_shifts()
});
// Alle Tage durchgehen
$end_day = Carbon::createTimestampFromDatetime(date('Y-m-d', $end) . ' 00:00');
$day = Carbon::createTimestampFromDatetime(date('Y-m-d', $start) . ' 00:00');
$end_day = Carbon::createFromDatetime($end->format('Y-m-d') . ' 00:00');
$day = Carbon::createFromDatetime($start->format('Y-m-d') . ' 00:00');
do {
// Alle Schichtwechselstunden durchgehen
for ($i = 0; $i < count($change_hours); $i++) {
@ -248,20 +249,21 @@ function admin_shifts()
// Normales Intervall zwischen zwei Schichtwechselstunden
$end_hour = $change_hours[$i + 1];
} 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];
} else {
// Letzte Schichtwechselstunde: Keine Schicht erstellen
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)) {
// 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 {
// 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
@ -288,14 +290,14 @@ function admin_shifts()
$shifts[] = [
'start' => $interval_start,
'end' => $interval_end,
'RID' => $rid,
'room_id' => $rid,
'title' => $title,
'shifttype_id' => $shifttype_id,
'shift_type_id' => $shifttype_id,
'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);
usort($shifts, function ($a, $b) {
@ -308,11 +310,11 @@ function admin_shifts()
$shifts_table_entry = [
'timeslot' =>
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 />'
. Room_name_render(Room::find($shift['RID'])),
. Room_name_render(Room::find($shift['room_id'])),
'title' =>
ShiftType_name_render(ShiftType::find($shifttype_id))
. ($shift['title'] ? '<br />' . $shift['title'] : ''),
@ -342,8 +344,8 @@ function admin_shifts()
form_hidden('description', $description),
form_hidden('title', $title),
form_hidden('rid', $rid),
form_hidden('start', date('Y-m-d H:i', $start)),
form_hidden('end', date('Y-m-d H:i', $end)),
form_hidden('start', $start->format('Y-m-d H:i')),
form_hidden('end', $end->format('Y-m-d H:i')),
form_hidden('mode', $mode),
form_hidden('length', $length),
form_hidden('change_hours', implode(', ', $change_hours)),
@ -369,15 +371,19 @@ function admin_shifts()
$transactionId = Str::uuid();
foreach ($session->get('admin_shifts_shifts', []) as $shift) {
$shift['URL'] = null;
$shift_id = Shift_create($shift, $transactionId);
$shift = new Shift($shift);
$shift->url = '';
$shift->transaction_id = $transactionId;
$shift->createdBy()->associate(auth()->user());
$shift->save();
$shift_id = $shift->id;
engelsystem_log(
'Shift created: ' . $shifttypes[$shift['shifttype_id']]
. ' with title ' . $shift['title']
. ' with description ' . $shift['description']
. ' from ' . date('Y-m-d H:i', $shift['start'])
. ' to ' . date('Y-m-d H:i', $shift['end'])
'Shift created: ' . $shifttypes[$shift->shift_type_id]
. ' with title ' . $shift->title
. ' with description ' . $shift->description
. ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . $shift->end->format('Y-m-d H:i')
. ', transaction: ' . $transactionId
);
@ -405,7 +411,7 @@ function admin_shifts()
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'));
} else {
$session->remove('admin_shifts_shifts');
@ -550,37 +556,32 @@ function admin_shifts_history(): string
$request = request();
$transactionId = $request->postData('transaction_id');
if ($request->hasPostData('delete') && $transactionId) {
$shifts = Db::select('
SELECT SID
FROM Shifts
WHERE transaction_id = ?
', [$transactionId]);
$shifts = Shift::whereTransactionId($transactionId);
engelsystem_log('Deleting ' . count($shifts) . ' shifts (transaction id ' . $transactionId . ')');
foreach ($shifts as $shift) {
$shift = Shift($shift['SID']);
$room = Room::find($shift['RID']);
foreach ($shift['ShiftEntry'] as $entry) {
$shift = Shift($shift);
foreach ($shift->shiftEntry as $entry) {
$type = AngelType::find($entry['TID']);
event('shift.entry.deleting', [
'user' => User::find($entry['user_id']),
'start' => Carbon::createFromTimestamp($shift['start']),
'end' => Carbon::createFromTimestamp($shift['end']),
'name' => $shift['name'],
'title' => $shift['title'],
'start' => $shift->start,
'end' => $shift->end,
'name' => $shift->shiftType->name,
'title' => $shift->title,
'type' => $type->name,
'room' => $room,
'room' => $shift->room,
'freeloaded' => (bool) $entry['freeloaded'],
]);
}
shift_delete($shift['SID']);
$shift->delete();
engelsystem_log(
'Deleted shift ' . $shift['name']
. ' from ' . date('Y-m-d H:i', $shift['start'])
. ' to ' . date('Y-m-d H:i', $shift['end'])
'Deleted shift ' . $shift->title . ' / ' . $shift->shiftType->name
. ' from ' . $shift->start->format('Y-m-d H:i')
. ' 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'));
}
$shifts = Db::select('
$shiftsData = Db::select('
SELECT
transaction_id,
title,
COUNT(SID) AS count,
COUNT(id) AS count,
MIN(start) AS start,
MAX(end) AS end,
created_by_user_id AS user_id,
created_at_timestamp AS created_at
FROM Shifts
created_by AS user_id,
MAX(created_at) AS created_at
FROM shifts
WHERE transaction_id IS NOT NULL
GROUP BY transaction_id
ORDER BY transaction_id DESC
ORDER BY created_at DESC
');
foreach ($shifts as &$shift) {
$shift['user'] = User_Nick_render(User::find($shift['user_id']));
$shift['start'] = Carbon::createFromTimestamp($shift['start'])->format(__('Y-m-d H:i'));
$shift['end'] = Carbon::createFromTimestamp($shift['end'])->format(__('Y-m-d H:i'));
$shift['created_at'] = Carbon::createFromTimestamp($shift['created_at'])->format(__('Y-m-d H:i'));
$shift['actions'] = form([
form_hidden('transaction_id', $shift['transaction_id']),
foreach ($shiftsData as &$shiftData) {
$shiftData['user'] = User_Nick_render(User::find($shiftData['user_id']));
$shiftData['start'] = Carbon::make($shiftData['start'])->format(__('Y-m-d H:i'));
$shiftData['end'] = Carbon::make($shiftData['end'])->format(__('Y-m-d H:i'));
$shiftData['created_at'] = Carbon::make($shiftData['created_at'])->format(__('Y-m-d H:i'));
$shiftData['actions'] = form([
form_hidden('transaction_id', $shiftData['transaction_id']),
form_submit('delete', icon('trash') . __('delete all'), 'btn-sm', true, 'danger'),
]);
}
@ -625,6 +626,6 @@ function admin_shifts_history(): string
'user' => __('User'),
'created_at' => __('Created'),
'actions' => ''
], $shifts)
], $shiftsData)
], true);
}

View File

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

View File

@ -1,7 +1,8 @@
<?php
use Carbon\Carbon;
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.
@ -31,7 +32,7 @@ function user_ical()
/**
* Renders an ical calendar from given shifts array.
*
* @param array $shifts Shift
* @param Shift[]|Collection $shifts Shift
*/
function send_ical_from_shifts($shifts)
{
@ -50,25 +51,22 @@ function send_ical_from_shifts($shifts)
/**
* Renders an ical vevent from given shift.
*
* @param array $shift
* @param Shift $shift
* @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 .= 'UID:' . md5($shift['start'] . $shift['end'] . $shift['name']) . "\r\n";
$output .= 'SUMMARY:' . str_replace("\n", "\\n", $shift['name'])
. ' (' . str_replace("\n", "\\n", $shift['title']) . ")\r\n";
if (isset($shift['Comment'])) {
$output .= 'DESCRIPTION:' . str_replace("\n", "\\n", $shift['Comment']) . "\r\n";
$output .= 'UID:' . md5($shift->start->timestamp . $shift->end->timestamp . $shift->shiftType->name) . "\r\n";
$output .= 'SUMMARY:' . str_replace("\n", "\\n", $shift->shiftType->name)
. ' (' . str_replace("\n", "\\n", $shift->title) . ")\r\n";
if (isset($shift->Comment)) {
$output .= 'DESCRIPTION:' . str_replace("\n", "\\n", $shift->Comment) . "\r\n";
}
$output .= 'DTSTAMP:' . $start->utc()->format('Ymd\THis\Z') . "\r\n";
$output .= 'DTSTART:' . $start->utc()->format('Ymd\THis\Z') . "\r\n";
$output .= 'DTEND:' . $end->utc()->format('Ymd\THis\Z') . "\r\n";
$output .= 'LOCATION:' . $shift['Name'] . "\r\n";
$output .= 'DTSTAMP:' . $shift->start->utc()->format('Ymd\THis\Z') . "\r\n";
$output .= 'DTSTART:' . $shift->start->utc()->format('Ymd\THis\Z') . "\r\n";
$output .= 'DTEND:' . $shift->end->utc()->format('Ymd\THis\Z') . "\r\n";
$output .= 'LOCATION:' . $shift->room->name . "\r\n";
$output .= "END:VEVENT\r\n";
return $output;
}

View File

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

View File

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

View File

@ -2,6 +2,7 @@
namespace Engelsystem;
use Engelsystem\Models\Shifts\Shift;
use Exception;
/**
@ -9,17 +10,15 @@ use Exception;
*/
class ShiftCalendarLane
{
/** @var array[] */
/** @var Shift[] */
private $shifts = [];
/**
* ShiftCalendarLane constructor.
*
* @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.
* 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.
*/
public function addShift($shift)
public function addShift(Shift $shift)
{
if ($this->shiftFits($shift)) {
$this->shifts[] = $shift;
@ -43,14 +42,14 @@ class ShiftCalendarLane
/**
* Returns true if given shift fits into this lane.
*
* @param array $newShift
* @param Shift $newShift
* @return bool
* @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) {
if (!($newShift['start'] >= $laneShift['end'] || $newShift['end'] <= $laneShift['start'])) {
if (!($newShift->start >= $laneShift->end || $newShift->end <= $laneShift->start)) {
return false;
}
}
@ -67,7 +66,7 @@ class ShiftCalendarLane
}
/**
* @return array[]
* @return Shift[]
*/
public function getShifts()
{

View File

@ -2,7 +2,7 @@
namespace Engelsystem;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
class ShiftCalendarRenderer
{
@ -45,7 +45,7 @@ class ShiftCalendarRenderer
/**
* ShiftCalendarRenderer constructor.
*
* @param array[] $shifts
* @param Shift[] $shifts
* @param array[] $needed_angeltypes
* @param array[] $shift_entries
* @param ShiftsFilter $shiftsFilter
@ -61,7 +61,7 @@ class ShiftCalendarRenderer
/**
* 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
*/
private function assignShiftsToLanes($shifts)
@ -70,20 +70,17 @@ class ShiftCalendarRenderer
$lanes = [];
foreach ($shifts as $shift) {
$room_id = $shift['RID'];
$room = new Room();
$room->name = $shift['room_name'];
$room->setAttribute('id', $room_id);
$room = $shift->room;
$header = Room_name_render($room);
if (!isset($lanes[$room_id])) {
if (!isset($lanes[$room->id])) {
// initialize room with one lane
$lanes[$room_id] = [
new ShiftCalendarLane($header, $this->getFirstBlockStartTime(), $this->getBlocksPerSlot())
$lanes[$room->id] = [
new ShiftCalendarLane($header)
];
}
// Try to add the shift to the existing lanes for this room
$shift_added = false;
foreach ($lanes[$room_id] as $lane) {
foreach ($lanes[$room->id] as $lane) {
/** @var ShiftCalendarLane $lane */
if ($lane->shiftFits($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 (!$shift_added) {
$newLane = new ShiftCalendarLane($header, $this->getFirstBlockStartTime(), $this->getBlocksPerSlot());
$newLane = new ShiftCalendarLane($header);
$newLane->addShift($shift);
$lanes[$room_id][] = $newLane;
$lanes[$room->id][] = $newLane;
}
}
@ -176,15 +173,15 @@ class ShiftCalendarRenderer
$rendered_until = $this->getFirstBlockStartTime();
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);
$rendered_until += ShiftCalendarRenderer::SECONDS_PER_ROW;
}
list ($shift_height, $shift_html) = $shift_renderer->render(
$shift,
$this->needed_angeltypes[$shift['SID']],
$this->shift_entries[$shift['SID']],
$this->needed_angeltypes[$shift->id],
$this->shift_entries[$shift->id],
auth()->user()
);
$html .= $shift_html;
@ -256,15 +253,15 @@ class ShiftCalendarRenderer
}
/**
* @param array[] $shifts
* @param Shift[] $shifts
* @return int
*/
private function calcFirstBlockStartTime($shifts)
{
$start_time = $this->shiftsFilter->getEndTime();
foreach ($shifts as $shift) {
if ($shift['start'] < $start_time) {
$start_time = $shift['start'];
if ($shift->start->timestamp < $start_time) {
$start_time = $shift->start->timestamp;
}
}
return ShiftCalendarRenderer::SECONDS_PER_ROW * floor(
@ -274,15 +271,15 @@ class ShiftCalendarRenderer
}
/**
* @param array[] $shifts
* @param Shift[] $shifts
* @return int
*/
private function calcLastBlockEndTime($shifts)
{
$end_time = $this->shiftsFilter->getStartTime();
foreach ($shifts as $shift) {
if ($shift['end'] > $end_time) {
$end_time = $shift['end'];
if ($shift->end->timestamp > $end_time) {
$end_time = $shift->end->timestamp;
}
}

View File

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

View File

@ -1,28 +1,28 @@
<?php
use Carbon\Carbon;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User;
/**
* Sign off from a user from a shift with admin permissions, asking for ack.
*
* @param array $shift
* @param Shift $shift
* @param AngelType $angeltype
* @param User $signoff_user
*
* @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(), [
info(sprintf(
__('Do you want to sign off %s from shift %s from %s to %s as %s?'),
User_Nick_render($signoff_user),
$shift['name'],
date('Y-m-d H:i', $shift['start']),
date('Y-m-d H:i', $shift['end']),
$shift->shiftType->name,
$shift->start->format('Y-m-d H:i'),
$shift->end->format('Y-m-d H:i'),
$angeltype->name
), true),
form([
@ -37,20 +37,20 @@ function ShiftEntry_delete_view_admin($shift, AngelType $angeltype, $signoff_use
/**
* Sign off from a shift, asking for ack.
*
* @param array $shift
* @param Shift $shift
* @param AngelType $angeltype
* @param int $signoff_user_id
*
* @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(), [
info(sprintf(
__('Do you want to sign off from your shift %s from %s to %s as %s?'),
$shift['name'],
date('Y-m-d H:i', $shift['start']),
date('Y-m-d H:i', $shift['end']),
$shift->shiftType->name,
$shift->start->format('Y-m-d H:i'),
$shift->end->format('Y-m-d H:i'),
$angeltype->name
), true),
@ -74,7 +74,7 @@ function ShiftEntry_delete_title()
/**
* Admin puts user into shift.
*
* @param array $shift
* @param Shift $shift
* @param Room $room
* @param AngelType $angeltype
* @param array $angeltypes_select
@ -83,17 +83,17 @@ function ShiftEntry_delete_title()
* @return string
*/
function ShiftEntry_create_view_admin(
$shift,
Shift $shift,
Room $room,
AngelType $angeltype,
$angeltypes_select,
$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(
ShiftEntry_create_title() . ': ' . $shift['name']
. ' <small title="' . $start . '" data-countdown-ts="' . $shift['start'] . '">%c</small>',
ShiftEntry_create_title() . ': ' . $shift->shiftType->name
. ' <small title="' . $start . '" data-countdown-ts="' . $shift->start->timestamp . '">%c</small>',
[
Shift_view_header($shift, $room),
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.
*
* @param array $shift
* @param Shift $shift
* @param Room $room
* @param AngelType $angeltype
* @param User $signup_user
* @param array $users_select
* @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(
ShiftEntry_create_title() . ': ' . $shift['name']
. ' <small title="' . $start . '" data-countdown-ts="' . $shift['start'] . '">%c</small>',
ShiftEntry_create_title() . ': ' . $shift->shiftType->name
. ' <small title="' . $start . '" data-countdown-ts="' . $shift->start->timestamp . '">%c</small>',
[
Shift_view_header($shift, $room),
info(sprintf(
@ -139,18 +139,18 @@ function ShiftEntry_create_view_supporter($shift, Room $room, AngelType $angelty
/**
* User joining a shift.
*
* @param array $shift
* @param Shift $shift
* @param Room $room
* @param AngelType $angeltype
* @param string $comment
* @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(
ShiftEntry_create_title() . ': ' . $shift['name']
. ' <small title="' . $start . '" data-countdown-ts="' . $shift['start'] . '">%c</small>',
ShiftEntry_create_title() . ': ' . $shift->shiftType->name
. ' <small title="' . $start . '" data-countdown-ts="' . $shift->start->timestamp . '">%c</small>',
[
Shift_view_header($shift, $room),
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
use Carbon\Carbon;
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\UserAngelType;
@ -12,35 +12,35 @@ use Illuminate\Support\Collection;
/**
* Renders the basic shift view header.
*
* @param array $shift
* @param Shift $shift
* @param Room $room
* @return string HTML
*/
function Shift_view_header($shift, Room $room)
function Shift_view_header(Shift $shift, Room $room)
{
return div('row', [
div('col-sm-3 col-xs-6', [
'<h4>' . __('Title') . '</h4>',
'<p class="lead">'
. ($shift['URL'] != ''
? '<a href="' . $shift['URL'] . '">' . $shift['title'] . '</a>'
: $shift['title'])
. ($shift->url != ''
? '<a href="' . $shift->url . '">' . $shift->title . '</a>'
: $shift->title)
. '</p>'
]),
div('col-sm-3 col-xs-6', [
'<h4>' . __('Start') . '</h4>',
'<p class="lead' . (time() >= $shift['start'] ? ' text-success' : '') . '">',
icon('calendar-event') . date(__('Y-m-d'), $shift['start']),
'<p class="lead' . (time() >= $shift->start->timestamp ? ' text-success' : '') . '">',
icon('calendar-event') . $shift->start->format(__('Y-m-d')),
'<br />',
icon('clock') . date('H:i', $shift['start']),
icon('clock') . $shift->start->format('H:i'),
'</p>'
]),
div('col-sm-3 col-xs-6', [
'<h4>' . __('End') . '</h4>',
'<p class="lead' . (time() >= $shift['end'] ? ' text-success' : '') . '">',
icon('calendar-event') . date(__('Y-m-d'), $shift['end']),
'<p class="lead' . (time() >= $shift->end->timestamp ? ' text-success' : '') . '">',
icon('calendar-event') . $shift->end->format(__('Y-m-d')),
'<br />',
icon('clock') . date('H:i', $shift['end']),
icon('clock') . $shift->end->format('H:i'),
'</p>'
]),
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
*/
function Shift_editor_info_render($shift)
function Shift_editor_info_render(Shift $shift)
{
$info = [];
if (!empty($shift['created_by_user_id'])) {
if (!empty($shift->created_by)) {
$info[] = sprintf(
icon('plus-lg') . __('created at %s by %s'),
date('Y-m-d H:i', $shift['created_at_timestamp']),
User_Nick_render(User::find($shift['created_by_user_id']))
$shift->created_at->format('Y-m-d H:i'),
User_Nick_render($shift->createdBy)
);
}
if (!empty($shift['edited_by_user_id'])) {
if (!empty($shift->updated_by)) {
$info[] = sprintf(
icon('pencil') . __('edited at %s by %s'),
date('Y-m-d H:i', $shift['edited_at_timestamp']),
User_Nick_render(User::find($shift['edited_by_user_id']))
$shift->updated_at->format('Y-m-d H:i'),
User_Nick_render($shift->updatedBy)
);
}
return join('<br />', $info);
}
/**
* @param array $shift
* @param Shift $shift
* @param AngelType $angeltype
* @return string
*/
function Shift_signup_button_render($shift, AngelType $angeltype)
function Shift_signup_button_render(Shift $shift, AngelType $angeltype)
{
/** @var UserAngelType|null $user_angeltype */
$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 Room $room
* @param AngelType[]|Collection $angeltypes_source
* @param ShiftSignupState $shift_signup_state
* @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');
$user_shift_admin = auth()->can('user_shifts_admin');
@ -131,12 +131,12 @@ function Shift_view($shift, ShiftType $shifttype, Room $room, $angeltypes_source
}
$needed_angels = '';
$neededAngels = new Collection($shift['NeedAngels']);
$neededAngels = new Collection($shift->neededAngels);
foreach ($neededAngels as $needed_angeltype) {
$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) {
/** @var Collection $angelTypes */
$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);
}
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(
__('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);
}
@ -188,7 +188,7 @@ function Shift_view($shift, ShiftType $shifttype, Room $room, $angeltypes_source
div('col-sm-6', [
'<h2>' . __('Description') . '</h2>',
$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);
}
$start = Carbon::createFromTimestamp($shift['start'])->format(__('Y-m-d H:i'));
$start = $shift->start->format(__('Y-m-d H:i'));
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
);
}
@ -207,11 +207,11 @@ function Shift_view($shift, ShiftType $shifttype, Room $room, $angeltypes_source
/**
* @param array $needed_angeltype
* @param AngelType[]|Collection $angeltypes
* @param array[] $shift
* @param Shift $shift
* @param bool $user_shift_admin
* @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_supporter = auth()->user()->isAngelTypeSupporter($angeltype)
@ -242,7 +242,7 @@ function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shi
);
$angels = [];
foreach ($shift['ShiftEntry'] as $shift_entry) {
foreach ($shift->shiftEntry as $shift_entry) {
if ($shift_entry['TID'] == $needed_angeltype['TID']) {
$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 bool $user_shift_admin
* @param bool $angeltype_supporter
* @param array $shift
* @param Shift $shift
* @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']));
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.
*
* @param array $shift
* @param Shift $shift
* @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(
(($shift['end'] - $shift['start']) % (60 * 60)) / 60,
(($shift->end->timestamp - $shift->start->timestamp) % (60 * 60)) / 60,
2,
'0',
STR_PAD_LEFT

View File

@ -3,7 +3,7 @@
use Carbon\Carbon;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\Group;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User;
use Engelsystem\Models\Worklog;
use Illuminate\Support\Collection;
@ -196,35 +196,39 @@ function User_shift_state_render($user)
return '';
}
$upcoming_shifts = ShiftEntries_upcoming_for_user($user->id);
$upcoming_shifts = ShiftEntries_upcoming_for_user($user);
if (empty($upcoming_shifts)) {
return '<span class="text-success">' . __('Free') . '</span>';
}
$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 ($nextShift['start'] - time() > 3600) {
return '<span class="text-success" title="' . $start . '" data-countdown-ts="' . $nextShift['start'] . '">'
if ($startTimestamp > time()) {
if ($startTimestamp - time() > 3600) {
return '<span class="text-success" title="' . $startFormat . '" data-countdown-ts="' . $startTimestamp . '">'
. __('Next shift %c')
. '</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')
. '</span>';
}
$halfway = ($nextShift['start'] + $nextShift['end']) / 2;
$halfway = ($startTimestamp + $endTimestamp) / 2;
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')
. '</span>';
}
$end = Carbon::createFromTimestamp($nextShift['end'])->format(__('Y-m-d H:i'));
return '<span class="text-danger" title="' . $end . '" data-countdown-ts="' . $nextShift['end'] . '">'
return '<span class="text-danger" title="' . $endFormat . '" data-countdown-ts="' . $endTimestamp . '">'
. __('Shift ends %c')
. '</span>';
}
@ -235,15 +239,15 @@ function User_last_shift_render($user)
return '';
}
$last_shifts = ShiftEntries_finished_by_user($user->id);
$last_shifts = ShiftEntries_finished_by_user($user);
if (empty($last_shifts)) {
return '';
}
$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')
. '</span>';
}
@ -275,44 +279,44 @@ function User_view_shiftentries($needed_angel_type)
/**
* Helper that renders a shift line for user view
*
* @param array $shift
* @param Shift $shift
* @param User $user_source
* @param bool $its_me
* @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>';
if ($shift['title']) {
$shift_info .= '<br /><a href="' . shift_link($shift) . '">' . $shift['title'] . '</a>';
$shift_info = '<a href="' . shift_link($shift) . '">' . $shift->shiftType->name . '</a>';
if ($shift->title) {
$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);
}
$myshift = [
'date' => icon('calendar-event')
. date('Y-m-d', $shift['start']) . '<br>'
. icon('clock-history') . date('H:i', $shift['start'])
. $shift->start->format('Y-m-d') . '<br>'
. icon('clock-history') . $shift->start->format('H:i')
. ' - '
. date('H:i', $shift['end']),
'duration' => sprintf('%.2f', ($shift['end'] - $shift['start']) / 3600) . '&nbsp;h',
'room' => Room_name_render(Room::find($shift['RID'])),
. $shift->end->format('H:i'),
'duration' => sprintf('%.2f', ($shift->end->timestamp - $shift->start->timestamp) / 3600) . '&nbsp;h',
'room' => Room_name_render($shift->room),
'shift_info' => $shift_info,
'comment' => ''
];
if ($its_me) {
$myshift['comment'] = $shift['Comment'];
$myshift['comment'] = $shift->Comment;
}
if ($shift['freeloaded']) {
if ($shift->freeloaded) {
$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>';
if (auth()->can('user_shifts_admin')) {
$myshift['comment'] .= '<br />'
. '<p class="text-danger">' . __('Freeloaded') . ': ' . $shift['freeload_comment'] . '</p>';
. '<p class="text-danger">' . __('Freeloaded') . ': ' . $shift->freeload_comment . '</p>';
} else {
$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')) {
$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'),
'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(
shift_entry_delete_link($shift),
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
*
* @param array[] $shifts
* @param Shift[]|Collection $shifts
* @param User $user_source
* @param bool $its_me
* @param int $tshirt_score
@ -365,11 +369,11 @@ function User_view_myshifts(
$myshifts_table = [];
$timeSum = 0;
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);
if (!$shift['freeloaded']) {
$timeSum += ($shift['end'] - $shift['start']);
if (!$shift->freeloaded) {
$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 AngelType[] $user_angeltypes
* @param Group[] $user_groups
* @param array[] $shifts
* @param Shift[]|Collection $shifts
* @param bool $its_me
* @param int $tshirt_score
* @param bool $tshirt_admin

View File

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

View File

@ -5,7 +5,10 @@ declare(strict_types=1);
namespace Engelsystem\Models;
use Carbon\Carbon;
use Engelsystem\Models\Shifts\Shift;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
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 $updated_at
*
* @property-read Collection|Shift[] $shifts
*
* @method static QueryBuilder|Room[] whereId($value)
* @method static QueryBuilder|Room[] whereName($value)
* @method static QueryBuilder|Room[] whereMapUrl($value)
@ -39,4 +44,9 @@ class Room extends BaseModel
'map_url',
'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\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Query\Builder as QueryBuilder;
/**
@ -20,6 +21,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
* @property Carbon $created_at
* @property Carbon $updated_at
*
* @property-read QueryBuilder|Collection|Shift[] $shifts
* @property-read QueryBuilder|Collection|ScheduleShift[] $scheduleShifts
* @property-read QueryBuilder|ShiftType $shiftType
*
@ -60,6 +62,11 @@ class Schedule extends BaseModel
return $this->hasMany(ScheduleShift::class);
}
public function shifts(): HasManyThrough
{
return $this->hasManyThrough(Shift::class, ScheduleShift::class, 'schedule_id', 'id');
}
public function shiftType(): BelongsTo
{
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-read QueryBuilder|Schedule $schedule
* @property-read QueryBuilder|Shift $shift
*
* @method static QueryBuilder|ScheduleShift[] whereShiftId($value)
* @method static QueryBuilder|ScheduleShift[] whereScheduleId($value)
@ -38,4 +39,9 @@ class ScheduleShift extends BaseModel
{
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-read Collection|Schedule[] $schedules
* @property-read Collection|Shift[] $shifts
*
* @method static QueryBuilder|ShiftType[] whereId($value)
* @method static QueryBuilder|ShiftType[] whereName($value)
@ -35,4 +36,9 @@ class ShiftType extends BaseModel
{
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\Privilege;
use Engelsystem\Models\Question;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\UserAngelType;
use Engelsystem\Models\Worklog;
use Illuminate\Database\Eloquent\Builder;
@ -53,6 +54,8 @@ use Illuminate\Support\Collection as SupportCollection;
* @property-read Collection|Message[] $messagesReceived
* @property-read Collection|Message[] $messagesSent
* @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[] whereName($value)
@ -215,9 +218,6 @@ class User extends BaseModel
->orderBy('id', 'DESC');
}
/**
* @return HasMany|QueryBuilder
*/
public function messagesReceived(): HasMany
{
return $this->hasMany(Message::class, 'receiver_id')
@ -236,4 +236,14 @@ class User extends BaseModel
->orderBy('read')
->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\Question;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\License;
use Engelsystem\Models\User\PasswordReset;
use Engelsystem\Models\User\PersonalData;
@ -176,6 +177,17 @@ class StatsTest extends TestCase
$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
*/

View File

@ -9,6 +9,7 @@ use Engelsystem\Models\NewsComment;
use Engelsystem\Models\Question;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\Contact;
use Engelsystem\Models\User\License;
use Engelsystem\Models\User\PasswordReset;
@ -23,27 +24,33 @@ class FactoriesTest extends TestCase
{
use HasDatabase;
/** @var string[] */
protected array $models = [
Contact::class,
Faq::class,
License::class,
Message::class,
News::class,
NewsComment::class,
PasswordReset::class,
PersonalData::class,
Question::class,
Room::class,
Schedule::class,
Settings::class,
State::class,
User::class,
Worklog::class,
/**
* @return string[][]
*/
public function factoriesProvider(): array
{
return [
[Contact::class],
[Faq::class],
[License::class],
[Message::class],
[News::class],
[NewsComment::class],
[PasswordReset::class],
[PersonalData::class],
[Question::class],
[Room::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\FaqFactory
@ -57,19 +64,20 @@ class FactoriesTest extends TestCase
* @covers \Database\Factories\Engelsystem\Models\RoomFactory
* @covers \Database\Factories\Engelsystem\Models\Shifts\ScheduleFactory
* @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\UserFactory
* @covers \Database\Factories\Engelsystem\Models\WorklogFactory
*
* @dataProvider factoriesProvider
*/
public function testFactories(): void
public function testFactories(string $model): void
{
$this->initDatabase();
foreach ($this->models as $model) {
$instance = (new $model())->factory()->create();
$this->assertInstanceOf(Model::class, $instance);
}
}
/**
* @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\ScheduleShift;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Test\Unit\Models\ModelTest;
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::shift
*/
public function testScheduleShifts(): void
{
@ -22,14 +24,19 @@ class ScheduleShiftTest extends ModelTest
'minutes_after' => 15,
]);
$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->shift()->associate($shift);
$scheduleShift->save();
/** @var ScheduleShift $scheduleShift */
$scheduleShift = (new ScheduleShift())->find(1);
$this->assertInstanceOf(BelongsTo::class, $scheduleShift->schedule());
$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\ScheduleShift;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Test\Unit\Models\ModelTest;
use Illuminate\Database\Eloquent\Collection;
class ScheduleTest extends ModelTest
{
@ -32,6 +34,24 @@ class ScheduleTest extends ModelTest
$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
*/

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;
use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Test\Unit\Models\ModelTest;
@ -21,4 +22,17 @@ class ShiftTypeTest extends ModelTest
$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\Privilege;
use Engelsystem\Models\Question;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\Contact;
use Engelsystem\Models\User\HasUserModel;
use Engelsystem\Models\User\License;
@ -103,8 +104,8 @@ class UserTest extends ModelTest
'text' => 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.',
'is_meeting' => true,
],
]
]
],
],
];
}
@ -415,4 +416,29 @@ class UserTest extends ModelTest
$this->assertContains($question1->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);
}
}