/ app / Models / Dossier.php
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  }