diff --git a/app/Actions/VacationRequest/BulkCreateAction.php b/app/Actions/VacationRequest/BulkCreateAction.php new file mode 100644 index 00000000..d2d989bd --- /dev/null +++ b/app/Actions/VacationRequest/BulkCreateAction.php @@ -0,0 +1,40 @@ +id; + + try { + $request = $this->createAction->execute($data, $creator); + + $requests->push($request); + } catch (ValidationException $e) { + $errors->push($user->profile->full_name . " - " . $e->getMessage()); + } + } + + return collect([ + "requests" => $requests, + "errors" => $errors, + ]); + } +} diff --git a/app/Http/Controllers/VacationRequestController.php b/app/Http/Controllers/VacationRequestController.php index 74a389c2..f3b94d09 100644 --- a/app/Http/Controllers/VacationRequestController.php +++ b/app/Http/Controllers/VacationRequestController.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\Response as SymfonyResponse; use Toby\Actions\VacationRequest\AcceptAsAdministrativeAction; use Toby\Actions\VacationRequest\AcceptAsTechnicalAction; +use Toby\Actions\VacationRequest\BulkCreateAction; use Toby\Actions\VacationRequest\CancelAction; use Toby\Actions\VacationRequest\CreateAction; use Toby\Actions\VacationRequest\RejectAction; @@ -23,6 +24,7 @@ use Toby\Domain\VacationRequestStatesRetriever; use Toby\Domain\VacationTypeConfigRetriever; use Toby\Enums\VacationType; +use Toby\Http\Requests\BulkVacationRequestRequest; use Toby\Http\Requests\VacationRequestRequest; use Toby\Http\Resources\SimpleUserResource; use Toby\Http\Resources\SimpleVacationRequestResource; @@ -246,6 +248,30 @@ public function create(Request $request): Response ]); } + public function bulkCreate(Request $request, VacationTypeConfigRetriever $configRetriever): Response + { + $this->authorize("createRequestsOnBehalfOfEmployee"); + + $users = User::query() + ->orderByProfileField("last_name") + ->orderByProfileField("first_name") + ->get(); + + foreach ($users as $user) { + $typesByUser[$user->id] = VacationType::all() + ->filter(fn(VacationType $type): bool => $configRetriever->isAvailableFor($type, $user->profile->employment_form)) + ->filter(fn(VacationType $type): bool => $configRetriever->isRequestAllowedFor($type, $request->user()->role)) + ->pluck("value"); + } + + return inertia("VacationRequest/BulkCreate", [ + "types" => VacationType::casesToSelect(), + "users" => SimpleUserResource::collection($users), + "typesByUser" => $typesByUser ?? [], + "vacationFromDate" => $request->get("from_date"), + ]); + } + /** * @throws AuthorizationException * @throws ValidationException @@ -267,6 +293,35 @@ public function store(VacationRequestRequest $request, CreateAction $createActio ->with("success", __("Request created.")); } + /** + * @throws AuthorizationException + * @throws ValidationException + */ + public function bulkStore(BulkVacationRequestRequest $request, BulkCreateAction $bulkCreateAction): RedirectResponse + { + $this->authorize("createRequestsOnBehalfOfEmployee"); + + if ($request->wantsSkipFlow()) { + $this->authorize("skipRequestFlow"); + } + + $result = $bulkCreateAction->execute($request->getUsers(), $request->getData(), $request->user()); + + if ($result["errors"]->isNotEmpty()) { + $messages = []; + + foreach ($result["errors"] as $key => $error) { + $messages["vacationRequests.$key"] = $error; + } + + throw ValidationException::withMessages($messages); + } + + return redirect() + ->route("vacation.requests.indexForApprovers") + ->with("success", __("Request created.")); + } + /** * @throws AuthorizationException */ diff --git a/app/Http/Requests/BulkVacationRequestRequest.php b/app/Http/Requests/BulkVacationRequestRequest.php new file mode 100644 index 00000000..240bc3bf --- /dev/null +++ b/app/Http/Requests/BulkVacationRequestRequest.php @@ -0,0 +1,50 @@ + ["required", "exists:users,id"], + "type" => ["required", new Enum(VacationType::class)], + "from" => ["required", "date_format:" . DateFormats::DATE], + "to" => ["required", "date_format:" . DateFormats::DATE], + "flowSkipped" => ["nullable", "boolean"], + "comment" => ["nullable"], + ]; + } + + public function getUsers(): Collection + { + $users = $this->collect("users"); + + return User::query()->whereKey($users)->get(); + } + + public function getData(): array + { + return [ + "type" => $this->get("type"), + "from" => $this->get("from"), + "to" => $this->get("to"), + "comment" => $this->get("comment"), + "flow_skipped" => $this->boolean("flowSkipped"), + ]; + } + + public function wantsSkipFlow(): bool + { + return $this->boolean("flowSkipped"); + } +} diff --git a/package-lock.json b/package-lock.json index 536175be..56d2d1a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "toby", + "name": "application", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/resources/js/Pages/VacationRequest/BulkCreate.vue b/resources/js/Pages/VacationRequest/BulkCreate.vue new file mode 100644 index 00000000..e2760927 --- /dev/null +++ b/resources/js/Pages/VacationRequest/BulkCreate.vue @@ -0,0 +1,383 @@ + + +