Dossier.php
1 <?php 2 3 namespace App\Models; 4 5 use App\Enums\RoleEnum; 6 use App\Enums\StatusEnum; 7 use App\Events\DossierComplete; 8 use App\Http\Services\DossierService; 9 use Illuminate\Database\Eloquent\Builder; 10 use Illuminate\Database\Eloquent\Casts\Attribute; 11 use Illuminate\Database\Eloquent\Factories\HasFactory; 12 use Illuminate\Database\Eloquent\Model; 13 use Illuminate\Database\Eloquent\Relations\HasMany; 14 use Illuminate\Database\Eloquent\Relations\HasManyThrough; 15 use Illuminate\Database\Eloquent\Relations\HasOne; 16 use Illuminate\Database\Eloquent\Relations\MorphMany; 17 use Illuminate\Database\Eloquent\Relations\MorphToMany; 18 use Illuminate\Database\Eloquent\SoftDeletes; 19 use Illuminate\Support\Collection; 20 use Spatie\Activitylog\LogOptions; 21 use Spatie\Activitylog\Traits\LogsActivity; 22 23 class Dossier extends Model 24 { 25 use HasFactory, LogsActivity, SoftDeletes; 26 27 protected $guarded = []; 28 protected $with = ['principalCertifier', 'toe']; 29 30 protected $dates = ['evaluation_end_date', 'certification_end_date']; 31 32 // Attributes 33 public function getCodeAttribute() 34 { 35 return $this->code_year . '-' . $this->code_seq; 36 } 37 38 /** 39 * PHPWord 40 */ 41 protected function getSTNameAttribute(): string 42 { 43 return $this->st_name ?? __('app.n/a'); //TOKEN para la declaración de seguridad : ${dossier.st_name} 44 } 45 46 /** 47 * PHPWord 48 * @return Attribute 49 */ 50 protected function ppNames(): Attribute 51 { 52 return Attribute::make( 53 get: fn() => implode(',', $this->PPs()->pluck('name')->toArray()), 54 ); 55 } 56 57 /** 58 * PHPWord 59 * @return Attribute 60 */ 61 protected function IARDocument(): Attribute 62 { 63 return Attribute::make( 64 get: fn() => $this->documents()->where('id', $this->iar_document_id)->first() 65 ); 66 } 67 68 /** 69 * PHPWord 70 * @return Attribute 71 */ 72 protected function product_system(): Attribute 73 { 74 return Attribute::make( 75 get: fn() => $this->toe->product_system 76 ); 77 } 78 79 /** 80 * PHPWord 81 * @return Attribute 82 */ 83 protected function additionalComponents(): Attribute 84 { 85 return Attribute::make( 86 get: function () { 87 $workbooks = $this->workbooks()->pluck('name')->toArray(); 88 89 if (empty($workbooks)) { 90 return __('app.n/a'); 91 } 92 93 return implode(', ', $workbooks); 94 }, 95 ); 96 } 97 98 /** 99 * PHPWord 100 * @return Attribute 101 */ 102 public function getEvaluationDateAttribute() 103 { 104 $evaluationEndDate = $this->evaluation_end_date; 105 106 if (empty($evaluationEndDate)) { 107 return __('app.n/a'); 108 } 109 110 return $evaluationEndDate->format('d/m/Y'); 111 } 112 113 /** 114 * PHPWord 115 * @return Attribute 116 */ 117 public function getEALNameAttribute() 118 { 119 $normScope = $this->normScope; 120 121 $scopeText = null; 122 if ($normScope) { 123 $scopeText = $normScope->name; 124 } 125 126 $workbooksText = null; 127 if ($this->workbooks->count() > 0) { 128 $workbooksText = implode(', ', $this->workbooks->pluck('name')->toArray()); 129 } 130 131 if ($scopeText and $workbooksText) { 132 return implode(' + ', [$scopeText, $workbooksText]); 133 } 134 135 return implode('', [$scopeText, $workbooksText]); 136 } 137 138 /*************** Certificators and people working in dossier ***************/ 139 140 public function users(): MorphToMany 141 { 142 143 // TODO: principal certifier no esta aqui... 144 return $this->morphToMany(User::class, 'entity', 'entity_user') 145 ->withPivot('role_id') 146 ->using(EntityUser::class); 147 } 148 149 public function getPointsOfContact(): Collection 150 { 151 $users = $this->users()->get(); 152 $this->principalCertifier ? $users->push($this->principalCertifier) : null; 153 154 $users = $users->merge($this->applicant ? $this->applicant->pointsOfContact() : collect()); 155 $users = $users->merge($this->laboratory ? $this->laboratory->pointsOfContact() : collect()); 156 $users = $users->merge($this->manufacturer ? $this->manufacturer->pointsOfContact() : collect()); 157 158 $users->unique('id'); 159 return $users; 160 } 161 162 public function pocs() 163 { 164 return $this->belongsToMany(User::class, 'dossier_poc'); 165 } 166 167 public function principalCertifier() 168 { 169 return $this->belongsTo(User::class, 'principal_certifier_id'); 170 } 171 172 public function secondaryCertifiers() 173 { 174 return $this->users() 175 ->wherePivot('entity_id', $this->id) 176 ->wherePivot('entity_type', self::class) 177 ->wherePivot('role_id', RoleEnum::secondaryCertifier()->value) 178 ->get(); 179 } 180 181 public function informedPersonal() 182 { 183 return $this->users() 184 ->wherePivot('entity_id', $this->id) 185 ->wherePivot('entity_type', self::class) 186 ->wherePivot('role_id', RoleEnum::informedStaff()->value) 187 ->get(); 188 } 189 190 public function externalCertifiers() 191 { 192 return $this->users() 193 ->wherePivot('entity_id', $this->id) 194 ->wherePivot('entity_type', self::class) 195 ->wherePivot('role_id', RoleEnum::externalCertifier()->value) 196 ->get(); 197 } 198 199 public function experts() 200 { 201 return $this->users() 202 ->wherePivot('entity_id', $this->id) 203 ->wherePivot('entity_type', self::class) 204 ->wherePivotBetween('role_id', [RoleEnum::internalExpert()->value, RoleEnum::externalExpert()->value]) 205 ->get(); 206 } 207 208 public function evaluators() 209 { 210 return $this->users() 211 ->wherePivot('entity_id', $this->id) 212 ->wherePivot('entity_type', self::class) 213 ->wherePivot('role_id', RoleEnum::evaluator()->value) 214 ->get(); 215 } 216 217 public function getCertifiers(): Collection 218 { 219 $externalCertifiers = $this->externalCertifiers(); 220 if ($externalCertifiers->isNotEmpty()) { 221 return $externalCertifiers; 222 } 223 224 $ppalCertifier = $this->principalCertifier; 225 if ($ppalCertifier) { 226 return collect([$ppalCertifier]); 227 } 228 229 return collect(User::technicalManager()); 230 } 231 232 public function getMainCertifier() 233 { 234 return $this->getCertifiers()->first(); 235 } 236 237 public function getCertifierId() 238 { 239 return $this->externalCertifiers()->first()->id ?? $this->principalCertifier->id ?? null; 240 } 241 242 public function getCertifier() 243 { 244 return $this->externalCertifiers()->first() ?? $this->principalCertifier ?? null; 245 } 246 247 public function getCertifierOrTechnicalManager() 248 { 249 return $this->getCertifierId() ?? User::technicalManager()->id; 250 } 251 252 public function getNotifiableUser() 253 { 254 return $this->externalCertifiers()->first() ?? $this->principalCertifier ?? User::technicalManager(); 255 } 256 257 public function scopeActive(Builder $query): void 258 { 259 $query->where('active', 1); 260 } 261 262 public function taskReceiver() 263 { 264 return $this->belongsTo(User::class, 'task_receiver_id'); 265 } 266 267 public function applicant() 268 { 269 return $this->belongsTo(Entity::class, 'applicant_id'); 270 } 271 272 public function manufacturer() 273 { 274 return $this->belongsTo(Entity::class, 'manufacturer_id'); 275 } 276 277 public function sponsor() 278 { 279 return $this->belongsTo(Entity::class, 'sponsor_id'); 280 } 281 282 /****************************************************************************/ 283 /*************** Dossier Parameters ***************/ 284 // public function dossierType() 285 // { 286 // return $this->belongsTo(DossierType::class); 287 // } 288 289 public function dossierType() 290 { 291 return $this->belongsTo(DossierType::class); 292 } 293 294 public function subtype() 295 { 296 return $this->belongsTo(DossierType::class, 'dossier_type_id', 'id'); 297 } 298 299 300 public function toe() 301 { 302 return $this->belongsTo(TOE::class, 'toe_id'); 303 } 304 305 /** 306 * Returns the current stage of the dossier 307 */ 308 public function stage(): HasOne 309 { 310 return $this->hasOne(Stage::class, 'id', 'current_stage_id'); 311 } 312 313 public function nextStage() 314 { 315 return $this->hasMany(Stage::class) 316 ->whereNot('stage_status_id', StatusEnum::finished()->value) 317 ->where('id', '>', $this->current_stage_id) 318 ->orderBy('order', 'asc') 319 ->first(); 320 } 321 322 public function stages(): HasMany 323 { 324 return $this->hasMany(Stage::class); 325 } 326 327 public function getStage(int $num) 328 { 329 return $this->stages()->whereName(__('stages.default.' . $num))->first(); 330 } 331 332 public function tasks() 333 { 334 return $this->hasManyThrough(Task::class, Stage::class); 335 } 336 337 public function roles() 338 { 339 return $this->belongsToMany(Role::class, 'role_dossier', 'dossier_id', 'role_id'); 340 } 341 342 public function laboratory() 343 { 344 return $this->belongsTo(Entity::class, 'laboratory_id'); 345 } 346 347 public function norm() 348 { 349 return $this->belongsTo(Norm::class); 350 } 351 352 public function normScope() 353 { 354 return $this->belongsTo(NormScope::class); 355 } 356 357 public function validityReport() 358 { 359 return $this->belongsTo(Document::class, 'validity_report_document_id'); 360 } 361 362 public function technicalReport() 363 { 364 return $this->belongsTo(Document::class, 'technical_report_document_id'); 365 } 366 367 public function certificationReport() 368 { 369 return $this->belongsTo(Document::class, 'certification_report_document_id'); 370 } 371 372 public function certificationApplication() 373 { 374 return $this->belongsTo(Document::class, 'certification_application_document_id'); 375 } 376 377 public function relatedDossiers() 378 { 379 return $this->protectionProfiles()->first(); 380 } 381 382 public function protectionProfiles() 383 { 384 return $this->belongsToMany(Document::class, 'dossier_document_pp', 'dossier_id', 'document_id'); 385 } 386 387 public function PPs() 388 { 389 return $this->belongsToMany(ProtectionProfile::class, 'dossier_protection_profiles', 'dossier_id', 'pp_id'); 390 } 391 392 public function workbooks() 393 { 394 return $this->belongsToMany(Workbook::class, 'dossier_workbook'); 395 } 396 397 public function validationsAsOriginalDossier() 398 { 399 return $this->hasMany(Validation::class, 'original_dossier_id'); 400 } 401 402 public function certificateInfos(): HasMany 403 { 404 return $this->hasMany(CertificateInfo::class, 'dossier_id'); 405 } 406 407 public function certificates(): HasMany 408 { 409 return $this->hasMany(Certificate::class); 410 } 411 412 public function inboxFiles(): HasMany 413 { 414 return $this->hasMany(InboxFile::class); 415 } 416 417 418 public function validations(): HasManyThrough 419 { 420 return $this->hasManyThrough(Validation::class, InboxFile::class, 'validation_id', 'id', 'id', 'validation_id'); 421 } 422 423 /****************************************************************************/ 424 /*************** Dossier Utils ***************/ 425 public function userCanAccess($user) 426 { 427 // $principal_certs = $this->principalCertifier()->toArray(); 428 if ($user->id === $this->principal_certifier_id) { 429 return true; 430 } 431 $secondary_certs = $this->secondaryCertifiers()->toArray(); 432 if (in_array($user->id, array_column($secondary_certs, 'user_id'))) { 433 return true; 434 } 435 $informed_personal = $this->informedPersonal()->toArray(); 436 if (in_array($user->id, array_column($informed_personal, 'user_id'))) { 437 return true; 438 } 439 $external_certs = $this->externalCertifiers()->toArray(); 440 if (in_array($user->id, array_column($external_certs, 'user_id'))) { 441 return true; 442 } 443 444 return false; 445 } 446 447 public function userWriteAccess($user) 448 { 449 // $principal_certs = $this->principalCertifier()->toArray(); 450 if ($user->id === $this->principal_certifier_id) { 451 return true; 452 } 453 $secondary_certs = $this->secondaryCertifiers()->toArray(); 454 if (in_array($user->id, array_column($secondary_certs, 'user_id'))) { 455 return true; 456 } 457 $external_certs = $this->externalCertifiers()->toArray(); 458 if (in_array($user->id, array_column($external_certs, 'user_id'))) { 459 return true; 460 } 461 462 return false; 463 } 464 465 public function isInDossier(Dossier $otherDossier) 466 { 467 $dossiersRelated = $otherDossier->relatedDossiers->toArray(); 468 469 return in_array($this->id, array_column($dossiersRelated, 'id')); 470 } 471 472 public function status() 473 { 474 return $this->belongsTo(DossierStatus::class, 'dossier_status_id'); 475 } 476 477 public function checkStatus() 478 { 479 $this->dossier_status_id = DossierService::calculateDossierStatus($this); 480 $this->save(); 481 482 if ($this->status === StatusEnum::finished()) { 483 DossierComplete::dispatch($this); 484 } 485 } 486 487 /****************************************************************************/ 488 489 public function audits() 490 { 491 return $this->hasMany(Audit::class); 492 } 493 494 public function documents() 495 { 496 return $this->hasMany(Document::class); 497 } 498 499 public function dossierLogs() 500 { 501 return $this->hasMany(DossierLog::class); 502 } 503 504 public function resolutions() 505 { 506 return $this->hasMany(Resolution::class); 507 } 508 509 public function resolution() 510 { 511 return $this->hasOne(Resolution::class)->ofMany('certification_date')->where('approved', true); 512 } 513 514 public function meets() 515 { 516 return $this->hasMany(Meet::class); 517 } 518 519 public function recommendations() 520 { 521 return $this->hasMany(Recommendation::class); 522 } 523 524 public function configComponents() 525 { 526 return $this->hasMany(ConfigComponent::class); 527 } 528 529 public function folders() 530 { 531 return $this->hasMany(Folder::class); 532 } 533 534 public function internalFolder() 535 { 536 return $this->folders()->where('name', __('documents.folders.internal'))->first(); 537 } 538 539 public function internalNotes(): MorphMany 540 { 541 return $this->morphMany(InternalNote::class, 'internal_noteable'); 542 } 543 544 public function workers(): Collection 545 { 546 $workers = collect(); 547 if ($this->principalCertifier) 548 $workers->push($this->principalCertifier); 549 $workers = $workers->merge($this->secondaryCertifiers()); 550 $workers = $workers->merge($this->externalCertifiers()); 551 552 return $workers; 553 } 554 555 public function getEntityRole(int $entityId) 556 { 557 return match ($entityId) { 558 $this->applicant_id => __('dossiers.dossier.panel.form.applicant'), 559 $this->laboratory_id => __('dossiers.dossier.panel.form.laboratory'), 560 $this->sponsor_id => __('dossiers.dossier.panel.form.sponsor'), 561 $this->manufacturer_id => __('dossiers.dossier.panel.form.manufacturer'), 562 default => __('entities.unassigned'), 563 }; 564 } 565 566 public function milestones(): HasManyThrough 567 { 568 return $this->tasks()->where('is_milestone', true); 569 } 570 571 public function scopeCertified(Builder $query): void 572 { 573 $query->where('dossier_status_id', DossierStatus::certified); 574 } 575 576 public static function report() 577 { 578 $select_statement = [ 579 'dossiers.id', 580 'dossiers.code_year', 581 'dossiers.code_seq', 582 'dossier_types.name as mode', 583 'toes.name as toe_name', 584 'dossier_statuses.name as status', 585 'laboratories.code as lab_code', 586 'laboratories.name as lab_name', 587 'applicant.name as applicant_name', 588 'sponsor.name as sponsor_name', 589 'users.username as principal_certifier', 590 ]; 591 592 // Joins 593 $query = Dossier::leftjoin('toes', 'toes.id', '=', 'dossiers.toe_id') 594 ->leftjoin('dossier_statuses', 'dossier_statuses.id', '=', 'dossiers.dossier_status_id') 595 ->leftjoin('entities', 'entity.id', '=', 'dossiers.laboratory_id') 596 ->leftjoin('companies as applicant', 'applicant.id', '=', 'dossiers.applicant_id') 597 ->leftjoin('companies as sponsor', 'sponsor.id', '=', 'dossiers.sponsor_id') 598 ->leftjoin('users', 'users.id', '=', 'dossiers.principal_certifier_id') 599 ->leftjoin('dossier_types', 'dossier_types.id', '=', 'dossiers.dossier_type_id') 600 ->select($select_statement); 601 602 return $query; 603 } 604 605 public function originalDossier() 606 { 607 return $this->belongsTo(Dossier::class, 'original_dossier_id'); 608 } 609 610 public function technicalDomain(): HasOne 611 { 612 return $this->hasOne(DossierTechnicalDomain::class, 'id', 'technical_domain_id'); 613 } 614 615 public function getActivitylogOptions(): LogOptions 616 { 617 return LogOptions::defaults() 618 ->logOnly([ 619 'code_year', 620 'code_seq', 621 'description', 622 'dossier_type_id', 623 'subtype.name', 624 'dossier_status_id', 625 'toe_id', 626 'toe.name', 627 'principal_certifier_id', 628 'task_receiver_id', 629 'taskReceiver.username', 630 'norm_id', 631 'norm.name', 632 'laboratory_id', 633 'laboratory.name', 634 'sponsor_id', 635 'sponsor.name', 636 'applicant_id', 637 'applicant.name', 638 'manufacturer_id', 639 'manufacturer.name', 640 'resolved', 641 'norm_scope_id', 642 'normScope.name', 643 'language', 644 'evaluation_end_date', 645 ])->logOnlyDirty() 646 ->dontSubmitEmptyLogs(); 647 } 648 }