'indicadorA', 'B' => 'indicadorB', 'C' => 'indicadorC', 'D' => 'indicadorD', 'E' => 'indicadorE', ]; private const CONSULTAS_MINIMAS = 9; private const DIAS_LIMITE_PRIMEIRA_CONSULTA = 30; private const ANOS_LIMITE_PUBLICO_ALVO = 2; private const DIAS_LIMITE_SEGUNDA_VISITA = 180; public static function create() { return [ 'form' => [ 'unidades' => [], 'infraestruturas' => [], 'equipes' => [], 'anos' => self::getAnos(), 'quadrimestres' => self::getQuadrimestres(), 'sexo' => [ ['id' => Sexo::MASCULINO, 'sexo' => 'Masculino'], ['id' => Sexo::FEMININO, 'sexo' => 'Feminino'], ], 'boas_praticas' => self::getBoasPraticas(), ], 'model' => [ 'periodo' => ['start' => null, 'end' => null], 'ano' => null, 'quadrimestre' => null, 'unidade_id' => null, 'infraestrutura_id' => null, 'equipe_id' => null, 'sexo_id' => null, 'boasPraticas' => ['A', 'B', 'C', 'D', 'E'], 'faixa_etaria' => [ 'inicial' => ['anos' => null, 'meses' => null, 'dias' => null], 'final' => ['anos' => null, 'meses' => null, 'dias' => null], ], 'paciente_id' => null, 'buscaAtiva' => null, 'foraArea' => false, ] ]; } public static function index($request) { $params = self::getParams($request); $dadosPaciente = self::getDadosPaciente($params); $boasPraticasSelecionadas = !empty($params->boas_praticas) ? $params->boas_praticas : ['A', 'B', 'C', 'D', 'E']; $dadosMensais = self::getDadosPacientePorMes($params); $totais = self::calcularTotais($dadosPaciente); $metricasBoasPraticas = self::calcularMetricasBoasPraticas($dadosPaciente, $totais['totalPacientes'], $boasPraticasSelecionadas); $pontosPorPaciente = self::calcularPontosPorPaciente($dadosPaciente, $boasPraticasSelecionadas); $denominador = $totais['totalPacientes']; $numerador = array_sum($pontosPorPaciente); $resultado = self::calcularResultadoMedioMensal($dadosMensais, $boasPraticasSelecionadas); return [ 'totalizadores' => self::getTotalizadores($totais, $metricasBoasPraticas, $denominador, $numerador, $resultado), 'resultadoPorEquipe' => self::getResultadoPorEquipe($dadosPaciente, $boasPraticasSelecionadas, $dadosMensais), 'buscaAtiva' => $params->busca_ativa ? self::getBuscaAtiva($dadosPaciente, $boasPraticasSelecionadas) : [], 'data' => $dadosPaciente, 'boasPraticasSelecionadas' => $boasPraticasSelecionadas, ]; } public static function list($request) { $params = self::getParams($request); switch (request()->listar) { case 'unidades': { $dadosListagem = self::getDadosListagem($params); return [ 'unidades' => self::getUnidades($params, $dadosListagem), 'infraestruturas' => self::getInfraestruturas($params, $dadosListagem), 'equipes' => self::getEquipes($params, $dadosListagem), ]; } case 'infraestruturas': { $dadosListagem = self::getDadosListagem($params); return [ 'infraestruturas' => self::getInfraestruturas($params, $dadosListagem), 'equipes' => self::getEquipes($params, $dadosListagem), ]; } case 'equipes': { $dadosListagem = self::getDadosListagem($params); return [ 'equipes' => self::getEquipes($params, $dadosListagem), ]; } } return []; } private static function calcularTotais($dados) { return [ 'totalPacientes' => $dados->pluck('pacienteId')->unique()->count(), 'totalEquipes' => $dados->pluck('equipeId')->unique()->count(), 'totalMicroareas' => $dados->pluck('equipeDescricao')->unique()->count(), ]; } private static function calcularMetricasBoasPraticas($dados, $totalPacientes, array $boasPraticasSelecionadas) { $metricas = []; foreach ($boasPraticasSelecionadas as $pratica) { $campo = self::MAPA_INDICADORES[$pratica] ?? null; if (!$campo) continue; $count = $dados->where($campo, true)->pluck('pacienteId')->unique()->count(); $metricas[$pratica] = [ 'pacientes' => $count, 'percentual' => $totalPacientes > 0 ? round(($count / $totalPacientes) * 100, 2) : 0, ]; } return $metricas; } private static function calcularPontosPorPaciente($dados, array $boasPraticasSelecionadas) { $pontos = []; foreach ($dados->pluck('pacienteId')->unique() as $pacienteId) { $paciente = $dados->where('pacienteId', $pacienteId)->first(); $pontos[$pacienteId] = self::calcularPontosPaciente($paciente, $boasPraticasSelecionadas); } return $pontos; } private static function calcularPontosPaciente($paciente, array $boasPraticasSelecionadas) { $pontos = 0; foreach ($boasPraticasSelecionadas as $pratica) { $campo = self::MAPA_INDICADORES[$pratica] ?? null; if ($campo && $paciente->{$campo}) { $pontos += 20; } } return $pontos; } private static function calcularResultadoFinal($denominador, $numerador, $maxPontosPorPaciente = 100) { if ($denominador <= 0 || $numerador <= 0) { return 0; } return round(($numerador / ($denominador * $maxPontosPorPaciente)) * 100, 2); } private static function gerarSubperiodosMensais(array $periodo): array { $subperiodos = []; $current = $periodo['start']->copy()->startOfMonth()->startOfDay(); $periodoEnd = $periodo['end']->copy(); while ($current->lte($periodoEnd)) { $monthEnd = $current->copy()->endOfMonth()->endOfDay(); $subStart = $current->lt($periodo['start']) ? $periodo['start']->copy() : $current->copy(); $subEnd = $monthEnd->gt($periodoEnd) ? $periodoEnd->copy() : $monthEnd; $subperiodos[] = ['start' => $subStart, 'end' => $subEnd]; $current->addMonthNoOverflow()->startOfMonth(); } return $subperiodos; } private static function getDadosPacientePorMes($params): array { $dadosMensais = []; foreach (self::gerarSubperiodosMensais($params->periodo) as $subperiodo) { $paramsmes = (object) (array) $params; $paramsmes->periodo = $subperiodo; $dadosMensais[] = self::getDadosPaciente($paramsmes); } return $dadosMensais; } private static function calcularResultadoMedioMensal(array $dadosMensais, array $boasPraticasSelecionadas): float { $resultados = []; $maxPontos = count($boasPraticasSelecionadas) * 20; foreach ($dadosMensais as $dadosMes) { $totais = self::calcularTotais($dadosMes); $pontos = self::calcularPontosPorPaciente($dadosMes, $boasPraticasSelecionadas); $resultados[] = self::calcularResultadoFinal( $totais['totalPacientes'], array_sum($pontos), $maxPontos ); } return count($resultados) > 0 ? round(array_sum($resultados) / count($resultados), 2) : 0; } private static function calcularPercentuaisPorEquipe($pacientes, $totalPacientes, array $boasPraticasSelecionadas) { $percentuais = []; foreach ($boasPraticasSelecionadas as $pratica) { $campo = self::MAPA_INDICADORES[$pratica] ?? null; if (!$campo) continue; $count = $pacientes->where($campo, true)->pluck('pacienteId')->unique()->count(); $percentuais['percentual' . $pratica] = $totalPacientes > 0 ? round(($count / $totalPacientes) * 100, 2) : 0; } return $percentuais; } private static function getTotalizadores($totais, $metricasBoasPraticas, $denominador, $numerador, $resultado) { return [ 'totalPacientes' => $totais['totalPacientes'], 'totalEquipes' => $totais['totalEquipes'], 'totalMicroareas' => $totais['totalMicroareas'], 'denominador' => $denominador, 'numerador' => $numerador, 'resultado' => $resultado, 'boasPraticas' => $metricasBoasPraticas, 'semPublicoAlvo' => $denominador === 0, ]; } private static function getClassificacao($resultado) { if ($resultado > 75) return ClassificacaoEnum::OTIMO; if ($resultado > 50) return ClassificacaoEnum::BOM; if ($resultado > 25) return ClassificacaoEnum::SUFICIENTE; return ClassificacaoEnum::REGULAR; } private static function getResultadoPorEquipe($dados, array $boasPraticasSelecionadas, array $dadosMensais = []) { $maxPontos = count($boasPraticasSelecionadas) * 20; $resultadosMensaisPorEquipe = []; foreach ($dadosMensais as $dadosMes) { foreach ($dadosMes->groupBy('equipeId') as $equipeId => $pacientesMes) { $totalMes = $pacientesMes->pluck('pacienteId')->unique()->count(); $pontosMes = array_sum(self::calcularPontosPorPaciente($pacientesMes, $boasPraticasSelecionadas)); $resultadosMensaisPorEquipe[$equipeId][] = self::calcularResultadoFinal($totalMes, $pontosMes, $maxPontos); } } $resultado = []; foreach ($dados->groupBy('equipeId') as $equipeId => $pacientes) { $primeiro = $pacientes->first(); $totalPacientes = $pacientes->pluck('pacienteId')->unique()->count(); $percentuais = self::calcularPercentuaisPorEquipe($pacientes, $totalPacientes, $boasPraticasSelecionadas); $pontos = self::calcularPontosPorPaciente($pacientes, $boasPraticasSelecionadas); $denominador = $totalPacientes; $numerador = array_sum($pontos); if (!empty($resultadosMensaisPorEquipe[$equipeId])) { $mensais = $resultadosMensaisPorEquipe[$equipeId]; $resultadoEquipe = round(array_sum($mensais) / count($mensais), 2); } else { $resultadoEquipe = self::calcularResultadoFinal($denominador, $numerador, $maxPontos); } $resultado[] = array_merge([ 'unidadeNomeFantasia' => $primeiro->unidadeNomeFantasia, 'equipeDescricao' => $primeiro->equipeDescricao, 'denominador' => $denominador, 'numerador' => $numerador, 'resultado' => $resultadoEquipe, 'classificacao' => self::getClassificacao($resultadoEquipe), ], $percentuais); } return $resultado; } private static function getBuscaAtiva($dados, array $boasPraticasSelecionadas) { $sexoMap = self::obterMapaSexo(); $resultado = []; foreach ($dados->unique('pacienteId') as $paciente) { $pontos = 0; $pendencias = []; foreach ($boasPraticasSelecionadas as $pratica) { $campo = self::MAPA_INDICADORES[$pratica] ?? null; if (!$campo) continue; if ($paciente->$campo) { $pontos += 20; } else { $pendencias[] = $pratica; } } $dataNascimento = Carbon::parse($paciente->pacienteDataNascimento); $idade = $dataNascimento->diffInYears(Carbon::now()); $meses = $dataNascimento->copy()->addYears($idade)->diffInMonths(Carbon::now()); if (empty($pendencias)) { continue; } $resultado[] = [ 'unidadeNomeFantasia' => $paciente->unidadeNomeFantasia, 'equipeDescricao' => $paciente->equipeDescricao, 'pacienteNome' => $paciente->pacienteNome, 'pacienteId' => $paciente->pacienteId, 'pacienteSexo' => $sexoMap[$paciente->pacienteSexo] ?? 'Indefinido', 'idadeCompleta' => "$idade anos e $meses meses", 'usuarioResponsavelNome' => $paciente->usuarioResponsavelNome, 'pacienteDataNascimento' => $dataNascimento->format('d/m/Y'), 'pacienteTelefone' => $paciente->pacienteTelefone, 'pacienteEndereco' => $paciente->pacienteEndereco, 'areaMicroareaDomicilio' => $paciente->areaMicroareaDomicilio, 'indicadorA' => $paciente->indicadorA, 'indicadorB' => $paciente->indicadorB, 'indicadorC' => $paciente->indicadorC, 'indicadorD' => $paciente->indicadorD, 'indicadorE' => $paciente->indicadorE, 'areaTipoId' => $paciente->area_tipo_id, 'pontuacaoTotal' => $pontos, ]; } return $resultado; } private static function baseQueryCadastroIndividual($params) { $periodoInicial = $params->periodo['start']->copy()->startOfDay(); return DB::table('atencao_basica_cadastro_individual') ->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_cadastro_individual.paciente_id') ->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_paciente.cidadao_id') ->leftJoin('cadastro_cidadao_telefone', function ($join) { $join->on('cadastro_cidadao_telefone.cidadao_id', 'cadastro_cidadao.id') ->where('cadastro_cidadao_telefone.principal', 1) ->whereNull('cadastro_cidadao_telefone.deleted_at'); }) ->leftJoin('cadastro_domicilio', 'cadastro_domicilio.id', 'cadastro_cidadao.domicilio_id') ->leftJoin('cadastro_endereco', 'cadastro_endereco.id', 'cadastro_domicilio.endereco_id') ->leftJoin('cadastro_logradouro', 'cadastro_logradouro.id', 'cadastro_endereco.logradouro_id') ->leftJoin('cadastro_bairro', 'cadastro_bairro.id', 'cadastro_logradouro.bairro_id') ->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_cadastro_individual.usuario_id') ->join('cadastro_cidadao as cidadaoUsuario', 'cidadaoUsuario.id', 'cadastro_usuario.cidadao_id') ->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_cadastro_individual.equipe_id') ->join('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'atencao_basica_parametrizacao_equipe.infraestrutura_id') ->join('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id') ->join('atencao_basica_parametrizacao_area', 'atencao_basica_parametrizacao_area.id', 'atencao_basica_parametrizacao_equipe.area_id') ->leftJoin('atencao_basica_parametrizacao_microarea', 'atencao_basica_parametrizacao_microarea.id', 'atencao_basica_parametrizacao_equipe.microarea_id') ->leftJoin('atencao_basica_cadastro_domiciliar', 'atencao_basica_cadastro_domiciliar.domicilio_id', 'cadastro_cidadao.domicilio_id') ->leftJoin('atencao_basica_parametrizacao_microarea as microareaDomicilio', 'microareaDomicilio.id', 'atencao_basica_cadastro_domiciliar.microarea_id') ->leftJoin('atencao_basica_parametrizacao_area as areaDomicilio', 'areaDomicilio.id', 'microareaDomicilio.area_id') ->leftJoin('cadastro_obito', function ($join) { $join->on('cadastro_cidadao.id', 'cadastro_obito.cidadao_id') ->whereNull('cadastro_obito.deleted_at'); }) ->where(function ($query) use ($periodoInicial) { $query->whereNull('cadastro_obito.id') ->orWhere('cadastro_obito.obito_em', '>=', $periodoInicial); }) ->where(function ($query) use ($periodoInicial) { $query->whereNull('atencao_basica_cadastro_individual.saida_cidadao_id') ->orWhere('atencao_basica_cadastro_individual.motivo_saida_data', '>=', $periodoInicial); }) ->where('cadastro_cidadao.data_nascimento', '>=', self::getDataNascimentoLimite($params)) ->whereIn('atencao_basica_parametrizacao_area.tipo_id', self::getAreaTipos()) ->when($params->unidade_id, fn ($q) => $q->where('cadastro_unidade.id', $params->unidade_id)) ->when($params->infraestrutura_id, fn ($q) => $q->where('cadastro_infraestrutura.id', $params->infraestrutura_id)) ->when($params->equipe_id, fn ($q) => $q->where('atencao_basica_parametrizacao_equipe.id', $params->equipe_id)) ->when(!$params->fora_area, function ($q) { $q->whereNotNull('atencao_basica_cadastro_domiciliar.id') ->where(function ($sub) { $sub->whereNull('atencao_basica_cadastro_domiciliar.fora_area') ->orWhere('atencao_basica_cadastro_domiciliar.fora_area', 0); }); }) ->when(self::hasFaixaEtaria($params), fn ($q) => self::aplicarFiltroFaixaEtaria($q, $params)); } private static function selectEquipeDescricao() { return DB::raw('concat( "Area: ", COALESCE(atencao_basica_parametrizacao_area.descricao, "Não informada"), " Microarea: ", COALESCE(atencao_basica_parametrizacao_microarea.descricao, "Não informada"), " - INE: ", COALESCE(atencao_basica_parametrizacao_area.numero_ine, "Não informada") ) as equipeDescricao'); } private static function getDadosListagem($params) { return self::baseQueryCadastroIndividual($params) ->select([ 'cadastro_unidade.id as unidadeId', 'cadastro_unidade.nome_fantasia as unidadeNomeFantasia', 'cadastro_infraestrutura.id as infraestruturaId', DB::raw('COALESCE(cadastro_infraestrutura.nome, cadastro_unidade.nome_fantasia) as infraestruturaNome'), 'atencao_basica_parametrizacao_equipe.id as equipeId', self::selectEquipeDescricao(), ]) ->distinct() ->get(); } public static function getDadosPaciente($params) { $pacientes = self::baseQueryCadastroIndividual($params) ->select([ 'atencao_basica_cadastro_individual.id', 'cadastro_cidadao.nome as pacienteNome', 'cadastro_paciente.id as pacienteId', 'cadastro_cidadao.data_nascimento as pacienteDataNascimento', 'cadastro_cidadao.sexo_id as pacienteSexo', 'cadastro_unidade.id as unidadeId', 'cadastro_unidade.nome_fantasia as unidadeNomeFantasia', 'cadastro_infraestrutura.id as infraestruturaId', 'cidadaoUsuario.nome as usuarioResponsavelNome', 'cadastro_cidadao_telefone.numero as pacienteTelefone', 'atencao_basica_parametrizacao_area.numero_ine as pacienteIne', 'atencao_basica_parametrizacao_area.tipo_id as area_tipo_id', 'atencao_basica_cadastro_domiciliar.fora_area as pacienteForaArea', DB::raw('CASE WHEN areaDomicilio.descricao IS NULL AND microareaDomicilio.descricao IS NULL THEN "Não informada" ELSE concat("Area: ", COALESCE(areaDomicilio.descricao, "Não informada"), " / Microarea: ", COALESCE(microareaDomicilio.descricao, "Não informada")) END as areaMicroareaDomicilio'), DB::raw('concat(cadastro_logradouro.nome, ", ", cadastro_endereco.numero, " - ", cadastro_bairro.nome, " - ", cadastro_logradouro.cep) as pacienteEndereco'), DB::raw('COALESCE(cadastro_infraestrutura.nome, cadastro_unidade.nome_fantasia) as infraestruturaNome'), 'atencao_basica_parametrizacao_equipe.id as equipeId', self::selectEquipeDescricao(), ]) ->when($params->sexo_id, fn ($q) => $q->where('cadastro_cidadao.sexo_id', $params->sexo_id)) ->when($params->paciente_id, fn ($q) => $q->where('cadastro_paciente.id', $params->paciente_id)) ->get(); if ($pacientes->isEmpty()) { return $pacientes; } $periodoFinal = Carbon::parse($params->periodo['end'])->endOfDay(); $pacienteIds = $pacientes->pluck('pacienteId')->unique()->toArray(); $indicadoresA = self::getBoaPraticaA($pacienteIds, $periodoFinal); $indicadoresB = self::getBoaPraticaB($pacienteIds, $periodoFinal); $indicadoresC = self::getBoaPraticaC($pacienteIds, $periodoFinal); $indicadoresD = self::getBoaPraticaD($pacienteIds, $periodoFinal); $indicadoresE = self::getBoaPraticaE($pacienteIds, $pacientes, $periodoFinal); return $pacientes->map(function ($item) use ($indicadoresA, $indicadoresB, $indicadoresC, $indicadoresD, $indicadoresE) { $keyIne = trim($item->pacienteId) . '-' . trim($item->pacienteIne ?? ''); $item->indicadorA = $indicadoresA[$keyIne] ?? false; $item->indicadorB = $indicadoresB[$keyIne] ?? false; $item->indicadorC = $indicadoresC[$keyIne] ?? false; $item->indicadorD = $indicadoresD[$keyIne] ?? false; $item->indicadorE = $indicadoresE[$item->pacienteId] ?? false; return $item; }); } private static function getBoaPraticaA(array $pacienteIds, $periodoFinal): array { return DB::table('atencao_basica_atendimento_individual as ai') ->join('cadastro_paciente as cp', 'cp.id', 'ai.paciente_id') ->join('cadastro_cidadao as cc', 'cc.id', 'cp.cidadao_id') ->join('sigtap.ocupacao as oc', 'oc.id', 'ai.usuario_ocupacao_id') ->join('atencao_basica_parametrizacao_equipe as pe', 'pe.id', 'ai.equipe_id') ->join('atencao_basica_parametrizacao_area as pa', 'pa.id', 'pe.area_id') ->select(['ai.paciente_id', 'pa.numero_ine']) ->where('ai.data_usuario_responsavel', '<=', $periodoFinal) ->whereIn('oc.codigo', CboBoasPratricasAeBEnum::all()) ->whereIn('ai.paciente_id', $pacienteIds) ->whereRaw('TIMESTAMPDIFF(DAY, cc.data_nascimento, ai.data_usuario_responsavel) <= ' . self::DIAS_LIMITE_PRIMEIRA_CONSULTA) ->distinct() ->get() ->mapWithKeys(fn ($registro) => [trim($registro->paciente_id) . '-' . trim($registro->numero_ine) => true]) ->toArray(); } private static function getBoaPraticaB(array $pacienteIds, $periodoFinal): array { return DB::table('atencao_basica_atendimento_individual as ai') ->join('cadastro_paciente as cp', 'cp.id', 'ai.paciente_id') ->join('cadastro_cidadao as cc', 'cc.id', 'cp.cidadao_id') ->join('sigtap.ocupacao as oc', 'oc.id', 'ai.usuario_ocupacao_id') ->join('atencao_basica_parametrizacao_equipe as pe', 'pe.id', 'ai.equipe_id') ->join('atencao_basica_parametrizacao_area as pa', 'pa.id', 'pe.area_id') ->select(['ai.paciente_id', 'pa.numero_ine']) ->where('ai.data_usuario_responsavel', '<=', $periodoFinal) ->whereIn('oc.codigo', CboBoasPratricasAeBEnum::all()) ->whereIn('ai.paciente_id', $pacienteIds) ->whereRaw('TIMESTAMPDIFF(YEAR, cc.data_nascimento, ai.data_usuario_responsavel) <= ' . self::ANOS_LIMITE_PUBLICO_ALVO) ->groupBy(['ai.paciente_id', 'pa.numero_ine']) ->havingRaw('COUNT(ai.id) >= ' . self::CONSULTAS_MINIMAS) ->get() ->mapWithKeys(fn ($registro) => [trim($registro->paciente_id) . '-' . trim($registro->numero_ine) => true]) ->toArray(); } private static function getBoaPraticaC(array $pacienteIds, $periodoFinal): array { $counts = []; $accumulate = function ($registros) use (&$counts) { foreach ($registros as $registro) { $key = trim($registro->paciente_id) . '-' . trim($registro->numero_ine); $counts[$key] = ($counts[$key] ?? 0) + $registro->cnt; } }; $accumulate( DB::table('atencao_basica_atendimento_individual as ai') ->join('atencao_basica_parametrizacao_equipe as pe', 'pe.id', 'ai.equipe_id') ->join('atencao_basica_parametrizacao_area as pa', 'pa.id', 'pe.area_id') ->join('sigtap.ocupacao as oc', 'oc.id', 'ai.usuario_ocupacao_id') ->select(['ai.paciente_id', 'pa.numero_ine', DB::raw('COUNT(*) as cnt')]) ->where('ai.data_usuario_responsavel', '<=', $periodoFinal) ->whereIn('oc.codigo', CboBoasPratricasCEnum::all()) ->whereIn('ai.paciente_id', $pacienteIds) ->whereNotNull('ai.peso') ->whereNotNull('ai.altura') ->groupBy(['ai.paciente_id', 'pa.numero_ine']) ->get() ); $accumulate( DB::table('atencao_basica_atividade_coletiva as ac') ->join('atencao_basica_atividade_coletiva_participante as acp', 'acp.atividade_coletiva_id', 'ac.id') ->join('atencao_basica_parametrizacao_equipe as pe', 'pe.id', 'ac.equipe_id') ->join('atencao_basica_parametrizacao_area as pa', 'pa.id', 'pe.area_id') ->join('sigtap.ocupacao as oc', 'oc.id', 'ac.usuario_ocupacao_id') ->select(['acp.paciente_id', 'pa.numero_ine', DB::raw('COUNT(*) as cnt')]) ->where('ac.data_cadastro', '<=', $periodoFinal) ->whereIn('oc.codigo', CboBoasPratricasCEnum::all()) ->whereIn('acp.paciente_id', $pacienteIds) ->whereNotNull('acp.peso') ->whereNotNull('acp.altura') ->groupBy(['acp.paciente_id', 'pa.numero_ine']) ->get() ); $accumulate( DB::table('atencao_basica_visita_domiciliar as vd') ->join('atencao_basica_parametrizacao_equipe as pe', 'pe.id', 'vd.equipe_id') ->join('atencao_basica_parametrizacao_area as pa', 'pa.id', 'pe.area_id') ->join('sigtap.ocupacao as oc', 'oc.id', 'vd.usuario_ocupacao_id') ->select(['vd.paciente_id', 'pa.numero_ine', DB::raw('COUNT(*) as cnt')]) ->where('vd.data_usuario_responsavel', '<=', $periodoFinal) ->whereIn('oc.codigo', CboBoasPratricasCEnum::all()) ->whereIn('vd.paciente_id', $pacienteIds) ->whereNotNull('vd.peso') ->whereNotNull('vd.altura') ->groupBy(['vd.paciente_id', 'pa.numero_ine']) ->get() ); $accumulate( DB::table('atencao_basica_procedimento as p') ->join('atencao_basica_parametrizacao_equipe as pe', 'pe.id', 'p.equipe_id') ->join('atencao_basica_parametrizacao_area as pa', 'pa.id', 'pe.area_id') ->join('atencao_basica_procedimento_atendimento as pa_att', 'pa_att.procedimento_id', 'p.id') ->join('atencao_basica_procedimento_atendimento_procedimento as pap', 'pap.procedimento_atendimento_id', 'pa_att.id') ->join('sigtap.ocupacao as oc', 'oc.id', 'p.ocupacao_id') ->select(['pa_att.paciente_id', 'pa.numero_ine', DB::raw('COUNT(*) as cnt')]) ->where('p.data_usuario_responsavel', '<=', $periodoFinal) ->whereIn('oc.codigo', CboBoasPratricasCEnum::all()) ->whereIn('pap.procedimento_id', ProcedimentosBoasPraticasC::all()) ->whereIn('pa_att.paciente_id', $pacienteIds) ->groupBy(['pa_att.paciente_id', 'pa.numero_ine']) ->get() ); return array_filter($counts, fn ($totalRegistros) => $totalRegistros >= self::CONSULTAS_MINIMAS); } private static function getBoaPraticaD(array $pacienteIds, $periodoFinal): array { return DB::table('atencao_basica_visita_domiciliar as vd') ->join('cadastro_paciente as cp', 'cp.id', 'vd.paciente_id') ->join('cadastro_cidadao as cc', 'cc.id', 'cp.cidadao_id') ->join('sigtap.ocupacao as oc', 'oc.id', 'vd.usuario_ocupacao_id') ->join('atencao_basica_parametrizacao_equipe as pe', 'pe.id', 'vd.equipe_id') ->join('atencao_basica_parametrizacao_area as pa', 'pa.id', 'pe.area_id') ->select(['vd.paciente_id', 'pa.numero_ine']) ->where('vd.data_usuario_responsavel', '<=', $periodoFinal) ->whereIn('oc.codigo', CboBoasPratricasDEnum::all()) ->whereIn('pa.tipo_id', [AreaTipo::EQUIPE_DE_ATENCAO_PRIMARIA]) ->whereIn('vd.paciente_id', $pacienteIds) ->whereExists(function ($subquery) { $subquery->select(DB::raw(1)) ->from('atencao_basica_visita_domiciliar_motivo_visita') ->whereColumn('atencao_basica_visita_domiciliar_motivo_visita.visita_domiciliar_id', 'vd.id') ->whereIn('atencao_basica_visita_domiciliar_motivo_visita.motivo_visita_id', [MotivoVisita::RECEM_NASCIDO, MotivoVisita::CRIANCA]); }) ->groupBy(['vd.paciente_id', 'pa.numero_ine']) ->havingRaw('SUM(CASE WHEN TIMESTAMPDIFF(DAY, cc.data_nascimento, vd.data_usuario_responsavel) <= ' . self::DIAS_LIMITE_PRIMEIRA_CONSULTA . ' THEN 1 ELSE 0 END) >= 1') ->havingRaw('SUM(CASE WHEN TIMESTAMPDIFF(DAY, cc.data_nascimento, vd.data_usuario_responsavel) > ' . self::DIAS_LIMITE_PRIMEIRA_CONSULTA . ' AND TIMESTAMPDIFF(DAY, cc.data_nascimento, vd.data_usuario_responsavel) <= ' . self::DIAS_LIMITE_SEGUNDA_VISITA . ' THEN 1 ELSE 0 END) >= 1') ->get() ->mapWithKeys(fn ($registro) => [trim($registro->paciente_id) . '-' . trim($registro->numero_ine) => true]) ->toArray(); } private static function getBoaPraticaE(array $pacienteIds, $pacientes, $periodoFinal): array { $gruposVacinais = self::getGruposVacinais(); $allVacinas = array_unique(array_merge(...array_column($gruposVacinais, 'vacinas'))); $aplicacoes = DB::table('imunizacao_registro_aplicacao as ira') ->join('imunizacao_entrada_vacina as iev', 'iev.id', 'ira.entrada_vacina_id') ->join('cadastro_vacina as cv', 'cv.id', 'iev.vacina_id') ->select(['ira.paciente_id', 'ira.data_aplicacao', 'cv.codigo_ria']) ->where('ira.data_aplicacao', '<=', $periodoFinal) ->whereIn('ira.paciente_id', $pacienteIds) ->whereIn('cv.codigo_ria', $allVacinas) ->distinct() ->get() ->groupBy('paciente_id'); $atualizacoes = DB::table('imunizacao_registro_atualizacao as iru') ->join('cadastro_vacina as cv', 'cv.id', 'iru.vacina_id') ->select(['iru.paciente_id', 'iru.data_atualizacao', 'cv.codigo_ria']) ->where('iru.data_atualizacao', '<=', $periodoFinal) ->whereIn('iru.paciente_id', $pacienteIds) ->whereIn('cv.codigo_ria', $allVacinas) ->distinct() ->get() ->groupBy('paciente_id'); $dataNascimentoMap = $pacientes->pluck('pacienteDataNascimento', 'pacienteId')->toArray(); $resultado = []; foreach ($pacienteIds as $pacienteId) { $registros = ($aplicacoes[$pacienteId] ?? collect())->merge($atualizacoes[$pacienteId] ?? collect()); $dataNascimento = Carbon::parse($dataNascimentoMap[$pacienteId] ?? null); $valido = true; foreach ($gruposVacinais as $grupo) { $registrosGrupo = $registros->whereIn('codigo_ria', $grupo['vacinas'])->values(); $dnValidacao = isset($grupo['idadeMinMeses']) ? $dataNascimento : null; if (!self::validarGrupoVacinal($registrosGrupo, $grupo['dosesMinimas'], $grupo['intervaloDias'], $dnValidacao, $grupo['idadeMinMeses'] ?? null)) { $valido = false; break; } } $resultado[$pacienteId] = $valido; } return $resultado; } private static function getGruposVacinais(): array { return [ ['vacinas' => [ VacinasEsquemaVacinalEnum::VACINA_HEPATITE_B_RECOMBINANTE, VacinasEsquemaVacinalEnum::VACINA_HAEMOPHILUS_INFLUENZA_B_CONJUGADA, VacinasEsquemaVacinalEnum::VACINA_DTPA_HIB_POLIO_INATIVA, VacinasEsquemaVacinalEnum::VACINA_DTP_HIB, VacinasEsquemaVacinalEnum::VACINA_DTP_HB_HIB, VacinasEsquemaVacinalEnum::VACINA_HEXAVALENTE, VacinasEsquemaVacinalEnum::VACINA_TRIPLA_BACTERIANA, VacinasEsquemaVacinalEnum::VACINA_TRIPLA_ACELULAR_INFANTIL, ], 'dosesMinimas' => 3, 'intervaloDias' => 30], ['vacinas' => [ VacinasEsquemaVacinalEnum::VACINA_POLIOMIELITE_INATIVADA, VacinasEsquemaVacinalEnum::VACINA_DTPA_HIB_POLIO_INATIVA, VacinasEsquemaVacinalEnum::VACINA_HEXAVALENTE, ], 'dosesMinimas' => 3, 'intervaloDias' => 30], ['vacinas' => [ VacinasEsquemaVacinalEnum::VACINA_TRIPLA_VIRAL, ], 'dosesMinimas' => 2, 'intervaloDias' => 0, 'idadeMinMeses' => 12], ['vacinas' => [ VacinasEsquemaVacinalEnum::VACINA_PNEUMOCOCICA_10V, VacinasEsquemaVacinalEnum::VACINA_PNEUMOCOCICA_13V, VacinasEsquemaVacinalEnum::VACINA_PNEUMOCOCICA_15V, ], 'dosesMinimas' => 2, 'intervaloDias' => 30], ['vacinas' => [ VacinasEsquemaVacinalEnum::VACINA_TRIPLA_ACELULAR_POLIOMIELITE_INATIVADA, ], 'dosesMinimas' => 1, 'intervaloDias' => 0], ['vacinas' => [ VacinasEsquemaVacinalEnum::VACINA_TETRA_VIRAL, ], 'dosesMinimas' => 1, 'intervaloDias' => 0], ['vacinas' => [ VacinasEsquemaVacinalEnum::VACINA_PNEUMOCOCICA_20V, ], 'dosesMinimas' => 1, 'intervaloDias' => 0], ]; } private static function validarGrupoVacinal($registros, $dosesMinimas, $intervaloDias = 0, $dataNascimento = null, $idadeMinMeses = null) { $registros = $registros->map(function ($item) { $item->data = $item->data_aplicacao ?? $item->data_atualizacao; return $item; })->sortBy('data')->values(); if ($idadeMinMeses !== null && $dataNascimento) { $registros = $registros->filter(function ($item) use ($dataNascimento, $idadeMinMeses) { return Carbon::parse($item->data)->diffInMonths($dataNascimento) >= $idadeMinMeses; })->values(); } $dosesValidas = []; $ultimaDataValida = null; foreach ($registros as $item) { $dataAtual = Carbon::parse($item->data); if (!$ultimaDataValida) { $dosesValidas[] = $dataAtual; $ultimaDataValida = $dataAtual; continue; } if ($intervaloDias === 0) { $dosesValidas[] = $dataAtual; continue; } if ($ultimaDataValida->diffInDays($dataAtual) >= $intervaloDias) { $dosesValidas[] = $dataAtual; $ultimaDataValida = $dataAtual; } } return count($dosesValidas) >= $dosesMinimas; } private static function getEquipes($params, $dadosListagem = null) { $dadosListagem = $dadosListagem ?? self::getDadosListagem($params); return $dadosListagem ->when($params->unidade_id, fn ($collection) => $collection->where('unidadeId', $params->unidade_id)) ->when($params->infraestrutura_id, fn ($collection) => $collection->where('infraestruturaId', $params->infraestrutura_id)) ->map(fn ($item) => (object) ['id' => $item->equipeId, 'descricao' => $item->equipeDescricao]) ->filter(fn ($item) => !is_null($item->id)) ->unique('id') ->sortBy('descricao') ->values(); } private static function getInfraestruturas($params, $dadosListagem = null) { $dadosListagem = $dadosListagem ?? self::getDadosListagem($params); return $dadosListagem ->when($params->unidade_id, fn ($collection) => $collection->where('unidadeId', $params->unidade_id)) ->map(fn ($item) => (object) ['id' => $item->infraestruturaId, 'nome' => $item->infraestruturaNome]) ->filter(fn ($item) => !is_null($item->id)) ->unique('id') ->sortBy('nome') ->values(); } private static function getUnidades($params, $dadosListagem = null) { $dadosListagem = $dadosListagem ?? self::getDadosListagem($params); return $dadosListagem ->map(fn ($item) => (object) ['id' => $item->unidadeId, 'nome_fantasia' => $item->unidadeNomeFantasia]) ->filter(fn ($item) => !is_null($item->id)) ->unique('id') ->sortBy('nome_fantasia') ->values(); } private static function getParams($request) { $faixaEtaria = self::formatarFaixaEtaria($request->input('faixa_etaria', [])); return (object) [ 'periodo' => self::obterPeriodoRequest($request), 'quadrimestre' => $request->input('quadrimestre'), 'unidade_id' => $request->input('unidade_id'), 'infraestrutura_id' => $request->input('infraestrutura_id'), 'equipe_id' => $request->input('equipe_id'), 'sexo_id' => $request->input('sexo_id'), 'boas_praticas' => $request->input('boas_praticas') ?? [], 'faixa_etaria' => $faixaEtaria, 'paciente_id' => $request->input('paciente_id'), 'busca_ativa' => $request->input('busca_ativa') ?? false, 'fora_area' => $request->input('fora_area') ?? false, ]; } private static function obterPeriodoRequest($request) { $periodoPorQuadrimestre = self::getPeriodoPorQuadrimestre($request->input('quadrimestre')); if ($periodoPorQuadrimestre) { return $periodoPorQuadrimestre; } $periodo = $request->input('periodo'); return [ 'start' => Carbon::createFromFormat('d/m/Y', $periodo['start'])->setTime(0, 0, 0), 'end' => Carbon::createFromFormat('d/m/Y', $periodo['end'])->setTime(23, 59, 59), ]; } private static function getPeriodoPorQuadrimestre($quadrimestre) { if (empty($quadrimestre) || !str_contains($quadrimestre, '-')) { return null; } [$ano, $numero] = array_pad(explode('-', $quadrimestre), 2, null); $faixas = [ 1 => ['inicio' => [1, 1], 'fim' => [4, 30]], 2 => ['inicio' => [5, 1], 'fim' => [8, 31]], 3 => ['inicio' => [9, 1], 'fim' => [12, 31]], ]; $numero = (int) $numero; $ano = (int) $ano; if (!isset($faixas[$numero]) || $ano < 2020) { return null; } return [ 'start' => Carbon::create($ano, $faixas[$numero]['inicio'][0], $faixas[$numero]['inicio'][1])->startOfDay(), 'end' => Carbon::create($ano, $faixas[$numero]['fim'][0], $faixas[$numero]['fim'][1])->endOfDay(), ]; } private static function formatarFaixaEtaria($faixaEtaria) { $sanitizar = function ($value, $max) { if ($value === null || $value === '') { return null; } return max(0, min((int) $value, $max)); }; return [ 'inicial' => [ 'anos' => $sanitizar($faixaEtaria['inicial']['anos'] ?? null, 2), 'meses' => $sanitizar($faixaEtaria['inicial']['meses'] ?? null, 11), 'dias' => $sanitizar($faixaEtaria['inicial']['dias'] ?? null, 30), ], 'final' => [ 'anos' => $sanitizar($faixaEtaria['final']['anos'] ?? null, 2), 'meses' => $sanitizar($faixaEtaria['final']['meses'] ?? null, 11), 'dias' => $sanitizar($faixaEtaria['final']['dias'] ?? null, 30), ], ]; } private static function hasFaixaEtaria($params) { $faixaEtaria = $params->faixa_etaria ?? ['inicial' => [], 'final' => []]; $inicial = $faixaEtaria['inicial'] ?? []; $final = $faixaEtaria['final'] ?? []; return isset($inicial['anos']) || isset($inicial['meses']) || isset($inicial['dias']) || isset($final['anos']) || isset($final['meses']) || isset($final['dias']); } private static function idadeParaDataNascimento(Carbon $referencia, array $idade) { return $referencia ->copy() ->subYears((int) ($idade['anos'] ?? 0)) ->subMonths((int) ($idade['meses'] ?? 0)) ->subDays((int) ($idade['dias'] ?? 0)); } private static function aplicarFiltroFaixaEtaria($query, $params) { $referencia = Carbon::today()->endOfDay(); $inicial = $params->faixa_etaria['inicial']; $final = $params->faixa_etaria['final']; $temInicial = isset($inicial['anos']) || isset($inicial['meses']) || isset($inicial['dias']); $temFinal = isset($final['anos']) || isset($final['meses']) || isset($final['dias']); if ($temFinal) { $nascimentoMinimo = self::idadeParaDataNascimento($referencia, $final); $query->whereDate('cadastro_cidadao.data_nascimento', '>', $nascimentoMinimo->toDateString()); } if ($temInicial) { $nascimentoMaximo = self::idadeParaDataNascimento($referencia, $inicial); $query->whereDate('cadastro_cidadao.data_nascimento', '<=', $nascimentoMaximo->toDateString()); } return $query; } private static function getDataNascimentoLimite($params) { return $params->periodo['end']->copy()->subYears(2); } private static function getAreaTipos() { return [ AreaTipo::EQUIPE_DE_SAUDE_DA_FAMILIA, AreaTipo::EQUIPE_DE_ATENCAO_PRIMARIA, ]; } private static function obterMapaSexo() { return [ Sexo::MASCULINO => 'Masculino', Sexo::FEMININO => 'Feminino', ]; } private static function getAnos() { $anos = []; for ($ano = now()->year; $ano >= 2020; $ano--) { $anos[] = ['id' => $ano, 'descricao' => (string) $ano]; } return $anos; } private static function getQuadrimestres() { return [ ['id' => 1, 'descricao' => '1º Quadrimestre (Jan - Abr)', 'inicio' => '01-01', 'fim' => '04-30'], ['id' => 2, 'descricao' => '2º Quadrimestre (Mai - Ago)', 'inicio' => '05-01', 'fim' => '08-31'], ['id' => 3, 'descricao' => '3º Quadrimestre (Set - Dez)', 'inicio' => '09-01', 'fim' => '12-31'], ]; } private static function getBoasPraticas() { return collect([ ['value' => 'A', 'text' => 'A - 1ª consulta até 30 dias de idade'], ['value' => 'B', 'text' => 'B - 9 consultas até 2 anos de idade'], ['value' => 'C', 'text' => 'C - 9 registros simultâneos de peso e altura'], ['value' => 'D', 'text' => 'D - 2 visitas domiciliares'], ['value' => 'E', 'text' => 'E - Vacinação completa'], ]); } }