Added room selection for schedules
This commit is contained in:
parent
197d0d724a
commit
caa699ff05
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Engelsystem\Migrations;
|
||||||
|
|
||||||
|
use Engelsystem\Database\Migration\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
|
||||||
|
class CreateScheduleLocationsTable extends Migration
|
||||||
|
{
|
||||||
|
use ChangesReferences;
|
||||||
|
use Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the new table
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$connection = $this->schema->getConnection();
|
||||||
|
|
||||||
|
$this->schema->create('schedule_locations', function (Blueprint $table): void {
|
||||||
|
$table->increments('id');
|
||||||
|
$this->references($table, 'schedules');
|
||||||
|
$this->references($table, 'locations');
|
||||||
|
|
||||||
|
$table->index(['schedule_id', 'location_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
$scheduleLocations = $connection
|
||||||
|
->table('schedule_shift')
|
||||||
|
->select(['schedules.id AS schedule_id', 'locations.id AS location_id'])
|
||||||
|
->leftJoin('schedules', 'schedules.id', 'schedule_shift.schedule_id')
|
||||||
|
->leftJoin('shifts', 'shifts.id', 'schedule_shift.shift_id')
|
||||||
|
->leftJoin('locations', 'locations.id', 'shifts.location_id')
|
||||||
|
->groupBy(['schedules.id', 'locations.id'])
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($scheduleLocations as $scheduleLocation) {
|
||||||
|
$connection->table('schedule_locations')
|
||||||
|
->insert((array)$scheduleLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops the table
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
$this->schema->drop('schedule_locations');
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,13 +92,14 @@ class ImportSchedule extends BaseController
|
||||||
{
|
{
|
||||||
$scheduleId = $request->getAttribute('schedule_id'); // optional
|
$scheduleId = $request->getAttribute('schedule_id'); // optional
|
||||||
|
|
||||||
$schedule = ScheduleUrl::find($scheduleId);
|
$schedule = ScheduleUrl::findOrFail($scheduleId);
|
||||||
|
|
||||||
return $this->response->withView(
|
return $this->response->withView(
|
||||||
'admin/schedule/edit.twig',
|
'admin/schedule/edit.twig',
|
||||||
[
|
[
|
||||||
'schedule' => $schedule,
|
'schedule' => $schedule,
|
||||||
'shift_types' => ShiftType::all()->pluck('name', 'id'),
|
'shift_types' => ShiftType::all()->sortBy('name')->pluck('name', 'id'),
|
||||||
|
'locations' => Location::all()->sortBy('name')->pluck('name', 'id'),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -114,6 +115,12 @@ class ImportSchedule extends BaseController
|
||||||
return $this->delete($schedule);
|
return $this->delete($schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$locationsList = Location::all()->pluck('id');
|
||||||
|
$locationsValidation = [];
|
||||||
|
foreach ($locationsList as $id) {
|
||||||
|
$locationsValidation['location_' . $id] = 'optional|checked';
|
||||||
|
}
|
||||||
|
|
||||||
$data = $this->validate($request, [
|
$data = $this->validate($request, [
|
||||||
'name' => 'required',
|
'name' => 'required',
|
||||||
'url' => 'required',
|
'url' => 'required',
|
||||||
|
@ -121,7 +128,7 @@ class ImportSchedule extends BaseController
|
||||||
'needed_from_shift_type' => 'optional|checked',
|
'needed_from_shift_type' => 'optional|checked',
|
||||||
'minutes_before' => 'int',
|
'minutes_before' => 'int',
|
||||||
'minutes_after' => 'int',
|
'minutes_after' => 'int',
|
||||||
]);
|
] + $locationsValidation);
|
||||||
|
|
||||||
if (!ShiftType::find($data['shift_type'])) {
|
if (!ShiftType::find($data['shift_type'])) {
|
||||||
throw new ErrorException('schedule.import.invalid-shift-type');
|
throw new ErrorException('schedule.import.invalid-shift-type');
|
||||||
|
@ -135,9 +142,22 @@ class ImportSchedule extends BaseController
|
||||||
$schedule->minutes_after = $data['minutes_after'];
|
$schedule->minutes_after = $data['minutes_after'];
|
||||||
|
|
||||||
$schedule->save();
|
$schedule->save();
|
||||||
|
$schedule->activeLocations()->detach();
|
||||||
|
|
||||||
|
$for = new Collection();
|
||||||
|
foreach ($locationsList as $id) {
|
||||||
|
if (!$data['location_' . $id]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$location = Location::find($id);
|
||||||
|
$schedule->activeLocations()->attach($location);
|
||||||
|
$for[] = $location->name;
|
||||||
|
}
|
||||||
|
|
||||||
$this->log->info(
|
$this->log->info(
|
||||||
'Schedule {name}: Url {url}, Shift Type {shift_type}, ({need}), minutes before/after {before}/{after}',
|
'Schedule {name}: Url {url}, Shift Type {shift_type}, ({need}), '
|
||||||
|
. 'minutes before/after {before}/{after}, for: {locations}',
|
||||||
[
|
[
|
||||||
'name' => $schedule->name,
|
'name' => $schedule->name,
|
||||||
'url' => $schedule->name,
|
'url' => $schedule->name,
|
||||||
|
@ -145,6 +165,7 @@ class ImportSchedule extends BaseController
|
||||||
'need' => $schedule->needed_from_shift_type ? 'from shift type' : 'from room',
|
'need' => $schedule->needed_from_shift_type ? 'from shift type' : 'from room',
|
||||||
'before' => $schedule->minutes_before,
|
'before' => $schedule->minutes_before,
|
||||||
'after' => $schedule->minutes_after,
|
'after' => $schedule->minutes_after,
|
||||||
|
'locations' => $for->implode(', '),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -509,6 +530,10 @@ class ImportSchedule extends BaseController
|
||||||
|
|
||||||
foreach ($schedule->getDay() as $day) {
|
foreach ($schedule->getDay() as $day) {
|
||||||
foreach ($day->getRoom() as $room) {
|
foreach ($day->getRoom() as $room) {
|
||||||
|
if (!$scheduleUrl->activeLocations->where('name', $room->getName())->count()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($room->getEvent() as $event) {
|
foreach ($room->getEvent() as $event) {
|
||||||
$scheduleEvents[$event->getGuid()] = $event;
|
$scheduleEvents[$event->getGuid()] = $event;
|
||||||
|
|
||||||
|
|
|
@ -1441,6 +1441,9 @@ msgstr "Minuten vor Talk beginn hinzufügen"
|
||||||
msgid "schedule.minutes-after"
|
msgid "schedule.minutes-after"
|
||||||
msgstr "Minuten nach Talk ende hinzufügen"
|
msgstr "Minuten nach Talk ende hinzufügen"
|
||||||
|
|
||||||
|
msgid "schedule.for_locations"
|
||||||
|
msgstr "Für Orte"
|
||||||
|
|
||||||
msgid "schedule.import.locations.add"
|
msgid "schedule.import.locations.add"
|
||||||
msgstr "Neue Orte"
|
msgstr "Neue Orte"
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,9 @@ msgstr "Add minutes before talk begins"
|
||||||
msgid "schedule.minutes-after"
|
msgid "schedule.minutes-after"
|
||||||
msgstr "Add minutes after talk ends"
|
msgstr "Add minutes after talk ends"
|
||||||
|
|
||||||
|
msgid "schedule.for_locations"
|
||||||
|
msgstr "For locations"
|
||||||
|
|
||||||
msgid "schedule.import.request_error"
|
msgid "schedule.import.request_error"
|
||||||
msgstr "Unable to load schedule."
|
msgstr "Unable to load schedule."
|
||||||
|
|
||||||
|
|
|
@ -12,50 +12,63 @@
|
||||||
{% block row_content %}
|
{% block row_content %}
|
||||||
{% if schedule and schedule.updated_at %}
|
{% if schedule and schedule.updated_at %}
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<p>{{ __('schedule.last_update', [schedule.updated_at.format(__('general.datetime'))]) }}</p>
|
<p>{{ __('schedule.last_update', [schedule.updated_at.format(__('general.datetime'))]) }}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{{ csrf() }}
|
{{ csrf() }}
|
||||||
|
|
||||||
<div class="col-lg-12">
|
<div class="row">
|
||||||
{{ f.input('name', __('schedule.name'), {
|
<div class="col-md-6">
|
||||||
'required': true,
|
{{ f.input('name', __('schedule.name'), {
|
||||||
'value': schedule ? schedule.name : '',
|
'required': true,
|
||||||
}) }}
|
'value': schedule ? schedule.name : '',
|
||||||
{{ f.input('url', __('schedule.url'), {
|
|
||||||
'type': 'url',
|
|
||||||
'required': true,
|
|
||||||
'value': schedule ? schedule.url : ''
|
|
||||||
}) }}
|
|
||||||
|
|
||||||
{{ f.select('shift_type', __('schedule.shift-type'), shift_types|default([]), {
|
|
||||||
'selected': schedule ? schedule.shift_type : '',
|
|
||||||
}) }}
|
|
||||||
|
|
||||||
{{ f.checkbox('needed_from_shift_type', __('schedule.needed-from-shift-type'), {
|
|
||||||
'checked': schedule ? schedule.needed_from_shift_type : '',
|
|
||||||
}) }}
|
|
||||||
|
|
||||||
{{ f.input('minutes_before', __('schedule.minutes-before'), {
|
|
||||||
'type': 'number',
|
|
||||||
'required': true,
|
|
||||||
'value': schedule ? schedule.minutes_before : 15
|
|
||||||
}) }}
|
|
||||||
{{ f.input('minutes_after', __('schedule.minutes-after'), {
|
|
||||||
'type': 'number',
|
|
||||||
'required': true,
|
|
||||||
'value': schedule ? schedule.minutes_after : 15
|
|
||||||
}) }}
|
|
||||||
|
|
||||||
{{ f.save(__('form.save')) }}
|
|
||||||
|
|
||||||
{% if schedule %}
|
|
||||||
{{ f.delete(__('form.delete'), {
|
|
||||||
'confirm_title': __('schedule.delete.title', [schedule.shifts|length])
|
|
||||||
}) }}
|
}) }}
|
||||||
{% endif %}
|
{{ f.input('url', __('schedule.url'), {
|
||||||
|
'type': 'url',
|
||||||
|
'required': true,
|
||||||
|
'value': schedule ? schedule.url : ''
|
||||||
|
}) }}
|
||||||
|
|
||||||
|
{{ f.select('shift_type', __('schedule.shift-type'), shift_types|default([]), {
|
||||||
|
'selected': schedule ? schedule.shift_type : '',
|
||||||
|
}) }}
|
||||||
|
|
||||||
|
{{ f.checkbox('needed_from_shift_type', __('schedule.needed-from-shift-type'), {
|
||||||
|
'checked': schedule ? schedule.needed_from_shift_type : '',
|
||||||
|
}) }}
|
||||||
|
|
||||||
|
{{ f.input('minutes_before', __('schedule.minutes-before'), {
|
||||||
|
'type': 'number',
|
||||||
|
'required': true,
|
||||||
|
'value': schedule ? schedule.minutes_before : 15
|
||||||
|
}) }}
|
||||||
|
{{ f.input('minutes_after', __('schedule.minutes-after'), {
|
||||||
|
'type': 'number',
|
||||||
|
'required': true,
|
||||||
|
'value': schedule ? schedule.minutes_after : 15
|
||||||
|
}) }}
|
||||||
|
|
||||||
|
{{ f.save(__('form.save')) }}
|
||||||
|
|
||||||
|
{% if schedule %}
|
||||||
|
{{ f.delete(__('form.delete'), {
|
||||||
|
'confirm_title': __('schedule.delete.title', [schedule.shifts|length])
|
||||||
|
}) }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h3>{{ __('schedule.for_locations') }}</h3>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% for id,name in locations %}
|
||||||
|
<div class="col-md-3">
|
||||||
|
{{ f.checkbox('location_' ~ id, name, {'checked': id in schedule.activeLocations.pluck('id')}) }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -6,9 +6,11 @@ namespace Engelsystem\Models;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Engelsystem\Models\Shifts\NeededAngelType;
|
use Engelsystem\Models\Shifts\NeededAngelType;
|
||||||
|
use Engelsystem\Models\Shifts\Schedule;
|
||||||
use Engelsystem\Models\Shifts\Shift;
|
use Engelsystem\Models\Shifts\Shift;
|
||||||
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\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||||
|
|
||||||
|
@ -21,6 +23,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||||
* @property Carbon|null $created_at
|
* @property Carbon|null $created_at
|
||||||
* @property Carbon|null $updated_at
|
* @property Carbon|null $updated_at
|
||||||
*
|
*
|
||||||
|
* @property-read Collection|Schedule[] $activeForSchedules
|
||||||
* @property-read Collection|NeededAngelType[] $neededAngelTypes
|
* @property-read Collection|NeededAngelType[] $neededAngelTypes
|
||||||
* @property-read Collection|Shift[] $shifts
|
* @property-read Collection|Shift[] $shifts
|
||||||
*
|
*
|
||||||
|
@ -54,6 +57,11 @@ class Location extends BaseModel
|
||||||
'description',
|
'description',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function activeForSchedules(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Schedule::class, 'schedule_locations');
|
||||||
|
}
|
||||||
|
|
||||||
public function neededAngelTypes(): HasMany
|
public function neededAngelTypes(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(NeededAngelType::class);
|
return $this->hasMany(NeededAngelType::class);
|
||||||
|
|
|
@ -6,9 +6,11 @@ namespace Engelsystem\Models\Shifts;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Engelsystem\Models\BaseModel;
|
use Engelsystem\Models\BaseModel;
|
||||||
|
use Engelsystem\Models\Location;
|
||||||
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\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||||
|
@ -24,6 +26,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||||
* @property Carbon $created_at
|
* @property Carbon $created_at
|
||||||
* @property Carbon $updated_at
|
* @property Carbon $updated_at
|
||||||
*
|
*
|
||||||
|
* @property-read QueryBuilder|Location[] $activeLocations
|
||||||
* @property-read QueryBuilder|Collection|Shift[] $shifts
|
* @property-read QueryBuilder|Collection|Shift[] $shifts
|
||||||
* @property-read QueryBuilder|Collection|ScheduleShift[] $scheduleShifts
|
* @property-read QueryBuilder|Collection|ScheduleShift[] $scheduleShifts
|
||||||
* @property-read QueryBuilder|ShiftType $shiftType
|
* @property-read QueryBuilder|ShiftType $shiftType
|
||||||
|
@ -63,6 +66,11 @@ class Schedule extends BaseModel
|
||||||
'minutes_after',
|
'minutes_after',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function activeLocations(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Location::class, 'schedule_locations');
|
||||||
|
}
|
||||||
|
|
||||||
public function scheduleShifts(): HasMany
|
public function scheduleShifts(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(ScheduleShift::class);
|
return $this->hasMany(ScheduleShift::class);
|
||||||
|
|
|
@ -6,11 +6,27 @@ namespace Engelsystem\Test\Unit\Models;
|
||||||
|
|
||||||
use Engelsystem\Models\Location;
|
use Engelsystem\Models\Location;
|
||||||
use Engelsystem\Models\Shifts\NeededAngelType;
|
use Engelsystem\Models\Shifts\NeededAngelType;
|
||||||
|
use Engelsystem\Models\Shifts\Schedule;
|
||||||
use Engelsystem\Models\Shifts\Shift;
|
use Engelsystem\Models\Shifts\Shift;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
||||||
class LocationTest extends ModelTest
|
class LocationTest extends ModelTest
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Models\Location::activeForSchedules
|
||||||
|
*/
|
||||||
|
public function testActiveForSchedules(): void
|
||||||
|
{
|
||||||
|
$location = new Location(['name' => 'Test location']);
|
||||||
|
$location->save();
|
||||||
|
|
||||||
|
$schedule = Schedule::factory()->create();
|
||||||
|
$location->activeForSchedules()->attach($schedule);
|
||||||
|
|
||||||
|
$location = Location::find($location->id);
|
||||||
|
$this->assertCount(1, $location->activeForSchedules);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Models\Location::shifts
|
* @covers \Engelsystem\Models\Location::shifts
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Engelsystem\Test\Unit\Models\Shifts;
|
namespace Engelsystem\Test\Unit\Models\Shifts;
|
||||||
|
|
||||||
|
use Engelsystem\Models\Location;
|
||||||
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;
|
||||||
|
@ -22,6 +23,21 @@ class ScheduleTest extends ModelTest
|
||||||
'minutes_after' => 10,
|
'minutes_after' => 10,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Models\Shifts\Schedule::activeLocations
|
||||||
|
*/
|
||||||
|
public function testActiveLocations(): void
|
||||||
|
{
|
||||||
|
$schedule = new Schedule($this->data);
|
||||||
|
$schedule->save();
|
||||||
|
|
||||||
|
$location = Location::factory()->create();
|
||||||
|
$schedule->activeLocations()->attach($location);
|
||||||
|
|
||||||
|
$schedule = Schedule::find($schedule->id);
|
||||||
|
$this->assertCount(1, $schedule->activeLocations);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Models\Shifts\Schedule::scheduleShifts
|
* @covers \Engelsystem\Models\Shifts\Schedule::scheduleShifts
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue