@@ -79,7 +79,7 @@ class OpportunityClassification
|
||||
[$table] = self::TARGETS[$kind];
|
||||
$query = DB::table($table);
|
||||
if (in_array($kind, ['sector', 'activity'], true)) {
|
||||
return $kind === 'sector' ? $query->whereNull('deleted_at')->where('is_active', true) : $query->where('status', 'published');
|
||||
return $kind === 'sector' ? $query->whereNull('deleted_at')->where('is_active', true) : app(EconomicReferenceCatalog::class)->constrain($query->where('status', 'published'), 'activity');
|
||||
}
|
||||
$query->whereNull('deleted_at');
|
||||
if ($kind === 'chain') {
|
||||
@@ -97,14 +97,16 @@ class OpportunityClassification
|
||||
return $query->where(fn ($q) => $q->where('data_classification', 'public')->orWhereIn('id', InvestmentPortfolioController::visible()->whereNotNull($foreign)->select($foreign)));
|
||||
}
|
||||
|
||||
return $query->where('data_classification', 'public');
|
||||
return app(EconomicReferenceCatalog::class)->constrain($query->where('data_classification', 'public'), $kind);
|
||||
}
|
||||
|
||||
public function options(): array
|
||||
{
|
||||
$targets = [];
|
||||
foreach (self::TARGETS as $kind => [$table, $label, $dimension]) {
|
||||
$targets[$kind] = $this->targets($kind)->orderBy($label)->get(['id', $label.' as label']);
|
||||
$targets[$kind] = $kind === 'activity'
|
||||
? app(EconomicReferenceCatalog::class)->activities()->map(fn ($r) => (object) ['id' => $r->id, 'label' => $r->display_label, 'code' => $r->code, 'level' => $r->level, 'path' => $r->path, 'source_version' => $r->source_version, 'edition' => $r->edition])
|
||||
: $this->targets($kind)->orderBy($label)->get(['id', $label.' as label']);
|
||||
}
|
||||
|
||||
return ['version' => self::VERSION, 'dimensions' => self::DIMENSIONS, 'types' => array_map(fn ($v) => $v[2], self::TARGETS), 'purposes' => self::PURPOSES, 'states' => self::STATES, 'externalStates' => self::EXTERNAL, 'targets' => $targets];
|
||||
@@ -112,6 +114,16 @@ class OpportunityClassification
|
||||
|
||||
public function validate(array $payload, Opportunity $record): void
|
||||
{
|
||||
$previousPayload = $record->exists ? (app(OpportunityManagement::class)->values($record)['canonical'] ?? []) : [];
|
||||
$catalog = app(EconomicReferenceCatalog::class);
|
||||
$lotIds = array_column($catalog->manifest()['activities'], 'id');
|
||||
if (in_array($payload['economic_activity_id'] ?? null, $lotIds, true) && ! $catalog->available()) {
|
||||
foreach (['sector_id', 'subsector_id', 'economic_activity_id'] as $field) {
|
||||
if (($payload[$field] ?? null) !== ($previousPayload[$field] ?? null)) {
|
||||
throw ValidationException::withMessages(['canonical.'.$field => 'Referência retirada: apenas a classificação histórica já guardada pode ser conservada.']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$seen = [];
|
||||
foreach ($payload['relations'] ?? [] as $i => $row) {
|
||||
if (! isset($row['classification'])) {
|
||||
@@ -142,7 +154,8 @@ class OpportunityClassification
|
||||
if (($kind === 'sector' && ! ctype_digit($id)) || ($kind !== 'sector' && ! Str::isUuid($id))) {
|
||||
$fail('related_id', 'Identificador de referência inválido.');
|
||||
}
|
||||
if (! isset(self::TARGETS[$kind]) || ! $this->targets($kind)->where('id', (string) ($row['related_id'] ?? ''))->exists()) {
|
||||
$previousRows = $record->exists ? (app(OpportunityManagement::class)->values($record)['canonical']['relations'] ?? []) : [];
|
||||
if (! isset(self::TARGETS[$kind]) || ! $this->canRetainReference($kind, $id, $row, $previousRows)) {
|
||||
$fail('related_id', 'Selecione uma referência existente e consultável; nenhum cadastro é criado por esta relação.');
|
||||
}
|
||||
$purpose = $c['purpose'] ?? '';
|
||||
@@ -172,6 +185,16 @@ class OpportunityClassification
|
||||
}
|
||||
}
|
||||
|
||||
/** Exact persisted relation retention is not permission to choose a withdrawn target anew. */
|
||||
public function canRetainReference(string $kind, string $id, array $row, array $previousRows): bool
|
||||
{
|
||||
if ($this->targets($kind)->where('id', $id)->exists()) { return true; }
|
||||
if (! app(EconomicReferenceCatalog::class)->historicalTarget($kind, $id)) { return false; }
|
||||
$content = fn ($r) => array_intersect_key($r, array_flip(['related_id', 'relation_type', 'classification']));
|
||||
|
||||
return collect($previousRows)->contains(fn ($old) => OpportunityCanonicalValue::hash($content($old)) === OpportunityCanonicalValue::hash($content($row)));
|
||||
}
|
||||
|
||||
public function hash(array $row): string
|
||||
{
|
||||
// Client identity/status cannot certify a relation. Object-order normalization is reused.
|
||||
@@ -320,6 +343,7 @@ class OpportunityClassification
|
||||
// Original documents, internal rationale, actor IDs and decision history stay private.
|
||||
$rows[] = ['dimension' => self::TARGETS[$kind][2], 'target_type' => $kind, 'target_id' => (string) $row['related_id'], 'label' => $target->{self::TARGETS[$kind][1]},
|
||||
'purpose' => $c['purpose'], 'purpose_label' => self::PURPOSES[$c['purpose']], 'target_code' => $target->code ?? null, 'catalog_version' => $target->catalog_version_id ?? null,
|
||||
'reference_path' => $kind === 'activity' ? (app(EconomicReferenceCatalog::class)->activities()->firstWhere('id', $target->id)?->path ?? []) : [],
|
||||
'physical_category' => $target->physical_category ?? null, 'legal_regime' => $target->legal_regime ?? null,
|
||||
'state' => $state, 'source_reference' => $c['source_reference'] ?? null, 'citation' => $c['citation'] ?? null,
|
||||
'authority_id' => $c['authority_id'] ?? null, 'external_status' => $c['external_status'] ?? 'not_assessed', 'availability_confirmed' => false,
|
||||
|
||||
Reference in New Issue
Block a user