Inconsistencia::CNES_DA_UNIDADE_COM_TAMANHO_DIFERENTE_DE_7_CARACTERES, IbgeIsValid::class => Inconsistencia::IBGE_DO_MUNICIPIO_SEM_PREENCHIMENTO_OU_INVALIDO, Cns::class => Inconsistencia::CNS_DO_PROFISSIONAL_INVALIDO, OcupacaoIsAllowed::class => Inconsistencia::OCUPACAO_DO_USUARIO_SEM_PERMISSAO_PARA_REGISTRAR_A_FICHA, DataAtendimentoIsValid::class => Inconsistencia::DATA_ATENDIMENTO_INVALIDO, ]; private const PACIENTE_ERROR_MAP = [ CnsPaciente::class => Inconsistencia::CNS_DO_PACIENTE_INVALIDO, Cpf::class => Inconsistencia::CPF_DO_PACIENTE_INVALIDO, DataNascimentoIsRequired::class => Inconsistencia::DATA_NASCIMENTO_SEM_PREENCHIMENTO, DataNascimentoIsValid::class => Inconsistencia::DATA_DE_NASCIMENTO_POSTERIOR_A_DATA_DE_ATENDIMENTO_SUPERIOR_A_130_ANOS_OU_NAO_PREENCHIDA, LocalAtendimentoIsRequired::class => Inconsistencia::LOCAL_DE_ATENDIMENTO_SEM_PREENCHIMENTO, LocalAtendimentoIsValid::class => Inconsistencia::LOCAL_DE_ATENDIMENTO_INVALIDO, TurnoIsRequired::class => Inconsistencia::TURNO_SEM_PREENCHIMENTO, ProcedimentoLimitIsValid::class => Inconsistencia::PACIENTE_COM_MAIS_DE_20_PROCEDIMENTOS_INSERIDOS, ProcedimentoIsValidForFicha::class => Inconsistencia::PROCEDIMENTO_INVALIDO, ProcedimentoIsRequired::class => Inconsistencia::PROCEDIMENTO_INVALIDO, AlturaLimitIsValid::class => Inconsistencia::ALTURA_DO_PACIENTE_MENOR_QUE_20_OU_MAIOR_QUE_250_CM, CircunferenciaAbdominalLimitIsValid::class => Inconsistencia::CIRCUNFERENCIA_ABDOMINAL_DO_PACIENTE_MENOR_QUE_0_OU_MAIOR_QUE_99999_CM, FrequenciaCardiacaLimitIsValid::class => Inconsistencia::FREQUENCIA_CARDIACA_DO_PACIENTE_MENOR_QUE_0_OU_MAIOR_QUE_999_BPM, FrequenciaRespiratoriaLimitIsValid::class => Inconsistencia::FREQUENCIA_RESPIRATORIA_DO_PACIENTE_MENOR_QUE_0_OU_MAIOR_QUE_200_MPM, GlicemiaLimitIsValid::class => Inconsistencia::GLICEMIA_DO_PACIENTE_MENOR_QUE_0_OU_MAIOR_QUE_800_KG, MomentoColetaGlicemiaIsRequired::class => Inconsistencia::MOMENTO_COLETA_GLICEMIA_SEM_PREENCHIMENTO, PerimetroCefalicoLimitIsValid::class => Inconsistencia::PERIMETRO_CEFALICO_MENOR_QUE_10_OU_MAIOR_QUE_200, PerimetroPanturrilhaLimitIsValid::class => Inconsistencia::PERIMETRO_PANTURRILHA_DO_PACIENTE_MENOR_QUE_10_OU_MAIOR_QUE_99_CM, PesoLimitIsValid::class => Inconsistencia::PESO_DO_PACIENTE_MENOR_QUE_0_5_OU_MAIOR_QUE_500_KG, PressaoArterialDiastolicaLimitIsValid::class => Inconsistencia::PRESSAO_ARTERIAL_DIASTOLICA_DO_PACIENTE_MENOR_QUE_0_OU_MAIOR_QUE_999_MMHG, PressaoArterialSistolicaLimitIsValid::class => Inconsistencia::PRESSAO_ARTERIAL_SISTOLICA_DO_PACIENTE_MENOR_QUE_0_OU_MAIOR_QUE_999_MMHG, SaturacaoO2LimitIsValid::class => Inconsistencia::SATURACAO_O2_DO_PACIENTE_MENOR_QUE_0_OU_MAIOR_QUE_100_PORCENTO, TemperaturaLimitIsValid::class => Inconsistencia::TEMPERATURA_DO_PACIENTE_MENOR_QUE_20_OU_MAIOR_QUE_45_GRAUS, ]; public static function index() { $mes_ano = request()->mes_ano; $date = Carbon::createFromFormat('mY', $mes_ano); $start = $date->copy()->startOfMonth()->toDateString(); $end = $date->copy()->endOfMonth()->toDateString(); $inconsistencias = DB::table('atencao_basica_inconsistencia') ->select('id', 'descricao') ->get() ->pluck('descricao', 'id'); $fichasProcedimento = DB::table('atencao_basica_procedimento') ->select([ 'atencao_basica_procedimento.id as id', 'atencao_basica_procedimento.usuario_id as usuario_id', 'atencao_basica_procedimento.data_usuario_responsavel as data_atendimento', 'cadastro_unidade.cnes as unidade_cnes', 'cadastro_municipio.ibge as municipio_ibge', 'sigtap.ocupacao.codigo as ocupacao_codigo', 'cadastro_cidadao.cns AS usuario_cns', 'cadastro_cidadao.nome AS profissional_nome', 'atencao_basica_procedimento.afericao_pa as afericao_pa', 'atencao_basica_procedimento.afericao_temperatura as afericao_temperatura', 'atencao_basica_procedimento.curativo_simples as curativo_simples', 'atencao_basica_procedimento.coleta_material_exame_laboratorial as coleta_material_exame_laboratorial', 'atencao_basica_procedimento.glicemia_capilar as glicemia_capilar', 'atencao_basica_procedimento.medicao_altura as medicao_altura', 'atencao_basica_procedimento.medicao_peso as medicao_peso', ]) ->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_procedimento.equipe_id') ->join('cadastro_unidade', 'cadastro_unidade.id', 'atencao_basica_parametrizacao_equipe.unidade_id') ->join('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'atencao_basica_parametrizacao_equipe.infraestrutura_id') ->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_procedimento.usuario_id') ->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id') ->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_procedimento.ocupacao_id') ->leftJoin('cadastro_endereco', 'cadastro_endereco.id', 'cadastro_infraestrutura.endereco_id') ->leftJoin('cadastro_logradouro', 'cadastro_logradouro.id', 'cadastro_endereco.logradouro_id') ->leftJoin('intranet.cadastro_municipio', 'cadastro_municipio.id', 'cadastro_logradouro.municipio_id') ->whereBetween('atencao_basica_procedimento.data_usuario_responsavel', [$start, $end]) ->when(request()->unidade_id, fn ($query) => $query->where('atencao_basica_parametrizacao_equipe.unidade_id', request()->unidade_id)) ->when(request()->infraestrutura_id, fn ($query) => $query->where('atencao_basica_parametrizacao_equipe.infraestrutura_id', request()->infraestrutura_id)) ->when(request()->usuario_id, fn ($query) => $query->where('atencao_basica_procedimento.usuario_id', request()->usuario_id)) ->get(); if ($fichasProcedimento->isEmpty()) { return []; } $fichaIds = $fichasProcedimento->pluck('id')->all(); $atendimentosPorFicha = self::loadAtendimentos($fichaIds); $atendimentoCountPorFicha = self::countAtendimentosPorFicha($fichaIds); $allAtendimentoIds = collect($atendimentosPorFicha) ->flatMap(fn ($items) => collect($items)->pluck('id')) ->all(); $procedimentosPorAtendimento = self::loadProcedimentos($allAtendimentoIds); $procedimentoCountByAtendimento = self::countAuxiliares($allAtendimentoIds); $groupedList = []; foreach ($fichasProcedimento as $ficha) { $errosHeader = RelatorioInconsistenciaService::validHeader($ficha, Permissao::PROCEDIMENTOS); $headerDescriptions = self::mapErrors($errosHeader, self::HEADER_ERROR_MAP, $inconsistencias); $procedimentoCount = $atendimentoCountPorFicha[$ficha->id] ?? 0; $fichaDescriptions = self::collectProcedimentoErrors($ficha, $procedimentoCount, $inconsistencias); $fichaSemPacienteDescriptions = collect($headerDescriptions->all())->merge($fichaDescriptions); if ($fichaSemPacienteDescriptions->isNotEmpty()) { $key = $ficha->data_atendimento . '||' . $ficha->usuario_id; if (!isset($groupedList[$key])) { $groupedList[$key] = [ 'data' => (new Carbon($ficha->data_atendimento))->format('d/m/Y'), 'ficha' => self::FICHA_LABEL, 'paciente_nome' => null, 'paciente_nome_social' => null, 'paciente_preferencia_nome_id' => null, 'paciente_prontuario' => null, 'paciente_cns' => '-', 'paciente_cpf' => '-', 'profissional' => $ficha->profissional_nome, 'erros' => $fichaSemPacienteDescriptions, ]; } else { $groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($fichaSemPacienteDescriptions); } } foreach ($atendimentosPorFicha[$ficha->id] ?? [] as $atendimento) { $counts = $procedimentoCountByAtendimento[$atendimento->id] ?? [ 'procedimentos' => 0, 'administracao' => 0, 'pequena_cirurgia' => 0, 'teste_rapido' => 0, ]; $procedimentos = $procedimentosPorAtendimento[$atendimento->id] ?? collect(); $errosPaciente = self::validPaciente($atendimento, $procedimentos, $counts); $pacienteDescriptions = self::mapErrors($errosPaciente, self::PACIENTE_ERROR_MAP, $inconsistencias); if ($pacienteDescriptions->isEmpty()) { continue; } $key = $ficha->data_atendimento . '|' . $atendimento->paciente_id . '|' . $ficha->usuario_id; if (!isset($groupedList[$key])) { $groupedList[$key] = [ 'data' => (new Carbon($ficha->data_atendimento))->format('d/m/Y'), 'ficha' => self::FICHA_LABEL, 'paciente_nome' => $atendimento->paciente_nome, 'paciente_nome_social' => $atendimento->paciente_nome_social, 'paciente_preferencia_nome_id' => $atendimento->paciente_preferencia_nome_id, 'paciente_prontuario' => $atendimento->paciente_prontuario, 'paciente_cns' => $atendimento->paciente_cns ? MaskUtil::getCnsMasked($atendimento->paciente_cns) : '-', 'paciente_cpf' => $atendimento->paciente_cpf ? MaskUtil::getCpfMasked($atendimento->paciente_cpf) : '-', 'profissional' => $ficha->profissional_nome, 'erros' => $pacienteDescriptions, ]; } else { $groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($pacienteDescriptions); } } } $result = []; foreach ($groupedList as $group) { $unique = $group['erros']->unique(); $group['erros'] = $unique->implode('
'); $group['total_erros'] = $unique->count(); $result[] = $group; } return $result; } private static function mapErrors(Collection $errors, array $map, Collection $inconsistencias): Collection { $descriptions = collect(); foreach ($errors as $error) { $inconsistenciaId = $map[$error] ?? null; if ($inconsistenciaId === null) { continue; } $descricao = $inconsistencias[$inconsistenciaId] ?? null; if ($descricao !== null) { $descriptions->push($descricao); } } return $descriptions; } private static function collectProcedimentoErrors($procedimento, int $procedimentoAtendimentoCount, Collection $inconsistencias): Collection { $descriptions = collect(); $consolidadoPreenchido = $procedimento->afericao_pa || $procedimento->afericao_temperatura || $procedimento->curativo_simples || $procedimento->coleta_material_exame_laboratorial || $procedimento->glicemia_capilar || $procedimento->medicao_altura || $procedimento->medicao_peso; $individualPreenchido = $procedimentoAtendimentoCount > 0; if ($procedimentoAtendimentoCount > 99) { $descricao = $inconsistencias[Inconsistencia::FICHA_COM_MAIS_DE_99_PACIENTES_INSERIDOS] ?? null; if ($descricao !== null) { $descriptions->push($descricao); } } if (!$consolidadoPreenchido && !$individualPreenchido) { $descricao = $inconsistencias[Inconsistencia::NENHUM_PROCEDIMENTO_CONSOLIDADO_OU_INDIVIDUALIZADO_PREENCHIDO] ?? null; if ($descricao !== null) { $descriptions->push($descricao); } } return $descriptions; } private static function validPaciente($atendimento, Collection $procedimentos, array $counts): Collection { $errors = collect(); if (!Cns::isValid($atendimento->paciente_cns) && !Cpf::isValid($atendimento->paciente_cpf)) { $errors->push(CnsPaciente::class); $errors->push(Cpf::class); } if (!DataNascimentoIsRequired::isValid($atendimento->paciente_data_nascimento)) { $errors->push(DataNascimentoIsRequired::class); } if (!DataNascimentoIsValid::isValid([ 'data_nascimento' => $atendimento->paciente_data_nascimento, 'data_atendimento' => $atendimento->data_atendimento ])) { $errors->push(DataNascimentoIsValid::class); } if (!LocalAtendimentoIsRequired::isValid($atendimento->local_atendimento_id)) { $errors->push(LocalAtendimentoIsRequired::class); } if (!LocalAtendimentoIsValid::isValid($atendimento->local_atendimento_id)) { $errors->push(LocalAtendimentoIsValid::class); } if (!TurnoIsRequired::isValid($atendimento->turno_id)) { $errors->push(TurnoIsRequired::class); } foreach ($procedimentos as $procedimento) { if ($procedimento->is_duplicate) { $errors->push(ProcedimentoIsDuplicate::class); } if ($procedimento->is_invalid_for_ficha) { $errors->push(ProcedimentoIsValidForFicha::class); } } if (!ProcedimentoLimitIsValid::isValid($counts['procedimentos'])) { $errors->push(ProcedimentoLimitIsValid::class); } $procedimentosTotais = $counts['procedimentos'] + $counts['administracao'] + $counts['pequena_cirurgia'] + $counts['teste_rapido']; if (!ProcedimentoIsRequired::isValid($atendimento, $procedimentosTotais)) { $errors->push(ProcedimentoIsRequired::class); } if (!AlturaLimitIsValid::isValid($atendimento->altura)) { $errors->push(AlturaLimitIsValid::class); } if (!CircunferenciaAbdominalLimitIsValid::isValid($atendimento->circunferencia_abdominal)) { $errors->push(CircunferenciaAbdominalLimitIsValid::class); } if (!FrequenciaCardiacaLimitIsValid::isValid($atendimento->frequencia_cardiaca)) { $errors->push(FrequenciaCardiacaLimitIsValid::class); } if (!FrequenciaRespiratoriaLimitIsValid::isValid($atendimento->frequencia_respiratoria)) { $errors->push(FrequenciaRespiratoriaLimitIsValid::class); } if (!GlicemiaLimitIsValid::isValid($atendimento->glicemia_capilar)) { $errors->push(GlicemiaLimitIsValid::class); } if (!is_null($atendimento->glicemia_capilar) && !MomentoColetaGlicemiaIsRequired::isValid($atendimento->momento_coleta_id)) { $errors->push(MomentoColetaGlicemiaIsRequired::class); } if (!PerimetroCefalicoLimitIsValid::isValid($atendimento->perimetro_cefalico)) { $errors->push(PerimetroCefalicoLimitIsValid::class); } if (!PerimetroPanturrilhaLimitIsValid::isValid($atendimento->perimetro_panturrilha)) { $errors->push(PerimetroPanturrilhaLimitIsValid::class); } if (!PesoLimitIsValid::isValid($atendimento->peso)) { $errors->push(PesoLimitIsValid::class); } if (!PressaoArterialDiastolicaLimitIsValid::isValid($atendimento->afericao_pa)) { $errors->push(PressaoArterialDiastolicaLimitIsValid::class); } if (!PressaoArterialSistolicaLimitIsValid::isValid($atendimento->afericao_pa)) { $errors->push(PressaoArterialSistolicaLimitIsValid::class); } if (!SaturacaoO2LimitIsValid::isValid($atendimento->saturacao_o2)) { $errors->push(SaturacaoO2LimitIsValid::class); } if (!TemperaturaLimitIsValid::isValid($atendimento->temperatura)) { $errors->push(TemperaturaLimitIsValid::class); } return $errors; } private static function loadAtendimentos(array $fichaIds): array { if (empty($fichaIds)) { return []; } $rows = DB::table('atencao_basica_procedimento_atendimento') ->select( 'atencao_basica_procedimento_atendimento.id as id', 'atencao_basica_procedimento_atendimento.procedimento_id', 'cadastro_cidadao.cns as paciente_cns', 'cadastro_cidadao.cpf as paciente_cpf', 'cadastro_cidadao.nome as paciente_nome', 'cadastro_cidadao.nome_social as paciente_nome_social', 'cadastro_cidadao.preferencia_nome_id as paciente_preferencia_nome_id', 'cadastro_cidadao.data_nascimento as paciente_data_nascimento', 'cadastro_cidadao.sexo_id as paciente_sexo_id', DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'), 'fp.data_usuario_responsavel as data_atendimento', 'atencao_basica_procedimento_atendimento.paciente_id as paciente_id', 'atencao_basica_procedimento_atendimento.local_atendimento_id as local_atendimento_id', 'atencao_basica_procedimento_atendimento.turno_id as turno_id', 'atencao_basica_procedimento_atendimento.escuta_inicial as escuta_inicial', 'atencao_basica_procedimento_atendimento.peso', 'atencao_basica_procedimento_atendimento.altura', 'atencao_basica_procedimento_atendimento.temperatura', 'atencao_basica_procedimento_atendimento.perimetro_cefalico', 'atencao_basica_procedimento_atendimento.frequencia_respiratoria', 'atencao_basica_procedimento_atendimento.frequencia_cardiaca', 'atencao_basica_procedimento_atendimento.saturacao_o2', 'atencao_basica_procedimento_atendimento.circunferencia_abdominal', 'atencao_basica_procedimento_atendimento.perimetro_panturrilha', 'atencao_basica_procedimento_atendimento.momento_coleta_id', 'atencao_basica_procedimento_atendimento.afericao_pa', 'atencao_basica_procedimento_atendimento.glicemia_capilar', ) ->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_procedimento_atendimento.paciente_id') ->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_paciente.cidadao_id') ->join('atencao_basica_procedimento as fp', 'fp.id', 'atencao_basica_procedimento_atendimento.procedimento_id') ->whereIn('atencao_basica_procedimento_atendimento.procedimento_id', $fichaIds) ->get(); $porFicha = []; foreach ($rows as $row) { $porFicha[$row->procedimento_id][] = $row; } return $porFicha; } private static function countAtendimentosPorFicha(array $fichaIds): array { if (empty($fichaIds)) { return []; } return DB::table('atencao_basica_procedimento_atendimento') ->whereIn('procedimento_id', $fichaIds) ->select('procedimento_id', DB::raw('COUNT(*) AS total')) ->groupBy('procedimento_id') ->pluck('total', 'procedimento_id') ->all(); } private static function loadProcedimentos(array $atendimentoIds): array { if (empty($atendimentoIds)) { return []; } $rows = DB::table('atencao_basica_procedimento_atendimento_procedimento') ->whereIn('procedimento_atendimento_id', $atendimentoIds) ->select('procedimento_atendimento_id', 'procedimento_id') ->get(); $procedimentoIds = $rows->pluck('procedimento_id')->unique()->filter()->values()->all(); $codigoInvalido = self::loadCodigosInvalidos($procedimentoIds); $countByKey = []; foreach ($rows as $row) { $key = $row->procedimento_atendimento_id . ':' . $row->procedimento_id; $countByKey[$key] = ($countByKey[$key] ?? 0) + 1; } $porAtendimento = []; foreach ($rows as $row) { $key = $row->procedimento_atendimento_id . ':' . $row->procedimento_id; $porAtendimento[$row->procedimento_atendimento_id][] = (object) [ 'procedimento_atendimento_id' => $row->procedimento_atendimento_id, 'procedimento_id' => $row->procedimento_id, 'is_duplicate' => $countByKey[$key] > 1, 'is_invalid_for_ficha' => $codigoInvalido[$row->procedimento_id] ?? false, ]; } return array_map(fn ($items) => collect($items), $porAtendimento); } private static function loadCodigosInvalidos(array $procedimentoIds): array { if (empty($procedimentoIds)) { return []; } $rows = DB::table('sigtap.procedimento') ->whereIn('id', $procedimentoIds) ->select('id', 'codigo') ->get(); $map = []; foreach ($rows as $row) { $map[$row->id] = $row->codigo == Procedimento::ESCUTA_INICIAL_ORIENTACAO_ACOLHIMENTO_A_DEMANDA_ESPONT_NEA; } return $map; } private static function countAuxiliares(array $atendimentoIds): array { if (empty($atendimentoIds)) { return []; } $fontes = [ 'procedimentos' => 'atencao_basica_procedimento_atendimento_procedimento', 'administracao' => 'atencao_basica_procedimento_via_administracao', 'pequena_cirurgia' => 'atencao_basica_procedimento_atendimento_pequena_cirurgia', 'teste_rapido' => 'atencao_basica_procedimento_atendimento_teste_rapido', ]; $counts = []; foreach ($atendimentoIds as $id) { $counts[$id] = [ 'procedimentos' => 0, 'administracao' => 0, 'pequena_cirurgia' => 0, 'teste_rapido' => 0, ]; } foreach ($fontes as $chave => $tabela) { $rows = DB::table($tabela) ->whereIn('procedimento_atendimento_id', $atendimentoIds) ->select('procedimento_atendimento_id', DB::raw('COUNT(*) AS total')) ->groupBy('procedimento_atendimento_id') ->get(); foreach ($rows as $row) { $counts[$row->procedimento_atendimento_id][$chave] = (int) $row->total; } } return $counts; } }