Add ShiftEntry model

This commit is contained in:
Igor Scheller 2023-01-18 13:02:11 +01:00 committed by GitHub
parent 89f9b423b1
commit 89dc85c3d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 787 additions and 606 deletions

View File

@ -0,0 +1,29 @@
<?php
namespace Database\Factories\Engelsystem\Models\Shifts;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class ShiftEntryFactory extends Factory
{
/** @var string */
protected $model = ShiftEntry::class; // phpcs:ignore
public function definition(): array
{
$freeloaded = $this->faker->optional(.01, false)->boolean();
return [
'shift_id' => Shift::factory(),
'angel_type_id' => AngelType::factory(),
'user_id' => User::factory(),
'user_comment' => $this->faker->optional(.05, '')->text(),
'freeloaded' => $freeloaded,
'freeloaded_comment' => $freeloaded ? $this->faker->text() : '',
];
}
}

View File

@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
use stdClass;
class CreateShiftEntriesTable 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('ShiftEntry');
$this->schema->create('shift_entries', function (Blueprint $table): void {
$table->increments('id');
$this->references($table, 'shifts');
$this->references($table, 'angel_types');
$this->referencesUser($table);
$table->mediumText('user_comment')->default('');
$table->boolean('freeloaded')->default(false)->index();
$table->mediumText('freeloaded_comment')->default('');
$table->index(['angel_type_id', 'shift_id']);
});
if (!$previous) {
return;
}
/** @var stdClass[] $records */
$records = $connection
->table('ShiftEntry')
->get();
foreach ($records as $record) {
$connection->table('shift_entries')->insert([
'id' => $record->id,
'shift_id' => $record->SID,
'angel_type_id' => $record->TID,
'user_id' => $record->UID,
'user_comment' => $record->Comment,
'freeloaded' => (bool) $record->freeloaded,
'freeloaded_comment' => $record->freeload_comment,
]);
}
$this->changeReferences(
'ShiftEntry',
'id',
'shift_entries',
'id'
);
$this->schema->drop('ShiftEntry');
}
/**
* Recreates the previous table, copies the data and drops the new one
*/
public function down(): void
{
$connection = $this->schema->getConnection();
$this->schema->create('ShiftEntry', function (Blueprint $table): void {
$table->increments('id');
$this->references($table, 'shifts', 'SID')->default(0);
$this->references($table, 'angel_types', 'TID')->default(0);
$this->references($table, 'users', 'UID')->default(0);
$table->mediumText('Comment')->nullable();
$table->mediumText('freeload_comment')->nullable()->default(null);
$table->boolean('freeloaded')->index();
$table->index(['SID', 'TID']);
});
/** @var stdClass[] $records */
$records = $connection
->table('shift_entries')
->get();
foreach ($records as $record) {
$connection->table('ShiftEntry')->insert([
'id' => $record->id,
'SID' => $record->shift_id,
'TID' => $record->angel_type_id,
'UID' => $record->user_id,
'Comment' => $record->user_comment,
'freeloaded' => (bool) $record->freeloaded,
'freeload_comment' => $record->freeloaded_comment,
]);
}
$this->changeReferences(
'shift_entries',
'id',
'ShiftEntry',
'id'
);
$this->schema->drop('shift_entries');
}
}

View File

@ -107,8 +107,8 @@ function public_dashboard_needed_angels($needed_angels, ShiftsFilter $filter = n
$result = []; $result = [];
foreach ($needed_angels as $needed_angel) { foreach ($needed_angels as $needed_angel) {
$need = $needed_angel['count'] - $needed_angel['taken']; $need = $needed_angel['count'] - $needed_angel['taken'];
if ($need > 0 && (!$filter || in_array($needed_angel['TID'], $filter->getTypes()))) { if ($need > 0 && (!$filter || in_array($needed_angel['angel_type_id'], $filter->getTypes()))) {
$angeltype = AngelType::find($needed_angel['TID']); $angeltype = AngelType::find($needed_angel['angel_type_id']);
if ($angeltype->show_on_dashboard) { if ($angeltype->show_on_dashboard) {
$result[] = [ $result[] = [
'need' => $need, 'need' => $need,

View File

@ -2,6 +2,7 @@
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Engelsystem\ShiftSignupState; use Engelsystem\ShiftSignupState;
@ -27,7 +28,7 @@ function shift_entries_controller(): array
return match ($action) { return match ($action) {
'create' => shift_entry_create_controller(), 'create' => shift_entry_create_controller(),
'delete' => shift_entry_delete_controller(), 'delete' => shift_entry_delete_controller(),
default => ['', ''], default => ['', ''],
}; };
} }
@ -41,7 +42,7 @@ function shift_entry_create_controller(): array
$user = auth()->user(); $user = auth()->user();
$request = request(); $request = request();
if (User_is_freeloader($user)) { if ($user->isFreeloader()) {
throw_redirect(page_link_to('user_myshifts')); throw_redirect(page_link_to('user_myshifts'));
} }
@ -99,14 +100,12 @@ function shift_entry_create_controller_admin(Shift $shift, ?AngelType $angeltype
} }
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
ShiftEntry_create([ $shiftEntry = new ShiftEntry();
'SID' => $shift->id, $shiftEntry->shift()->associate($shift);
'TID' => $angeltype->id, $shiftEntry->angelType()->associate($angeltype);
'UID' => $signup_user->id, $shiftEntry->user()->associate($signup_user);
'Comment' => '', $shiftEntry->save();
'freeloaded' => false, ShiftEntry_onCreate($shiftEntry);
'freeload_comment' => ''
]);
success(sprintf(__('%s has been subscribed to the shift.'), User_Nick_render($signup_user))); success(sprintf(__('%s has been subscribed to the shift.'), User_Nick_render($signup_user)));
throw_redirect(shift_link($shift)); throw_redirect(shift_link($shift));
@ -150,14 +149,12 @@ function shift_entry_create_controller_supporter(Shift $shift, AngelType $angelt
} }
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
ShiftEntry_create([ $shiftEntry = new ShiftEntry();
'SID' => $shift->id, $shiftEntry->shift()->associate($shift);
'TID' => $angeltype->id, $shiftEntry->angelType()->associate($angeltype);
'UID' => $signup_user->id, $shiftEntry->user()->associate($signup_user);
'Comment' => '', $shiftEntry->save();
'freeloaded' => false, ShiftEntry_onCreate($shiftEntry);
'freeload_comment' => ''
]);
success(sprintf(__('%s has been subscribed to the shift.'), User_Nick_render($signup_user))); success(sprintf(__('%s has been subscribed to the shift.'), User_Nick_render($signup_user)));
throw_redirect(shift_link($shift)); throw_redirect(shift_link($shift));
@ -214,7 +211,9 @@ function shift_entry_create_controller_user(Shift $shift, AngelType $angeltype):
$signup_user = auth()->user(); $signup_user = auth()->user();
$needed_angeltype = (new AngelType())->forceFill(NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype)); $needed_angeltype = (new AngelType())->forceFill(NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype));
$shift_entries = ShiftEntries_by_shift_and_angeltype($shift->id, $angeltype->id); $shift_entries = $shift->shiftEntries()
->where('angel_type_id', $angeltype->id)
->get();
$shift_signup_state = Shift_signup_allowed( $shift_signup_state = Shift_signup_allowed(
$signup_user, $signup_user,
$shift, $shift,
@ -232,14 +231,14 @@ function shift_entry_create_controller_user(Shift $shift, AngelType $angeltype):
$comment = ''; $comment = '';
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
$comment = strip_request_item_nl('comment'); $comment = strip_request_item_nl('comment');
ShiftEntry_create([
'SID' => $shift->id, $shiftEntry = new ShiftEntry();
'TID' => $angeltype->id, $shiftEntry->shift()->associate($shift);
'UID' => $signup_user->id, $shiftEntry->angelType()->associate($angeltype);
'Comment' => $comment, $shiftEntry->user()->associate($signup_user);
'freeloaded' => false, $shiftEntry->user_comment = $comment;
'freeload_comment' => '' $shiftEntry->save();
]); ShiftEntry_onCreate($shiftEntry);
if ( if (
!$angeltype->restricted !$angeltype->restricted
@ -299,7 +298,7 @@ function shift_entry_create_link_admin(Shift $shift, $params = [])
/** /**
* Load a shift entry from get parameter shift_entry_id. * Load a shift entry from get parameter shift_entry_id.
* *
* @return array * @return ShiftEntry
*/ */
function shift_entry_load() function shift_entry_load()
{ {
@ -308,11 +307,7 @@ function shift_entry_load()
if (!$request->has('shift_entry_id') || !test_request_int('shift_entry_id')) { if (!$request->has('shift_entry_id') || !test_request_int('shift_entry_id')) {
throw_redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
$shiftEntry = ShiftEntry($request->input('shift_entry_id')); $shiftEntry = ShiftEntry::findOrFail($request->input('shift_entry_id'));
if (empty($shiftEntry)) {
error(__('Shift entry not found.'));
throw_redirect(page_link_to('user_shifts'));
}
return $shiftEntry; return $shiftEntry;
} }
@ -328,9 +323,9 @@ function shift_entry_delete_controller()
$request = request(); $request = request();
$shiftEntry = shift_entry_load(); $shiftEntry = shift_entry_load();
$shift = Shift($shiftEntry['SID']); $shift = Shift($shiftEntry->shift);
$angeltype = AngelType::find($shiftEntry['TID']); $angeltype = $shiftEntry->angelType;
$signout_user = User::find($shiftEntry['UID']); $signout_user = $shiftEntry->user;
if (!Shift_signout_allowed($shift, $angeltype, $signout_user->id)) { if (!Shift_signout_allowed($shift, $angeltype, $signout_user->id)) {
error(__( error(__(
'You are not allowed to remove this shift entry. If necessary, ask your supporter or heaven to do so.' 'You are not allowed to remove this shift entry. If necessary, ask your supporter or heaven to do so.'
@ -339,7 +334,8 @@ function shift_entry_delete_controller()
} }
if ($request->hasPostData('delete')) { if ($request->hasPostData('delete')) {
ShiftEntry_delete($shiftEntry); $shiftEntry->delete();
ShiftEntry_onDelete($shiftEntry);
success(__('Shift entry removed.')); success(__('Shift entry removed.'));
throw_redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
@ -347,7 +343,7 @@ function shift_entry_delete_controller()
if ($user->id == $signout_user->id) { if ($user->id == $signout_user->id) {
return [ return [
ShiftEntry_delete_title(), ShiftEntry_delete_title(),
ShiftEntry_delete_view($shift, $angeltype, $signout_user->id) ShiftEntry_delete_view($shift, $angeltype, $signout_user)
]; ];
} }
@ -360,8 +356,8 @@ function shift_entry_delete_controller()
/** /**
* Link to delete a shift entry. * Link to delete a shift entry.
* *
* @param array|Shift $shiftEntry * @param Shift|ShiftEntry $shiftEntry
* @param array $params * @param array $params
* @return string URL * @return string URL
*/ */
function shift_entry_delete_link($shiftEntry, $params = []) function shift_entry_delete_link($shiftEntry, $params = [])

View File

@ -6,7 +6,6 @@ use Engelsystem\Models\AngelType;
use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User;
use Engelsystem\ShiftSignupState; use Engelsystem\ShiftSignupState;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -159,7 +158,6 @@ function shift_edit_controller()
$shift->updatedBy()->associate(auth()->user()); $shift->updatedBy()->associate(auth()->user());
// Remove merged data as it is not really part of the model and thus can't be saved // Remove merged data as it is not really part of the model and thus can't be saved
unset($shift->shiftEntry);
unset($shift->neededAngels); unset($shift->neededAngels);
$shift->save(); $shift->save();
@ -244,24 +242,23 @@ function shift_delete_controller()
// Schicht löschen bestätigt // Schicht löschen bestätigt
if ($request->hasPostData('delete')) { if ($request->hasPostData('delete')) {
foreach ($shift->shiftEntry as $entry) { foreach ($shift->shiftEntries as $entry) {
$type = AngelType::find($entry['TID']);
event('shift.entry.deleting', [ event('shift.entry.deleting', [
'user' => User::find($entry['user_id']), 'user' => $entry->user,
'start' => $shift->start, 'start' => $shift->start,
'end' => $shift->end, 'end' => $shift->end,
'name' => $shift->shiftType->name, 'name' => $shift->shiftType->name,
'title' => $shift->title, 'title' => $shift->title,
'type' => $type->name, 'type' => $entry->angelType->name,
'room' => $shift->room, 'room' => $shift->room,
'freeloaded' => (bool) $entry['freeloaded'], 'freeloaded' => $entry->freeloaded,
]); ]);
} }
$shift->delete(); $shift->delete();
engelsystem_log( engelsystem_log(
'Deleted shift ' . $shift->title . ': ' . $shift->shiftType->name 'Deleted shift ' . $shift->title . ': ' . $shift->shiftType->name
. ' from ' . $shift->start->format('Y-m-d H:i') . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . $shift->end->format('Y-m-d H:i') . ' to ' . $shift->end->format('Y-m-d H:i')
); );
@ -318,7 +315,9 @@ function shift_controller()
continue; continue;
} }
$shift_entries = ShiftEntries_by_shift_and_angeltype($shift->id, $angeltype->id); $shift_entries = $shift->shiftEntries()
->where('angel_type_id', $angeltype->id)
->get();
$needed_angeltype = (new AngelType())->forceFill($needed_angeltype); $needed_angeltype = (new AngelType())->forceFill($needed_angeltype);
$angeltype_signup_state = Shift_signup_allowed( $angeltype_signup_state = Shift_signup_allowed(
@ -351,8 +350,8 @@ function shifts_controller()
} }
return match ($request->input('action')) { return match ($request->input('action')) {
'view' => shift_controller(), 'view' => shift_controller(),
'next' => shift_next_controller(), // throws redirect 'next' => shift_next_controller(), // throws redirect
default => throw_redirect(page_link_to('/')), default => throw_redirect(page_link_to('/')),
}; };
} }
@ -368,8 +367,8 @@ function shift_next_controller()
$upcoming_shifts = ShiftEntries_upcoming_for_user(auth()->user()); $upcoming_shifts = ShiftEntries_upcoming_for_user(auth()->user());
if (!empty($upcoming_shifts)) { if (!$upcoming_shifts->isEmpty()) {
throw_redirect(shift_link($upcoming_shifts[0])); throw_redirect(shift_link($upcoming_shifts[0]->shift));
} }
throw_redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
@ -406,41 +405,41 @@ function shifts_json_export_controller()
// See engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/models/Shift.kt // See engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/models/Shift.kt
$data = [ $data = [
// Name of the shift (type) // Name of the shift (type)
'name' => $shift->shiftType->name, 'name' => $shift->shiftType->name,
// Shift / Talk title // Shift / Talk title
'title' => $shift->title, 'title' => $shift->title,
// Shift description // Shift description
'description' => $shift->description, 'description' => $shift->description,
// Users comment // Users comment
'Comment' => $shift->Comment, 'Comment' => $shift->user_comment,
// Shift id // Shift id
'SID' => $shift->id, 'SID' => $shift->id,
// Shift type id // Shift type id
'shifttype_id' => $shift->shift_type_id, 'shifttype_id' => $shift->shift_type_id,
// Talk URL // Talk URL
'URL' => $shift->url, 'URL' => $shift->url,
// Room name // Room name
'Name' => $shift->room->name, 'Name' => $shift->room->name,
// Location map url // Location map url
'map_url' => $shift->room->map_url, 'map_url' => $shift->room->map_url,
// Start timestamp // Start timestamp
/** @deprecated start_date should be used */ /** @deprecated start_date should be used */
'start' => $shift->start->timestamp, 'start' => $shift->start->timestamp,
// Start date // Start date
'start_date' => $shift->start->toRfc3339String(), 'start_date' => $shift->start->toRfc3339String(),
// End timestamp // End timestamp
/** @deprecated end_date should be used */ /** @deprecated end_date should be used */
'end' => $shift->end->timestamp, 'end' => $shift->end->timestamp,
// End date // End date
'end_date' => $shift->end->toRfc3339String(), 'end_date' => $shift->end->toRfc3339String(),
// Timezone offset like "+01:00" // Timezone offset like "+01:00"
/** @deprecated should be retrieved from start_date or end_date */ /** @deprecated should be retrieved from start_date or end_date */
'timezone' => $timeZone->toOffsetName(), 'timezone' => $timeZone->toOffsetName(),
// The events timezone like "Europe/Berlin" // The events timezone like "Europe/Berlin"
'event_timezone' => $timeZone->getName(), 'event_timezone' => $timeZone->getName(),
]; ];

View File

@ -1,6 +1,7 @@
<?php <?php
use Engelsystem\Database\Db; use Engelsystem\Database\Db;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\State; use Engelsystem\Models\User\State;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\ShiftCalendarRenderer; use Engelsystem\ShiftCalendarRenderer;
@ -209,9 +210,9 @@ function user_controller()
$shift->needed_angeltypes = Db::select( $shift->needed_angeltypes = Db::select(
' '
SELECT DISTINCT `angel_types`.* SELECT DISTINCT `angel_types`.*
FROM `ShiftEntry` FROM `shift_entries`
JOIN `angel_types` ON `ShiftEntry`.`TID`=`angel_types`.`id` JOIN `angel_types` ON `shift_entries`.`angel_type_id`=`angel_types`.`id`
WHERE `ShiftEntry`.`SID` = ? WHERE `shift_entries`.`shift_id` = ?
ORDER BY `angel_types`.`name` ORDER BY `angel_types`.`name`
', ',
[$shift->id] [$shift->id]
@ -220,11 +221,11 @@ function user_controller()
foreach ($neededAngeltypes as &$needed_angeltype) { foreach ($neededAngeltypes as &$needed_angeltype) {
$needed_angeltype['users'] = Db::select( $needed_angeltype['users'] = Db::select(
' '
SELECT `ShiftEntry`.`freeloaded`, `users`.* SELECT `shift_entries`.`freeloaded`, `users`.*
FROM `ShiftEntry` FROM `shift_entries`
JOIN `users` ON `ShiftEntry`.`UID`=`users`.`id` JOIN `users` ON `shift_entries`.`user_id`=`users`.`id`
WHERE `ShiftEntry`.`SID` = ? WHERE `shift_entries`.`shift_id` = ?
AND `ShiftEntry`.`TID` = ? AND `shift_entries`.`angel_type_id` = ?
', ',
[$shift->id, $needed_angeltype['id']] [$shift->id, $needed_angeltype['id']]
); );
@ -247,7 +248,7 @@ function user_controller()
User_view( User_view(
$user_source, $user_source,
auth()->can('admin_user'), auth()->can('admin_user'),
User_is_freeloader($user_source), $user_source->isFreeloader(),
$user_source->userAngelTypes, $user_source->userAngelTypes,
$user_source->groups, $user_source->groups,
$shifts, $shifts,
@ -300,7 +301,12 @@ function users_list_controller()
->orderBy('name') ->orderBy('name')
->get(); ->get();
foreach ($users as $user) { foreach ($users as $user) {
$user->setAttribute('freeloads', count(ShiftEntries_freeloaded_by_user($user->id))); $user->setAttribute(
'freeloads',
$user->shiftEntries()
->where('freeloaded', true)
->count()
);
} }
$users = $users->sortBy(function (User $user) use ($order_by) { $users = $users->sortBy(function (User $user) use ($order_by) {
@ -321,7 +327,7 @@ function users_list_controller()
State::whereArrived(true)->count(), State::whereArrived(true)->count(),
State::whereActive(true)->count(), State::whereActive(true)->count(),
State::whereForceActive(true)->count(), State::whereForceActive(true)->count(),
ShiftEntries_freeloaded_count(), ShiftEntry::whereFreeloaded(true)->count(),
State::whereGotShirt(true)->count(), State::whereGotShirt(true)->count(),
State::query()->sum('got_voucher') State::query()->sum('got_voucher')
) )
@ -360,6 +366,7 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
$shift_entries_source = ShiftEntries_by_ShiftsFilter($shiftsFilter); $shift_entries_source = ShiftEntries_by_ShiftsFilter($shiftsFilter);
$needed_angeltypes = []; $needed_angeltypes = [];
/** @var ShiftEntry[][] $shift_entries */
$shift_entries = []; $shift_entries = [];
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$needed_angeltypes[$shift->id] = []; $needed_angeltypes[$shift->id] = [];
@ -367,8 +374,8 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
} }
foreach ($shift_entries_source as $shift_entry) { foreach ($shift_entries_source as $shift_entry) {
if (isset($shift_entries[$shift_entry['SID']])) { if (isset($shift_entries[$shift_entry->shift_id])) {
$shift_entries[$shift_entry['SID']][] = $shift_entry; $shift_entries[$shift_entry->shift_id][] = $shift_entry;
} }
} }
@ -403,8 +410,8 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
foreach ($shift_entries[$shift->id] as $shift_entry) { foreach ($shift_entries[$shift->id] as $shift_entry) {
if ( if (
$needed_angeltype['angel_type_id'] == $shift_entry['TID'] $needed_angeltype['angel_type_id'] == $shift_entry->angel_type_id
&& $shift_entry['freeloaded'] == 0 && !$shift_entry->freeloaded
) { ) {
$taken++; $taken++;
} }

View File

@ -1,11 +1,16 @@
<?php <?php
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Collection;
function mail_shift_change(Shift $old_shift, Shift $new_shift) function mail_shift_change(Shift $old_shift, Shift $new_shift)
{ {
$users = ShiftEntries_by_shift($old_shift->id); /** @var ShiftEntry[]|Collection $shiftEntries */
$shiftEntries = $old_shift->shiftEntries()
->with(['user', 'user.settings'])
->get();
$old_room = $old_shift->room; $old_room = $old_shift->room;
$new_room = $new_shift->room; $new_room = $new_shift->room;
@ -65,8 +70,8 @@ function mail_shift_change(Shift $old_shift, Shift $new_shift)
$message .= $new_room->name . "\n\n"; $message .= $new_room->name . "\n\n";
$message .= url('/shifts', ['action' => 'view', 'shift_id' => $new_shift->id]) . "\n"; $message .= url('/shifts', ['action' => 'view', 'shift_id' => $new_shift->id]) . "\n";
foreach ($users as $user) { foreach ($shiftEntries as $shiftEntry) {
$user = (new User())->forceFill($user); $user = $shiftEntry->user;
if ($user->settings->email_shiftinfo) { if ($user->settings->email_shiftinfo) {
engelsystem_email_to_user( engelsystem_email_to_user(
$user, $user,

View File

@ -1,6 +1,8 @@
<?php <?php
use Engelsystem\Database\Db; use Engelsystem\Database\Db;
use Engelsystem\Models\Shifts\ShiftEntry;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Entity needed angeltypes describes how many angels of given type are needed for a shift or in a room. * Entity needed angeltypes describes how many angels of given type are needed for a shift or in a room.
@ -108,13 +110,16 @@ function NeededAngelTypes_by_shift($shiftId)
', [$shiftId]); ', [$shiftId]);
} }
$shift_entries = ShiftEntries_by_shift($shiftId); /** @var ShiftEntry[]|Collection $shift_entries */
$shift_entries = ShiftEntry::with('user', 'angelType')
->where('shift_id', $shiftId)
->get();
$needed_angeltypes = []; $needed_angeltypes = [];
foreach ($needed_angeltypes_source as $angeltype) { foreach ($needed_angeltypes_source as $angeltype) {
$angeltype['shift_entries'] = []; $angeltype['shift_entries'] = [];
$angeltype['taken'] = 0; $angeltype['taken'] = 0;
foreach ($shift_entries as $shift_entry) { foreach ($shift_entries as $shift_entry) {
if ($shift_entry['TID'] == $angeltype['angel_type_id'] && $shift_entry['freeloaded'] == 0) { if ($shift_entry->angel_type_id == $angeltype['angel_type_id'] && !$shift_entry->freeloaded) {
$angeltype['taken']++; $angeltype['taken']++;
$angeltype['shift_entries'][] = $shift_entry; $angeltype['shift_entries'][] = $shift_entry;
} }

View File

@ -1,153 +1,40 @@
<?php <?php
use Carbon\Carbon; use Carbon\Carbon;
use Engelsystem\Database\Db; use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Collection;
/**
* Counts all freeloaded shifts.
*
* @return int
*/
function ShiftEntries_freeloaded_count()
{
$result = Db::selectOne('SELECT COUNT(*) FROM `ShiftEntry` WHERE `freeloaded` = 1');
if (empty($result)) {
return 0;
}
return (int) array_shift($result);
}
/**
* List users subscribed to a given shift.
*
* @param int $shift_id
* @return array
*/
function ShiftEntries_by_shift($shift_id)
{
return Db::select(
'
SELECT
`users`.*,
`ShiftEntry`.`UID`,
`ShiftEntry`.`TID`,
`ShiftEntry`.`SID`,
`angel_types`.`name` AS `angel_type_name`,
`ShiftEntry`.`Comment`,
`ShiftEntry`.`freeloaded`
FROM `ShiftEntry`
JOIN `users` ON `ShiftEntry`.`UID`=`users`.`id`
JOIN `angel_types` ON `ShiftEntry`.`TID`=`angel_types`.`id`
WHERE `ShiftEntry`.`SID` = ?
',
[$shift_id]
);
}
/** /**
* Create a new shift entry. * Create a new shift entry.
*
* @param array $shift_entry
* @return bool
*/ */
function ShiftEntry_create($shift_entry) function ShiftEntry_onCreate(ShiftEntry $shiftEntry): void
{ {
$user = User::find($shift_entry['UID']); $shift = $shiftEntry->shift;
$shift = Shift($shift_entry['SID']);
$shifttype = $shift->shiftType;
$room = $shift->room;
$angeltype = AngelType::find($shift_entry['TID']);
$result = Db::insert(
'
INSERT INTO `ShiftEntry` (
`SID`,
`TID`,
`UID`,
`Comment`,
`freeload_comment`,
`freeloaded`
)
VALUES(?, ?, ?, ?, ?, ?)
',
[
$shift_entry['SID'],
$shift_entry['TID'],
$shift_entry['UID'],
$shift_entry['Comment'],
$shift_entry['freeload_comment'],
(int) $shift_entry['freeloaded'],
]
);
engelsystem_log( engelsystem_log(
'User ' . User_Nick_render($user, true) 'User ' . User_Nick_render($shiftEntry->user, true)
. ' signed up for shift ' . $shift->title . ' signed up for shift ' . $shiftEntry->shift->title
. ' (' . $shifttype->name . ')' . ' (' . $shift->shiftType->name . ')'
. ' at ' . $room->name . ' at ' . $shift->room->name
. ' from ' . $shift->start->format('Y-m-d H:i') . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . $shift->end->format('Y-m-d H:i') . ' to ' . $shift->end->format('Y-m-d H:i')
. ' as ' . $angeltype->name . ' as ' . $shiftEntry->angelType->name
); );
mail_shift_assign($user, $shift); mail_shift_assign($shiftEntry->user, $shift);
return $result;
}
/**
* Update a shift entry.
*
* @param array $shift_entry
*/
function ShiftEntry_update($shift_entry)
{
Db::update(
'
UPDATE `ShiftEntry`
SET
`Comment` = ?,
`freeload_comment` = ?,
`freeloaded` = ?
WHERE `id` = ?
',
[
$shift_entry['Comment'],
$shift_entry['freeload_comment'],
(int) $shift_entry['freeloaded'],
$shift_entry['id']
]
);
}
/**
* Get a shift entry.
*
* @param int $shift_entry_id
* @return array|null
*/
function ShiftEntry($shift_entry_id)
{
$shiftEntry = Db::selectOne('SELECT * FROM `ShiftEntry` WHERE `id` = ?', [$shift_entry_id]);
return empty($shiftEntry) ? null : $shiftEntry;
} }
/** /**
* Delete a shift entry. * Delete a shift entry.
* *
* @param array $shiftEntry * @param ShiftEntry $shiftEntry
*/ */
function ShiftEntry_delete($shiftEntry) function ShiftEntry_onDelete(ShiftEntry $shiftEntry)
{ {
Db::delete('DELETE FROM `ShiftEntry` WHERE `id` = ?', [$shiftEntry['id']]); $signout_user = $shiftEntry->user;
$shift = Shift($shiftEntry->shift);
$signout_user = User::find($shiftEntry['UID']);
$shift = Shift($shiftEntry['SID']);
$shifttype = $shift->shiftType; $shifttype = $shift->shiftType;
$room = $shift->room; $room = $shift->room;
$angeltype = AngelType::find($shiftEntry['TID']); $angeltype = $shiftEntry->angelType;
engelsystem_log( engelsystem_log(
'Shift signout: ' . User_Nick_render($signout_user, true) 'Shift signout: ' . User_Nick_render($signout_user, true)
@ -159,100 +46,44 @@ function ShiftEntry_delete($shiftEntry)
. ' as ' . $angeltype->name . ' as ' . $angeltype->name
); );
mail_shift_removed(User::find($shiftEntry['UID']), $shift); mail_shift_removed($signout_user, $shift);
} }
/** /**
* Returns next (or current) shifts of given user. * Returns next (or current) shifts of given user.
* *
* @param User $user * @param User $user
* @return array * @return ShiftEntry[]|Collection
*/ */
function ShiftEntries_upcoming_for_user(User $user) function ShiftEntries_upcoming_for_user(User $user)
{ {
return Db::select( return $user->shiftEntries()
' ->with(['shift', 'shift.shiftType'])
SELECT *, shifts.id as shift_id ->join('shifts', 'shift_entries.shift_id', 'shifts.id')
FROM `ShiftEntry` ->where('shifts.end', '>', Carbon::now())
JOIN `shifts` ON (`shifts`.`id` = `ShiftEntry`.`SID`) ->orderBy('shifts.end')
JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id` ->get();
WHERE `ShiftEntry`.`UID` = ?
AND `shifts`.`end` > NOW()
ORDER BY `shifts`.`end`
',
[
$user->id
]
);
} }
/** /**
* Returns shifts completed by the given user. * Returns shifts completed by the given user.
* *
* @param User $user * @param User $user
* @param Carbon|null $sinceTime * @param Carbon|null $sinceTime
* @return array * @return ShiftEntry[]|Collection
*/ */
function ShiftEntries_finished_by_user(User $user, Carbon $sinceTime = null) function ShiftEntries_finished_by_user(User $user, Carbon $sinceTime = null)
{ {
return Db::select( $query = $user->shiftEntries()
' ->with(['shift', 'shift.shiftType'])
SELECT * ->join('shifts', 'shift_entries.shift_id', 'shifts.id')
FROM `ShiftEntry` ->where('shifts.end', '<', Carbon::now())
JOIN `shifts` ON (`shifts`.`id` = `ShiftEntry`.`SID`) ->where('freeloaded', false)
JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id` ->orderByDesc('shifts.end');
WHERE `ShiftEntry`.`UID` = ?
AND `shifts`.`end` < NOW()
AND `ShiftEntry`.`freeloaded` = 0
' . ($sinceTime ? 'AND shifts.start >= "' . $sinceTime->toString() . '"' : '') . '
ORDER BY `shifts`.`end` desc
',
[
$user->id,
]
);
}
/** if ($sinceTime) {
* Returns all shift entries in given shift for given angeltype. $query = $query->where('shifts.start', '>=', $sinceTime);
* }
* @param int $shift_id
* @param int $angeltype_id
* @return array
*/
function ShiftEntries_by_shift_and_angeltype($shift_id, $angeltype_id)
{
return Db::select(
'
SELECT *
FROM `ShiftEntry`
WHERE `SID` = ?
AND `TID` = ?
',
[
$shift_id,
$angeltype_id,
]
);
}
/** return $query->get();
* Returns all freeloaded shifts for given user.
*
* @param int $userId
* @return array
*/
function ShiftEntries_freeloaded_by_user($userId)
{
return Db::select(
'
SELECT *
FROM `ShiftEntry`
WHERE `freeloaded` = 1
AND `UID` = ?
',
[
$userId
]
);
} }

View File

@ -4,11 +4,13 @@ use Engelsystem\Database\Db;
use Engelsystem\Helpers\Carbon; use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Engelsystem\ShiftsFilter; use Engelsystem\ShiftsFilter;
use Engelsystem\ShiftSignupState; use Engelsystem\ShiftSignupState;
use Illuminate\Support\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Collection as SupportCollection;
/** /**
* @param AngelType $angeltype * @param AngelType $angeltype
@ -57,7 +59,7 @@ function Shifts_free($start, $end, ShiftsFilter $filter = null)
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE (`end` > ? AND `start` < ?) WHERE (`end` > ? AND `start` < ?)
AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`shifts`.`id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`shifts`.`id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
> (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`shifts`.`id` AND `freeloaded`=0' . ($filter ? ' AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') > (SELECT COUNT(*) FROM `shift_entries` WHERE `shift_entries`.`shift_id`=`shifts`.`id` AND shift_entries.`freeloaded`=0' . ($filter ? ' AND shift_entries.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
AND s.shift_id IS NULL AND s.shift_id IS NULL
' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
@ -68,7 +70,7 @@ function Shifts_free($start, $end, ShiftsFilter $filter = null)
LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id
WHERE (`end` > ? AND `start` < ?) WHERE (`end` > ? AND `start` < ?)
AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`shifts`.`room_id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`shifts`.`room_id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
> (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`shifts`.`id` AND `freeloaded`=0' . ($filter ? ' AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') > (SELECT COUNT(*) FROM `shift_entries` WHERE `shift_entries`.`shift_id`=`shifts`.`id` AND `freeloaded`=0' . ($filter ? ' AND shift_entries.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ')
AND NOT s.shift_id IS NULL AND NOT s.shift_id IS NULL
' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . '
) AS `tmp` ) AS `tmp`
@ -246,41 +248,21 @@ function NeededAngeltype_by_Shift_and_Angeltype(Shift $shift, AngelType $angelty
/** /**
* @param ShiftsFilter $shiftsFilter * @param ShiftsFilter $shiftsFilter
* @return array[] * @return ShiftEntry[]|Collection
*/ */
function ShiftEntries_by_ShiftsFilter(ShiftsFilter $shiftsFilter) function ShiftEntries_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
{ {
$sql = sprintf( return ShiftEntry::with('user')
' ->join('shifts', 'shifts.id', 'shift_entries.shift_id')
SELECT ->whereIn('shifts.room_id', $shiftsFilter->getRooms())
users.*, ->whereBetween('start', [$shiftsFilter->getStart(), $shiftsFilter->getEnd()])
`ShiftEntry`.`UID`, ->get();
`ShiftEntry`.`TID`,
`ShiftEntry`.`SID`,
`ShiftEntry`.`Comment`,
`ShiftEntry`.`freeloaded`
FROM `shifts`
JOIN `ShiftEntry` ON `ShiftEntry`.`SID`=`shifts`.`id`
JOIN `users` ON `ShiftEntry`.`UID`=`users`.`id`
WHERE `shifts`.`room_id` IN (%s)
AND `start` BETWEEN ? AND ?
ORDER BY `shifts`.`start`
',
implode(',', $shiftsFilter->getRooms())
);
return Db::select(
$sql,
[
$shiftsFilter->getStart(),
$shiftsFilter->getEnd(),
]
);
} }
/** /**
* Check if a shift collides with other shifts (in time). * Check if a shift collides with other shifts (in time).
* *
* @param Shift $shift * @param Shift $shift
* @param Shift[]|Collection $shifts * @param Shift[]|Collection $shifts
* @return bool * @return bool
*/ */
@ -290,8 +272,8 @@ function Shift_collides(Shift $shift, $shifts)
if ($shift->id != $other_shift->id) { if ($shift->id != $other_shift->id) {
if ( if (
!( !(
$shift->start->timestamp >= $other_shift->end->timestamp $shift->start->timestamp >= $other_shift->end->timestamp
|| $shift->end->timestamp <= $other_shift->start->timestamp || $shift->end->timestamp <= $other_shift->start->timestamp
) )
) { ) {
return true; return true;
@ -304,15 +286,15 @@ function Shift_collides(Shift $shift, $shifts)
/** /**
* Returns the number of needed angels/free shift entries for an angeltype. * Returns the number of needed angels/free shift entries for an angeltype.
* *
* @param AngelType $needed_angeltype * @param AngelType $needed_angeltype
* @param array[] $shift_entries * @param ShiftEntry[]|Collection $shift_entries
* @return int * @return int
*/ */
function Shift_free_entries(AngelType $needed_angeltype, $shift_entries) function Shift_free_entries(AngelType $needed_angeltype, $shift_entries)
{ {
$taken = 0; $taken = 0;
foreach ($shift_entries as $shift_entry) { foreach ($shift_entries as $shift_entry) {
if ($shift_entry['freeloaded'] == 0) { if (!$shift_entry->freeloaded) {
$taken++; $taken++;
} }
} }
@ -330,7 +312,7 @@ function Shift_free_entries(AngelType $needed_angeltype, $shift_entries)
* @param array|null $user_angeltype * @param array|null $user_angeltype
* @param SHift[]|Collection|null $user_shifts List of the users shifts * @param SHift[]|Collection|null $user_shifts List of the users shifts
* @param AngelType $needed_angeltype * @param AngelType $needed_angeltype
* @param array[] $shift_entries * @param ShiftEntry[]|Collection $shift_entries
* @return ShiftSignupState * @return ShiftSignupState
*/ */
function Shift_signup_allowed_angel( function Shift_signup_allowed_angel(
@ -410,8 +392,8 @@ function Shift_signup_allowed_angel(
/** /**
* Check if an angeltype supporter can sign up a user to a shift. * Check if an angeltype supporter can sign up a user to a shift.
* *
* @param AngelType $needed_angeltype * @param AngelType $needed_angeltype
* @param array[] $shift_entries * @param ShiftEntry[]|Collection $shift_entries
* @return ShiftSignupState * @return ShiftSignupState
*/ */
function Shift_signup_allowed_angeltype_supporter(AngelType $needed_angeltype, $shift_entries) function Shift_signup_allowed_angeltype_supporter(AngelType $needed_angeltype, $shift_entries)
@ -427,8 +409,8 @@ function Shift_signup_allowed_angeltype_supporter(AngelType $needed_angeltype, $
/** /**
* Check if an admin can sign up a user to a shift. * Check if an admin can sign up a user to a shift.
* *
* @param AngelType $needed_angeltype * @param AngelType $needed_angeltype
* @param array[] $shift_entries * @param ShiftEntry[]|Collection $shift_entries
* @return ShiftSignupState * @return ShiftSignupState
*/ */
function Shift_signup_allowed_admin(AngelType $needed_angeltype, $shift_entries) function Shift_signup_allowed_admin(AngelType $needed_angeltype, $shift_entries)
@ -484,7 +466,7 @@ function Shift_signout_allowed(Shift $shift, AngelType $angeltype, $signout_user
* @param array|null $user_angeltype * @param array|null $user_angeltype
* @param Shift[]|Collection|null $user_shifts List of the users shifts * @param Shift[]|Collection|null $user_shifts List of the users shifts
* @param AngelType $needed_angeltype * @param AngelType $needed_angeltype
* @param array[] $shift_entries * @param ShiftEntry[]|Collection $shift_entries
* @return ShiftSignupState * @return ShiftSignupState
*/ */
function Shift_signup_allowed( function Shift_signup_allowed(
@ -522,10 +504,10 @@ function Shift_signup_allowed(
* Return users shifts. * Return users shifts.
* *
* @param int $userId * @param int $userId
* @param bool $include_freeload_comments * @param bool $include_freeloaded_comments
* @return Collection|Shift[] * @return SupportCollection|Shift[]
*/ */
function Shifts_by_user($userId, $include_freeload_comments = false) function Shifts_by_user($userId, $include_freeloaded_comments = false)
{ {
$shiftsData = Db::select( $shiftsData = Db::select(
' '
@ -534,19 +516,19 @@ function Shifts_by_user($userId, $include_freeload_comments = false)
`rooms`.name AS Name, `rooms`.name AS Name,
`shift_types`.`id` AS `shifttype_id`, `shift_types`.`id` AS `shifttype_id`,
`shift_types`.`name`, `shift_types`.`name`,
`ShiftEntry`.`id` as shift_entry_id, `shift_entries`.`id` as shift_entry_id,
`ShiftEntry`.`SID`, `shift_entries`.`shift_id`,
`ShiftEntry`.`TID`, `shift_entries`.`angel_type_id`,
`ShiftEntry`.`UID`, `shift_entries`.`user_id`,
`ShiftEntry`.`freeloaded`, `shift_entries`.`freeloaded`,
`ShiftEntry`.`Comment`, `shift_entries`.`user_comment`,
' . ($include_freeload_comments ? '`ShiftEntry`.`freeload_comment`, ' : '') . ' ' . ($include_freeloaded_comments ? '`shift_entries`.`freeloaded_comment`, ' : '') . '
`shifts`.* `shifts`.*
FROM `ShiftEntry` FROM `shift_entries`
JOIN `shifts` ON (`ShiftEntry`.`SID` = `shifts`.`id`) JOIN `shifts` ON (`shift_entries`.`shift_id` = `shifts`.`id`)
JOIN `shift_types` ON (`shift_types`.`id` = `shifts`.`shift_type_id`) JOIN `shift_types` ON (`shift_types`.`id` = `shifts`.`shift_type_id`)
JOIN `rooms` ON (`shifts`.`room_id` = `rooms`.`id`) JOIN `rooms` ON (`shifts`.`room_id` = `rooms`.`id`)
WHERE ShiftEntry.`UID` = ? WHERE shift_entries.`user_id` = ?
ORDER BY `start` ORDER BY `start`
', ',
[ [
@ -578,22 +560,14 @@ function Shift($shift)
return null; return null;
} }
$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]);
$neededAngels = []; $neededAngels = [];
$angelTypes = NeededAngelTypes_by_shift($shift->id); $angelTypes = NeededAngelTypes_by_shift($shift->id);
foreach ($angelTypes as $type) { foreach ($angelTypes as $type) {
$neededAngels[] = [ $neededAngels[] = [
'TID' => $type['angel_type_id'], 'angel_type_id' => $type['angel_type_id'],
'count' => $type['count'], 'count' => $type['count'],
'restricted' => $type['restricted'], 'restricted' => $type['restricted'],
'taken' => $type['taken'] 'taken' => $type['taken']
]; ];
} }
$shift->neededAngels = $neededAngels; $shift->neededAngels = $neededAngels;

View File

@ -16,10 +16,10 @@ function stats_currently_working(ShiftsFilter $filter = null)
' '
SELECT SUM(( SELECT SUM((
SELECT COUNT(*) SELECT COUNT(*)
FROM `ShiftEntry` FROM `shift_entries`
WHERE `ShiftEntry`.`SID`=`shifts`.`id` WHERE `shift_entries`.`shift_id`=`shifts`.`id`
AND `freeloaded`=0 AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND shift_entries.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
)) AS `count` )) AS `count`
FROM `shifts` FROM `shifts`
WHERE (`end` >= NOW() AND `start` <= NOW()) WHERE (`end` >= NOW() AND `start` <= NOW())
@ -89,12 +89,12 @@ function stats_angels_needed_three_hours(ShiftsFilter $filter = null)
AND `NeededAngelTypes`.`shift_id`=`shifts`.`id` AND `NeededAngelTypes`.`shift_id`=`shifts`.`id`
' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) - ( ) - (
SELECT COUNT(*) FROM `ShiftEntry` SELECT COUNT(*) FROM `shift_entries`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID` JOIN `angel_types` ON `angel_types`.`id`=`shift_entries`.`angel_type_id`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`shifts`.`id` AND `shift_entries`.`shift_id`=`shifts`.`id`
AND `freeloaded`=0 AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND shift_entries.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) )
) )
AS `count` AS `count`
@ -116,12 +116,13 @@ function stats_angels_needed_three_hours(ShiftsFilter $filter = null)
AND `NeededAngelTypes`.`room_id`=`shifts`.`room_id` AND `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) - ( ) - (
SELECT COUNT(*) FROM `ShiftEntry` SELECT COUNT(*)
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID` FROM `shift_entries`
JOIN `angel_types` ON `angel_types`.`id`=`shift_entries`.`angel_type_id`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`shifts`.`id` AND `shift_entries`.`shift_id`=`shifts`.`id`
AND `freeloaded`=0 AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND shift_entries.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) )
) )
AS `count` AS `count`
@ -168,12 +169,12 @@ function stats_angels_needed_for_nightshifts(ShiftsFilter $filter = null)
AND `NeededAngelTypes`.`shift_id`=`shifts`.`id` AND `NeededAngelTypes`.`shift_id`=`shifts`.`id`
' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) - ( ) - (
SELECT COUNT(*) FROM `ShiftEntry` SELECT COUNT(*) FROM `shift_entries`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID` JOIN `angel_types` ON `angel_types`.`id`=`shift_entries`.`angel_type_id`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`shifts`.`id` AND `shift_entries`.`shift_id`=`shifts`.`id`
AND `freeloaded`=0 AND shift_entries.`freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND shift_entries.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) )
) )
AS `count` AS `count`
@ -195,12 +196,12 @@ function stats_angels_needed_for_nightshifts(ShiftsFilter $filter = null)
AND `NeededAngelTypes`.`room_id`=`shifts`.`room_id` AND `NeededAngelTypes`.`room_id`=`shifts`.`room_id`
' . ($filter ? 'AND angel_types.id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND angel_types.id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) - ( ) - (
SELECT COUNT(*) FROM `ShiftEntry` SELECT COUNT(*) FROM `shift_entries`
JOIN `angel_types` ON `angel_types`.`id`=`ShiftEntry`.`TID` JOIN `angel_types` ON `angel_types`.`id`=`shift_entries`.`angel_type_id`
WHERE `angel_types`.`show_on_dashboard`=TRUE WHERE `angel_types`.`show_on_dashboard`=TRUE
AND `ShiftEntry`.`SID`=`shifts`.`id` AND `shift_entries`.`shift_id`=`shifts`.`id`
AND `freeloaded`=0 AND `freeloaded`=0
' . ($filter ? 'AND ShiftEntry.TID IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ' . ($filter ? 'AND shift_entries.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . '
) )
) )
AS `count` AS `count`

View File

@ -25,8 +25,8 @@ function User_tshirt_score($userId)
$shift_sum_formula = User_get_shifts_sum_query(); $shift_sum_formula = User_get_shifts_sum_query();
$result_shifts = Db::selectOne(sprintf(' $result_shifts = Db::selectOne(sprintf('
SELECT ROUND((%s) / 3600, 2) AS `tshirt_score` SELECT ROUND((%s) / 3600, 2) AS `tshirt_score`
FROM `users` LEFT JOIN `ShiftEntry` ON `users`.`id` = `ShiftEntry`.`UID` FROM `users` LEFT JOIN `shift_entries` ON `users`.`id` = `shift_entries`.`user_id`
LEFT JOIN `shifts` ON `ShiftEntry`.`SID` = `shifts`.`id` LEFT JOIN `shifts` ON `shift_entries`.`shift_id` = `shifts`.`id`
WHERE `users`.`id` = ? WHERE `users`.`id` = ?
AND `shifts`.`end` < NOW() AND `shifts`.`end` < NOW()
GROUP BY `users`.`id` GROUP BY `users`.`id`
@ -45,17 +45,6 @@ function User_tshirt_score($userId)
return $result_shifts['tshirt_score'] + $worklogHours; return $result_shifts['tshirt_score'] + $worklogHours;
} }
/**
* Returns true if user is freeloader
*
* @param User $user
* @return bool
*/
function User_is_freeloader($user)
{
return count(ShiftEntries_freeloaded_by_user($user->id)) >= config('max_freeloadable_shifts');
}
/** /**
* Returns all users that are not member of given angeltype. * Returns all users that are not member of given angeltype.
* *
@ -196,15 +185,15 @@ function User_get_eligable_voucher_count($user)
? Carbon::createFromFormat('Y-m-d', $voucher_settings['voucher_start'])->setTime(0, 0) ? Carbon::createFromFormat('Y-m-d', $voucher_settings['voucher_start'])->setTime(0, 0)
: null; : null;
$shifts = ShiftEntries_finished_by_user($user, $start); $shiftEntries = ShiftEntries_finished_by_user($user, $start);
$worklog = UserWorkLogsForUser($user->id, $start); $worklog = UserWorkLogsForUser($user->id, $start);
$shifts_done = $shifts_done =
count($shifts) count($shiftEntries)
+ $worklog->count(); + $worklog->count();
$shiftsTime = 0; $shiftsTime = 0;
foreach ($shifts as $shift) { foreach ($shiftEntries as $shiftEntry) {
$shiftsTime += (Carbon::make($shift['end'])->timestamp - Carbon::make($shift['start'])->timestamp) / 60 / 60; $shiftsTime += ($shiftEntry->shift->end->timestamp - $shiftEntry->shift->start->timestamp) / 60 / 60;
} }
foreach ($worklog as $entry) { foreach ($worklog as $entry) {
$shiftsTime += $entry->hours; $shiftsTime += $entry->hours;
@ -248,7 +237,7 @@ function User_get_shifts_sum_query()
OR (HOUR(shifts.start) <= %1$d AND HOUR(shifts.end) >= %2$d) OR (HOUR(shifts.start) <= %1$d AND HOUR(shifts.end) >= %2$d)
)) ))
* (UNIX_TIMESTAMP(shifts.end) - UNIX_TIMESTAMP(shifts.start)) * (UNIX_TIMESTAMP(shifts.end) - UNIX_TIMESTAMP(shifts.start))
* (1 - (%3$d + 1) * `ShiftEntry`.`freeloaded`) * (1 - (%3$d + 1) * `shift_entries`.`freeloaded`)
), 0) ), 0)
', ',
$nightShifts['start'], $nightShifts['start'],

View File

@ -60,7 +60,7 @@ function admin_active()
sprintf( sprintf(
' '
users.*, users.*,
COUNT(ShiftEntry.id) AS shift_count, COUNT(shift_entries.id) AS shift_count,
(%s + ( (%s + (
SELECT COALESCE(SUM(`hours`) * 3600, 0) SELECT COALESCE(SUM(`hours`) * 3600, 0)
FROM `worklogs` WHERE `user_id`=`users`.`id` FROM `worklogs` WHERE `user_id`=`users`.`id`
@ -70,8 +70,8 @@ function admin_active()
$shift_sum_formula $shift_sum_formula
) )
) )
->leftJoin('ShiftEntry', 'users.id', '=', 'ShiftEntry.UID') ->leftJoin('shift_entries', 'users.id', '=', 'shift_entries.user_id')
->leftJoin('shifts', 'ShiftEntry.SID', '=', 'shifts.id') ->leftJoin('shifts', 'shift_entries.shift_id', '=', 'shifts.id')
->leftJoin('users_state', 'users.id', '=', 'users_state.user_id') ->leftJoin('users_state', 'users.id', '=', 'users_state.user_id')
->where('users_state.arrived', '=', true) ->where('users_state.arrived', '=', true)
->groupBy('users.id') ->groupBy('users.id')
@ -152,7 +152,7 @@ function admin_active()
sprintf( sprintf(
' '
users.*, users.*,
COUNT(ShiftEntry.id) AS shift_count, COUNT(shift_entries.id) AS shift_count,
(%s + ( (%s + (
SELECT COALESCE(SUM(`hours`) * 3600, 0) SELECT COALESCE(SUM(`hours`) * 3600, 0)
FROM `worklogs` WHERE `user_id`=`users`.`id` FROM `worklogs` WHERE `user_id`=`users`.`id`
@ -162,10 +162,10 @@ function admin_active()
$shift_sum_formula $shift_sum_formula
) )
) )
->leftJoin('ShiftEntry', 'users.id', '=', 'ShiftEntry.UID') ->leftJoin('shift_entries', 'users.id', '=', 'shift_entries.user_id')
->leftJoin('shifts', function ($join) use ($show_all_shifts) { ->leftJoin('shifts', function ($join) use ($show_all_shifts) {
/** @var JoinClause $join */ /** @var JoinClause $join */
$join->on('ShiftEntry.SID', '=', 'shifts.id'); $join->on('shift_entries.shift_id', '=', 'shifts.id');
if (!$show_all_shifts) { if (!$show_all_shifts) {
$join->where(function ($query) { $join->where(function ($query) {
/** @var Builder $query */ /** @var Builder $query */

View File

@ -40,11 +40,11 @@ function admin_free()
if ($request->has('submit')) { if ($request->has('submit')) {
$query = User::with('personalData') $query = User::with('personalData')
->select('users.*') ->select('users.*')
->leftJoin('ShiftEntry', 'users.id', 'ShiftEntry.UID') ->leftJoin('shift_entries', 'users.id', 'shift_entries.user_id')
->leftJoin('users_state', 'users.id', 'users_state.user_id') ->leftJoin('users_state', 'users.id', 'users_state.user_id')
->leftJoin('shifts', function ($join) { ->leftJoin('shifts', function ($join) {
/** @var JoinClause $join */ /** @var JoinClause $join */
$join->on('ShiftEntry.SID', '=', 'shifts.id') $join->on('shift_entries.shift_id', '=', 'shifts.id')
->where('shifts.start', '<', Carbon::now()) ->where('shifts.start', '<', Carbon::now())
->where('shifts.end', '>', Carbon::now()); ->where('shifts.end', '>', Carbon::now());
}) })

View File

@ -189,15 +189,14 @@ function admin_rooms()
$shifts = $room->shifts; $shifts = $room->shifts;
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$shift = Shift($shift); $shift = Shift($shift);
foreach ($shift->shiftEntry as $entry) { foreach ($shift->shiftEntries as $entry) {
$type = AngelType::find($entry['TID']);
event('shift.entry.deleting', [ event('shift.entry.deleting', [
'user' => User::find($entry['user_id']), 'user' => User::find($entry['user_id']),
'start' => $shift->start, 'start' => $shift->start,
'end' => $shift->end, 'end' => $shift->end,
'name' => $shift->shiftType->name, 'name' => $shift->shiftType->name,
'title' => $shift->title, 'title' => $shift->title,
'type' => $type->name, 'type' => $entry->angelType->name,
'room' => $room, 'room' => $room,
'freeloaded' => (bool) $entry['freeloaded'], 'freeloaded' => (bool) $entry['freeloaded'],
]); ]);

View File

@ -563,17 +563,16 @@ function admin_shifts_history(): string
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
$shift = Shift($shift); $shift = Shift($shift);
foreach ($shift->shiftEntry as $entry) { foreach ($shift->shiftEntries as $entry) {
$type = AngelType::find($entry['TID']);
event('shift.entry.deleting', [ event('shift.entry.deleting', [
'user' => User::find($entry['user_id']), 'user' => $entry->user,
'start' => $shift->start, 'start' => $shift->start,
'end' => $shift->end, 'end' => $shift->end,
'name' => $shift->shiftType->name, 'name' => $shift->shiftType->name,
'title' => $shift->title, 'title' => $shift->title,
'type' => $type->name, 'type' => $entry->angelType->name,
'room' => $shift->room, 'room' => $shift->room,
'freeloaded' => (bool) $entry['freeloaded'], 'freeloaded' => $entry->freeloaded,
]); ]);
} }

View File

@ -266,15 +266,15 @@ class ImportSchedule extends BaseController
protected function fireDeleteShiftEntryEvents(Event $event): void protected function fireDeleteShiftEntryEvents(Event $event): void
{ {
$shiftEntries = $this->db $shiftEntries = $this->db
->table('ShiftEntry') ->table('shift_entries')
->select([ ->select([
'shift_types.name', 'shifts.title', 'angel_types.name AS type', 'rooms.id AS room_id', 'shift_types.name', 'shifts.title', 'angel_types.name AS type', 'rooms.id AS room_id',
'shifts.start', 'shifts.end', 'ShiftEntry.UID as user_id', 'ShiftEntry.freeloaded' 'shifts.start', 'shifts.end', 'shift_entries.user_id', 'shift_entries.freeloaded'
]) ])
->join('shifts', 'shifts.id', 'ShiftEntry.SID') ->join('shifts', 'shifts.id', 'shift_entries.shift_id')
->join('schedule_shift', 'shifts.id', 'schedule_shift.shift_id') ->join('schedule_shift', 'shifts.id', 'schedule_shift.shift_id')
->join('rooms', 'rooms.id', 'shifts.room_id') ->join('rooms', 'rooms.id', 'shifts.room_id')
->join('angel_types', 'angel_types.id', 'ShiftEntry.TID') ->join('angel_types', 'angel_types.id', 'shift_entries.angel_type_id')
->join('shift_types', 'shift_types.id', 'shifts.shift_type_id') ->join('shift_types', 'shift_types.id', 'shifts.shift_type_id')
->where('schedule_shift.guid', $event->getGuid()) ->where('schedule_shift.guid', $event->getGuid())
->get(); ->get();

View File

@ -60,8 +60,8 @@ function make_ical_entry_from_shift(Shift $shift)
$output .= 'UID:' . md5($shift->start->timestamp . $shift->end->timestamp . $shift->shiftType->name) . "\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) $output .= 'SUMMARY:' . str_replace("\n", "\\n", $shift->shiftType->name)
. ' (' . str_replace("\n", "\\n", $shift->title) . ")\r\n"; . ' (' . str_replace("\n", "\\n", $shift->title) . ")\r\n";
if (isset($shift->Comment)) { if (isset($shift->user_comment)) {
$output .= 'DESCRIPTION:' . str_replace("\n", "\\n", $shift->Comment) . "\r\n"; $output .= 'DESCRIPTION:' . str_replace("\n", "\\n", $shift->user_comment) . "\r\n";
} }
$output .= 'DTSTAMP:' . $shift->start->utc()->format('Ymd\THis\Z') . "\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 .= 'DTSTART:' . $shift->start->utc()->format('Ymd\THis\Z') . "\r\n";

View File

@ -1,7 +1,6 @@
<?php <?php
use Engelsystem\Database\Db; use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
/** /**
@ -49,60 +48,38 @@ function user_myshifts()
]); ]);
} elseif ($request->has('edit') && preg_match('/^\d+$/', $request->input('edit'))) { } elseif ($request->has('edit') && preg_match('/^\d+$/', $request->input('edit'))) {
$shift_entry_id = $request->input('edit'); $shift_entry_id = $request->input('edit');
$shift = Db::selectOne( /** @var ShiftEntry $shiftEntry */
' $shiftEntry = ShiftEntry::where('id', $shift_entry_id)
SELECT ->where('user_id', $shifts_user->id)
`ShiftEntry`.`freeloaded`, ->with(['shift', 'shift.shiftType', 'shift.room', 'user'])
`ShiftEntry`.`freeload_comment`, ->first();
`ShiftEntry`.`Comment`, if (!empty($shiftEntry)) {
`ShiftEntry`.`UID`, $shift = $shiftEntry->shift;
`shift_types`.`name`, $freeloaded = $shiftEntry->freeloaded;
`shifts`.*, $freeloaded_comment = $shiftEntry->freeloaded_comment;
`angel_types`.`name` AS `angel_type`
FROM `ShiftEntry`
JOIN `angel_types` ON (`ShiftEntry`.`TID` = `angel_types`.`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
',
[
$shift_entry_id,
$shifts_user->id,
]
);
if (!empty($shift)) {
/** @var Shift $shift */
$shift = (new Shift())->forceFill($shift);
$freeloaded = $shift->freeloaded;
$freeload_comment = $shift->freeloaded_comment;
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
$valid = true; $valid = true;
if (auth()->can('user_shifts_admin')) { if (auth()->can('user_shifts_admin')) {
$freeloaded = $request->has('freeloaded'); $freeloaded = $request->has('freeloaded');
$freeload_comment = strip_request_item_nl('freeload_comment'); $freeloaded_comment = strip_request_item_nl('freeloaded_comment');
if ($freeloaded && $freeload_comment == '') { if ($freeloaded && $freeloaded_comment == '') {
$valid = false; $valid = false;
error(__('Please enter a freeload comment!')); error(__('Please enter a freeload comment!'));
} }
} }
$comment = $shift->Comment; $comment = $shiftEntry->user_comment;
$user_source = User::find($shift->UID); $user_source = $shiftEntry->user;
if (auth()->user()->id == $user_source->id) { if (auth()->user()->id == $user_source->id) {
$comment = strip_request_item_nl('comment'); $comment = strip_request_item_nl('comment');
} }
if ($valid) { if ($valid) {
ShiftEntry_update([ $shiftEntry->user_comment = $comment;
'id' => $shift_entry_id, $shiftEntry->freeloaded = $freeloaded;
'Comment' => $comment, $shiftEntry->freeloaded_comment = $freeloaded_comment;
'freeloaded' => $freeloaded, $shiftEntry->save();
'freeload_comment' => $freeload_comment
]);
engelsystem_log( engelsystem_log(
'Updated ' . User_Nick_render($user_source, true) . '\'s shift ' 'Updated ' . User_Nick_render($user_source, true) . '\'s shift '
@ -110,7 +87,7 @@ function user_myshifts()
. ' from ' . $shift->start->format('Y-m-d H:i') . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . $shift->end->format('Y-m-d H:i') . ' to ' . $shift->end->format('Y-m-d H:i')
. ' with comment ' . $comment . ' with comment ' . $comment
. '. Freeloaded: ' . ($freeloaded ? 'YES Comment: ' . $freeload_comment : 'NO') . '. Freeloaded: ' . ($freeloaded ? 'YES Comment: ' . $freeloaded_comment : 'NO')
); );
success(__('Shift saved.')); success(__('Shift saved.'));
throw_redirect(page_link_to('users', ['action' => 'view', 'user_id' => $shifts_user->id])); throw_redirect(page_link_to('users', ['action' => 'view', 'user_id' => $shifts_user->id]));
@ -122,10 +99,10 @@ function user_myshifts()
$shift->start->format('Y-m-d H:i') . ', ' . shift_length($shift), $shift->start->format('Y-m-d H:i') . ', ' . shift_length($shift),
$shift->room->name, $shift->room->name,
$shift->shiftType->name, $shift->shiftType->name,
$shift->angel_type, $shiftEntry->angelType->name,
$shift->Comment, $shiftEntry->user_comment,
$shift->freeloaded, $shiftEntry->freeloaded,
$shift->freeload_comment, $shiftEntry->freeloaded_comment,
auth()->can('user_shifts_admin') auth()->can('user_shifts_admin')
); );
} else { } else {

View File

@ -31,7 +31,7 @@ function user_shifts()
{ {
$request = request(); $request = request();
if (User_is_freeloader(auth()->user())) { if (auth()->user()->isFreeloader()) {
throw_redirect(page_link_to('user_myshifts')); throw_redirect(page_link_to('user_myshifts'));
} }

View File

@ -3,6 +3,8 @@
namespace Engelsystem; namespace Engelsystem;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Illuminate\Support\Collection;
class ShiftCalendarRenderer class ShiftCalendarRenderer
{ {
@ -45,10 +47,10 @@ class ShiftCalendarRenderer
/** /**
* ShiftCalendarRenderer constructor. * ShiftCalendarRenderer constructor.
* *
* @param Shift[] $shifts * @param Shift[] $shifts
* @param array[] $needed_angeltypes * @param array[] $needed_angeltypes
* @param array[] $shift_entries * @param ShiftEntry[][]|Collection $shift_entries
* @param ShiftsFilter $shiftsFilter * @param ShiftsFilter $shiftsFilter
*/ */
public function __construct($shifts, private $needed_angeltypes, private $shift_entries, ShiftsFilter $shiftsFilter) public function __construct($shifts, private $needed_angeltypes, private $shift_entries, ShiftsFilter $shiftsFilter)
{ {
@ -204,7 +206,7 @@ class ShiftCalendarRenderer
/** /**
* Renders a tick/block for given time * Renders a tick/block for given time
* *
* @param int $time unix timestamp * @param int $time unix timestamp
* @param boolean $label Should time labels be generated? * @param boolean $label Should time labels be generated?
* @return string rendered tick html * @return string rendered tick html
*/ */

View File

@ -4,7 +4,9 @@ namespace Engelsystem;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Support\Collection;
use function theme_type; use function theme_type;
@ -16,10 +18,10 @@ class ShiftCalendarShiftRenderer
/** /**
* Renders a shift * Renders a shift
* *
* @param Shift $shift The shift to render * @param Shift $shift The shift to render
* @param array[] $needed_angeltypes * @param array[] $needed_angeltypes
* @param array $shift_entries * @param ShiftEntry[]|Collection $shift_entries
* @param User $user The user who is viewing the shift calendar * @param User $user The user who is viewing the shift calendar
* @return array * @return array
*/ */
public function render(Shift $shift, $needed_angeltypes, $shift_entries, $user) public function render(Shift $shift, $needed_angeltypes, $shift_entries, $user)
@ -80,10 +82,10 @@ class ShiftCalendarShiftRenderer
} }
/** /**
* @param Shift $shift * @param Shift $shift
* @param array[] $needed_angeltypes * @param array[] $needed_angeltypes
* @param array[] $shift_entries * @param ShiftEntry[]|Collection $shift_entries
* @param User $user * @param User $user
* @return array * @return array
*/ */
private function renderShiftNeededAngeltypes(Shift $shift, $needed_angeltypes, $shift_entries, $user) private function renderShiftNeededAngeltypes(Shift $shift, $needed_angeltypes, $shift_entries, $user)
@ -93,7 +95,7 @@ class ShiftCalendarShiftRenderer
$shift_entries_filtered[$needed_angeltype['id']] = []; $shift_entries_filtered[$needed_angeltype['id']] = [];
} }
foreach ($shift_entries as $shift_entry) { foreach ($shift_entries as $shift_entry) {
$shift_entries_filtered[$shift_entry['TID']][] = $shift_entry; $shift_entries_filtered[$shift_entry->angel_type_id][] = $shift_entry;
} }
$html = ''; $html = '';
@ -144,11 +146,11 @@ class ShiftCalendarShiftRenderer
/** /**
* Renders a list entry containing the needed angels for an angeltype * Renders a list entry containing the needed angels for an angeltype
* *
* @param Shift $shift The shift which is rendered * @param Shift $shift The shift which is rendered
* @param array[] $shift_entries * @param ShiftEntry[]|Collection $shift_entries
* @param array $angeltype The angeltype, containing information about needed angeltypes * @param array $angeltype The angeltype, containing information about needed angeltypes
* and already signed up angels * and already signed up angels
* @param User $user The user who is viewing the shift calendar * @param User $user The user who is viewing the shift calendar
* @return array * @return array
*/ */
private function renderShiftNeededAngeltype(Shift $shift, $shift_entries, $angeltype, $user) private function renderShiftNeededAngeltype(Shift $shift, $shift_entries, $angeltype, $user)
@ -156,8 +158,8 @@ class ShiftCalendarShiftRenderer
$angeltype = (new AngelType())->forceFill($angeltype); $angeltype = (new AngelType())->forceFill($angeltype);
$entry_list = []; $entry_list = [];
foreach ($shift_entries as $entry) { foreach ($shift_entries as $entry) {
$class = $entry['freeloaded'] ? 'text-decoration-line-through' : ''; $class = $entry->freeloaded ? 'text-decoration-line-through' : '';
$entry_list[] = '<span class="text-nowrap ' . $class . '">' . User_Nick_render($entry) . '</span>'; $entry_list[] = '<span class="text-nowrap ' . $class . '">' . User_Nick_render($entry->user) . '</span>';
} }
$shift_signup_state = Shift_signup_allowed( $shift_signup_state = Shift_signup_allowed(
$user, $user,

View File

@ -39,11 +39,11 @@ function ShiftEntry_delete_view_admin(Shift $shift, AngelType $angeltype, User $
* *
* @param Shift $shift * @param Shift $shift
* @param AngelType $angeltype * @param AngelType $angeltype
* @param int $signoff_user_id * @param User $signoff_user
* *
* @return string HTML * @return string HTML
*/ */
function ShiftEntry_delete_view(Shift $shift, AngelType $angeltype, $signoff_user_id) function ShiftEntry_delete_view(Shift $shift, AngelType $angeltype, User $signoff_user)
{ {
return page_with_title(ShiftEntry_delete_title(), [ return page_with_title(ShiftEntry_delete_title(), [
info(sprintf( info(sprintf(
@ -56,7 +56,7 @@ function ShiftEntry_delete_view(Shift $shift, AngelType $angeltype, $signoff_use
form([ form([
buttons([ buttons([
button(user_link($signoff_user_id), icon('x-lg') . __('cancel')), button(user_link($signoff_user->id), icon('x-lg') . __('cancel')),
form_submit('delete', icon('trash') . __('delete'), 'btn-danger', false), form_submit('delete', icon('trash') . __('delete'), 'btn-danger', false),
]), ]),
]), ]),
@ -180,7 +180,7 @@ function ShiftEntry_create_title()
* @param string $type * @param string $type
* @param string $comment * @param string $comment
* @param bool $freeloaded * @param bool $freeloaded
* @param string $freeload_comment * @param string $freeloaded_comment
* @param bool $user_admin_shifts * @param bool $user_admin_shifts
* @return string * @return string
*/ */
@ -192,7 +192,7 @@ function ShiftEntry_edit_view(
$type, $type,
$comment, $comment,
$freeloaded, $freeloaded,
$freeload_comment, $freeloaded_comment,
$user_admin_shifts = false $user_admin_shifts = false
) { ) {
$freeload_form = []; $freeload_form = [];
@ -200,9 +200,9 @@ function ShiftEntry_edit_view(
$freeload_form = [ $freeload_form = [
form_checkbox('freeloaded', __('Freeloaded'), $freeloaded), form_checkbox('freeloaded', __('Freeloaded'), $freeloaded),
form_textarea( form_textarea(
'freeload_comment', 'freeloaded_comment',
__('Freeload comment (Only for shift coordination):'), __('Freeload comment (Only for shift coordination):'),
$freeload_comment $freeloaded_comment
) )
]; ];
} }

View File

@ -3,8 +3,8 @@
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Engelsystem\ShiftSignupState; use Engelsystem\ShiftSignupState;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -75,8 +75,8 @@ function Shift_editor_info_render(Shift $shift)
} }
/** /**
* @param Shift $shift * @param Shift $shift
* @param AngelType $angeltype * @param AngelType $angeltype
* @return string * @return string
*/ */
function Shift_signup_button_render(Shift $shift, AngelType $angeltype) function Shift_signup_button_render(Shift $shift, AngelType $angeltype)
@ -136,16 +136,16 @@ function Shift_view(Shift $shift, ShiftType $shifttype, Room $room, $angeltypes_
$needed_angels .= Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shift, $user_shift_admin); $needed_angels .= Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shift, $user_shift_admin);
} }
$shiftEntry = new Collection($shift->shiftEntry); $shiftEntry = $shift->shiftEntries;
foreach ($shiftEntry->groupBy('TID') as $angelTypes) { foreach ($shiftEntry->groupBy('angel_type_id') as $angelTypes) {
/** @var Collection $angelTypes */ /** @var Collection $angelTypes */
$type = $angelTypes->first()['TID']; $type = $angelTypes->first()['angel_type_id'];
if (!$neededAngels->where('TID', $type)->first()) { if (!$neededAngels->where('angel_type_id', $type)->first()) {
$needed_angels .= Shift_view_render_needed_angeltype([ $needed_angels .= Shift_view_render_needed_angeltype([
'TID' => $type, 'angel_type_id' => $type,
'count' => 0, 'count' => 0,
'restricted' => true, 'restricted' => true,
'taken' => $angelTypes->count(), 'taken' => $angelTypes->count(),
], $angeltypes, $shift, $user_shift_admin); ], $angeltypes, $shift, $user_shift_admin);
} }
} }
@ -213,7 +213,7 @@ function Shift_view(Shift $shift, ShiftType $shifttype, Room $room, $angeltypes_
*/ */
function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, Shift $shift, $user_shift_admin) function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, Shift $shift, $user_shift_admin)
{ {
$angeltype = $angeltypes[$needed_angeltype['TID']]; $angeltype = $angeltypes[$needed_angeltype['angel_type_id']];
$angeltype_supporter = auth()->user()->isAngelTypeSupporter($angeltype) $angeltype_supporter = auth()->user()->isAngelTypeSupporter($angeltype)
|| auth()->can('admin_user_angeltypes'); || auth()->can('admin_user_angeltypes');
@ -242,8 +242,8 @@ function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, Shif
); );
$angels = []; $angels = [];
foreach ($shift->shiftEntry as $shift_entry) { foreach ($shift->shiftEntries as $shift_entry) {
if ($shift_entry['TID'] == $needed_angeltype['TID']) { if ($shift_entry->angel_type_id == $needed_angeltype['angel_type_id']) {
$angels[] = Shift_view_render_shift_entry($shift_entry, $user_shift_admin, $angeltype_supporter, $shift); $angels[] = Shift_view_render_shift_entry($shift_entry, $user_shift_admin, $angeltype_supporter, $shift);
} }
} }
@ -255,30 +255,30 @@ function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, Shif
} }
/** /**
* @param array $shift_entry * @param ShiftEntry $shift_entry
* @param bool $user_shift_admin * @param bool $user_shift_admin
* @param bool $angeltype_supporter * @param bool $angeltype_supporter
* @param Shift $shift * @param Shift $shift
* @return string * @return string
*/ */
function Shift_view_render_shift_entry($shift_entry, $user_shift_admin, $angeltype_supporter, Shift $shift) function Shift_view_render_shift_entry(ShiftEntry $shift_entry, $user_shift_admin, $angeltype_supporter, Shift $shift)
{ {
$entry = User_Nick_render(User::find($shift_entry['UID'])); $entry = User_Nick_render($shift_entry->user);
if ($shift_entry['freeloaded']) { if ($shift_entry->freeloaded) {
$entry = '<del>' . $entry . '</del>'; $entry = '<del>' . $entry . '</del>';
} }
$isUser = $shift_entry['UID'] == auth()->user()->id; $isUser = $shift_entry->user_id == auth()->user()->id;
if ($user_shift_admin || $angeltype_supporter || $isUser) { if ($user_shift_admin || $angeltype_supporter || $isUser) {
$entry .= ' <div class="btn-group m-1">'; $entry .= ' <div class="btn-group m-1">';
if ($user_shift_admin || $isUser) { if ($user_shift_admin || $isUser) {
$entry .= button_icon( $entry .= button_icon(
page_link_to('user_myshifts', ['edit' => $shift_entry['id'], 'id' => $shift_entry['UID']]), page_link_to('user_myshifts', ['edit' => $shift_entry->id, 'id' => $shift_entry->user_id]),
'pencil', 'pencil',
'btn-sm' 'btn-sm'
); );
} }
$angeltype = AngelType::find($shift_entry['TID']); $angeltype = $shift_entry->angelType;
$disabled = Shift_signout_allowed($shift, $angeltype, $shift_entry['UID']) ? '' : ' btn-disabled'; $disabled = Shift_signout_allowed($shift, $angeltype, $shift_entry->user_id) ? '' : ' btn-disabled';
$entry .= button_icon(shift_entry_delete_link($shift_entry), 'trash', 'btn-sm' . $disabled); $entry .= button_icon(shift_entry_delete_link($shift_entry), 'trash', 'btn-sm' . $disabled);
$entry .= '</div>'; $entry .= '</div>';
} }

View File

@ -4,6 +4,7 @@ use Carbon\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Group; use Engelsystem\Models\Group;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Models\Worklog; use Engelsystem\Models\Worklog;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -197,14 +198,15 @@ function User_shift_state_render($user)
} }
$upcoming_shifts = ShiftEntries_upcoming_for_user($user); $upcoming_shifts = ShiftEntries_upcoming_for_user($user);
if (empty($upcoming_shifts)) { if ($upcoming_shifts->isEmpty()) {
return '<span class="text-success">' . __('Free') . '</span>'; return '<span class="text-success">' . __('Free') . '</span>';
} }
$nextShift = array_shift($upcoming_shifts); /** @var ShiftEntry $nextShiftEntry */
$nextShiftEntry = $upcoming_shifts->first();
$start = Carbon::make($nextShift['start']); $start = $nextShiftEntry->shift->start;
$end = Carbon::make($nextShift['end']); $end = $nextShiftEntry->shift->end;
$startFormat = $start->format(__('Y-m-d H:i')); $startFormat = $start->format(__('Y-m-d H:i'));
$endFormat = $end->format(__('Y-m-d H:i')); $endFormat = $end->format(__('Y-m-d H:i'));
$startTimestamp = $start->timestamp; $startTimestamp = $start->timestamp;
@ -244,8 +246,9 @@ function User_last_shift_render($user)
return ''; return '';
} }
$lastShift = array_shift($last_shifts); /** @var ShiftEntry $lastShiftEntry */
$end = Carbon::make($lastShift['end']); $lastShiftEntry = $last_shifts->first();
$end = $lastShiftEntry->shift->end;
return '<span title="' . $end->format(__('Y-m-d H:i')) . '" data-countdown-ts="' . $end->timestamp . '">' return '<span title="' . $end->format(__('Y-m-d H:i')) . '" data-countdown-ts="' . $end->timestamp . '">'
. __('Shift ended %c') . __('Shift ended %c')
@ -307,7 +310,7 @@ function User_view_myshift(Shift $shift, $user_source, $its_me)
]; ];
if ($its_me) { if ($its_me) {
$myshift['comment'] = $shift->Comment; $myshift['comment'] = $shift->user_comment;
} }
if ($shift->freeloaded) { if ($shift->freeloaded) {
@ -316,7 +319,7 @@ function User_view_myshift(Shift $shift, $user_source, $its_me)
. '</p>'; . '</p>';
if (auth()->can('user_shifts_admin')) { if (auth()->can('user_shifts_admin')) {
$myshift['comment'] .= '<br />' $myshift['comment'] .= '<br />'
. '<p class="text-danger">' . __('Freeloaded') . ': ' . $shift->freeload_comment . '</p>'; . '<p class="text-danger">' . __('Freeloaded') . ': ' . $shift->freeloaded_comment . '</p>';
} else { } else {
$myshift['comment'] .= '<br /><p class="text-danger">' . __('Freeloaded') . '</p>'; $myshift['comment'] .= '<br /><p class="text-danger">' . __('Freeloaded') . '</p>';
} }
@ -332,7 +335,8 @@ function User_view_myshift(Shift $shift, $user_source, $its_me)
'btn-sm' 'btn-sm'
); );
} }
if (Shift_signout_allowed($shift, (new AngelType())->forceFill(['id' => $shift->TID]), $user_source->id)) {
if (Shift_signout_allowed($shift, (new AngelType())->forceFill(['id' => $shift->angel_type_id]), $user_source->id)) {
$myshift['actions'][] = button( $myshift['actions'][] = button(
shift_entry_delete_link($shift), shift_entry_delete_link($shift),
icon('trash') . __('sign off'), icon('trash') . __('sign off'),
@ -517,7 +521,7 @@ function User_view(
return page_with_title( return page_with_title(
'<span class="icon-icon_angel"></span> ' '<span class="icon-icon_angel"></span> '
. ( . (
(config('enable_pronoun') && $user_source->personalData->pronoun) (config('enable_pronoun') && $user_source->personalData->pronoun)
? '<small>' . htmlspecialchars($user_source->personalData->pronoun) . '</small> ' ? '<small>' . htmlspecialchars($user_source->personalData->pronoun) . '</small> '
: '' : ''
) )
@ -554,7 +558,7 @@ function User_view(
), ),
icon('valentine') . __('Vouchers') icon('valentine') . __('Vouchers')
) )
: '', : '',
$admin_user_worklog_privilege ? button( $admin_user_worklog_privilege ? button(
url('/admin/user/' . $user_source->id . '/worklog'), url('/admin/user/' . $user_source->id . '/worklog'),
icon('clock-history') . __('worklog.add') icon('clock-history') . __('worklog.add')
@ -572,13 +576,13 @@ function User_view(
icon('braces') . __('JSON Export') icon('braces') . __('JSON Export')
) : '', ) : '',
($its_me && ( ($its_me && (
$auth->can('shifts_json_export') $auth->can('shifts_json_export')
|| $auth->can('ical') || $auth->can('ical')
|| $auth->can('atom') || $auth->can('atom')
)) ? button( )) ? button(
page_link_to('user_myshifts', ['reset' => 1]), page_link_to('user_myshifts', ['reset' => 1]),
icon('arrow-repeat') . __('Reset API key') icon('arrow-repeat') . __('Reset API key')
) : '' ) : ''
]) ])
]) ])
]), ]),
@ -591,7 +595,7 @@ function User_view(
. $user_source->contact->dect . $user_source->contact->dect
. '</a>' . '</a>'
) )
: '' , : '',
config('enable_mobile_show') && $user_source->contact->mobile ? config('enable_mobile_show') && $user_source->contact->mobile ?
$user_source->settings->mobile_show ? $user_source->settings->mobile_show ?
heading( heading(
@ -600,15 +604,15 @@ function User_view(
. $user_source->contact->mobile . $user_source->contact->mobile
. '</a>' . '</a>'
) )
: '' : ''
: '' , : '',
$auth->can('user_messages') ? $auth->can('user_messages') ?
heading( heading(
'<a href="' . page_link_to('/messages/' . $user_source->id) . '">' '<a href="' . page_link_to('/messages/' . $user_source->id) . '">'
. icon('envelope') . icon('envelope')
. '</a>' . '</a>'
) )
: '' , : '',
]), ]),
User_view_state($admin_user_privilege, $freeloader, $user_source), User_view_state($admin_user_privilege, $freeloader, $user_source),
User_angeltypes_render($user_angeltypes), User_angeltypes_render($user_angeltypes),
@ -794,8 +798,8 @@ function User_oauth_render(User $user)
foreach ($user->oauth as $oauth) { foreach ($user->oauth as $oauth) {
$output[] = __( $output[] = __(
isset($config[$oauth->provider]['name']) isset($config[$oauth->provider]['name'])
? $config[$oauth->provider]['name'] ? $config[$oauth->provider]['name']
: Str::ucfirst($oauth->provider) : Str::ucfirst($oauth->provider)
); );
} }
@ -887,7 +891,7 @@ function render_user_departure_date_hint()
*/ */
function render_user_freeloader_hint() function render_user_freeloader_hint()
{ {
if (User_is_freeloader(auth()->user())) { if (auth()->user()->isFreeloader()) {
return sprintf( return sprintf(
__('You freeloaded at least %s shifts. Shift signup is locked. Please go to heavens desk to be unlocked again.'), __('You freeloaded at least %s shifts. Shift signup is locked. Please go to heavens desk to be unlocked again.'),
config('max_freeloadable_shifts') config('max_freeloadable_shifts')

View File

@ -45,27 +45,25 @@ class Stats
$query = State::whereArrived(true); $query = State::whereArrived(true);
if (!is_null($working)) { if (!is_null($working)) {
// @codeCoverageIgnoreStart
$query $query
->leftJoin('worklogs', 'worklogs.user_id', '=', 'users_state.user_id') ->leftJoin('worklogs', 'worklogs.user_id', '=', 'users_state.user_id')
->leftJoin('ShiftEntry', 'ShiftEntry.UID', '=', 'users_state.user_id') ->leftJoin('shift_entries', 'shift_entries.user_id', '=', 'users_state.user_id')
->distinct(); ->distinct();
$query->where(function ($query) use ($working): void { $query->where(function ($query) use ($working): void {
/** @var QueryBuilder $query */ /** @var QueryBuilder $query */
if ($working) { if ($working) {
$query $query
->whereNotNull('ShiftEntry.SID') ->whereNotNull('shift_entries.shift_id')
->orWhereNotNull('worklogs.hours'); ->orWhereNotNull('worklogs.hours');
return; return;
} }
$query $query
->whereNull('ShiftEntry.SID') ->whereNull('shift_entries.shift_id')
->whereNull('worklogs.hours'); ->whereNull('worklogs.hours');
}); });
// @codeCoverageIgnoreEnd
} }
return $query->count('users_state.user_id'); return $query->count('users_state.user_id');
@ -104,18 +102,17 @@ class Stats
* The number of currently working users * The number of currently working users
* *
* @param bool|null $freeloaded * @param bool|null $freeloaded
* @codeCoverageIgnore
*/ */
public function currentlyWorkingUsers(bool $freeloaded = null): int public function currentlyWorkingUsers(bool $freeloaded = null): int
{ {
$query = User::query() $query = User::query()
->join('ShiftEntry', 'ShiftEntry.UID', '=', 'users.id') ->join('shift_entries', 'shift_entries.user_id', '=', 'users.id')
->join('shifts', 'shifts.id', '=', 'ShiftEntry.SID') ->join('shifts', 'shifts.id', '=', 'shift_entries.shift_id')
->where('shifts.start', '<=', Carbon::now()) ->where('shifts.start', '<=', Carbon::now())
->where('shifts.end', '>', Carbon::now()); ->where('shifts.end', '>', Carbon::now());
if (!is_null($freeloaded)) { if (!is_null($freeloaded)) {
$query->where('ShiftEntry.freeloaded', '=', $freeloaded); $query->where('shift_entries.freeloaded', '=', $freeloaded);
} }
return $query->count(); return $query->count();
@ -202,13 +199,13 @@ class Stats
* @param bool|null $done * @param bool|null $done
* @param bool|null $freeloaded * @param bool|null $freeloaded
* *
* @codeCoverageIgnore * @codeCoverageIgnore because it is only used in functions that use TIMESTAMPDIFF
*/ */
protected function workSecondsQuery(bool $done = null, bool $freeloaded = null): QueryBuilder protected function workSecondsQuery(bool $done = null, bool $freeloaded = null): QueryBuilder
{ {
$query = $this $query = $this
->getQuery('ShiftEntry') ->getQuery('shift_entries')
->join('shifts', 'shifts.id', '=', 'ShiftEntry.SID'); ->join('shifts', 'shifts.id', '=', 'shift_entries.shift_id');
if (!is_null($freeloaded)) { if (!is_null($freeloaded)) {
$query->where('freeloaded', '=', $freeloaded); $query->where('freeloaded', '=', $freeloaded);
@ -227,13 +224,13 @@ class Stats
* @param bool|null $done * @param bool|null $done
* @param bool|null $freeloaded * @param bool|null $freeloaded
* *
* @codeCoverageIgnore * @codeCoverageIgnore as TIMESTAMPDIFF is not implemented in SQLite
*/ */
public function workSeconds(bool $done = null, bool $freeloaded = null): int public function workSeconds(bool $done = null, bool $freeloaded = null): int
{ {
$query = $this->workSecondsQuery($done, $freeloaded); $query = $this->workSecondsQuery($done, $freeloaded);
return (int) $query->sum($this->raw('end - start')); return (int) $query->sum($this->raw('TIMESTAMPDIFF(MINUTE, start, end) * 60'));
} }
/** /**
@ -242,22 +239,19 @@ class Stats
* @param bool|null $done * @param bool|null $done
* @param bool|null $freeloaded * @param bool|null $freeloaded
* *
* @codeCoverageIgnore * @codeCoverageIgnore as TIMESTAMPDIFF is not implemented in SQLite
*/ */
public function workBuckets(array $buckets, bool $done = null, bool $freeloaded = null): array public function workBuckets(array $buckets, bool $done = null, bool $freeloaded = null): array
{ {
return $this->getBuckets( return $this->getBuckets(
$buckets, $buckets,
$this->workSecondsQuery($done, $freeloaded), $this->workSecondsQuery($done, $freeloaded),
'UID', 'user_id',
'SUM(end - start)', 'SUM(TIMESTAMPDIFF(MINUTE, start, end) * 60)',
'SUM(end - start)' 'SUM(TIMESTAMPDIFF(MINUTE, start, end) * 60)'
); );
} }
/**
* @codeCoverageIgnore As long as its only used for old tables
*/
protected function getBuckets( protected function getBuckets(
array $buckets, array $buckets,
BuilderContract $basicQuery, BuilderContract $basicQuery,
@ -281,18 +275,12 @@ class Stats
return $return; return $return;
} }
/**
* @codeCoverageIgnore
*/
public function worklogSeconds(): int public function worklogSeconds(): int
{ {
return (int) Worklog::query() return (int) Worklog::query()
->sum($this->raw('hours * 60 * 60')); ->sum($this->raw('hours * 60 * 60'));
} }
/**
* @codeCoverageIgnore
*/
public function worklogBuckets(array $buckets): array public function worklogBuckets(array $buckets): array
{ {
return $this->getBuckets( return $this->getBuckets(

View File

@ -4,28 +4,31 @@ declare(strict_types=1);
namespace Engelsystem\Models; namespace Engelsystem\Models;
use Engelsystem\Models\Shifts\ShiftEntry;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Database\Query\Builder as QueryBuilder;
/** /**
* @property int $id * @property int $id
* @property string $name * @property string $name
* @property string $description * @property string $description
* @property string $contact_name * @property string $contact_name
* @property string $contact_dect * @property string $contact_dect
* @property string $contact_email * @property string $contact_email
* @property boolean $restricted # If users need an introduction * @property boolean $restricted # If users need an introduction
* @property boolean $requires_driver_license # If users must have a driver license * @property boolean $requires_driver_license # If users must have a driver license
* @property boolean $no_self_signup # Users can sign up for shifts * @property boolean $no_self_signup # Users can sign up for shifts
* @property boolean $show_on_dashboard # Show on public dashboard * @property boolean $show_on_dashboard # Show on public dashboard
* @property boolean $hide_register # Hide from registration page * @property boolean $hide_register # Hide from registration page
* *
* @property-read Collection|User[] $userAngelTypes * @property-read UserAngelType $pivot
* @property-read UserAngelType $pivot * @property-read Collection|ShiftEntry[] $shiftEntries
* @property-read Collection|User[] $userAngelTypes
* *
* @method static QueryBuilder|AngelType[] whereId($value) * @method static QueryBuilder|AngelType[] whereId($value)
* @method static QueryBuilder|AngelType[] whereName($value) * @method static QueryBuilder|AngelType[] whereName($value)
@ -68,6 +71,11 @@ class AngelType extends BaseModel
'hide_register' => 'boolean', 'hide_register' => 'boolean',
]; ];
public function shiftEntries(): HasMany
{
return $this->hasMany(ShiftEntry::class);
}
public function userAngelTypes(): BelongsToMany public function userAngelTypes(): BelongsToMany
{ {
return $this return $this

View File

@ -8,31 +8,34 @@ use Carbon\Carbon;
use Engelsystem\Models\BaseModel; use Engelsystem\Models\BaseModel;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Database\Query\Builder as QueryBuilder;
/** /**
* @property int $id * @property int $id
* @property string $title * @property string $title
* @property string $description * @property string $description
* @property string $url * @property string $url
* @property Carbon $start * @property Carbon $start
* @property Carbon $end * @property Carbon $end
* @property int $shift_type_id * @property int $shift_type_id
* @property int $room_id * @property int $room_id
* @property string $transaction_id * @property string $transaction_id
* @property int $created_by * @property int $created_by
* @property int|null $updated_by * @property int|null $updated_by
* @property Carbon|null $created_at * @property Carbon|null $created_at
* @property Carbon|null $updated_at * @property Carbon|null $updated_at
* *
* @property-read QueryBuilder|Schedule $schedule * @property-read QueryBuilder|Schedule $schedule
* @property-read QueryBuilder|ShiftType $shiftType * @property-read QueryBuilder|Collection|ShiftEntry[] $shiftEntries
* @property-read QueryBuilder|Room $room * @property-read QueryBuilder|ShiftType $shiftType
* @property-read QueryBuilder|User $createdBy * @property-read QueryBuilder|Room $room
* @property-read QueryBuilder|User|null $updatedBy * @property-read QueryBuilder|User $createdBy
* @property-read QueryBuilder|User|null $updatedBy
* *
* @method static QueryBuilder|Shift[] whereId($value) * @method static QueryBuilder|Shift[] whereId($value)
* @method static QueryBuilder|Shift[] whereTitle($value) * @method static QueryBuilder|Shift[] whereTitle($value)
@ -88,6 +91,11 @@ class Shift extends BaseModel
return $this->hasOneThrough(Schedule::class, ScheduleShift::class, null, 'id', null, 'schedule_id'); return $this->hasOneThrough(Schedule::class, ScheduleShift::class, null, 'id', null, 'schedule_id');
} }
public function shiftEntries(): HasMany
{
return $this->hasMany(ShiftEntry::class);
}
public function shiftType(): BelongsTo public function shiftType(): BelongsTo
{ {
return $this->belongsTo(ShiftType::class); return $this->belongsTo(ShiftType::class);

View File

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Models\Shifts;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\BaseModel;
use Engelsystem\Models\User\UsesUserModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Query\Builder as QueryBuilder;
/**
* @property int $id
* @property int $shift_id
* @property int $angel_type_id
* @property string $user_comment
* @property bool $freeloaded
* @property string $freeloaded_comment
*
* @property-read Shift $shift
* @property-read AngelType $angelType
*
* @method static QueryBuilder|ShiftEntry[] whereId($value)
* @method static QueryBuilder|ShiftEntry[] whereShiftId($value)
* @method static QueryBuilder|ShiftEntry[] whereAngelTypeId($value)
* @method static QueryBuilder|ShiftEntry[] whereUserComment($value)
* @method static QueryBuilder|ShiftEntry[] whereFreeloaded($value)
* @method static QueryBuilder|ShiftEntry[] whereFreeloadedComment($value)
*/
class ShiftEntry extends BaseModel
{
use HasFactory;
use UsesUserModel;
/** @var array<string> */
protected $fillable = [ // phpcs:ignore
'shift_id',
'angel_type_id',
'user_id',
'user_comment',
'freeloaded',
'freeloaded_comment',
];
/** @var array<string, string|bool> default attributes */
protected $attributes = [ // phpcs:ignore
'user_comment' => '',
'freeloaded' => false,
'freeloaded_comment' => '',
];
public function shift(): BelongsTo
{
return $this->belongsTo(Shift::class);
}
public function angelType(): BelongsTo
{
return $this->belongsTo(AngelType::class);
}
}

View File

@ -13,6 +13,7 @@ use Engelsystem\Models\OAuth;
use Engelsystem\Models\Privilege; use Engelsystem\Models\Privilege;
use Engelsystem\Models\Question; use Engelsystem\Models\Question;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Engelsystem\Models\Worklog; use Engelsystem\Models\Worklog;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
@ -47,6 +48,7 @@ use Illuminate\Support\Collection as SupportCollection;
* @property-read SupportCollection|Privilege[] $privileges * @property-read SupportCollection|Privilege[] $privileges
* @property-read Collection|AngelType[] $userAngelTypes * @property-read Collection|AngelType[] $userAngelTypes
* @property-read UserAngelType $pivot * @property-read UserAngelType $pivot
* @property-read Collection|ShiftEntry[] $shiftEntries
* @property-read Collection|Worklog[] $worklogs * @property-read Collection|Worklog[] $worklogs
* @property-read Collection|Worklog[] $worklogsCreated * @property-read Collection|Worklog[] $worklogsCreated
* @property-read Collection|Question[] $questionsAsked * @property-read Collection|Question[] $questionsAsked
@ -109,6 +111,14 @@ class User extends BaseModel
return $this->belongsToMany(Group::class, 'users_groups'); return $this->belongsToMany(Group::class, 'users_groups');
} }
public function isFreeloader(): bool
{
return $this->shiftEntries()
->where('freeloaded', true)
->count()
>= config('max_freeloadable_shifts');
}
public function license(): HasOne public function license(): HasOne
{ {
return $this return $this
@ -189,6 +199,11 @@ class User extends BaseModel
return $this->hasMany(OAuth::class); return $this->hasMany(OAuth::class);
} }
public function shiftEntries(): HasMany
{
return $this->hasMany(ShiftEntry::class);
}
public function worklogs(): HasMany public function worklogs(): HasMany
{ {
return $this->hasMany(Worklog::class); return $this->hasMany(Worklog::class);

View File

@ -76,8 +76,10 @@ class TwigServiceProvider extends ServiceProvider
$this->app->instance('twig.loader', $twigLoader); $this->app->instance('twig.loader', $twigLoader);
$cache = $this->app->get('path.cache.views'); $cache = $this->app->get('path.cache.views');
$twigDebug = false;
if ($config->get('environment') == 'development') { if ($config->get('environment') == 'development') {
$cache = false; $cache = false;
$twigDebug = true;
} }
$twig = $this->app->make( $twig = $this->app->make(
@ -86,7 +88,8 @@ class TwigServiceProvider extends ServiceProvider
'options' => [ 'options' => [
'cache' => $cache, 'cache' => $cache,
'auto_reload' => true, 'auto_reload' => true,
'strict_variables' => ($config->get('environment') == 'development'), 'debug' => $twigDebug,
'strict_variables' => $twigDebug,
], ],
] ]
); );

View File

@ -13,6 +13,7 @@ use Engelsystem\Models\OAuth;
use Engelsystem\Models\Question; use Engelsystem\Models\Question;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\License; use Engelsystem\Models\User\License;
use Engelsystem\Models\User\PasswordReset; use Engelsystem\Models\User\PasswordReset;
use Engelsystem\Models\User\PersonalData; use Engelsystem\Models\User\PersonalData;
@ -163,6 +164,35 @@ class StatsTest extends TestCase
$this->assertEquals(2.4 * 60 * 60 + 1.2 * 60 * 60, $seconds); $this->assertEquals(2.4 * 60 * 60 + 1.2 * 60 * 60, $seconds);
} }
/**
* @covers \Engelsystem\Controllers\Metrics\Stats::worklogBuckets
* @covers \Engelsystem\Controllers\Metrics\Stats::getBuckets
*/
public function testWorklogBuckets(): void
{
Worklog::factory()->create(['hours' => 1.2, 'worked_at' => Carbon::now()->subDay()]);
Worklog::factory()->create(['hours' => 1.9, 'worked_at' => Carbon::now()->subDay()]);
Worklog::factory()->create(['hours' => 3, 'worked_at' => Carbon::now()->subDay()]);
Worklog::factory()->create(['hours' => 10, 'worked_at' => Carbon::now()->subDay()]);
$stats = new Stats($this->database);
$buckets = $stats->worklogBuckets([
1 * 60 * 60,
2 * 60 * 60,
3 * 60 * 60,
4 * 60 * 60,
'+Inf'
]);
$this->assertEquals([
3600 => 0,
7200 => 2,
10800 => 3,
14400 => 3,
'+Inf' => 4,
], $buckets);
}
/** /**
* @covers \Engelsystem\Controllers\Metrics\Stats::rooms * @covers \Engelsystem\Controllers\Metrics\Stats::rooms
*/ */
@ -251,9 +281,13 @@ class StatsTest extends TestCase
public function testArrivedUsers(): void public function testArrivedUsers(): void
{ {
$this->addUsers(); $this->addUsers();
ShiftEntry::factory()->create(['user_id' => 3]);
ShiftEntry::factory()->create(['user_id' => 4]);
$stats = new Stats($this->database); $stats = new Stats($this->database);
$this->assertEquals(7, $stats->arrivedUsers()); $this->assertEquals(7, $stats->arrivedUsers());
$this->assertEquals(5, $stats->arrivedUsers(false));
$this->assertEquals(2, $stats->arrivedUsers(true));
} }
/** /**
@ -293,6 +327,25 @@ class StatsTest extends TestCase
$this->assertEquals(1, $stats->email('news')); $this->assertEquals(1, $stats->email('news'));
} }
/**
* @covers \Engelsystem\Controllers\Metrics\Stats::currentlyWorkingUsers
*/
public function testCurrentlyWorkingUsers(): void
{
$this->addUsers();
/** @var Shift $shift */
$shift = Shift::factory()->create(['start' => Carbon::now()->subHour(), 'end' => Carbon::now()->addHour()]);
ShiftEntry::factory()->create(['shift_id' => $shift->id]);
ShiftEntry::factory()->create(['shift_id' => $shift->id]);
ShiftEntry::factory()->create(['shift_id' => $shift->id, 'freeloaded' => true]);
$stats = new Stats($this->database);
$this->assertEquals(3, $stats->currentlyWorkingUsers());
$this->assertEquals(2, $stats->currentlyWorkingUsers(false));
$this->assertEquals(1, $stats->currentlyWorkingUsers(true));
}
/** /**
* @covers \Engelsystem\Controllers\Metrics\Stats::faq * @covers \Engelsystem\Controllers\Metrics\Stats::faq
*/ */

View File

@ -10,6 +10,7 @@ use Engelsystem\Models\Question;
use Engelsystem\Models\Room; use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Schedule; use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\Contact; use Engelsystem\Models\User\Contact;
use Engelsystem\Models\User\License; use Engelsystem\Models\User\License;
use Engelsystem\Models\User\PasswordReset; use Engelsystem\Models\User\PasswordReset;
@ -41,6 +42,7 @@ class FactoriesTest extends TestCase
[Question::class], [Question::class],
[Room::class], [Room::class],
[Schedule::class], [Schedule::class],
[ShiftEntry::class],
[Settings::class], [Settings::class],
[Shift::class], [Shift::class],
[State::class], [State::class],

View File

@ -3,6 +3,7 @@
namespace Engelsystem\Test\Unit\Models; namespace Engelsystem\Test\Unit\Models;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
@ -47,8 +48,7 @@ class AngelTypeTest extends ModelTest
User::factory(1)->create(); User::factory(1)->create();
$user2 = User::factory()->create(); $user2 = User::factory()->create();
$angelType = new AngelType(['name' => 'Test']); $angelType = AngelType::create(['name' => 'Test']);
$angelType->save();
$angelType->userAngelTypes()->attach($user1); $angelType->userAngelTypes()->attach($user1);
$angelType->userAngelTypes()->attach($user2); $angelType->userAngelTypes()->attach($user2);
@ -61,6 +61,18 @@ class AngelTypeTest extends ModelTest
$this->assertCount(2, $angeltypes); $this->assertCount(2, $angeltypes);
} }
/**
* @covers \Engelsystem\Models\AngelType::shiftEntries
*/
public function testShiftEntries(): void
{
$angelType = AngelType::create(['name' => 'test type']);
ShiftEntry::factory(3)->create(['angel_type_id' => $angelType->id]);
$this->assertCount(3, $angelType->shiftEntries);
}
/** /**
* @covers \Engelsystem\Models\AngelType::boot * @covers \Engelsystem\Models\AngelType::boot
*/ */

View File

@ -0,0 +1,37 @@
<?php
namespace Engelsystem\Test\Unit\Models\Shifts;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\User;
use Engelsystem\Test\Unit\Models\ModelTest;
class ShiftEntryTest extends ModelTest
{
/**
* @covers \Engelsystem\Models\Shifts\ShiftEntry::shift
* @covers \Engelsystem\Models\Shifts\ShiftEntry::angelType
*/
public function testShift(): void
{
/** @var Shift $shift */
$shift = Shift::factory()->create();
/** @var AngelType $angelType */
$angelType = AngelType::factory()->create();
/** @var User $user */
$user = User::factory()->create();
$model = new ShiftEntry();
$model->shift()->associate($shift);
$model->angelType()->associate($angelType);
$model->user()->associate($user);
$model->save();
$model = ShiftEntry::find(1);
$this->assertEquals($shift->id, $model->shift->id);
$this->assertEquals($angelType->id, $model->angelType->id);
$this->assertEquals($user->id, $model->user->id);
}
}

View File

@ -9,6 +9,7 @@ use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Schedule; use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Test\Unit\Models\ModelTest; use Engelsystem\Test\Unit\Models\ModelTest;
@ -73,4 +74,18 @@ class ShiftTest extends ModelTest
$this->assertEquals(1, Shift::find(2)->schedule->id); $this->assertEquals(1, Shift::find(2)->schedule->id);
$this->assertEquals(1, Shift::find(3)->schedule->id); $this->assertEquals(1, Shift::find(3)->schedule->id);
} }
/**
* @covers \Engelsystem\Models\Shifts\Shift::shiftEntries
*/
public function testShiftEntries(): void
{
/** @var Shift $shift */
$shift = Shift::factory()->make();
$shift->save();
ShiftEntry::factory(5)->create(['shift_id' => $shift->id]);
$this->assertCount(5, $shift->shiftEntries);
}
} }

View File

@ -4,6 +4,7 @@ namespace Engelsystem\Test\Unit\Models\User;
use Carbon\Carbon; use Carbon\Carbon;
use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
use Engelsystem\Config\Config;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\BaseModel; use Engelsystem\Models\BaseModel;
use Engelsystem\Models\Group; use Engelsystem\Models\Group;
@ -13,6 +14,7 @@ use Engelsystem\Models\OAuth;
use Engelsystem\Models\Privilege; use Engelsystem\Models\Privilege;
use Engelsystem\Models\Question; use Engelsystem\Models\Question;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\Contact; use Engelsystem\Models\User\Contact;
use Engelsystem\Models\User\HasUserModel; use Engelsystem\Models\User\HasUserModel;
use Engelsystem\Models\User\License; use Engelsystem\Models\User\License;
@ -209,6 +211,30 @@ class UserTest extends ModelTest
$this->assertEquals($relatedModelIds, $user->{$name}->modelKeys()); $this->assertEquals($relatedModelIds, $user->{$name}->modelKeys());
} }
/**
* @covers \Engelsystem\Models\User\User::isFreeloader
*/
public function testIsFreeloader(): void
{
$this->app->instance('config', new Config([
'max_freeloadable_shifts' => 2,
]));
$user = new User($this->data);
$user->save();
$this->assertFalse($user->isFreeloader());
ShiftEntry::factory()->create(['user_id' => $user->id]);
ShiftEntry::factory()->create(['user_id' => $user->id, 'freeloaded' => true]);
$this->assertFalse($user->isFreeloader());
ShiftEntry::factory()->create(['user_id' => $user->id, 'freeloaded' => true]);
$this->assertTrue($user->isFreeloader());
ShiftEntry::factory()->create(['user_id' => $user->id, 'freeloaded' => true]);
$this->assertTrue($user->isFreeloader());
}
/** /**
* @covers \Engelsystem\Models\User\User::userAngelTypes * @covers \Engelsystem\Models\User\User::userAngelTypes
*/ */
@ -334,6 +360,19 @@ class UserTest extends ModelTest
$this->assertCount(1, $oauth); $this->assertCount(1, $oauth);
} }
/**
* @covers \Engelsystem\Models\User\User::shiftEntries
*/
public function testShiftEntries(): void
{
$user = new User($this->data);
$user->save();
ShiftEntry::factory(2)->create(['user_id' => $user->id]);
$this->assertCount(2, $user->shiftEntries);
}
/** /**
* @covers \Engelsystem\Models\User\User::worklogs * @covers \Engelsystem\Models\User\User::worklogs
*/ */

View File

@ -40,7 +40,7 @@ abstract class ExtensionTest extends TestCase
*/ */
protected function assertExtensionExists( protected function assertExtensionExists(
string $name, string $name,
callable $callback, mixed $callback,
array $functions, array $functions,
array $options = [] array $options = []
): void { ): void {

View File

@ -136,7 +136,12 @@ class TwigServiceProviderTest extends ServiceProviderTest
->method('make') ->method('make')
->withConsecutive( ->withConsecutive(
[TwigLoader::class, ['paths' => $viewsPath]], [TwigLoader::class, ['paths' => $viewsPath]],
[Twig::class, ['options' => ['cache' => false, 'auto_reload' => true, 'strict_variables' => true]]], [Twig::class, ['options' => [
'cache' => false,
'auto_reload' => true,
'strict_variables' => true,
'debug' => true,
]]],
[TwigEngine::class] [TwigEngine::class]
)->willReturnOnConsecutiveCalls( )->willReturnOnConsecutiveCalls(
$twigLoader, $twigLoader,
@ -162,10 +167,10 @@ class TwigServiceProviderTest extends ServiceProviderTest
$this->setExpects($app, 'tag', ['renderer.twigEngine', ['renderer.engine']]); $this->setExpects($app, 'tag', ['renderer.twigEngine', ['renderer.engine']]);
$config->expects($this->exactly(3)) $config->expects($this->exactly(2))
->method('get') ->method('get')
->withConsecutive(['environment'], ['environment'], ['timezone']) ->withConsecutive(['environment'], ['timezone'])
->willReturnOnConsecutiveCalls('development', 'development', 'The/World'); ->willReturnOnConsecutiveCalls('development', 'The/World');
$twig->expects($this->once()) $twig->expects($this->once())
->method('getExtension') ->method('getExtension')