'Banca', 'credit' => 'Crédito e microfinanças', 'investment' => 'Gestão de investimento', 'pensions' => 'Fundos de pensões', 'insurance' => 'Seguros e resseguros', 'intermediation' => 'Mediação de seguros', 'exchange' => 'Câmbio', 'payments' => 'Pagamentos e serviços interbancários', 'supervision' => 'Supervisão', 'other' => 'Outras instituições']; public const TYPES = ['commercial_bank' => 'Banco comercial', 'investment_bank' => 'Banco de investimento', 'bank' => 'Banco (tipo não especificado)', 'microbank' => 'Microbanco', 'cooperative' => 'Cooperativa de crédito', 'savings' => 'Organização de poupança e empréstimo', 'investment_manager' => 'Gestora de fundos de investimento', 'investment_company' => 'Sociedade de investimento', 'pension_manager' => 'Gestora de fundos de pensões', 'insurer' => 'Seguradora', 'reinsurer' => 'Resseguradora', 'microinsurer' => 'Micro-seguradora', 'broker' => 'Corretora de seguros', 'agent' => 'Agente de seguros', 'reinsurance_broker' => 'Corretor de resseguro', 'exchange_house' => 'Casa de câmbio', 'electronic_money' => 'Instituição de moeda electrónica', 'interbank' => 'Serviços interbancários', 'supervisor' => 'Supervisor', 'other' => 'Outro tipo documentado']; // Category/type semantics in the supplied, accepted institutional reference. public const CATEGORY_TYPES = ['banking' => ['commercial_bank', 'investment_bank', 'bank'], 'credit' => ['microbank', 'cooperative', 'savings'], 'investment' => ['investment_manager', 'investment_company'], 'pensions' => ['pension_manager', 'insurer'], 'insurance' => ['insurer', 'reinsurer', 'microinsurer', 'pension_manager'], 'intermediation' => ['broker', 'agent', 'reinsurance_broker'], 'exchange' => ['exchange_house'], 'payments' => ['electronic_money', 'interbank'], 'supervision' => ['supervisor'], 'other' => ['other']]; /** Structural validity is independent of whether matching records exist. * @param array $filters */ public function validateFilters(array $filters): void { $v = Validator::make($filters, ['category' => ['nullable', Rule::in(array_keys(self::CATEGORIES))], 'institution_type' => ['nullable', Rule::in(array_keys(self::TYPES))], 'kind' => ['nullable', Rule::in(array_keys(FundingProgrammes::KINDS))], 'location_mode' => 'nullable|in:physical,coverage'])->validate(); $errors = []; if (! empty($v['category']) && $v['category'] !== 'other' && ! empty($v['institution_type']) && $v['institution_type'] !== 'other' && ! in_array($v['institution_type'], self::CATEGORY_TYPES[$v['category']], true)) { $errors['institution_type'] = 'O tipo de instituição não pertence à categoria escolhida. Reveja esse critério; isto não é uma pesquisa sem resultados.'; } if (! empty($v['kind']) && $v['kind'] !== 'institution') { if (! empty($v['category']) || ! empty($v['institution_type'])) $errors['kind'] = 'Categoria e tipo de instituição aplicam-se à consulta de instituições. Um fundo ou programa é um registo distinto.'; if (($v['location_mode'] ?? '') === 'physical') $errors['location_mode'] = 'Sede e balcões aplicam-se a instituições. Para fundos e programas, escolha a cobertura do financiamento.'; } if ($errors) throw \Illuminate\Validation\ValidationException::withMessages($errors); } /** @param array $input * @return array */ public function normalize(array $input): array { return Validator::make($input, [ 'institution_categories' => 'array|max:10', 'institution_categories.*' => ['required', 'distinct', Rule::in(array_keys(self::CATEGORIES))], 'institution_types' => 'array|max:10', 'institution_types.*' => ['required', 'distinct', Rule::in(array_keys(self::TYPES))], 'institution_services' => 'array|max:12', 'institution_services.*' => ['required', 'distinct', Rule::in(array_keys(FundingInstitutionDetails::NEEDS))], 'institution_status' => 'nullable|in:reported,historical,merged,unknown', 'successor_key' => 'nullable|string|max:100', 'register_reference' => 'nullable|string|max:2000', 'aliases' => 'array|max:100', 'aliases.*' => 'required|string|max:500', 'source_rows' => 'array|max:250', 'source_rows.*' => 'required|array:id,section,text', 'source_rows.*.id' => 'required|string|max:100', 'source_rows.*.section' => 'required|string|max:250', 'source_rows.*.text' => 'required|string|max:8000', ])->validate(); } /** @param array $filters */ public function filter(Builder $query, array $filters): void { $this->validateFilters($filters); foreach (['category' => 'institution_categories', 'institution_type' => 'institution_types'] as $key => $field) { if (! empty($filters[$key])) { $query->whereRaw('v.payload->? @> ?::jsonb', [$field, json_encode([$filters[$key]], JSON_THROW_ON_ERROR)]); } } if (empty($filters['include_historical'])) { $query->whereRaw("COALESCE(v.payload->>'institution_status','reported') NOT IN ('historical','merged')"); } if (($filters['location_mode'] ?? 'coverage') !== 'physical') { return; } $query->whereRaw("v.payload->>'kind'='institution'"); if (empty($filters['territory'])) { return; } $unknown = ($filters['certainty'] ?? 'include_unknown') === 'include_unknown'; $path = array_column(app(InvestmentGeography::class)->path($filters['territory']), 'id'); $clauses = ["l->'territory_path' @> ?::jsonb"]; $bindings = [json_encode([['id' => $filters['territory']]], JSON_THROW_ON_ERROR)]; if ($unknown) { foreach ($path as $id) { $clauses[] = "l->>'territory_id' = ?"; $bindings[] = $id; } } $status = $unknown ? '' : " AND COALESCE(l->>'evidence_status','reported') NOT IN ('to_confirm','historical','conflict','unlocated')"; $query->where(function (Builder $q) use ($clauses, $bindings, $status, $unknown): void { $q->whereRaw("EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(v.payload->'locations','[]'::jsonb)) l WHERE (".implode(' OR ', $clauses).')'.$status.')', $bindings); if ($unknown) { $q->orWhereRaw("NOT EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(v.payload->'locations','[]'::jsonb)) l WHERE NULLIF(l->>'territory_id','') IS NOT NULL)"); } }); } /** @param array $payload */ public function permitsUnknownProduct(array $payload, ?string $need): bool { return ! $need || ! isset($payload['institution_services']) || in_array($need, $payload['institution_services'], true); } }