117 lines
5.1 KiB
PHP
117 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
/**
|
|
* @property int|null $owner_user_id
|
|
* @property string|null $organization_id
|
|
* @property array<string,mixed>|null $workflow_context
|
|
* @property string|null $primary_territory_id
|
|
* @property int $territory_version
|
|
* @property int $management_version
|
|
* @property string|null $management_draft
|
|
* @property string|null $management_creation_key
|
|
* @property string|null $first_submitted_at
|
|
* @property bool $management_closed_once
|
|
* @property string|null $opportunity_category
|
|
* @property string|null $estimated_investment_currency
|
|
*/
|
|
class Opportunity extends Model
|
|
{
|
|
use SoftDeletes;
|
|
|
|
protected $hidden = ['management_draft', 'management_creation_key', 'management_version', 'first_submitted_at', 'management_closed_once', 'owner_user_id', 'organization_id', 'workflow_context'];
|
|
|
|
protected $appends = ['maturity_catalogue_version'];
|
|
|
|
public function toArray(): array
|
|
{
|
|
$data = parent::toArray();
|
|
if (isset($data['description'])) $data['description'] = \App\Services\OpportunityPublicText::description($this, $data['description']);
|
|
if ($this->exists && ($proactive = app(\App\Services\ProactiveInvestment::class)->publicData($this))) $data['proactive'] = $proactive;
|
|
return $data;
|
|
}
|
|
|
|
public function getMaturityCatalogueVersionAttribute(): ?string
|
|
{
|
|
// Only published metadata is public; never expose the current private draft.
|
|
$version = data_get($this->workflow_context, 'publication.maturity.catalogue_version');
|
|
|
|
return in_array($version, [\App\Services\OpportunityMaturity::VERSION, \App\Services\OpportunityMaturity::LEGACY], true)
|
|
? $version : null;
|
|
}
|
|
|
|
protected $fillable = [
|
|
'sector_id', 'reference', 'title', 'slug', 'summary', 'description',
|
|
'province', 'district', 'promoter_name', 'promoter_type',
|
|
'maturity_level', 'maturity_label', 'estimated_investment_amount',
|
|
'estimated_investment_currency', 'financing_modality',
|
|
'expected_impact', 'status', 'is_featured', 'published_at',
|
|
'last_validated_at',
|
|
'primary_territory_id', 'territory_version',
|
|
'geography_scope',
|
|
];
|
|
|
|
protected $casts = [
|
|
'workflow_context' => 'array',
|
|
'is_featured' => 'boolean',
|
|
'territory_version' => 'integer',
|
|
'published_at' => 'datetime',
|
|
'last_validated_at' => 'datetime',
|
|
'estimated_investment_amount' => 'decimal:2',
|
|
];
|
|
|
|
/** @return array{scope:string,label:string,public_catalog:bool} */
|
|
public function publicationPresentation(): array
|
|
{
|
|
$public = $this->exists && self::query()->whereKey($this->id)->published()->exists();
|
|
$visible = ! $this->trashed() && $this->status === 'published' && $this->published_at !== null && $this->published_at->lte(now());
|
|
$scope = $public ? 'public' : ($visible && $this->owner_user_id !== null ? 'restricted' : 'unpublished');
|
|
|
|
return ['scope' => $scope, 'public_catalog' => $public, 'label' => match ($scope) {
|
|
'public' => data_get($this->workflow_context, 'publication.purpose') === 'promotional_preparation'
|
|
? 'Resumo divulgado — Em preparação; não autoriza execução' : 'Publicada no catálogo público',
|
|
'restricted' => 'Disponibilizada com acesso restrito — perfis e organização autorizados',
|
|
default => 'Sem versão disponibilizada; aprovação interna é uma etapa distinta',
|
|
}];
|
|
}
|
|
|
|
/** @return BelongsTo<Sector, $this> */
|
|
public function sector(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Sector::class);
|
|
}
|
|
|
|
/** @return BelongsTo<GeoLocation, $this> */
|
|
public function primaryTerritory(): BelongsTo
|
|
{
|
|
return $this->belongsTo(GeoLocation::class, 'primary_territory_id');
|
|
}
|
|
|
|
/** @return BelongsToMany<GeoLocation, $this> */
|
|
public function additionalTerritories(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(GeoLocation::class, 'opportunity_locations', 'opportunity_id', 'geo_location_id');
|
|
}
|
|
|
|
/** @param Builder<Opportunity> $query
|
|
* @return Builder<Opportunity>
|
|
*/
|
|
public function scopePublished(Builder $query): Builder
|
|
{
|
|
if (Schema::hasColumn('opportunities', 'workflow_context')) {
|
|
$query->where(fn ($q) => $q->whereNull('owner_user_id')->orWhere('workflow_context->publication->visibility', 'public'));
|
|
}
|
|
|
|
return $query->where('status', 'published')->whereNotNull('published_at')->where('published_at', '<=', now())
|
|
->whereRaw("COALESCE((SELECT origin.after_data->>'classification' FROM audit_events origin WHERE origin.source_system='opportunity-origin-review' AND origin.after_data->>'opportunity_id'=opportunities.id::text ORDER BY origin.occurred_at DESC,origin.id DESC LIMIT 1),'undetermined') <> 'example'");
|
|
}
|
|
}
|