TaskService.php
1 <?php 2 3 namespace App\Http\Services; 4 5 use App\Enums\InboxFileTypeEnum; 6 use App\Http\Livewire\Dossiers\Dossier\Validation; 7 use Exception; 8 use App\Models\Task; 9 use App\Models\User; 10 use App\Enums\StatusEnum; 11 use App\Enums\TaskTypeEnum; 12 use App\Models\Dossier; 13 use App\Models\EntityUser; 14 use App\Models\Role; 15 use App\Models\Stage; 16 17 use Illuminate\Support\Facades\DB; 18 use App\Notifications\TaskAssigned; 19 use Auth; 20 use Carbon\Carbon; 21 22 class TaskService 23 { 24 25 public static function searchInStage(int $stageId, string $searchString, string $sortField, string $sortDirection) 26 { 27 $stageQuery = Task::where('stage_id', $stageId) 28 ->where('is_milestone', false) 29 ->search($searchString); 30 31 if ($sortField && $sortDirection) { 32 // Ordenamiento por campos especiales 33 if ($sortField === 'role') { 34 $stageQuery->orderByRole($sortDirection); 35 } else if ($sortField === 'dossier') { 36 $stageQuery->orderByDossier($sortDirection); 37 } else if ($sortField === 'status') { 38 $stageQuery->orderByStatus($sortDirection); 39 } else { 40 $stageQuery->orderBy($sortField, $sortDirection); 41 } 42 return $stageQuery; 43 } 44 45 return $stageQuery; 46 } 47 48 /** 49 * Display a listing of the resource. 50 * 51 * @return \Illuminate\Http\Response 52 */ 53 public static function find(int $taskId) 54 { 55 return Task::where('id', $taskId)->first(); 56 } 57 58 public static function findByUserAndDossier(int $userId, int $dossierId) 59 { 60 return Task::where('user_id', $userId) 61 ->whereHas('stage', function ($query) use ($dossierId) { 62 $query->whereHas('dossier', function ($query) use ($dossierId) { 63 $query->where('id', $dossierId); 64 }); 65 })->first(); 66 } 67 68 public static function findTaskTitleAndDossier(string $taskTitle, int $dossierId) 69 { 70 return Task::where('title', $taskTitle) 71 ->whereHas('stage', function ($query) use ($dossierId) { 72 $query->where('dossier_id', $dossierId); 73 })->latest()->first(); 74 } 75 76 public static function findByType(int $dossierId, TaskTypeEnum $type) 77 { 78 $dossier = Dossier::find($dossierId); 79 return $dossier->tasks()->where('type', $type)->latest()->first(); 80 } 81 82 public static function getByType(int $dossierId, TaskTypeEnum $type) 83 { 84 return Dossier::find($dossierId)->tasks()->where('type', $type)->get(); 85 } 86 87 public static function findTaskByDossierAndDocument(int $dossierId, int $revisionId) 88 { 89 return Task::where('revision_id', $revisionId) 90 ->whereHas('stage', function ($query) use ($dossierId) { 91 $query->where('dossier_id', $dossierId); 92 })->first(); 93 } 94 95 public static function getAllTaskByTitleAndDossier(string $taskTitle, int $dossierId) 96 { 97 return Task::where('title', $taskTitle) 98 ->whereHas('stage', function ($query) use ($dossierId) { 99 $query->whereHas('dossier', function ($query) use ($dossierId) { 100 $query->where('id', $dossierId); 101 }); 102 })->get(); 103 } 104 105 /** 106 * Create resources 107 * 108 * @return \Illuminate\Http\Response 109 */ 110 public static function searchTasks(string $searchString, string $sortField, string $sortDirection, array|null $filters, User $user = null) 111 { 112 $tasksQuery = Task::query(); 113 114 if ( 115 $user and !$user->hasRole('root') and 116 !$user->hasRole('technical_manager') and 117 !$user->hasRole('quality_manager') and 118 !$user->hasRole('security_manager') and 119 !$user->hasRole('area_manager') 120 ) { 121 $tasksQuery->where(function ($query) use ($user) { 122 $query->where(function ($query) use ($user) { 123 $query->where('user_id', $user->id) 124 ->orWhere('author_id', $user->id); 125 }) 126 ->orWhere(function ($query) use ($user) { 127 $query->whereHas('role', function ($query) use ($user) { 128 $query->whereHas('users', function ($query) use ($user) { 129 $query->where('users.id', $user->id); 130 }); 131 }); 132 }); 133 }); 134 } 135 136 if ($filters && $filters['withTrashed']) { 137 $tasksQuery->withTrashed(); 138 } 139 140 $tasksQuery->search($searchString); 141 142 if ($filters && $filters['created_at_since']) { 143 $tasksQuery->where('created_at', '>=', $filters['created_at_since']); 144 } 145 146 if ($filters && $filters['created_at_to']) { 147 $tasksQuery->where('created_at', '<=', $filters['created_at_to']); 148 } 149 150 if ($filters && $filters['status']) { 151 $tasksQuery->whereIn('task_status_id', $filters['status']); 152 } 153 154 if ($filters && $filters['users']) { 155 $tasksQuery->where(function ($query) use ($filters) { 156 $query->whereIn('user_id', $filters['users'])->orWhereHas('role', function ($query) use ($filters) { 157 $query->whereHas('users', function ($query) use ($filters) { 158 $query->whereIn('user_id', $filters['users']); 159 }); 160 }); 161 }); 162 } 163 164 if ($filters && $filters['roles']) { 165 $tasksQuery->whereIn('role_id', $filters['roles']); 166 } 167 168 if ($filters && $filters['dossiers']) { 169 $tasksQuery->whereHas('stage', function ($query) use ($filters) { 170 $query->whereIn('dossier_id', $filters['dossiers']); 171 }); 172 } 173 174 if ($filters && $filters['end_date_since']) { 175 $tasksQuery->where('end_date', '>=', $filters['end_date_since']); 176 } 177 178 if ($filters && $filters['end_date_to']) { 179 $tasksQuery->where('end_date', '<=', $filters['end_date_to']); 180 } 181 182 if ($filters && $filters['expiration_date_since']) { 183 $tasksQuery->where('expiration_date', '>=', $filters['expiration_date_since']); 184 } 185 186 if ($filters && $filters['expiration_date_to']) { 187 $tasksQuery->where('expiration_date', '<=', $filters['expiration_date_to']); 188 } 189 190 if ($sortField && $sortDirection) { 191 // Ordenamiento por campos especiales 192 if ($sortField === 'role') { 193 $tasksQuery->orderByRole($sortDirection); 194 } else if ($sortField === 'dossier') { 195 $tasksQuery->orderByDossier($sortDirection); 196 } else if ($sortField === 'status') { 197 $tasksQuery->orderByStatus($sortDirection); 198 } else { 199 $tasksQuery->orderBy($sortField, $sortDirection); 200 } 201 } 202 203 return $tasksQuery; 204 } 205 206 /** 207 * Create a resource 208 * 209 */ 210 public static function create(array $taskData, array $relation, ?int $stageId = null, ?int $revisionId = null): Task|Exception 211 { 212 $task = Task::create( 213 [ 214 'title' => $taskData['title'], 215 'stage_id' => $stageId, 216 'user_id' => $relation['user_id'] ?? null, 217 'role_id' => $relation['role_id'] ?? null, 218 'source' => 'internal', 219 'description' => $taskData['description'], 220 'start_date' => $taskData['start_date'] ?? Carbon::now(), 221 'end_date' => $taskData['end_date'] ?? null, 222 'expiration_date' => $taskData['expiration_date'] ?? null, 223 'task_status_id' => $taskData['task_status_id'] ?? StatusEnum::pending()->value, 224 'revision_id' => $revisionId, 225 'type' => $taskData['type'] ?? TaskTypeEnum::default->name, 226 ] 227 ); 228 229 $users = isset($relation['role_id']) 230 ? Role::find($relation['role_id'])->users 231 : User::where('id', $relation['user_id'])->get(); 232 233 if ($task->wasRecentlyCreated) { 234 foreach ($users as $user) { 235 $user->notify(new TaskAssigned($task)); 236 } 237 } 238 239 return $task; 240 } 241 242 /** 243 * Update a resource 244 * 245 * @return \Illuminate\Http\Response 246 */ 247 public static function update(array $data, int $taskId) 248 { 249 try { 250 DB::beginTransaction(); 251 252 $task = Task::findOrFail($taskId); 253 $task->title = $data['task']['title']; 254 $task->description = $data['task']['description']; 255 $task->start_date = $data['task']['start_date']; 256 $task->end_date = $data['task']['end_date']; 257 $task->expiration_date = $data['task']['expiration_date']; 258 $task->user_id = $data['relation']['user_id']; 259 $task->stage_id = $data['relation']['stage_id']; 260 $task->role_id = $data['relation']['role_id']; 261 $task->milestone_end_date = $task->is_milestone ? $data['task']['milestone_end_date'] : null; 262 $task->task_status_id = $data['task']['status_id']; 263 $task->save(); 264 265 DB::commit(); 266 return true; 267 } catch (Exception $e) { 268 logger()->error($e); 269 DB::rollBack(); 270 //throw new Exception($e->getMessage()); 271 throw new Exception(__('tasks.task.notifications.update.error.message')); 272 } 273 } 274 275 /** 276 * Destroy a resource 277 * 278 * @return \Illuminate\Http\Response 279 */ 280 public static function remove(int $taskId) 281 { 282 try { 283 Task::find($taskId)->delete(); 284 return True; 285 } catch (Exception $e) { 286 throw new Exception(__('tasks.task.notifications.remove.error.message')); 287 } 288 } 289 290 /** 291 * Restore a resource 292 * 293 * @return \Illuminate\Http\Response 294 */ 295 public static function restore(int $taskId) 296 { 297 try { 298 $task = Task::withTrashed()->find($taskId); 299 $task->restore(); 300 return True; 301 } catch (Exception $e) { 302 throw new Exception(__('tasks.task.notifications.restore.error.message')); 303 } 304 } 305 306 public static function setStatus(int $taskId, int $statusEnumValue) 307 { 308 try { 309 $task = Task::findOrFail($taskId); 310 311 if ($statusEnumValue === StatusEnum::finished()->value) { 312 $task->end_date = Carbon::now(); 313 $task->milestone_end_date = Carbon::now(); 314 } elseif ($statusEnumValue === StatusEnum::notApplicable()->value) { 315 $task->milestone_end_date = null; 316 } else { 317 $task->end_date = null; 318 } 319 320 $task->task_status_id = $statusEnumValue; 321 $task->save(); 322 323 return true; 324 } catch (Exception $e) { 325 DB::rollBack(); 326 throw $e; 327 } 328 } 329 330 public static function getStatusMessage(int $taskId) 331 { 332 $statusId = Task::findOrFail($taskId)->task_status_id; 333 334 return match ($statusId) { 335 StatusEnum::inProcess()->value => __('tasks.task.cards.actions.statuses.' . config('template.TEMPLATE_ROUTE') . '.finished'), 336 StatusEnum::finished()->value, 337 StatusEnum::pending()->value => __('tasks.task.cards.actions.statuses.' . config('template.TEMPLATE_ROUTE') . '.inProcess'), 338 default => "", 339 }; 340 } 341 342 public static function isCompleted(int $taskId) 343 { 344 return Task::findOrFail($taskId)->task_status_id === StatusEnum::finished()->value; 345 } 346 347 public static function createTasksFromEPJson(array $ep, int $dossierId) 348 { 349 try { 350 DB::beginTransaction(); 351 $data = $ep['resources']['timePlanning']; 352 $stage = Stage::firstOrCreate( 353 [ 354 'dossier_id' => $dossierId, 355 'name' => 'Evaluation Plan', 356 'stage_status_id' => StatusEnum::pending()->value 357 ] 358 ); 359 $tasks = []; 360 foreach ($data as $task) { 361 $tasks[] = Task::create([ 362 'source' => 'external', 363 'title' => $task['name'], 364 'description' => $task['justification'], 365 'user_id' => null, 366 'stage_id' => $stage->id, 367 'start_date' => $task['startDate'], 368 'end_date' => $task['endDate'], 369 ]); 370 } 371 DB::commit(); 372 373 return $tasks; 374 } catch (Exception $e) { 375 DB::rollBack(); 376 throw new Exception($e->getMessage()); 377 } 378 } 379 380 public static function generateTask($inboxFile, bool $success = true) 381 { 382 $stage = $inboxFile->dossier->stages()->where('name', __('stages.default.1'))->first(); 383 384 if (!$success) { 385 if ($certifiers = $inboxFile->dossier->externalCertifiers()) { 386 foreach ($certifiers as $certifier) { 387 self::createRectifyTask($certifier->id, $stage->id); 388 } 389 } 390 391 if ($principalCertifier = $inboxFile->dossier->principalCertifier) { 392 self::createRectifyTask($principalCertifier->id, $stage->id); 393 } else { 394 self::createRectifyTask(User::technicalManager()->id, $stage->id); 395 } 396 } 397 } 398 399 public static function createTask(Dossier $dossier, $title, $description, ?int $notifiableUserId = null, TaskTypeEnum $type = TaskTypeEnum::default): Task 400 { 401 $userId = $notifiableUser?->id ?? User::technicalManager()->id; 402 403 return self::create( 404 self::getTaskData($title, $description, type: $type), 405 ['user_id' => $notifiableUserId], 406 $dossier->stage->id 407 ); 408 } 409 410 public static function createNewDossierTask(Dossier $dossier, int $notifyTo = null): void 411 { 412 self::create( 413 self::getTaskData( 414 __('tasks.assign_certifiers.title', ['code' => $dossier->code]), 415 __('tasks.assign_certifiers.description'), 416 type: TaskTypeEnum::assignCertifiers 417 ), 418 ['role_id' => Role::whereName('technical_manager')->first()->id], 419 $dossier->stage->id, 420 ); 421 } 422 423 public static function finishAssignCertifiersTask(Dossier $dossier): void 424 { 425 $task = $dossier->tasks() 426 ->where('type', TaskTypeEnum::assignCertifiers->name) 427 ->whereNot('task_status_id', StatusEnum::finished()->value) 428 ->first(); 429 430 if ($task) { 431 self::setStatus($task->id, StatusEnum::finished()->value); 432 } 433 } 434 435 public static function handleAssignCertifierTaskCompleted(Task $task): void 436 { 437 $dossier = $task->dossier(); 438 self::closeMilestone($dossier, __('tasks.milestones.certifiers-assigned')); 439 440 // Skip 441 if (self::existsByType($dossier, TaskTypeEnum::startCertification)) { 442 return; 443 } 444 445 $userId = null; 446 $roleId = null; 447 448 if ($dossier->externalCertifiers()->isNotEmpty()) { 449 $roleId = Role::whereName('external_certifier')->first()->id; 450 } elseif ($certifier = $dossier->principalCertifier) { 451 $userId = $certifier->id; 452 } 453 454 self::createStartCertificationTask($userId, $roleId, $dossier); 455 } 456 457 public static function createStartCertificationTask(?int $userId, ?int $roleId, Dossier $dossier): void 458 { 459 // searches the latest certification request in the dossier 460 $revision_id = $dossier 461 ->inboxfiles() 462 ->whereType(InboxFileTypeEnum::certification_request()->value) 463 ->latest()->first()?->document?->head_id; 464 465 466 self::create( 467 self::getTaskData( 468 __('tasks.start_certification.title'), 469 __('tasks.start_certification.description'), 470 type: TaskTypeEnum::startCertification 471 ), 472 ['user_id' => $userId, 'role_id' => $roleId], 473 $dossier->current_stage_id, 474 $revision_id, 475 ); 476 } 477 478 public static function createCloseDossier(Dossier $dossier): void 479 { 480 self::create( 481 self::getTaskData( 482 __('tasks.close_dossier.title'), 483 __('tasks.close_dossier.description'), 484 type: TaskTypeEnum::closeDossier 485 ), 486 ['user_id' => $dossier->getCertifierId()], 487 $dossier->current_stage_id 488 ); 489 } 490 491 public static function closeMilestone(Dossier $dossier, string $title): void 492 { 493 $milestoneId = $dossier->milestones() 494 ->where('title', $title) 495 ->first()->id ?? null; 496 497 if ($milestoneId) { 498 self::setStatus($milestoneId, StatusEnum::finished()->value); 499 } 500 } 501 502 private static function existsByType(Dossier $dossier, TaskTypeEnum $type): bool 503 { 504 return $dossier->tasks()->where('title', $type->name)->exists(); 505 } 506 507 public static function handleNotifyStartCertificationTaskCompleted(Task $task): void 508 { 509 self::closeMilestone($task->dossier(), __('tasks.milestones.start-notification')); 510 } 511 512 public static function handleKickOffActDone(Task $task): void 513 { 514 self::closeMilestone($task->dossier(), __('tasks.milestones.evaluation-meet-finished')); 515 516 $principalCertifierId = $task->dossier()->principalCertifier->id ?? null; 517 $stageId = $task->dossier()->getStage(2)->id ?? null; 518 if (!$principalCertifierId || !$stageId) { 519 return; 520 } 521 self::createSendEvaluationKickoffActTask($principalCertifierId, $stageId); 522 } 523 524 public static function handleKickOffActSent(Task $task): void 525 { 526 self::closeMilestone($task->dossier(), __('tasks.milestones.evaluation-meet-minutes-sent')); 527 } 528 529 public static function handlePreResolutionActDone(Task $task): void 530 { 531 self::closeMilestone($task->dossier(), __('tasks.milestones.pre-hearing-meeting-convened')); 532 } 533 534 535 public static function handlePreResolutionActSent(Task $task): void 536 { 537 $dossier = $task->dossier(); 538 $dossier->current_stage_id = $dossier->getStage(5)->id; 539 $dossier->save(); 540 541 TaskService::createDraftResolutionProposal($dossier); 542 self::closeMilestone($dossier, __('tasks.milestones.pre-hearing-meeting-minutes-sent')); 543 } 544 545 public static function createRectifyTask(int $userId, int $stageId): void 546 { 547 self::create( 548 self::getTaskData( 549 __('tasks.rectify.title'), 550 __('tasks.rectify.description'), 551 StatusEnum::waiting()->value 552 ), 553 ['user_id' => $userId], 554 $stageId 555 ); 556 } 557 558 public static function createReviewTask(int $roleId, int $stageId, string $documentName, int $revisionId): void 559 { 560 self::create( 561 self::getTaskData( 562 __('tasks.review.title', ['documentName' => $documentName]), 563 __('tasks.review.description', ['documentName' => $documentName]) 564 ), 565 ['role_id' => $roleId], 566 $stageId, 567 $revisionId 568 ); 569 } 570 571 public static function createApprovalTask(int $roleId, int $stageId, string $documentName, ?int $revisionId): void 572 { 573 self::create( 574 self::getTaskData( 575 __('tasks.approve.title', ['documentName' => $documentName]), 576 __('tasks.approve.description', ['documentName' => $documentName]) 577 ), 578 ['role_id' => $roleId], 579 $stageId, 580 $revisionId 581 ); 582 } 583 584 public static function createEvaluationKickoffMeetingTask(int $userId, Dossier $dossier): void 585 { 586 $nextStageId = $dossier->getStage(2)->id; 587 588 self::create( 589 self::getTaskData( 590 __('tasks.convene_evaluation_start_meet.title'), 591 __('tasks.convene_evaluation_start_meet.description'), 592 null, 593 null, 594 Carbon::now()->addMonth(), 595 type: TaskTypeEnum::conveneEvaluationStartMeet 596 ), 597 ['user_id' => $userId], 598 $nextStageId 599 ); 600 601 DossierService::setNewCurrentStage($dossier, $nextStageId); 602 } 603 604 public static function createDraftEvaluationKickoffMeetingActTask(int $userId, int $stageId): Task 605 { 606 return self::create( 607 self::getTaskData( 608 __('tasks.draft_evaluation_kick-off_act.title'), 609 __('tasks.draft_evaluation_kick-off_act.description'), 610 type: TaskTypeEnum::draftEvaluationKickOffAct 611 ), 612 ['user_id' => $userId], 613 $stageId 614 ); 615 } 616 617 public static function createSendEvaluationKickoffActTask(int $userId, int $stageId) 618 { 619 self::create( 620 self::getTaskData( 621 __('tasks.send_act_evaluation_start.title', ['dossierCode' => Stage::find($stageId)->dossier->code]), 622 __('tasks.send_act_evaluation_start.description'), 623 type: TaskTypeEnum::sendActEvaluationStart 624 ), 625 ['user_id' => $userId], 626 $stageId 627 ); 628 } 629 630 public static function createDraftPreResolutionHearingMeetingActTask(int $userId, int $stageId): Task 631 { 632 return self::create( 633 self::getTaskData( 634 __('tasks.draft_pre_resolution_hearing_act.title'), 635 __('tasks.draft_pre_resolution_hearing_act.description'), 636 type: TaskTypeEnum::draftPreResolutionHearingAct 637 ), 638 ['user_id' => $userId], 639 $stageId 640 ); 641 } 642 643 public static function createHandleDismissedDossierTask(int $userId, int $stageId): void 644 { 645 self::create( 646 self::getTaskData( 647 __('tasks.handle_dismissed.title', ['dossierCode' => Stage::find($stageId)->dossier->code]), 648 __('tasks.handle_dismissed.description') 649 ), 650 ['user_id' => $userId], 651 $stageId 652 ); 653 } 654 655 public static function createValidatePartialReportTask(int $userId, Dossier $dossier, string $partialClass, \App\Models\Validation $validation, bool $waiting=false): Task 656 { 657 $task = self::create( 658 self::getTaskData( 659 __('tasks.validate_partial_report.title', ['partialClass' => $partialClass]), 660 __('tasks.validate_partial_report.description'), 661 statusId: TaskStatus::onHold()->id, 662 type: TaskTypeEnum::validateEtrp 663 ), 664 ['user_id' => $userId], 665 //$nextStageId 666 $dossier->stage->id, 667 $validation->inboxFile?->document?->head_id, 668 ); 669 670 $task->milestoneValidation()->associate($validation); 671 $task->save(); 672 673 return $task; 674 } 675 676 public static function createValidateETRTask(int $userId, Dossier $dossier, \App\Models\Validation $validation): Task 677 { 678 /* $found = $dossier->inboxFiles()->where('type', InboxFileTypeEnum::ETR()->value)->whereColumn('updated_at', '>', 'created_at')->exists(); 679 $nextStageId = $found ? $dossier->stage->id : $dossier->nextStage()->id; */ 680 681 $task = self::create( 682 self::getTaskData( 683 __('tasks.validate_etr.title'), 684 __('tasks.validate_etr.description'), 685 type: TaskTypeEnum::validateEtr 686 ), 687 ['user_id' => $userId], 688 //$nextStageId 689 $dossier->stage->id, 690 $validation->inboxFile?->document?->head_id, 691 ); 692 $task->milestoneValidation()->associate($validation); 693 $task->save(); 694 /* 695 if (!$found) 696 DossierService::setNewCurrentStage($dossier, $nextStageId); */ 697 698 return $task; 699 } 700 701 public static function createReviewORTask(int $userId, int $stageId): void 702 { 703 self::create( 704 self::getTaskData( 705 __('tasks.review_or.title', ['dossierCode' => Stage::find($stageId)->dossier->code]), 706 __('tasks.review_or.description') 707 ), 708 ['user_id' => $userId], 709 $stageId 710 ); 711 } 712 713 public static function createSendETRTask(int $userId, int $stageId): Task 714 { 715 return self::create( 716 self::getTaskData( 717 title: __('tasks.send_etr.title'), 718 description: __('tasks.send_etr.description'), 719 type: TaskTypeEnum::sendEtr 720 ), 721 ['user_id' => $userId], 722 $stageId 723 ); 724 } 725 726 public static function createSendPartialReportTask(int $userId, int $stageId, string $partialClass): Task 727 { 728 $task = self::create( 729 self::getTaskData( 730 title: __('tasks.send_partial_report.title', ['partialClass' => $partialClass]), 731 description: __('tasks.send_partial_report.description'), 732 type: TaskTypeEnum::sendEtrp 733 ), 734 ['user_id' => $userId], 735 $stageId 736 ); 737 return $task; 738 } 739 740 741 public static function createSendPartialReportTaskMilestone(Stage $stage, string $partialClass, Task $task, \App\Models\Validation $validation) 742 { 743 $milestone = TaskService::createMilestone($stage, __('tasks.milestones.etrp-sent', [ 744 'partialClass' => $partialClass, 745 'number' => Task::where('stage_id', $stage->id)->where('title', $task->title)->count() 746 ]), null, TaskTypeEnum::sendEtrp->name); 747 748 749 $milestone->milestoneValidation()->associate($validation); 750 $milestone->save(); 751 752 return $milestone; 753 } 754 755 public static function createValidatePartialReportTaskMilestone(Stage $stage, string $partialClass, Task $task): Task 756 { 757 $milestone = TaskService::createMilestone($stage, __('tasks.milestones.etrp-received', [ 758 'partialClass' => $partialClass, 759 'number' => Task::where('stage_id', $stage->id)->where('title', $task->title)->count(), 760 ])); 761 762 763 return self::completeMilestoneNow($milestone); 764 } 765 766 public static function createSendETRTaskMilestone(Stage $stage, Task $task, \App\Models\Validation $validation): Task 767 { 768 $milestone = TaskService::createMilestone($stage, __('tasks.milestones.etr-sent', [ 769 'number' => Task::where('stage_id', $stage->id)->where('title', $task->title)->count() 770 ]), null, TaskTypeEnum::sendEtr->name); 771 772 $milestone->milestoneValidation()->associate($validation); 773 $milestone->save(); 774 return $milestone; 775 } 776 777 public static function createValidateETRTaskMilestone(Stage $stage, Task $task): Task 778 { 779 $number = Task::where('stage_id', $stage->id)->where('title', $task->title)->count(); 780 $milestone = TaskService::createMilestone($stage, __('tasks.milestones.etr-received', [ 781 'number' => $number, 782 ])); 783 784 return self::completeMilestoneNow($milestone); 785 } 786 787 public static function completeMilestoneNow(Task $milestone): Task 788 { 789 $milestone->milestone_end_date = now(); 790 $milestone->task_status_id = StatusEnum::finished(); 791 792 $milestone->save(); 793 794 return $milestone; 795 } 796 797 public static function findMilestoneByValidation(\App\Models\Validation $validation, TaskTypeEnum $type = TaskTypeEnum::default): ?Task 798 { 799 return Task::where('milestone_validation_id', $validation->id) 800 ->where('is_milestone', true) 801 ->where('type', $type->name) 802 ->first(); 803 } 804 805 public static function CompleteMilestoneByValidation(\App\Models\Validation $validation): void 806 { 807 $milestone = self::findMilestoneByValidation($validation); 808 809 if ($milestone) 810 self::completeMilestoneNow($milestone); 811 } 812 813 public static function createDraftCertificationReport(int $userId, Dossier $dossier) 814 { 815 self::create( 816 self::getTaskData( 817 __('tasks.create_draft_certification_report.title'), 818 __('tasks.create_draft_certification_report.description', ['dossierCode' => $dossier->code]), 819 type: TaskTypeEnum::createDraftCertificationReport 820 ), 821 ['user_id' => $userId], 822 $dossier->current_stage_id 823 ); 824 } 825 826 public static function createSendPreResolutionHearingMeeting(int $userId, int $stageId) 827 { 828 self::create( 829 self::getTaskData( 830 __('tasks.send_pre-hearing_minutes.title', ['dossierCode' => Stage::find($stageId)->dossier->code]), 831 __('tasks.send_pre-hearing_minutes.description'), 832 type: TaskTypeEnum::sendPreHearingMinutes 833 ), 834 ['user_id' => $userId], 835 $stageId 836 ); 837 } 838 839 public static function createSendPreResolutionHearingForm(?int $userId, int $stageId): Task 840 { 841 842 return self::create( 843 self::getTaskData( 844 __('tasks.send_pre-resolution_hearing_form.title', ['dossierCode' => Stage::find($stageId)->dossier->code]), 845 __('tasks.send_pre-resolution_hearing_form.description') 846 ), 847 ['user_id' => $userId], 848 $stageId 849 ); 850 } 851 852 public static function createConveneHearingMeetingTask(int $userId, int $stageId): void 853 { 854 self::create( 855 self::getTaskData( 856 __('tasks.convene_hearing_meeting_prior_to_resolution.title'), 857 __('tasks.convene_hearing_meeting_prior_to_resolution.description', ['dossierCode' => Stage::find($stageId)->dossier->code]), 858 type: TaskTypeEnum::convenePreResolutionMeet 859 ), 860 ['user_id' => $userId], 861 $stageId 862 ); 863 } 864 865 public static function createDraftResolutionProposal(Dossier $dossier): void 866 { 867 self::create( 868 self::getTaskData( 869 __('tasks.draft_resolution_proposal.title'), 870 __('tasks.draft_resolution_proposal.description', ['dossierCode' => $dossier->code]), 871 type: TaskTypeEnum::draftResolutionProposal 872 ), 873 ['user_id' => $dossier->principalCertifier->id], 874 $dossier->current_stage_id 875 ); 876 } 877 878 public static function createProcessResolution(int $userId, int $stageId): void 879 { 880 self::create( 881 self::getTaskData( 882 __('tasks.process_resolution.title'), 883 __('tasks.process_resolution.description', ['dossierCode' => Stage::find($stageId)->dossier->code]), 884 StatusEnum::waiting()->value, 885 type: TaskTypeEnum::processResolution 886 ), 887 ['user_id' => $userId], 888 $stageId 889 ); 890 } 891 892 public static function handleResolutionProcessed(Task $task): void 893 { 894 $dossier = $task->dossier(); 895 self::closeMilestone($dossier, __('tasks.milestones.resolution-processed')); 896 self::createFollowUpResolution($dossier); 897 } 898 899 public static function createFollowUpResolution(Dossier $dossier): void 900 { 901 self::create( 902 self::getTaskData( 903 __('tasks.follow-up_resolution.title'), 904 __('tasks.follow-up_resolution.description', ['dossierCode' => $dossier->code]), 905 StatusEnum::waiting()->value, 906 type: TaskTypeEnum::followUpResolution 907 ), 908 ['user_id' => $dossier->principalCertifier->id], 909 $dossier->current_stage_id 910 ); 911 } 912 913 public static function handleSignedResolution(Task $task): void 914 { 915 $dossier = $task->dossier(); 916 917 $resolution = $dossier->resolutions()->latest()->first(); 918 if (!$resolution or $resolution->approved === null) 919 throw new Exception(__('tasks.follow-up_resolution.error')); 920 921 self::closeMilestone($dossier, __('tasks.milestones.resolution-signed')); 922 923 if ($resolution->approved) { 924 self::createFollowUpSentToBoe($dossier); 925 return; 926 } 927 928 // If resolution not approved, then skip to stage 6, setting any other milestone to N/A 929 $task->stage->discardNonFinishedMilestones(); 930 $stage = $dossier->getStage(6); 931 $dossier->current_stage_id = $stage->id; 932 $dossier->save(); 933 self::finalizeCertificationReport($dossier->getCertifierId(), $dossier); 934 } 935 936 public static function createFollowUpSentToBoe(Dossier $dossier): void 937 { 938 self::create( 939 self::getTaskData( 940 __('tasks.follow-up_sent_to_boe.title'), 941 __('tasks.follow-up_sent_to_boe.description', ['dossierCode' => $dossier->code]), 942 StatusEnum::waiting()->value, 943 type: TaskTypeEnum::followUpSentToBoe 944 ), 945 ['user_id' => $dossier->principalCertifier->id], 946 $dossier->current_stage_id 947 ); 948 } 949 950 public static function handleSentToBoe(Task $task): void 951 { 952 $dossier = $task->dossier(); 953 self::closeMilestone($dossier, __('tasks.milestones.resolution-sent-to-boe')); 954 self::createFollowUpPublishedInBoe($dossier); 955 } 956 957 public static function createFollowUpPublishedInBoe(Dossier $dossier): void 958 { 959 self::create( 960 self::getTaskData( 961 __('tasks.follow-up_published_in_boe.title'), 962 __('tasks.follow-up_published_in_boe.description', ['dossierCode' => $dossier->code]), 963 StatusEnum::waiting()->value, 964 type: TaskTypeEnum::followUpPublishedInBoe 965 ), 966 ['user_id' => $dossier->principalCertifier->id], 967 $dossier->current_stage_id 968 ); 969 } 970 971 public static function handlePublishedInBoe(Task $task): void 972 { 973 $dossier = $task->dossier(); 974 self::closeMilestone($dossier, __('tasks.milestones.resolution-published-in-boe')); 975 self::createFollowUpCertificateSent($dossier); 976 } 977 978 public static function createFollowUpCertificateSent(Dossier $dossier): void 979 { 980 self::create( 981 self::getTaskData( 982 __('tasks.follow-up_certificate_sent.title'), 983 __('tasks.follow-up_certificate_sent.description', ['dossierCode' => $dossier->code]), 984 StatusEnum::waiting()->value, 985 type: TaskTypeEnum::followUpCertificateSent 986 ), 987 ['user_id' => $dossier->principalCertifier->id], 988 $dossier->current_stage_id 989 ); 990 } 991 992 public static function handleSignedCertificateSent(Task $task): void 993 { 994 $dossier = $task->dossier(); 995 $dossier->current_stage_id = $dossier->getStage(6)->id; 996 $dossier->save(); 997 998 self::closeMilestone($dossier, __('tasks.milestones.resolution-certificate-sent')); 999 self::finalizeCertificationReport($dossier); 1000 } 1001 1002 public static function finalizeCertificationReport(Dossier $dossier): void 1003 { 1004 self::create( 1005 self::getTaskData( 1006 __('tasks.finalize_certification_report.title'), 1007 __('tasks.finalize_certification_report.description', ['dossierCode' => $dossier->code]), 1008 type: TaskTypeEnum::finalizeCertificationReport 1009 ), 1010 ['user_id' => $dossier->getCertifierId()], 1011 $dossier->current_stage_id 1012 ); 1013 } 1014 1015 public static function updateCertifiedToeInformation(Dossier $dossier): void 1016 { 1017 self::create( 1018 self::getTaskData( 1019 __('tasks.update_certified_toe_information.title'), 1020 __('tasks.update_certified_toe_information.description', ['dossierCode' => $dossier->code]), 1021 type: TaskTypeEnum::updateCertifiedToeInformation 1022 ), 1023 ['user_id' => $dossier->getCertifierId()], 1024 $dossier->current_stage_id 1025 ); 1026 } 1027 1028 public static function handleUpdatedTOE(Task $task): void 1029 { 1030 $dossier = $task->dossier(); 1031 $resolution = $dossier->resolutions()->latest()->first(); 1032 if (!$resolution) 1033 return; 1034 1035 if ($resolution->approved) { 1036 self::updateCertifiedProductsList($dossier); 1037 return; 1038 } 1039 1040 $dossier->current_stage_id = $dossier->getStage(12)->id; 1041 self::createCloseDossier($dossier); 1042 } 1043 1044 1045 public static function updateCertifiedProductsList(Dossier $dossier): void 1046 { 1047 self::create( 1048 self::getTaskData( 1049 __('tasks.update_certified_products_list.title'), 1050 __('tasks.update_certified_products_list.description', ['dossierCode' => $dossier->code]), 1051 type: TaskTypeEnum::updateCertifiedProductsList 1052 ), 1053 ['user_id' => $dossier->getCertifierId()], 1054 $dossier->current_stage_id 1055 ); 1056 } 1057 1058 public static function handleUpdateCertifiedProductList(Task $task): void 1059 { 1060 $dossier = $task->dossier(); 1061 self::closeMilestone($dossier, __('tasks.milestones.certified-list-updated')); 1062 self::publishCertifiedProductOnWeb($dossier); 1063 } 1064 1065 public static function publishCertifiedProductOnWeb(Dossier $dossier): void 1066 { 1067 self::create( 1068 self::getTaskData( 1069 __('tasks.publish_certified_product_on_web.title'), 1070 __('tasks.publish_certified_product_on_web.description', ['dossierCode' => $dossier->code]), 1071 type: TaskTypeEnum::publishCertifiedProductOnWeb 1072 ), 1073 ['user_id' => $dossier->getCertifierId()], 1074 $dossier->current_stage_id 1075 ); 1076 } 1077 1078 public static function handlePublishCertifiedProductOnWeb(Task $task): void 1079 { 1080 $dossier = $task->dossier(); 1081 $dossier->current_stage_id = $dossier->getStage(12)->id; 1082 $dossier->save(); 1083 1084 self::closeMilestone($dossier, __('tasks.milestones.certified-product-web-published')); 1085 self::createCloseDossier($dossier); 1086 } 1087 1088 public static function handleCloseDossier(Task $task): void 1089 { 1090 $dossier = $task->dossier(); 1091 self::closeMilestone($dossier, __('tasks.milestones.dossier-closed')); 1092 } 1093 1094 public static function createMilestone(Stage $stage, string $title, ?Validation $validation = null, ?string $type = null): Task 1095 { 1096 $task = new Task([ 1097 'title' => $title, 1098 'description' => "", 1099 'start_date' => Carbon::now(), 1100 'task_status_id' => $type === TaskTypeEnum::certificationRequestReceived->name ? StatusEnum::finished()->value : StatusEnum::pending()->value, 1101 'stage_id' => $stage->id, 1102 'is_milestone' => true, 1103 'type' => $type->name ?? TaskTypeEnum::default->name, 1104 'milestone_end_date' => $type === TaskTypeEnum::certificationRequestReceived->name ? now() : null, 1105 ]); 1106 1107 $task->milestone_stage_number = $stage->getNumber(); 1108 $task->milestone_number = self::getMilestoneNumber($stage); 1109 1110 $task->save(); 1111 1112 return $task; 1113 } 1114 1115 private static function getMilestoneNumber(Stage $stage): int 1116 { 1117 return Task::where('stage_id', $stage->id) 1118 ->whereIsMilestone(true) 1119 ->count() + 1; 1120 } 1121 1122 private static function getTaskData( 1123 string $title, 1124 string $description, 1125 int $statusId = null, 1126 Carbon $end_date = null, 1127 Carbon $expiration_date = null, 1128 TaskTypeEnum $type = TaskTypeEnum::default 1129 ): array { 1130 if (!$statusId) { 1131 $statusId = StatusEnum::pending()->value; 1132 } 1133 1134 return [ 1135 'title' => $title, 1136 'description' => $description, 1137 'start_date' => Carbon::now(), 1138 'end_date' => $end_date, 1139 'expiration_date' => $expiration_date, 1140 'task_status_id' => $statusId, 1141 'type' => $type->name, 1142 ]; 1143 } 1144 1145 /* 1146 * This method handles the completion of a task 1147 * based on the title of the task 1148 */ 1149 public static function handleCompletion(Task $task): void 1150 { 1151 $userId = $task->dossier()->getCertifierId(); 1152 if (!$userId) { 1153 return; 1154 } 1155 1156 $stageId = $task->stage->id; 1157 1158 //ETR SENT milestones are related to a task 1159 $relatedMilestone = Task::where('milestone_related_task_id', $task->id)->first(); 1160 if ($relatedMilestone) { 1161 self::completeMilestoneNow($relatedMilestone); 1162 } 1163 1164 match ($task->type) { 1165 // stage 1 1166 'assignCertifiers' => self::handleAssignCertifierTaskCompleted($task), 1167 'startCertification' => self::handleNotifyStartCertificationTaskCompleted($task), 1168 1169 // stage 2 1170 'conveneEvaluationStartMeet' => self::createDraftEvaluationKickoffMeetingActTask($userId, $stageId), 1171 'draftEvaluationKickOffAct' => self::handleKickOffActDone($task), 1172 'sendActEvaluationStart' => self::handleKickOffActSent($task), 1173 1174 // stage 3 1175 1176 // stage 4 1177 'createDraftCertificationReport' => self::createConveneHearingMeetingTask($userId, $task->stage->id), 1178 'convenePreResolutionMeet' => self::createDraftPreResolutionHearingMeetingActTask($userId, $stageId), 1179 'draftPreResolutionHearingAct' => self::handlePreResolutionActDone($task), 1180 'sendPreHearingMinutes' => self::handlePreResolutionActSent($task), 1181 1182 // stage 5 1183 'draftResolutionProposal' => self::createProcessResolution($userId, $stageId), 1184 'processResolution' => self::handleResolutionProcessed($task), 1185 'followUpResolution' => self::handleSignedResolution($task), 1186 'followUpSentToBoe' => self::handleSentToBoe($task), 1187 'followUpPublishedInBoe' => self::handlePublishedInBoe($task), 1188 'followUpCertificateSent' => self::handleSignedCertificateSent($task), 1189 1190 // stage 6 1191 'finalizeCertificationReport' => self::updateCertifiedToeInformation($task->dossier()), 1192 'updateCertifiedToeInformation' => self::handleUpdatedTOE($task), 1193 'updateCertifiedProductsList' => self::handleUpdateCertifiedProductList($task), 1194 'publishCertifiedProductOnWeb' => self::handlePublishCertifiedProductOnWeb($task), 1195 1196 // stage 12 1197 'closeDossier' => self::handleCloseDossier($task), 1198 default => null, 1199 }; 1200 } 1201 1202 public static function getInternalRoles() 1203 { 1204 return Role::where('scope', 'global') 1205 ->where('is_internal', true) 1206 ->get(['id', 'name']) 1207 ->map(function ($role) {; 1208 return [ 1209 'id' => $role->id, 1210 'name' => __('roles.' . $role->name), 1211 ]; 1212 }); 1213 } 1214 }