init commit
All checks were successful
build-api-image / build (push) Successful in 51s

This commit is contained in:
Antonio Lopes dos Santos
2026-08-04 16:52:02 -03:00
commit afec302c3e
11594 changed files with 1636728 additions and 0 deletions

View File

@@ -0,0 +1,438 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\CondicaoAvaliacaoDomiciliarIsValidForFicha;
use App\Rules\AtencaoBasica\Exportacao\CondutaDesfechoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCiapIsDuplicated;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCiapIsValid;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCidIsDuplicated;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCidIsValid;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCidOuCiapIsRequired;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\ProcedimentoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\SexoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TipoAtendimentoDomiciliarIsValid;
use App\Rules\AtencaoBasica\Exportacao\TipoAtendimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TipoModalidadeIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TurnoIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaAtendimentoDomiciliarService extends Service
{
private const ERROR_MAP = [
CnesIsValid::class => 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,
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,
CondutaDesfechoIsRequired::class => Inconsistencia::CONDUTA_DESFECHO_SEM_PREENCHIMENTO,
SexoIsRequired::class => Inconsistencia::SEXO_DO_PACIENTE_SEM_PREENCHIMENTO,
ProcedimentoLimitIsValid::class => Inconsistencia::PACIENTE_COM_MAIS_DE_20_PROCEDIMENTOS_INSERIDOS,
TipoAtendimentoIsRequired::class => Inconsistencia::TIPO_DE_ATENDIMENTO_SEM_PREENCHIMENTO,
TipoAtendimentoDomiciliarIsValid::class => Inconsistencia::MODALIDADE_INVALIDA,
TipoModalidadeIsRequired::class => Inconsistencia::MODALIDADE_SEM_PREENCHIMENTO,
CondicaoAvaliacaoDomiciliarIsValidForFicha::class => Inconsistencia::CONDICAO_AVALIADA_INVALIDA,
ProblemaCondicaoIsRequired::class => Inconsistencia::PROBLEMA_OU_CONDICAO_SEM_PREENCHIMENTO,
ProblemaCondicaoCidOuCiapIsRequired::class => Inconsistencia::PROBLEMA_E_CONDICAO_SEM_CIAP2_E_SEM_CID10,
ProblemaCondicaoCiapIsDuplicated::class => Inconsistencia::CIAP2_EM_DUPLICIDADE,
ProblemaCondicaoCiapIsValid::class => Inconsistencia::PROBLEMA_E_CONDICAO_COM_CIAP2_INVALIDO,
ProblemaCondicaoCidIsDuplicated::class => Inconsistencia::CID_EM_DUPLICIDADE,
ProblemaCondicaoCidIsValid::class => Inconsistencia::PROBLEMA_E_CONDICAO_COM_CID10_INVALIDO,
];
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');
$fichasAtendimentoDomiciliar = DB::table('atencao_basica_atendimento_domiciliar')
->select([
'atencao_basica_atendimento_domiciliar.id as id',
'atencao_basica_atendimento_domiciliar.usuario_id as usuario_id',
'atencao_basica_atendimento_domiciliar.data 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',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_atendimento_domiciliar.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_atendimento_domiciliar.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_atendimento_domiciliar.ocupacao_id')
->whereBetween('atencao_basica_atendimento_domiciliar.data', [$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_atendimento_domiciliar.usuario_id', request()->usuario_id))
->get();
if ($fichasAtendimentoDomiciliar->isEmpty()) {
return [];
}
$atendimentosPorFicha = self::loadAtendimentos($fichasAtendimentoDomiciliar->pluck('id')->all());
$allAtendimentoIds = [];
foreach ($atendimentosPorFicha as $atendimentos) {
foreach ($atendimentos as $atendimento) {
$allAtendimentoIds[] = $atendimento->id;
}
}
$problemasCondicoesPorAtendimento = self::loadProblemasCondicoes($allAtendimentoIds);
$groupedList = [];
foreach ($fichasAtendimentoDomiciliar as $ficha) {
$errosHeader = RelatorioInconsistenciaService::validHeader($ficha, Permissao::ATENDIMENTO_DOMICILIAR);
foreach ($atendimentosPorFicha[$ficha->id] ?? [] as $atendimento) {
$problemasCondicoes = $problemasCondicoesPorAtendimento[$atendimento->id] ?? collect();
$errors = $errosHeader->merge(self::validAtendimento($atendimento, $ficha, $problemasCondicoes));
$errorDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
if ($errorDescriptions->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' => 'Atendimento domiciliar',
'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' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
$inconsistenciaId = self::ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function validAtendimento($atendimento, $fichaAtendimentoDomiciliar, Collection $problemasCondicoes): 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' => $fichaAtendimentoDomiciliar->data_atendimento
])) {
$errors->push(DataNascimentoIsValid::class);
}
if (!LocalAtendimentoIsRequired::isValid($atendimento->local_atendimento_id)) {
$errors->push(LocalAtendimentoIsRequired::class);
}
if (!TipoAtendimentoIsRequired::isValid($atendimento->tipo_atendimento_id)) {
$errors->push(TipoAtendimentoIsRequired::class);
}
if (!TipoAtendimentoDomiciliarIsValid::isValid($atendimento->tipo_atendimento_id)) {
$errors->push(TipoAtendimentoDomiciliarIsValid::class);
}
if (!TipoModalidadeIsRequired::isValid($atendimento)) {
$errors->push(TipoModalidadeIsRequired::class);
}
if (!CondicaoAvaliacaoDomiciliarIsValidForFicha::isValid($atendimento)) {
$errors->push(CondicaoAvaliacaoDomiciliarIsValidForFicha::class);
}
if (!TurnoIsRequired::isValid($atendimento->turno_id)) {
$errors->push(TurnoIsRequired::class);
}
if (!CondutaDesfechoIsRequired::isValid($atendimento->conduta_desfecho_id)) {
$errors->push(CondutaDesfechoIsRequired::class);
}
if (!SexoIsRequired::isValid($atendimento->paciente_sexo_id)) {
$errors->push(SexoIsRequired::class);
}
if (!ProblemaCondicaoIsRequired::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoIsRequired::class);
}
if (!ProblemaCondicaoCidOuCiapIsRequired::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCidOuCiapIsRequired::class);
}
if (!ProblemaCondicaoCiapIsDuplicated::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCiapIsDuplicated::class);
}
if (!ProblemaCondicaoCiapIsValid::isValid($problemasCondicoes, $atendimento->paciente_idade)) {
$errors->push(ProblemaCondicaoCiapIsValid::class);
}
if (!ProblemaCondicaoCidIsDuplicated::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCidIsDuplicated::class);
}
if (!ProblemaCondicaoCidIsValid::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCidIsValid::class);
}
return $errors;
}
private static function loadAtendimentos(array $fichaIds): array
{
if (empty($fichaIds)) {
return [];
}
$rows = DB::table('atencao_basica_atendimento_domiciliar_atendimento as ad')
->select([
'ad.id as id',
'ad.atendimento_domiciliar_id',
'paciente_cidadao.cns as paciente_cns',
'paciente_cidadao.cpf as paciente_cpf',
'paciente_cidadao.nome as paciente_nome',
'paciente_cidadao.nome_social as paciente_nome_social',
'paciente_cidadao.preferencia_nome_id as paciente_preferencia_nome_id',
'paciente_cidadao.data_nascimento as paciente_data_nascimento',
'paciente_cidadao.sexo_id as paciente_sexo_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
'ad.local_atendimento_id',
'ad.turno_id',
'ad.tipo_atendimento_id',
'ad.modalidade_id',
'ad.conduta_desfecho_id',
'ad.paciente_id',
DB::raw('TIMESTAMPDIFF(YEAR, paciente_cidadao.data_nascimento, NOW()) AS paciente_idade'),
])
->join('cadastro_paciente', 'cadastro_paciente.id', 'ad.paciente_id')
->join('cadastro_cidadao as paciente_cidadao', 'paciente_cidadao.id', 'cadastro_paciente.cidadao_id')
->whereIn('ad.atendimento_domiciliar_id', $fichaIds)
->get();
$porFicha = [];
foreach ($rows as $row) {
$porFicha[$row->atendimento_domiciliar_id][] = $row;
}
return $porFicha;
}
private static function loadProblemasCondicoes(array $atendimentoIds): array
{
if (empty($atendimentoIds)) {
return [];
}
$problemasCondicoesRaw = DB::table('atencao_basica_atendimento_domiciliar_atend_problema_condicao as pc')
->select([
'pc.atendimento_domiciliar_atendimento_id',
'pc.ciap_id',
'pc.cid_id',
'geral_ciap.codigo as ciap_codigo',
'geral_ciap.grupo_id as ciap_grupo_id',
'cid.codigo as cid_codigo',
DB::raw("IF (cid.codigo LIKE 'Z34%', 1, 0) as is_cid_familia_Z34"),
])
->leftJoin('geral_ciap', 'geral_ciap.id', 'pc.ciap_id')
->leftJoin('sigtap.cid', 'cid.id', 'pc.cid_id')
->whereIn('pc.atendimento_domiciliar_atendimento_id', $atendimentoIds)
->get()
->groupBy('atendimento_domiciliar_atendimento_id');
$codigosEsusPorAtendimento = self::loadCodigosEsus($atendimentoIds);
$todosCodigos = collect($codigosEsusPorAtendimento)->flatten()->unique()->values()->all();
$ciapsPorCodigoEsus = self::loadCiapsPorCodigoEsus($todosCodigos);
$resultado = [];
foreach ($atendimentoIds as $id) {
$problemasCondicoes = collect($problemasCondicoesRaw[$id] ?? [])
->map(function ($pc) {
$pc->atendimento_domiciliar_atendimento_id = null;
$pc->is_cid_familia_Z34 = (bool) $pc->is_cid_familia_Z34;
return $pc;
})
->values();
$ciapsAdicionais = collect();
$vistos = [];
foreach ($codigosEsusPorAtendimento[$id] ?? [] as $codigo) {
foreach ($ciapsPorCodigoEsus[$codigo] ?? [] as $ciap) {
if (!isset($vistos[$ciap->ciap_id])) {
$ciapsAdicionais->push($ciap);
$vistos[$ciap->ciap_id] = true;
}
}
}
$resultado[$id] = $problemasCondicoes->concat($ciapsAdicionais)->values();
}
return $resultado;
}
private static function loadCodigosEsus(array $atendimentoIds): array
{
$fontes = [
['atencao_basica_atendimento_domiciliar_atend_condicao_avaliada', 'atencao_basica_cadastro_condicao_avaliada', 'condicao_avaliada_id'],
['atencao_basica_atendimento_domiciliar_atend_doenca_transmissivel', 'atencao_basica_cadastro_doenca_transmissivel', 'doenca_transmissivel_id'],
['atencao_basica_atendimento_domiciliar_atend_rastreamento', 'atencao_basica_cadastro_rastreamento', 'rastreamento_id'],
];
$codigos = [];
foreach ($fontes as [$tabelaLink, $tabelaCadastro, $fk]) {
$rows = DB::table($tabelaLink)
->join($tabelaCadastro, "{$tabelaCadastro}.id", "{$tabelaLink}.{$fk}")
->whereIn("{$tabelaLink}.atendimento_domiciliar_atendimento_id", $atendimentoIds)
->select(
"{$tabelaLink}.atendimento_domiciliar_atendimento_id",
"{$tabelaCadastro}.codigo_esus"
)
->get();
foreach ($rows as $row) {
$codigos[$row->atendimento_domiciliar_atendimento_id][] = $row->codigo_esus;
}
}
return $codigos;
}
private static function loadCiapsPorCodigoEsus(array $codigos): array
{
if (empty($codigos)) {
return [];
}
$rows = DB::table('geral_ciap_codigo_esus')
->join('geral_ciap', 'geral_ciap.id', 'geral_ciap_codigo_esus.ciap_id')
->whereIn('geral_ciap_codigo_esus.codigo', $codigos)
->select(
'geral_ciap_codigo_esus.codigo as esus_codigo',
'geral_ciap.id',
'geral_ciap.codigo',
'geral_ciap.grupo_id'
)
->get();
$ciapsPorCodigo = [];
foreach ($rows as $row) {
$ciapsPorCodigo[$row->esus_codigo][] = (object) [
'ciap_id' => $row->id,
'cid_id' => null,
'ciap_codigo' => $row->codigo,
'ciap_grupo_id' => $row->grupo_id,
'cid_codigo' => null,
'is_cid_familia_Z34' => false,
];
}
return $ciapsPorCodigo;
}
}

View File

@@ -0,0 +1,495 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\AtendimentoIndividual\CondicaoAvaliadaCodigoEsus;
use App\Enums\AtencaoBasica\AtendimentoIndividual\RastreamentoCodigoEsus;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Rules\AtencaoBasica\Exportacao\AlturaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\CircunferenciaAbdominalLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\FrequenciaCardiacaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\FrequenciaRespiratoriaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\GlicemiaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\MomentoColetaGlicemiaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\PerimetroCefalicoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PerimetroPanturrilhaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PesoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PressaoArterialDiastolicaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PressaoArterialSistolicaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCiapIsDuplicated;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCiapIsValid;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCidIsDuplicated;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCidIsValid;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\SaturacaoO2LimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\SexoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TemperaturaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\TipoAtendimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TipoAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\TurnoIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaAtendimentoIndividualService extends Service
{
private const HEADER_ERROR_MAP = [
CnesIsValid::class => 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 ATENDIMENTO_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,
SexoIsRequired::class => Inconsistencia::SEXO_DO_PACIENTE_SEM_PREENCHIMENTO,
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,
TipoAtendimentoIsRequired::class => Inconsistencia::TIPO_DE_ATENDIMENTO_SEM_PREENCHIMENTO,
TipoAtendimentoIsValid::class => Inconsistencia::TIPO_DE_ATENDIMENTO_INVALIDO,
ProblemaCondicaoIsRequired::class => Inconsistencia::PROBLEMA_OU_CONDICAO_SEM_PREENCHIMENTO,
ProblemaCondicaoCiapIsDuplicated::class => Inconsistencia::CIAP2_EM_DUPLICIDADE,
ProblemaCondicaoCiapIsValid::class => Inconsistencia::PROBLEMA_E_CONDICAO_COM_CIAP2_INVALIDO,
ProblemaCondicaoCidIsDuplicated::class => Inconsistencia::CID_EM_DUPLICIDADE,
ProblemaCondicaoCidIsValid::class => Inconsistencia::PROBLEMA_E_CONDICAO_COM_CID10_INVALIDO,
];
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');
$fichasAtendimentoIndividual = DB::table('atencao_basica_atendimento_individual')
->select([
'atencao_basica_atendimento_individual.id as id',
'atencao_basica_atendimento_individual.usuario_id as usuario_id',
'atencao_basica_atendimento_individual.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',
'paciente_cidadao.cns as paciente_cns',
'paciente_cidadao.cpf as paciente_cpf',
'paciente_cidadao.nome as paciente_nome',
'paciente_cidadao.nome_social as paciente_nome_social',
'paciente_cidadao.preferencia_nome_id as paciente_preferencia_nome_id',
'paciente_cidadao.data_nascimento as paciente_data_nascimento',
DB::raw("TIMESTAMPDIFF(YEAR, paciente_cidadao.data_nascimento, NOW()) AS paciente_idade"),
'paciente_cidadao.sexo_id as paciente_sexo_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
'atencao_basica_atendimento_individual.local_atendimento_id',
'atencao_basica_atendimento_individual.turno_id',
'atencao_basica_atendimento_individual.tipo_atendimento_id',
'atencao_basica_atendimento_individual.perimetro_cefalico',
'atencao_basica_atendimento_individual.peso',
'atencao_basica_atendimento_individual.altura',
'atencao_basica_atendimento_individual.temperatura',
'atencao_basica_atendimento_individual.pressao_arterial',
'atencao_basica_atendimento_individual.frequencia_respiratoria',
'atencao_basica_atendimento_individual.frequencia_cardiaca',
'atencao_basica_atendimento_individual.saturacao_o2',
'atencao_basica_atendimento_individual.circunferencia_abdominal',
'atencao_basica_atendimento_individual.perimetro_panturrilha',
'atencao_basica_atendimento_individual.glicemia',
'atencao_basica_atendimento_individual.momento_coleta_id',
'atencao_basica_atendimento_individual.paciente_id',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_atendimento_individual.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_atendimento_individual.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_atendimento_individual.usuario_ocupacao_id')
->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_atendimento_individual.paciente_id')
->join('cadastro_cidadao as paciente_cidadao', 'paciente_cidadao.id', 'cadastro_paciente.cidadao_id')
->whereBetween('atencao_basica_atendimento_individual.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_atendimento_individual.usuario_id', request()->usuario_id))
->get();
if ($fichasAtendimentoIndividual->isEmpty()) {
return [];
}
$atendimentoIds = $fichasAtendimentoIndividual->pluck('id')->all();
$problemasCondicoesPorAtendimento = self::loadProblemasCondicoes($atendimentoIds);
$groupedList = [];
foreach ($fichasAtendimentoIndividual as $ficha) {
$problemasCondicoes = $problemasCondicoesPorAtendimento[$ficha->id] ?? collect();
$errors = RelatorioInconsistenciaService::validHeader($ficha, Permissao::ATENDIMENTO_INDIVIDUAL)
->merge(self::validAtendimento($ficha, $problemasCondicoes));
$errorDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $ficha->paciente_id . '|' . $ficha->usuario_id;
if (!isset($groupedList[$key])) {
$groupedList[$key] = [
'data' => (new Carbon($ficha->data_atendimento))->format('d/m/Y'),
'ficha' => 'Atendimento individual',
'paciente_nome' => $ficha->paciente_nome,
'paciente_nome_social' => $ficha->paciente_nome_social,
'paciente_preferencia_nome_id' => $ficha->paciente_preferencia_nome_id,
'paciente_prontuario' => $ficha->paciente_prontuario,
'profissional' => $ficha->profissional_nome,
'paciente_cns' => $ficha->paciente_cns ? MaskUtil::getCnsMasked($ficha->paciente_cns) : '-',
'paciente_cpf' => $ficha->paciente_cpf ? MaskUtil::getCpfMasked($ficha->paciente_cpf) : '-',
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$map = self::HEADER_ERROR_MAP + self::ATENDIMENTO_ERROR_MAP;
$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 validAtendimento($atendimento, Collection $problemasCondicoes)
{
$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 (!TipoAtendimentoIsRequired::isValid($atendimento->tipo_atendimento_id)) {
$errors->push(TipoAtendimentoIsRequired::class);
}
if (!TipoAtendimentoIsValid::isValid($atendimento->tipo_atendimento_id)) {
$errors->push(TipoAtendimentoIsValid::class);
}
if (!TurnoIsRequired::isValid($atendimento->turno_id)) {
$errors->push(TurnoIsRequired::class);
}
if (!SexoIsRequired::isValid($atendimento->paciente_sexo_id)) {
$errors->push(SexoIsRequired::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)) {
$errors->push(GlicemiaLimitIsValid::class);
}
if (!is_null($atendimento->glicemia) && !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->pressao_arterial)) {
$errors->push(PressaoArterialDiastolicaLimitIsValid::class);
}
if (!PressaoArterialSistolicaLimitIsValid::isValid($atendimento->pressao_arterial)) {
$errors->push(PressaoArterialSistolicaLimitIsValid::class);
}
if (!SaturacaoO2LimitIsValid::isValid($atendimento->saturacao_o2)) {
$errors->push(SaturacaoO2LimitIsValid::class);
}
if (!TemperaturaLimitIsValid::isValid($atendimento->temperatura)) {
$errors->push(TemperaturaLimitIsValid::class);
}
if (!ProblemaCondicaoIsRequired::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoIsRequired::class);
}
if (!ProblemaCondicaoCiapIsDuplicated::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCiapIsDuplicated::class);
}
if (!ProblemaCondicaoCiapIsValid::isValid($problemasCondicoes, $atendimento->paciente_idade)) {
$errors->push(ProblemaCondicaoCiapIsValid::class);
}
if (!ProblemaCondicaoCidIsDuplicated::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCidIsDuplicated::class);
}
if (!ProblemaCondicaoCidIsValid::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCidIsValid::class);
}
return $errors;
}
private static function loadProblemasCondicoes(array $atendimentoIds): array
{
if (empty($atendimentoIds)) {
return [];
}
$problemasCondicoesRaw = DB::table('atencao_basica_atendimento_individual_problema_condicao as pc')
->leftJoin('geral_ciap as ciap', 'ciap.id', 'pc.ciap_id')
->leftJoin('sigtap.cid as cid', 'cid.id', 'pc.cid_id')
->whereIn('pc.atendimento_individual_id', $atendimentoIds)
->select([
'pc.atendimento_individual_id',
'ciap.id as ciap_id',
'cid.id as cid_id',
'ciap.codigo as ciap_codigo',
'ciap.grupo_id as ciap_grupo_id',
'cid.codigo as cid_codigo',
DB::raw("IF (cid.codigo LIKE 'Z34%', 1, 0) as is_cid_familia_Z34"),
])
->get()
->groupBy('atendimento_individual_id');
$codigosEsusPorAtendimento = self::loadCodigosEsusPorAtendimento($atendimentoIds);
$ciapsPorCodigoEsus = self::loadCiapsPorCodigoEsus(
collect($codigosEsusPorAtendimento)->flatten()->unique()->values()->all()
);
$resultado = [];
foreach ($atendimentoIds as $id) {
$problemasCondicoes = collect($problemasCondicoesRaw[$id] ?? [])
->map(function ($pc) {
$pc->atendimento_individual_id = null;
$pc->is_cid_familia_Z34 = (bool) $pc->is_cid_familia_Z34;
return $pc;
})
->values();
$codigosEsus = collect($codigosEsusPorAtendimento[$id] ?? [])->values();
if (self::isCodigoEsusEspecial($codigosEsus)) {
$problemasCondicoes->push((object) [
'codigo_esus' => $codigosEsus,
]);
} else {
$ciapsAdicionais = collect();
$vistos = [];
foreach ($codigosEsus as $codigo) {
foreach ($ciapsPorCodigoEsus[$codigo] ?? [] as $ciap) {
if (!isset($vistos[$ciap->ciap_id])) {
$ciapsAdicionais->push($ciap);
$vistos[$ciap->ciap_id] = true;
}
}
}
$problemasCondicoes = $problemasCondicoes->concat($ciapsAdicionais)->values();
}
$resultado[$id] = $problemasCondicoes;
}
return $resultado;
}
private static function loadCodigosEsusPorAtendimento(array $atendimentoIds): array
{
$fontes = [
['atencao_basica_atendimento_individual_condicao_avaliada', 'atencao_basica_cadastro_condicao_avaliada', 'condicao_avaliada_id'],
['atencao_basica_atendimento_individual_doenca_transmissivel', 'atencao_basica_cadastro_doenca_transmissivel', 'doenca_transmissivel_id'],
['atencao_basica_atendimento_individual_rastreamento', 'atencao_basica_cadastro_rastreamento', 'rastreamento_id'],
];
$codigosPorAtendimento = [];
foreach ($fontes as [$tabelaLink, $tabelaCadastro, $fk]) {
$rows = DB::table($tabelaLink)
->join($tabelaCadastro, "{$tabelaCadastro}.id", "{$tabelaLink}.{$fk}")
->whereIn("{$tabelaLink}.atendimento_individual_id", $atendimentoIds)
->select(
"{$tabelaLink}.atendimento_individual_id",
"{$tabelaCadastro}.codigo_esus"
)
->get();
foreach ($rows as $row) {
$codigosPorAtendimento[$row->atendimento_individual_id][] = $row->codigo_esus;
}
}
return $codigosPorAtendimento;
}
private static function loadCiapsPorCodigoEsus(array $codigos): array
{
if (empty($codigos)) {
return [];
}
$rows = DB::table('geral_ciap_codigo_esus')
->join('geral_ciap', 'geral_ciap.id', 'geral_ciap_codigo_esus.ciap_id')
->whereIn('geral_ciap_codigo_esus.codigo', $codigos)
->select(
'geral_ciap_codigo_esus.codigo as esus_codigo',
'geral_ciap.id',
'geral_ciap.codigo',
'geral_ciap.grupo_id'
)
->get();
$ciapsPorCodigo = [];
foreach ($rows as $row) {
$ciapsPorCodigo[$row->esus_codigo][] = (object) [
'ciap_id' => $row->id,
'cid_id' => null,
'ciap_codigo' => $row->codigo,
'ciap_grupo_id' => $row->grupo_id,
'cid_codigo' => null,
'is_cid_familia_Z34' => false,
];
}
return $ciapsPorCodigo;
}
private static function isCodigoEsusEspecial(Collection $codigosEsus): bool
{
return $codigosEsus->contains(CondicaoAvaliadaCodigoEsus::SAUDE_MENTAL)
|| $codigosEsus->contains(CondicaoAvaliadaCodigoEsus::SAUDE_SEXUAL_E_REPRODUTIVA)
|| $codigosEsus->contains(CondicaoAvaliadaCodigoEsus::REABILITACAO)
|| $codigosEsus->contains(RastreamentoCodigoEsus::CANCER_MAMA)
|| $codigosEsus->contains(RastreamentoCodigoEsus::CANCER_COLO_UTERO);
}
}

View File

@@ -0,0 +1,512 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Rules\AtencaoBasica\Exportacao\AlturaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\CircunferenciaAbdominalLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\CondutaEncaminhamentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\CondutaEncaminhamentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\FrequenciaCardiacaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\FrequenciaRespiratoriaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\GestanteIsValidForFicha;
use App\Rules\AtencaoBasica\Exportacao\GlicemiaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\MomentoColetaGlicemiaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\PerimetroCefalicoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PerimetroPanturrilhaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PesoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PressaoArterialDiastolicaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PressaoArterialSistolicaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCiapIsDuplicated;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCiapIsValid;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCidIsDuplicated;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCidIsValid;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoCidOuCiapIsRequired;
use App\Rules\AtencaoBasica\Exportacao\ProblemaCondicaoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\ProcedimentoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\SaturacaoO2LimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\SexoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TemperaturaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\TipoAtendimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TipoAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\TipoConsultaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TipoConsultaIsValid;
use App\Rules\AtencaoBasica\Exportacao\TurnoIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaAtendimentoOdontologicoService extends Service
{
private const ERROR_MAP = [
CnesIsValid::class => 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,
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,
GestanteIsValidForFicha::class => Inconsistencia::GESTANTE_PREENCHIDO_PARA_PACIENTES_DO_SEXO_MASCULINO,
CondutaEncaminhamentoIsValid::class => Inconsistencia::CONDUTA_OU_ENCAMINHAMENTO_INVALIDO,
CondutaEncaminhamentoIsRequired::class => Inconsistencia::CONDUTA_OU_ENCAMINHAMENTO_SEM_PREENCHIMENTO,
TipoConsultaIsRequired::class => Inconsistencia::TIPO_CONSULTA_SEM_PREENCHIMENTO,
TipoConsultaIsValid::class => Inconsistencia::TIPO_CONSULTA_INVALIDO,
SexoIsRequired::class => Inconsistencia::SEXO_DO_PACIENTE_SEM_PREENCHIMENTO,
ProcedimentoLimitIsValid::class => Inconsistencia::PACIENTE_COM_MAIS_DE_20_PROCEDIMENTOS_INSERIDOS,
TipoAtendimentoIsRequired::class => Inconsistencia::TIPO_DE_ATENDIMENTO_SEM_PREENCHIMENTO,
TipoAtendimentoIsValid::class => Inconsistencia::TIPO_DE_ATENDIMENTO_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,
ProblemaCondicaoIsRequired::class => Inconsistencia::PROBLEMA_OU_CONDICAO_SEM_PREENCHIMENTO,
ProblemaCondicaoCidOuCiapIsRequired::class => Inconsistencia::PROBLEMA_E_CONDICAO_SEM_CIAP2_E_SEM_CID10,
ProblemaCondicaoCiapIsDuplicated::class => Inconsistencia::CIAP2_EM_DUPLICIDADE,
ProblemaCondicaoCiapIsValid::class => Inconsistencia::PROBLEMA_E_CONDICAO_COM_CIAP2_INVALIDO,
ProblemaCondicaoCidIsDuplicated::class => Inconsistencia::CID_EM_DUPLICIDADE,
ProblemaCondicaoCidIsValid::class => Inconsistencia::PROBLEMA_E_CONDICAO_COM_CID10_INVALIDO,
];
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');
$fichasAtendimentoOdontologico = DB::table('atencao_basica_atendimento_odontologico')
->select([
'atencao_basica_atendimento_odontologico.id as id',
'atencao_basica_atendimento_odontologico.usuario_id as usuario_id',
'atencao_basica_atendimento_odontologico.data_atendimento 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',
'paciente_cidadao.cns as paciente_cns',
'paciente_cidadao.cpf as paciente_cpf',
'paciente_cidadao.nome as paciente_nome',
'paciente_cidadao.nome_social as paciente_nome_social',
'paciente_cidadao.preferencia_nome_id as paciente_preferencia_nome_id',
'paciente_cidadao.data_nascimento as paciente_data_nascimento',
DB::raw("TIMESTAMPDIFF(YEAR, paciente_cidadao.data_nascimento, NOW()) AS paciente_idade"),
'paciente_cidadao.sexo_id as paciente_sexo_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
'atencao_basica_atendimento_odontologico.local_atendimento_id',
'atencao_basica_atendimento_odontologico.turno_id',
'atencao_basica_atendimento_odontologico.tipo_atendimento_id',
'atencao_basica_atendimento_odontologico.tipo_consulta_id',
'atencao_basica_atendimento_odontologico.paciente_id',
'atencao_basica_atendimento_odontologico.gestante',
'atencao_basica_atendimento_odontologico.perimetro_cefalico',
'atencao_basica_atendimento_odontologico.peso',
'atencao_basica_atendimento_odontologico.altura',
'atencao_basica_atendimento_odontologico.temperatura',
'atencao_basica_atendimento_odontologico.pressao_arterial',
'atencao_basica_atendimento_odontologico.frequencia_respiratoria',
'atencao_basica_atendimento_odontologico.frequencia_cardiaca',
'atencao_basica_atendimento_odontologico.saturacao_o2',
'atencao_basica_atendimento_odontologico.circunferencia_abdominal',
'atencao_basica_atendimento_odontologico.perimetro_panturrilha',
'atencao_basica_atendimento_odontologico.glicemia',
'atencao_basica_atendimento_odontologico.momento_coleta_id',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_atendimento_odontologico.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_atendimento_odontologico.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_atendimento_odontologico.ocupacao_id')
->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_atendimento_odontologico.paciente_id')
->join('cadastro_cidadao as paciente_cidadao', 'paciente_cidadao.id', 'cadastro_paciente.cidadao_id')
->whereBetween('atencao_basica_atendimento_odontologico.data_atendimento', [$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_atendimento_odontologico.usuario_id', request()->usuario_id))
->get();
if ($fichasAtendimentoOdontologico->isEmpty()) {
return [];
}
$atendimentoIds = $fichasAtendimentoOdontologico->pluck('id')->all();
$problemasCondicoesPorAtendimento = self::loadProblemasCondicoes($atendimentoIds);
$groupedList = [];
foreach ($fichasAtendimentoOdontologico as $ficha) {
$problemasCondicoes = $problemasCondicoesPorAtendimento[$ficha->id] ?? collect();
$errors = RelatorioInconsistenciaService::validHeader($ficha, Permissao::ATENDIMENTO_ODONTOLOGICO)
->merge(self::validAtendimento($ficha, $problemasCondicoes));
$errorDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $ficha->paciente_id . '|' . $ficha->usuario_id;
if (!isset($groupedList[$key])) {
$groupedList[$key] = [
'data' => (new Carbon($ficha->data_atendimento))->format('d/m/Y'),
'ficha' => 'Atendimento odontológico',
'paciente_nome' => $ficha->paciente_nome,
'paciente_nome_social' => $ficha->paciente_nome_social,
'paciente_preferencia_nome_id' => $ficha->paciente_preferencia_nome_id,
'paciente_prontuario' => $ficha->paciente_prontuario,
'profissional' => $ficha->profissional_nome,
'paciente_cns' => $ficha->paciente_cns ? MaskUtil::getCnsMasked($ficha->paciente_cns) : '-',
'paciente_cpf' => $ficha->paciente_cpf ? MaskUtil::getCpfMasked($ficha->paciente_cpf) : '-',
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
$inconsistenciaId = self::ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function validAtendimento($atendimento, Collection $problemasCondicoes): 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 (!TipoAtendimentoIsRequired::isValid($atendimento->tipo_atendimento_id)) {
$errors->push(TipoAtendimentoIsRequired::class);
}
if (!TipoAtendimentoIsValid::isValid($atendimento->tipo_atendimento_id)) {
$errors->push(TipoAtendimentoIsValid::class);
}
if (!TurnoIsRequired::isValid($atendimento->turno_id)) {
$errors->push(TurnoIsRequired::class);
}
if (!SexoIsRequired::isValid($atendimento->paciente_sexo_id)) {
$errors->push(SexoIsRequired::class);
}
if (!GestanteIsValidForFicha::isValid($atendimento)) {
$errors->push(GestanteIsValidForFicha::class);
}
if (!CondutaEncaminhamentoIsRequired::isValid($atendimento)) {
$errors->push(CondutaEncaminhamentoIsRequired::class);
}
if (!CondutaEncaminhamentoIsValid::isValid($atendimento)) {
$errors->push(CondutaEncaminhamentoIsValid::class);
}
if (!TipoConsultaIsRequired::isValid($atendimento)) {
$errors->push(TipoConsultaIsRequired::class);
}
if (!TipoConsultaIsValid::isValid($atendimento)) {
$errors->push(TipoConsultaIsValid::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)) {
$errors->push(GlicemiaLimitIsValid::class);
}
if (!is_null($atendimento->glicemia) && !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->pressao_arterial)) {
$errors->push(PressaoArterialDiastolicaLimitIsValid::class);
}
if (!PressaoArterialSistolicaLimitIsValid::isValid($atendimento->pressao_arterial)) {
$errors->push(PressaoArterialSistolicaLimitIsValid::class);
}
if (!SaturacaoO2LimitIsValid::isValid($atendimento->saturacao_o2)) {
$errors->push(SaturacaoO2LimitIsValid::class);
}
if (!TemperaturaLimitIsValid::isValid($atendimento->temperatura)) {
$errors->push(TemperaturaLimitIsValid::class);
}
if (!ProblemaCondicaoIsRequired::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoIsRequired::class);
}
if (!ProblemaCondicaoCidOuCiapIsRequired::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCidOuCiapIsRequired::class);
}
if (!ProblemaCondicaoCiapIsDuplicated::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCiapIsDuplicated::class);
}
if (!ProblemaCondicaoCiapIsValid::isValid($problemasCondicoes, $atendimento->paciente_idade)) {
$errors->push(ProblemaCondicaoCiapIsValid::class);
}
if (!ProblemaCondicaoCidIsDuplicated::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCidIsDuplicated::class);
}
if (!ProblemaCondicaoCidIsValid::isValid($problemasCondicoes)) {
$errors->push(ProblemaCondicaoCidIsValid::class);
}
return $errors;
}
private static function loadProblemasCondicoes(array $atendimentoIds): array
{
if (empty($atendimentoIds)) {
return [];
}
$problemasCondicoesRaw = DB::table('atencao_basica_atendimento_odontologico_problema_condicao as pc')
->select([
'pc.atendimento_odontologico_id',
'pc.ciap_id',
'pc.cid_id',
'geral_ciap.codigo as ciap_codigo',
'geral_ciap.grupo_id as ciap_grupo_id',
'cid.codigo as cid_codigo',
DB::raw("IF (cid.codigo LIKE 'Z34%', 1, 0) as is_cid_familia_Z34"),
])
->leftJoin('geral_ciap', 'geral_ciap.id', 'pc.ciap_id')
->leftJoin('sigtap.cid', 'cid.id', 'pc.cid_id')
->whereIn('pc.atendimento_odontologico_id', $atendimentoIds)
->get()
->groupBy('atendimento_odontologico_id');
$codigosEsusPorAtendimento = self::loadCodigosEsus($atendimentoIds);
$todosCodigos = collect($codigosEsusPorAtendimento)->flatten()->unique()->values()->all();
$ciapsPorCodigoEsus = self::loadCiapsPorCodigoEsus($todosCodigos);
$resultado = [];
foreach ($atendimentoIds as $id) {
$problemasCondicoes = collect($problemasCondicoesRaw[$id] ?? [])
->map(function ($pc) {
$pc->atendimento_odontologico_id = null;
$pc->is_cid_familia_Z34 = (bool) $pc->is_cid_familia_Z34;
return $pc;
})
->values();
$ciapsExistentes = $problemasCondicoes->pluck('ciap_id')->filter()->flip()->all();
$ciapsAdicionais = collect();
$vistos = [];
foreach ($codigosEsusPorAtendimento[$id] ?? [] as $codigo) {
foreach ($ciapsPorCodigoEsus[$codigo] ?? [] as $ciap) {
if (isset($ciapsExistentes[$ciap->ciap_id]) || isset($vistos[$ciap->ciap_id])) {
continue;
}
$ciapsAdicionais->push($ciap);
$vistos[$ciap->ciap_id] = true;
}
}
$resultado[$id] = $problemasCondicoes->concat($ciapsAdicionais)->values();
}
return $resultado;
}
private static function loadCodigosEsus(array $atendimentoIds): array
{
$fontes = [
['atencao_basica_atendimento_odontologico_condicao_avaliada', 'atencao_basica_cadastro_condicao_avaliada', 'condicao_avaliada_id'],
['atencao_basica_atendimento_odontologico_doenca_transmissivel', 'atencao_basica_cadastro_doenca_transmissivel', 'doenca_transmissivel_id'],
['atencao_basica_atendimento_odontologico_rastreamento', 'atencao_basica_cadastro_rastreamento', 'rastreamento_id'],
];
$codigos = [];
foreach ($fontes as [$tabelaLink, $tabelaCadastro, $fk]) {
$rows = DB::table($tabelaLink)
->join($tabelaCadastro, "{$tabelaCadastro}.id", "{$tabelaLink}.{$fk}")
->whereIn("{$tabelaLink}.atendimento_odontologico_id", $atendimentoIds)
->select(
"{$tabelaLink}.atendimento_odontologico_id",
"{$tabelaCadastro}.codigo_esus"
)
->get();
foreach ($rows as $row) {
$codigos[$row->atendimento_odontologico_id][] = $row->codigo_esus;
}
}
return $codigos;
}
private static function loadCiapsPorCodigoEsus(array $codigos): array
{
if (empty($codigos)) {
return [];
}
$rows = DB::table('geral_ciap_codigo_esus')
->join('geral_ciap', 'geral_ciap.id', 'geral_ciap_codigo_esus.ciap_id')
->whereIn('geral_ciap_codigo_esus.codigo', $codigos)
->select(
'geral_ciap_codigo_esus.codigo as esus_codigo',
'geral_ciap.id',
'geral_ciap.codigo',
'geral_ciap.grupo_id'
)
->get();
$ciapsPorCodigo = [];
foreach ($rows as $row) {
$ciapsPorCodigo[$row->esus_codigo][] = (object) [
'ciap_id' => $row->id,
'cid_id' => null,
'ciap_codigo' => $row->codigo,
'ciap_grupo_id' => $row->grupo_id,
'cid_codigo' => null,
'is_cid_familia_Z34' => false,
];
}
return $ciapsPorCodigo;
}
}

View File

@@ -0,0 +1,220 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\TurnoIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaAtividadeColetivaService extends Service
{
private const ERROR_MAP = [
CnesIsValid::class => 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,
TurnoIsRequired::class => Inconsistencia::TURNO_SEM_PREENCHIMENTO,
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,
];
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');
$fichasAtividadeColetiva = DB::table('atencao_basica_atividade_coletiva')
->select([
'atencao_basica_atividade_coletiva.id as id',
'atencao_basica_atividade_coletiva.turno_id',
'atencao_basica_atividade_coletiva.usuario_id as usuario_id',
'cadastro_cidadao.cns AS usuario_cns',
'cadastro_cidadao.nome AS profissional_nome',
'atencao_basica_atividade_coletiva.data_usuario_responsavel as data_atendimento',
'cadastro_unidade.cnes as unidade_cnes',
'cadastro_municipio.ibge as municipio_ibge',
'sigtap.ocupacao.codigo as ocupacao_codigo',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_atividade_coletiva.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_atividade_coletiva.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_atividade_coletiva.usuario_ocupacao_id')
->whereBetween('atencao_basica_atividade_coletiva.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_atividade_coletiva.usuario_id', request()->usuario_id))
->get();
if ($fichasAtividadeColetiva->isEmpty()) {
return [];
}
$atividadeIds = $fichasAtividadeColetiva->pluck('id')->all();
$residentesPorAtividade = self::loadResidentes($atividadeIds);
$groupedList = [];
foreach ($fichasAtividadeColetiva as $ficha) {
$errosHeader = RelatorioInconsistenciaService::validHeader($ficha, Permissao::ATIVIDADE_COLETIVA);
foreach ($residentesPorAtividade[$ficha->id] ?? [] as $residente) {
$errors = $errosHeader->merge(self::validAtendimento($ficha, $residente));
$errorDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $residente->paciente_id . '|' . $ficha->usuario_id;
if (!isset($groupedList[$key])) {
$groupedList[$key] = [
'data' => (new Carbon($ficha->data_atendimento))->format('d/m/Y'),
'ficha' => 'Atividade coletiva',
'paciente_nome' => $residente->paciente_nome,
'paciente_nome_social' => $residente->paciente_nome_social,
'paciente_preferencia_nome_id' => $residente->paciente_preferencia_nome_id,
'paciente_prontuario' => $residente->paciente_prontuario,
'paciente_cns' => $residente->cns ? MaskUtil::getCnsMasked($residente->cns) : '-',
'paciente_cpf' => $residente->cpf ? MaskUtil::getCpfMasked($residente->cpf) : '-',
'profissional' => $ficha->profissional_nome,
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
$inconsistenciaId = self::ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function validAtendimento($cadastro, $residente): Collection
{
$errors = collect();
if (!TurnoIsRequired::isValid($cadastro->turno_id)) {
$errors->push(TurnoIsRequired::class);
}
if (!Cns::isValid($residente->cns) && !Cpf::isValid($residente->cpf)) {
$errors->push(CnsPaciente::class);
$errors->push(Cpf::class);
}
if (!DataNascimentoIsRequired::isValid($residente->data_nascimento)) {
$errors->push(DataNascimentoIsRequired::class);
}
if (!DataNascimentoIsValid::isValid([
'data_nascimento' => $residente->data_nascimento,
'data_atendimento' => $cadastro->data_atendimento
])) {
$errors->push(DataNascimentoIsValid::class);
}
return $errors;
}
private static function loadResidentes(array $atividadeIds): array
{
if (empty($atividadeIds)) {
return [];
}
$rows = DB::table('atencao_basica_atividade_coletiva_participante')
->select([
'atencao_basica_atividade_coletiva_participante.atividade_coletiva_id',
'cadastro_cidadao.data_nascimento',
'cadastro_cidadao.sexo_id',
'cadastro_cidadao.cns',
'cadastro_cidadao.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_paciente.id as paciente_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
])
->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_atividade_coletiva_participante.paciente_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_paciente.cidadao_id')
->whereIn('atencao_basica_atividade_coletiva_participante.atividade_coletiva_id', $atividadeIds)
->whereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('cadastro_obito')
->whereColumn('cadastro_obito.cidadao_id', 'cadastro_cidadao.id');
})
->get();
$porAtividade = [];
foreach ($rows as $row) {
$porAtividade[$row->atividade_coletiva_id][] = $row;
}
return $porAtividade;
}
}

View File

@@ -0,0 +1,249 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Enums\Cadastro\SujeitoAtencao\RacaCor;
use App\Rules\AtencaoBasica\Exportacao\CidAavaliacaoIsDuplicate;
use App\Rules\AtencaoBasica\Exportacao\CidIsRequired;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\ConclusaoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\ProcedenciaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\RacaCorIsRequired;
use App\Rules\AtencaoBasica\Exportacao\SexoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TurnoIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaAvaliacaoElegibilidadeService extends Service
{
private const FICHA_LABEL = 'Avaliaçăo de elegibilidade e admissăo';
private const ERROR_MAP = [
CnesIsValid::class => 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,
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,
TurnoIsRequired::class => Inconsistencia::TURNO_SEM_PREENCHIMENTO,
SexoIsRequired::class => Inconsistencia::SEXO_DO_PACIENTE_SEM_PREENCHIMENTO,
RacaCorIsRequired::class => Inconsistencia::RACA_COR_SEM_PREENCHIMENTO,
ProcedenciaIsRequired::class => Inconsistencia::PROCEDENCIA_SEM_PREENCHIMENTO,
ConclusaoIsRequired::class => Inconsistencia::CONCLUSAO_SEM_PREENCHIMENTO,
CidIsRequired::class => Inconsistencia::CID_PRINCIPAL_SEM_PREENCHIMENTO,
CidAavaliacaoIsDuplicate::class => Inconsistencia::CID_EM_DUPLICIDADE,
];
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');
$fichasAvaliacao = DB::table('atencao_basica_avaliacao')
->select([
'atencao_basica_avaliacao.id as id',
'atencao_basica_avaliacao.usuario_id as usuario_id',
'atencao_basica_avaliacao.data 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',
'paciente_cidadao.raca_cor_id as paciente_raca_cor_id',
'paciente_cidadao.cns as paciente_cns',
'paciente_cidadao.cpf as paciente_cpf',
'paciente_cidadao.nome as paciente_nome',
'paciente_cidadao.nome_social as paciente_nome_social',
'paciente_cidadao.preferencia_nome_id as paciente_preferencia_nome_id',
'paciente_cidadao.data_nascimento as paciente_data_nascimento',
'paciente_cidadao.sexo_id as paciente_sexo_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
'atencao_basica_avaliacao.turno_id',
'atencao_basica_avaliacao.paciente_id',
'atencao_basica_avaliacao.procedencia_id',
'atencao_basica_avaliacao.conclusao_id',
'atencao_basica_avaliacao.cid_primario',
'atencao_basica_avaliacao.cid_secundario',
'atencao_basica_avaliacao.cid_terciario',
'atencao_basica_avaliacao.cns_cuidador',
'atencao_basica_avaliacao.cpf_cuidador',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_avaliacao.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_avaliacao.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_avaliacao.ocupacao_id')
->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_avaliacao.paciente_id')
->join('cadastro_cidadao as paciente_cidadao', 'paciente_cidadao.id', 'cadastro_paciente.cidadao_id')
->whereBetween('atencao_basica_avaliacao.data', [$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_avaliacao.usuario_id', request()->usuario_id))
->get();
$groupedList = [];
foreach ($fichasAvaliacao as $ficha) {
$errosHeader = RelatorioInconsistenciaService::validHeader($ficha, Permissao::AVALIACAO_ELEGIBILIDADE_ADMISSAO);
$errosAtendimento = self::validAtendimento($ficha);
$errors = $errosHeader->merge($errosAtendimento);
$errorDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
if (
$errosAtendimento->isNotEmpty()
&& ($ficha->cns_cuidador || $ficha->cpf_cuidador)
&& !Cns::isValid($ficha->paciente_cns)
&& !Cpf::isValid($ficha->paciente_cpf)
) {
$descricao = $inconsistencias[Inconsistencia::CNS_DO_CUIDADOR_INVALIDO] ?? null;
if ($descricao !== null) {
$errorDescriptions->push($descricao);
}
}
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $ficha->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' => $ficha->paciente_nome,
'paciente_nome_social' => $ficha->paciente_nome_social,
'paciente_preferencia_nome_id' => $ficha->paciente_preferencia_nome_id,
'paciente_prontuario' => $ficha->paciente_prontuario,
'paciente_cns' => $ficha->paciente_cns ? MaskUtil::getCnsMasked($ficha->paciente_cns) : '-',
'paciente_cpf' => $ficha->paciente_cpf ? MaskUtil::getCpfMasked($ficha->paciente_cpf) : '-',
'profissional' => $ficha->profissional_nome,
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
$inconsistenciaId = self::ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function validAtendimento($ficha): Collection
{
$errors = collect();
if (!Cns::isValid($ficha->paciente_cns) && !Cpf::isValid($ficha->paciente_cpf)) {
$errors->push(CnsPaciente::class);
$errors->push(Cpf::class);
}
if (!DataNascimentoIsRequired::isValid($ficha->paciente_data_nascimento)) {
$errors->push(DataNascimentoIsRequired::class);
}
if (!DataNascimentoIsValid::isValid([
'data_nascimento' => $ficha->paciente_data_nascimento,
'data_atendimento' => $ficha->data_atendimento
])) {
$errors->push(DataNascimentoIsValid::class);
}
if (!TurnoIsRequired::isValid($ficha->turno_id)) {
$errors->push(TurnoIsRequired::class);
}
if (!SexoIsRequired::isValid($ficha->paciente_sexo_id)) {
$errors->push(SexoIsRequired::class);
}
if (!RacaCorIsRequired::isValid($ficha->paciente_raca_cor_id)) {
$errors->push(RacaCorIsRequired::class);
}
if ($ficha->paciente_raca_cor_id == RacaCor::SEM_INFORMACAO) {
$errors->push(RacaCorIsRequired::class);
}
if (!ProcedenciaIsRequired::isValid($ficha->procedencia_id)) {
$errors->push(ProcedenciaIsRequired::class);
}
if (!ConclusaoIsRequired::isValid($ficha->conclusao_id)) {
$errors->push(ConclusaoIsRequired::class);
}
if (!CidIsRequired::isValid($ficha->cid_primario)) {
$errors->push(CidIsRequired::class);
}
if (!CidAavaliacaoIsDuplicate::isValid($ficha)) {
$errors->push(CidAavaliacaoIsDuplicate::class);
}
return $errors;
}
}

View File

@@ -0,0 +1,299 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\CadastroDomiciliar\ImovelTipo;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Rules\AtencaoBasica\Exportacao\BairroIsRequired;
use App\Rules\AtencaoBasica\Exportacao\CepIsRequired;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsRequired;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\LogradouroIsRequired;
use App\Rules\AtencaoBasica\Exportacao\LogradouroTipoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\MicroAreaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\ResponsavelTecnicoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\UfIsRequired;
use App\Rules\AtencaoBasica\Exportacao\ZonaIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaCadastroDomiciliarService extends Service
{
private const ERROR_MAP = [
CnesIsValid::class => Inconsistencia::CNES_DA_UNIDADE_COM_TAMANHO_DIFERENTE_DE_7_CARACTERES,
IbgeIsValid::class => Inconsistencia::IBGE_DO_MUNICIPIO_SEM_PREENCHIMENTO_OU_INVALIDO,
IbgeIsRequired::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,
BairroIsRequired::class => Inconsistencia::BAIRRO_SEM_PREENCHIMENTO,
CepIsRequired::class => Inconsistencia::CEP_SEM_PREENCHIMENTO,
LogradouroIsRequired::class => Inconsistencia::NOME_LOGRADOURO_SEM_PREENCHIMENTO,
UfIsRequired::class => Inconsistencia::UF_SEM_PREENCHIMENTO,
LogradouroTipoIsRequired::class => Inconsistencia::TIPO_DE_LOGRADOURO_SEM_PREENCHIMENTO,
MicroAreaIsRequired::class => Inconsistencia::MICROAREA_DO_PACIENTE_SEM_PREENCHIMENTO,
ZonaIsRequired::class => Inconsistencia::ZONA_SEM_PREENCHIMENTO,
ResponsavelTecnicoIsRequired::class => Inconsistencia::NOME_RESPONSAVEL_TECNICO_SEM_PREENCHIMENTO,
];
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');
$fichasCadastroDomiciliar = DB::table('atencao_basica_cadastro_domiciliar')
->select([
'atencao_basica_cadastro_domiciliar.id as id',
'atencao_basica_cadastro_domiciliar.usuario_id as usuario_id',
'atencao_basica_cadastro_domiciliar.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',
'logradouro.bairro_id as logradouro_bairro_id',
'logradouro.cep as logradouro_cep',
'logradouro.nome AS logradouro_nome',
'endereco.numero as endereco_numero',
'municipio.ibge as logradouro_municipio_ibge',
'estado.codigo as uf_codigo',
'logradouro_tipo.codigo as logradouro_tipo_codigo',
'atencao_basica_cadastro_domiciliar.domicilio_id',
'atencao_basica_cadastro_domiciliar.fora_area',
'atencao_basica_cadastro_domiciliar.microarea_id as logradouro_microarea_id',
'atencao_basica_cadastro_domiciliar.domicilio_zona_id',
'atencao_basica_cadastro_domiciliar.situacao_domicilio_id',
'atencao_basica_cadastro_domiciliar.instituicao_nome_responsavel_tecnico',
'atencao_basica_cadastro_imovel_tipo.codigo_esus as imovel_tipo_codigo_esus',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_cadastro_domiciliar.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_cadastro_domiciliar.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_cadastro_domiciliar.usuario_ocupacao_id')
->join('cadastro_domicilio', 'cadastro_domicilio.id', 'atencao_basica_cadastro_domiciliar.domicilio_id')
->join('atencao_basica_cadastro_imovel_tipo', 'atencao_basica_cadastro_imovel_tipo.id', 'atencao_basica_cadastro_domiciliar.imovel_tipo_id')
->leftJoin('cadastro_endereco as endereco', 'endereco.id', 'cadastro_domicilio.endereco_id')
->leftJoin('cadastro_logradouro as logradouro', 'logradouro.id', 'endereco.logradouro_id')
->leftJoin('cadastro_logradouro_tipo as logradouro_tipo', 'logradouro_tipo.id', 'logradouro.tipo_id')
->leftJoin('intranet.cadastro_municipio as municipio', 'municipio.id', 'logradouro.municipio_id')
->leftJoin('intranet.cadastro_estado as estado', 'estado.id', 'municipio.estado_id')
->whereBetween('atencao_basica_cadastro_domiciliar.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_cadastro_domiciliar.usuario_id', request()->usuario_id))
->get();
if ($fichasCadastroDomiciliar->isEmpty()) {
return [];
}
$residentesPorDomicilio = self::loadResidentes(
$fichasCadastroDomiciliar->pluck('domicilio_id')->filter()->unique()->all()
);
$groupedList = [];
foreach ($fichasCadastroDomiciliar as $ficha) {
$errosHeader = RelatorioInconsistenciaService::validHeader($ficha, Permissao::CADASTRO_DOMICILIAR);
$errosAtendimento = self::validAtendimento($ficha);
$errors = $errosHeader->merge($errosAtendimento);
$baseDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
foreach ($residentesPorDomicilio[$ficha->domicilio_id] ?? [] as $residente) {
$errorDescriptions = collect($baseDescriptions->all());
if (
$errosAtendimento->isNotEmpty()
&& !Cns::isValid($residente->cns)
&& !Cpf::isValid($residente->cpf)
) {
$descricao = $inconsistencias[Inconsistencia::CNS_DO_RESPONSAVEL_FAMILIAR_INVALIDO] ?? null;
if ($descricao !== null) {
$errorDescriptions->push($descricao);
}
}
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $residente->paciente_id . '|' . $ficha->usuario_id;
if (!isset($groupedList[$key])) {
$groupedList[$key] = [
'data' => (new Carbon($ficha->data_atendimento))->format('d/m/Y'),
'ficha' => 'Cadastro domiciliar',
'paciente_nome' => $residente->paciente_nome,
'paciente_nome_social' => $residente->paciente_nome_social,
'paciente_preferencia_nome_id' => $residente->paciente_preferencia_nome_id,
'paciente_prontuario' => $residente->paciente_prontuario,
'paciente_cns' => $residente->cns ? MaskUtil::getCnsMasked($residente->cns) : '-',
'paciente_cpf' => $residente->cpf ? MaskUtil::getCpfMasked($residente->cpf) : '-',
'profissional' => $ficha->profissional_nome,
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
$inconsistenciaId = self::ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function validAtendimento($cadastro): Collection
{
$errors = collect();
if (!BairroIsRequired::isValid($cadastro->logradouro_bairro_id)) {
$errors->push(BairroIsRequired::class);
}
if (!CepIsRequired::isValid($cadastro->logradouro_cep)) {
$errors->push(CepIsRequired::class);
}
if (!IbgeIsRequired::isValid($cadastro->logradouro_municipio_ibge)) {
$errors->push(IbgeIsRequired::class);
}
if (!IbgeIsValid::isValid($cadastro->logradouro_municipio_ibge)) {
$errors->push(IbgeIsValid::class);
}
if (!LogradouroIsRequired::isValid($cadastro->logradouro_nome)) {
$errors->push(LogradouroIsRequired::class);
}
if (!UfIsRequired::isValid($cadastro->uf_codigo)) {
$errors->push(UfIsRequired::class);
}
if (!LogradouroTipoIsRequired::isValid($cadastro->logradouro_tipo_codigo)) {
$errors->push(LogradouroTipoIsRequired::class);
}
if (!$cadastro->fora_area && !MicroAreaIsRequired::isValid($cadastro->logradouro_microarea_id)) {
$errors->push(MicroAreaIsRequired::class);
}
if (!ZonaIsRequired::isValid($cadastro->domicilio_zona_id)) {
$errors->push(ZonaIsRequired::class);
}
if (self::handleTipoImovelPermanencia($cadastro->imovel_tipo_codigo_esus)
&& !ResponsavelTecnicoIsRequired::isValid($cadastro->instituicao_nome_responsavel_tecnico)) {
$errors->push(ResponsavelTecnicoIsRequired::class);
}
return $errors;
}
private static function loadResidentes(array $domicilioIds): array
{
if (empty($domicilioIds)) {
return [];
}
$rows = DB::table('cadastro_cidadao')
->select([
'cadastro_cidadao.domicilio_id',
'atencao_basica_cadastro_individual.responsavel AS responsavel',
'atencao_basica_cadastro_individual.responsavel_cidadao_id AS responsavel_cidadao_id',
'cadastro_cidadao.data_nascimento',
'cadastro_cidadao.cns',
'cadastro_cidadao.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',
'atencao_basica_cadastro_individual.renda_mensal as renda_responsavel',
'cadastro_paciente.id as paciente_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
])
->join('cadastro_paciente', 'cadastro_paciente.cidadao_id', 'cadastro_cidadao.id')
->join('atencao_basica_cadastro_individual', 'atencao_basica_cadastro_individual.paciente_id', 'cadastro_paciente.id')
->whereIn('cadastro_cidadao.domicilio_id', $domicilioIds)
->where('atencao_basica_cadastro_individual.responsavel', true)
->whereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('cadastro_obito')
->whereColumn('cadastro_obito.cidadao_id', 'cadastro_cidadao.id');
})
->get();
$porDomicilio = [];
foreach ($rows as $row) {
$porDomicilio[$row->domicilio_id][] = $row;
}
return $porDomicilio;
}
private static function handleTipoImovelPermanencia($tipoImovel): bool
{
return $tipoImovel == ImovelTipo::ABRIGO
|| $tipoImovel == ImovelTipo::INSTITUICAO_LONGA_PERMANENCIA_IDOSOS
|| $tipoImovel == ImovelTipo::UNIDADE_PRISIONAL
|| $tipoImovel == ImovelTipo::UNIDADE_MEDIDA_SOCIO_EDUCATIVA
|| $tipoImovel == ImovelTipo::DELEGACIA;
}
}

View File

@@ -0,0 +1,282 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Enums\Cadastro\SujeitoAtencao\LocalNascimento;
use App\Enums\Cadastro\SujeitoAtencao\Nacionalidade;
use App\Enums\Cadastro\SujeitoAtencao\RacaCor;
use App\Rules\AtencaoBasica\Exportacao\AlimentosAcabaramIsRequired;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\ComeuALgunsAlimentosIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataEntradaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\EtniaIndigenaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\PaisNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\PaisNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\RacaCorIsRequired;
use App\Rules\AtencaoBasica\Exportacao\SexoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TelefoneIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaCadastroIndividualService extends Service
{
private const ERROR_MAP = [
CnesIsValid::class => 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,
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,
SexoIsRequired::class => Inconsistencia::SEXO_DO_PACIENTE_SEM_PREENCHIMENTO,
RacaCorIsRequired::class => Inconsistencia::RACA_COR_SEM_PREENCHIMENTO,
TelefoneIsRequired::class => Inconsistencia::TELEFONE_CELULAR_INVALIDO,
PaisNascimentoIsRequired::class => Inconsistencia::PAIS_NASCIMENTO_SEM_PREENCHIMENTO,
DataEntradaIsRequired::class => Inconsistencia::DATA_ENTRADA_BRASIL_SEM_PREENCHIMENTO,
PaisNascimentoIsValid::class => Inconsistencia::PAIS_NASCIMENTO_INVALIDO,
EtniaIndigenaIsRequired::class => Inconsistencia::ETNIA_SEM_PREENCHIMENTO_QUANDO_RACA_DO_PACIENTE_E_INDIGENA,
AlimentosAcabaramIsRequired::class => Inconsistencia::ALIMENTOS_ACABARAM_SEM_PREENCHIMENTO,
ComeuALgunsAlimentosIsRequired::class => Inconsistencia::COMEU_APENAS_ALGUNS_ALIMENTOS_SEM_PREENCHIMENTO,
];
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');
$fichasCadastroIndividual = DB::table('atencao_basica_cadastro_individual')
->select([
'atencao_basica_cadastro_individual.id as id',
'atencao_basica_cadastro_individual.usuario_id as usuario_id',
'atencao_basica_cadastro_individual.alimentos_acabaram',
'atencao_basica_cadastro_individual.comeu_alguns_alimentos',
'atencao_basica_cadastro_individual.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',
'paciente_cidadao.cns as paciente_cns',
'paciente_cidadao.cpf as paciente_cpf',
'paciente_cidadao.nome as paciente_nome',
'paciente_cidadao.nome_social as paciente_nome_social',
'paciente_cidadao.preferencia_nome_id as paciente_preferencia_nome_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
'responsavel_cidadao.cns as responsavel_cns',
'responsavel_cidadao.cpf as responsavel_cpf',
'paciente_cidadao.data_nascimento as paciente_data_nascimento',
'paciente_cidadao.sexo_id as paciente_sexo_id',
'paciente_cidadao.raca_cor_id as paciente_raca_cor_id',
'atencao_basica_cadastro_individual.paciente_id',
'atencao_basica_cadastro_individual.responsavel_cidadao_id',
'paciente_cidadao.nacionalidade_id',
'paciente_cidadao.pais_nascimento_id',
'paciente_cidadao.etnia_indigena_id',
'paciente_cidadao.local_nascimento_id',
'entrada_brasil.data_entrada',
DB::raw("
(
SELECT numero
FROM cadastro_cidadao_telefone
WHERE cadastro_cidadao_telefone.cidadao_id = paciente_cidadao.id
AND cadastro_cidadao_telefone.principal = TRUE
LIMIT 1
) AS paciente_telefone
"),
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_cadastro_individual.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_cadastro_individual.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_cadastro_individual.usuario_ocupacao_id')
->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_cadastro_individual.paciente_id')
->join('cadastro_cidadao as paciente_cidadao', 'paciente_cidadao.id', 'cadastro_paciente.cidadao_id')
->leftJoin('cadastro_cidadao as responsavel_cidadao', 'responsavel_cidadao.id', 'atencao_basica_cadastro_individual.responsavel_cidadao_id')
->leftJoin('cadastro_estrangeiro_entrada_brasil as entrada_brasil', 'entrada_brasil.cidadao_id', 'paciente_cidadao.id')
->whereBetween('atencao_basica_cadastro_individual.data_usuario_responsavel', [$start, $end])
->whereNull('cadastro_paciente.deleted_at')
->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_cadastro_individual.usuario_id', request()->usuario_id))
->get();
$groupedList = [];
foreach ($fichasCadastroIndividual as $ficha) {
$errosHeader = RelatorioInconsistenciaService::validHeader($ficha, Permissao::CADASTRO_INDIVIDUAL);
$errosAtendimento = self::validAtendimento($ficha);
$errors = $errosHeader->merge($errosAtendimento);
$errorDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
if (
$errosAtendimento->isNotEmpty()
&& $ficha->responsavel_cidadao_id
&& !Cns::isValid($ficha->responsavel_cns)
&& !Cpf::isValid($ficha->responsavel_cpf)
) {
$descricao = $inconsistencias[Inconsistencia::CNS_DO_RESPONSAVEL_FAMILIAR_INVALIDO] ?? null;
if ($descricao !== null) {
$errorDescriptions->push($descricao);
}
}
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $ficha->paciente_id . '|' . $ficha->usuario_id;
if (!isset($groupedList[$key])) {
$groupedList[$key] = [
'data' => (new Carbon($ficha->data_atendimento))->format('d/m/Y'),
'ficha' => 'Cadastro individual',
'paciente_nome' => $ficha->paciente_nome,
'paciente_nome_social' => $ficha->paciente_nome_social,
'paciente_preferencia_nome_id' => $ficha->paciente_preferencia_nome_id,
'paciente_prontuario' => $ficha->paciente_prontuario,
'paciente_cns' => $ficha->paciente_cns ? MaskUtil::getCnsMasked($ficha->paciente_cns) : '-',
'paciente_cpf' => $ficha->paciente_cpf ? MaskUtil::getCpfMasked($ficha->paciente_cpf) : '-',
'profissional' => $ficha->profissional_nome,
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
$inconsistenciaId = self::ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function validAtendimento($cadastro): Collection
{
$errors = collect();
if (!Cns::isValid($cadastro->paciente_cns) && !Cpf::isValid($cadastro->paciente_cpf)) {
$errors->push(CnsPaciente::class);
$errors->push(Cpf::class);
}
if (!DataNascimentoIsRequired::isValid($cadastro->paciente_data_nascimento)) {
$errors->push(DataNascimentoIsRequired::class);
}
if (!DataNascimentoIsValid::isValid([
'data_nascimento' => $cadastro->paciente_data_nascimento,
'data_atendimento' => $cadastro->data_atendimento
])) {
$errors->push(DataNascimentoIsValid::class);
}
if (!SexoIsRequired::isValid($cadastro->paciente_sexo_id)) {
$errors->push(SexoIsRequired::class);
}
if (!RacaCorIsRequired::isValid($cadastro->paciente_raca_cor_id)) {
$errors->push(RacaCorIsRequired::class);
}
if ($cadastro->paciente_raca_cor_id == RacaCor::SEM_INFORMACAO) {
$errors->push(RacaCorIsRequired::class);
}
if (($cadastro->nacionalidade_id == Nacionalidade::BRASILEIRO || $cadastro->nacionalidade_id == Nacionalidade::NATURALIZADO_BRASILEIRO)
&& !TelefoneIsRequired::isValid($cadastro->paciente_telefone)) {
$errors->push(TelefoneIsRequired::class);
}
if ($cadastro->nacionalidade_id == Nacionalidade::ESTRANGEIRO && !PaisNascimentoIsRequired::isValid($cadastro->pais_nascimento_id)) {
$errors->push(PaisNascimentoIsRequired::class);
}
if ($cadastro->nacionalidade_id == LocalNascimento::EM_OUTRO_PAIS && !DataEntradaIsRequired::isValid($cadastro->data_entrada) && $cadastro->nacionalidade_id == Nacionalidade::ESTRANGEIRO) {
$errors->push(DataEntradaIsRequired::class);
}
if ($cadastro->nacionalidade_id == Nacionalidade::BRASILEIRO && !PaisNascimentoIsValid::isValid($cadastro->pais_nascimento_id)) {
$errors->push(PaisNascimentoIsValid::class);
}
if ($cadastro->paciente_raca_cor_id == RacaCor::INDIGENA && !EtniaIndigenaIsRequired::isValid($cadastro->etnia_indigena_id)) {
$errors->push(EtniaIndigenaIsRequired::class);
}
if (
(!is_null($cadastro->comeu_alguns_alimentos) && ($cadastro->comeu_alguns_alimentos == true || $cadastro->comeu_alguns_alimentos == false))
&& $cadastro->alimentos_acabaram === null
) {
$errors->push(AlimentosAcabaramIsRequired::class);
}
if (
(!is_null($cadastro->alimentos_acabaram) && ($cadastro->alimentos_acabaram == true || $cadastro->alimentos_acabaram == false))
&& $cadastro->comeu_alguns_alimentos === null
) {
$errors->push(ComeuALgunsAlimentosIsRequired::class);
}
return $errors;
}
}

View File

@@ -0,0 +1,232 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\ExameIsRequired;
use App\Rules\AtencaoBasica\Exportacao\ExameIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\TurnoIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaComplementarService extends Service
{
private const ERROR_MAP = [
CnesIsValid::class => 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,
CnsPaciente::class => Inconsistencia::CNS_DO_PACIENTE_INVALIDO,
Cpf::class => Inconsistencia::CPF_DO_PACIENTE_INVALIDO,
TurnoIsRequired::class => Inconsistencia::TURNO_SEM_PREENCHIMENTO,
ExameIsRequired::class => Inconsistencia::NENHUM_EXAME_PREENCHIDO,
ExameIsValid::class => Inconsistencia::EXAME_COM_DATA_PREENCHIDA_E_SEM_RESULTADO,
];
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');
$fichasComplementar = DB::table('atencao_basica_ficha_complementar')
->select([
'atencao_basica_ficha_complementar.id as id',
'atencao_basica_ficha_complementar.usuario_id as usuario_id',
'atencao_basica_ficha_complementar.data_atendimento 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',
'paciente_cidadao.cns as paciente_cns',
'paciente_cidadao.cpf as paciente_cpf',
'paciente_cidadao.nome as paciente_nome',
'paciente_cidadao.nome_social as paciente_nome_social',
'paciente_cidadao.preferencia_nome_id as paciente_preferencia_nome_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
'atencao_basica_ficha_complementar.turno_id',
'atencao_basica_ficha_complementar.paciente_id',
'atencao_basica_ficha_complementar.data_realizacao_teste_olhinho',
'atencao_basica_ficha_complementar.resultado_teste_olhinho_id',
'atencao_basica_ficha_complementar.data_realizacao_fundo_olho',
'atencao_basica_ficha_complementar.resultado_fundo_olho_id',
'atencao_basica_ficha_complementar.data_realizacao_teste_orelhinha',
'atencao_basica_ficha_complementar.resultado_teste_orelhinha_id',
'atencao_basica_ficha_complementar.data_realizacao_us_transfontanela',
'atencao_basica_ficha_complementar.resultado_us_transfontanela_id',
'atencao_basica_ficha_complementar.data_realizacao_tomografia_computadorizada',
'atencao_basica_ficha_complementar.resultado_tomografia_computadorizada_id',
'atencao_basica_ficha_complementar.data_realizacao_ressonancia_magnetica',
'atencao_basica_ficha_complementar.resultado_ressonancia_magnetica_id',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_ficha_complementar.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_ficha_complementar.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_ficha_complementar.cbo_id')
->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_ficha_complementar.paciente_id')
->join('cadastro_cidadao as paciente_cidadao', 'paciente_cidadao.id', 'cadastro_paciente.cidadao_id')
->whereBetween('atencao_basica_ficha_complementar.data_atendimento', [$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_ficha_complementar.usuario_id', request()->usuario_id))
->get();
if ($fichasComplementar->isEmpty()) {
return [];
}
$responsaveisPorPaciente = self::loadResponsaveis(
$fichasComplementar->pluck('paciente_id')->unique()->all()
);
$groupedList = [];
foreach ($fichasComplementar as $ficha) {
$errosHeader = RelatorioInconsistenciaService::validHeader($ficha, Permissao::FICHA_COMPLEMENTAR);
$errosAtendimento = self::validAtendimento($ficha);
$errors = $errosHeader->merge($errosAtendimento);
$errorDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
if ($errosAtendimento->isNotEmpty()) {
$responsavel = $responsaveisPorPaciente[$ficha->paciente_id] ?? null;
if ($responsavel && !Cns::isValid($responsavel->cns) && !Cpf::isValid($responsavel->cpf)) {
$descricao = $inconsistencias[Inconsistencia::CNS_DO_RESPONSAVEL_FAMILIAR_INVALIDO] ?? null;
if ($descricao !== null) {
$errorDescriptions->push($descricao);
}
}
}
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $ficha->paciente_id . '|' . $ficha->usuario_id;
if (!isset($groupedList[$key])) {
$groupedList[$key] = [
'data' => (new Carbon($ficha->data_atendimento))->format('d/m/Y'),
'ficha' => 'Ficha complementar',
'paciente_nome' => $ficha->paciente_nome,
'paciente_nome_social' => $ficha->paciente_nome_social,
'paciente_preferencia_nome_id' => $ficha->paciente_preferencia_nome_id,
'paciente_prontuario' => $ficha->paciente_prontuario,
'paciente_cns' => $ficha->paciente_cns ? MaskUtil::getCnsMasked($ficha->paciente_cns) : '-',
'paciente_cpf' => $ficha->paciente_cpf ? MaskUtil::getCpfMasked($ficha->paciente_cpf) : '-',
'profissional' => $ficha->profissional_nome,
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
$inconsistenciaId = self::ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function validAtendimento($atendimento): Collection
{
$errors = collect();
if (!Cns::isValid($atendimento->paciente_cns) && !Cpf::isValid($atendimento->paciente_cpf)) {
$errors->push(CnsPaciente::class);
$errors->push(Cpf::class);
}
if (!TurnoIsRequired::isValid($atendimento->turno_id)) {
$errors->push(TurnoIsRequired::class);
}
if (!ExameIsRequired::isValid($atendimento)) {
$errors->push(ExameIsRequired::class);
}
if (!ExameIsValid::isValid($atendimento)) {
$errors->push(ExameIsValid::class);
}
return $errors;
}
private static function loadResponsaveis(array $pacienteIds): array
{
if (empty($pacienteIds)) {
return [];
}
return DB::table('atencao_basica_cadastro_individual')
->whereIn('paciente_id', $pacienteIds)
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'atencao_basica_cadastro_individual.responsavel_cidadao_id')
->select(
'atencao_basica_cadastro_individual.paciente_id',
'cadastro_cidadao.cns',
'cadastro_cidadao.cpf'
)
->get()
->keyBy('paciente_id')
->all();
}
}

View File

@@ -0,0 +1,278 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\MarcadorConsumoAlimentar\GrupoPerguntas;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaConsumoAlimentarService extends Service
{
private const FICHA_LABEL = 'Ficha de marcadores de consumo alimentar';
private const ERROR_MAP = [
CnesIsValid::class => 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,
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,
];
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');
$fichasPorGrupo = [
GrupoPerguntas::CRIANCAS_MENORES_6_MESES => self::queryFichas($start, $end)
->whereRaw('TIMESTAMPDIFF(MONTH, cadastro_cidadao.data_nascimento, atencao_basica_consumo_alimentar.data_atendimento) < 6')
->get(),
GrupoPerguntas::CRIANCAS_DE_6_A_23_MESES => self::queryFichas($start, $end)
->whereRaw('TIMESTAMPDIFF(MONTH, cadastro_cidadao.data_nascimento, atencao_basica_consumo_alimentar.data_atendimento) >= 6')
->whereRaw('TIMESTAMPDIFF(MONTH, cadastro_cidadao.data_nascimento, atencao_basica_consumo_alimentar.data_atendimento) <= 23')
->get(),
GrupoPerguntas::CRIANCAS_ACIMA_DE_2_ANOS => self::queryFichas($start, $end)
->whereRaw('TIMESTAMPDIFF(YEAR, paciente_cidadao.data_nascimento, atencao_basica_consumo_alimentar.data_atendimento) >= 2')
->get(),
];
$allFichaIds = collect($fichasPorGrupo)
->flatMap(fn ($collection) => $collection->pluck('id'))
->unique()
->all();
if (empty($allFichaIds)) {
return [];
}
$perguntasFaltandoPorFicha = self::computeMissingResponses($allFichaIds);
$groupedList = [];
foreach ($fichasPorGrupo as $codigoGrupo => $fichas) {
foreach ($fichas as $ficha) {
$errors = RelatorioInconsistenciaService::validHeader($ficha, Permissao::MARCADORES_DE_CONSUMO_ALIMENTAR)
->merge(self::validConsumo($ficha));
$errorDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
$faltaResposta = in_array(
$codigoGrupo,
$perguntasFaltandoPorFicha[$ficha->id] ?? [],
true
);
if ($faltaResposta) {
$descricao = $inconsistencias[Inconsistencia::PERGUNTA_SEM_RESPOSTA] ?? null;
if ($descricao !== null) {
$errorDescriptions->push($descricao);
}
}
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $ficha->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' => $ficha->paciente_nome,
'paciente_nome_social' => $ficha->paciente_nome_social,
'paciente_preferencia_nome_id' => $ficha->paciente_preferencia_nome_id,
'paciente_prontuario' => $ficha->paciente_prontuario,
'profissional' => $ficha->profissional_nome,
'paciente_cns' => $ficha->paciente_cns ? MaskUtil::getCnsMasked($ficha->paciente_cns) : '-',
'paciente_cpf' => $ficha->paciente_cpf ? MaskUtil::getCpfMasked($ficha->paciente_cpf) : '-',
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function queryFichas(string $start, string $end)
{
return DB::table('atencao_basica_consumo_alimentar')
->select([
'atencao_basica_consumo_alimentar.id as id',
'atencao_basica_consumo_alimentar.usuario_id as usuario_id',
'atencao_basica_consumo_alimentar.data_atendimento as data_atendimento',
'cadastro_unidade.cnes as unidade_cnes',
'cadastro_municipio.ibge as municipio_ibge',
'cadastro_cidadao.cns AS usuario_cns',
'cadastro_cidadao.nome AS profissional_nome',
'paciente_cidadao.cns as paciente_cns',
'paciente_cidadao.cpf as paciente_cpf',
'paciente_cidadao.nome as paciente_nome',
'paciente_cidadao.nome_social as paciente_nome_social',
'paciente_cidadao.preferencia_nome_id as paciente_preferencia_nome_id',
'paciente_cidadao.data_nascimento as paciente_data_nascimento',
'paciente_cidadao.sexo_id as paciente_sexo_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
'sigtap.ocupacao.codigo as ocupacao_codigo',
'atencao_basica_consumo_alimentar.paciente_id as paciente_id',
'atencao_basica_consumo_alimentar.local_atendimento_id as local_atendimento_id',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_consumo_alimentar.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_consumo_alimentar.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_consumo_alimentar.cbo_id')
->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_consumo_alimentar.paciente_id')
->join('cadastro_cidadao as paciente_cidadao', 'paciente_cidadao.id', 'cadastro_paciente.cidadao_id')
->whereBetween('atencao_basica_consumo_alimentar.data_atendimento', [$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_consumo_alimentar.usuario_id', request()->usuario_id));
}
private static function computeMissingResponses(array $fichaIds): array
{
if (empty($fichaIds)) {
return [];
}
$perguntasPorGrupo = DB::table('atencao_basica_marcador_consumo_alimentar_perguntas')
->select('id', 'codigo_grupo')
->get()
->groupBy('codigo_grupo')
->map(fn ($items) => $items->pluck('id')->all())
->all();
$respostasPorFicha = DB::table('atencao_basica_marcador_consumo_alimentar_pergunta_resposta')
->whereIn('marcador_consumo_alimentar_id', $fichaIds)
->select('marcador_consumo_alimentar_id', 'pergunta_id')
->get()
->groupBy('marcador_consumo_alimentar_id')
->map(fn ($items) => $items->pluck('pergunta_id')->unique()->flip()->all())
->all();
$faltandoPorFicha = [];
foreach ($fichaIds as $fichaId) {
$respondidas = $respostasPorFicha[$fichaId] ?? [];
foreach ($perguntasPorGrupo as $codigoGrupo => $perguntaIds) {
foreach ($perguntaIds as $perguntaId) {
if (!isset($respondidas[$perguntaId])) {
$faltandoPorFicha[$fichaId][] = $codigoGrupo;
break;
}
}
}
}
return $faltandoPorFicha;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
$inconsistenciaId = self::ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function validConsumo($consumo): Collection
{
$errors = collect();
if (!Cns::isValid($consumo->paciente_cns) && !Cpf::isValid($consumo->paciente_cpf)) {
$errors->push(CnsPaciente::class);
$errors->push(Cpf::class);
}
if (!DataNascimentoIsRequired::isValid($consumo->paciente_data_nascimento)) {
$errors->push(DataNascimentoIsRequired::class);
}
if (!DataNascimentoIsValid::isValid([
'data_nascimento' => $consumo->paciente_data_nascimento,
'data_atendimento' => $consumo->data_atendimento
])) {
$errors->push(DataNascimentoIsValid::class);
}
if (!LocalAtendimentoIsRequired::isValid($consumo->local_atendimento_id)) {
$errors->push(LocalAtendimentoIsRequired::class);
}
if (!LocalAtendimentoIsValid::isValid($consumo->local_atendimento_id)) {
$errors->push(LocalAtendimentoIsValid::class);
}
return $errors;
}
}

View File

@@ -0,0 +1,557 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Enums\Sigtap\Procedimento;
use App\Rules\AtencaoBasica\Exportacao\AlturaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\CircunferenciaAbdominalLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\FrequenciaCardiacaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\FrequenciaRespiratoriaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\GlicemiaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\MomentoColetaGlicemiaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\PerimetroCefalicoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PerimetroPanturrilhaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PesoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PressaoArterialDiastolicaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\PressaoArterialSistolicaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\ProcedimentoIsDuplicate;
use App\Rules\AtencaoBasica\Exportacao\ProcedimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\ProcedimentoIsValidForFicha;
use App\Rules\AtencaoBasica\Exportacao\ProcedimentoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\SaturacaoO2LimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\TemperaturaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\TurnoIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaProcedimentoService extends Service
{
private const FICHA_LABEL = 'Procedimento';
private const HEADER_ERROR_MAP = [
CnesIsValid::class => 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('<br />');
$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;
}
}

View File

@@ -0,0 +1,460 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Exportacao\Imunobiologico;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Exportacao\VacinaAplicadaExterior;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Rules\AtencaoBasica\Exportacao\AplicadoExteriorIsRequired;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\ComunicanteHanseniaseIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataRegistroAnteriorIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataRegistroAnteriorIsValid;
use App\Rules\AtencaoBasica\Exportacao\DoseIsValid;
use App\Rules\AtencaoBasica\Exportacao\EstrategiaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\EstrategiaIsValid;
use App\Rules\AtencaoBasica\Exportacao\EstrategiaIsValidForFicha;
use App\Rules\AtencaoBasica\Exportacao\FabricanteIsRequired;
use App\Rules\AtencaoBasica\Exportacao\FabricanteIsValid;
use App\Rules\AtencaoBasica\Exportacao\GestanteIsValidForFicha;
use App\Rules\AtencaoBasica\Exportacao\GrupoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\GrupoIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\LocalAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\LoteIsRequired;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\PuerperaAndGestanteIsChecked;
use App\Rules\AtencaoBasica\Exportacao\PuerperaIsValidForFicha;
use App\Rules\AtencaoBasica\Exportacao\VacinaIsDuplicate;
use App\Rules\AtencaoBasica\Exportacao\VacinaIsValid;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaVacinacaoService extends Service
{
private const FICHA_LABEL = 'Ficha de vacinação';
private const HEADER_ERROR_MAP = [
CnesIsValid::class => 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,
GestanteIsValidForFicha::class => Inconsistencia::GESTANTE_PREENCHIDO_PARA_PACIENTES_DO_SEXO_MASCULINO,
PuerperaIsValidForFicha::class => Inconsistencia::PUERPERA_PREENCHIDO_PARA_PACIENTES_DO_SEXO_MASCULINO,
PuerperaAndGestanteIsChecked::class => Inconsistencia::GESTANTE_E_PUERPERA_AMBOS_PREENCHIDOS,
ComunicanteHanseniaseIsRequired::class => Inconsistencia::COMUNICANTE_HANSENIASE_SEM_PREENCHIMENTO,
];
private const VACINA_ERROR_MAP = [
EstrategiaIsValid::class => Inconsistencia::ESTRATEGIA_INVALIDA,
EstrategiaIsRequired::class => Inconsistencia::ESTRATEGIA_SEM_PREENCHIMENTO,
DoseIsValid::class => Inconsistencia::DOSE_INVALIDA,
LoteIsRequired::class => Inconsistencia::LOTE_EM_PREENCHIMENTO,
DataRegistroAnteriorIsRequired::class => Inconsistencia::DATA_REGISTRO_ANTERIOR_SEM_PREENCHIMENTO,
FabricanteIsRequired::class => Inconsistencia::FABRICANTE_SEM_PREENCHIMENTO,
AplicadoExteriorIsRequired::class => Inconsistencia::APLICADO_EXTERIOR_SEM_PREENCHIMENTO,
GrupoIsRequired::class => Inconsistencia::GRUPO_SEM_PREENCHIMENTO,
GrupoIsValid::class => Inconsistencia::GRUPO_INVALIDO,
VacinaIsDuplicate::class => Inconsistencia::VACINA_DUPLICADA,
VacinaIsValid::class => Inconsistencia::VACINA_INVALIDA,
];
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');
$fichasVacinacao = DB::table('atencao_basica_ficha_vacinacao')
->select([
'atencao_basica_ficha_vacinacao.id as id',
'atencao_basica_ficha_vacinacao.usuario_id as usuario_id',
'atencao_basica_ficha_vacinacao.data_atendimento as data_atendimento',
'cadastro_unidade.cnes as unidade_cnes',
'cadastro_municipio.ibge as municipio_ibge',
'cadastro_cidadao.cns AS usuario_cns',
'cadastro_cidadao.nome AS profissional_nome',
'sigtap.ocupacao.codigo as ocupacao_codigo',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_ficha_vacinacao.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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_ficha_vacinacao.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_ficha_vacinacao.ocupacao_id')
->whereBetween('atencao_basica_ficha_vacinacao.data_atendimento', [$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_ficha_vacinacao.usuario_id', request()->usuario_id))
->get();
if ($fichasVacinacao->isEmpty()) {
return [];
}
$vacinacoesPorFicha = self::loadVacinacoes($fichasVacinacao->pluck('id')->all());
$todosVacinacaoIds = [];
foreach ($vacinacoesPorFicha as $vacinacoes) {
foreach ($vacinacoes as $vacinacao) {
$todosVacinacaoIds[] = $vacinacao->id;
}
}
$itensPorVacinacao = self::loadVacinacaoItens($todosVacinacaoIds);
$groupedList = [];
foreach ($fichasVacinacao as $ficha) {
$errosHeader = RelatorioInconsistenciaService::validHeader($ficha, Permissao::FICHA_DE_VACINACAO);
$headerDescriptions = self::mapErrors($errosHeader, self::HEADER_ERROR_MAP, $inconsistencias);
foreach ($vacinacoesPorFicha[$ficha->id] ?? [] as $vacinacao) {
$errosVacinacao = self::validVacinacao($vacinacao, $ficha->data_atendimento);
$pacienteDescriptions = self::mapErrors($errosVacinacao, self::PACIENTE_ERROR_MAP, $inconsistencias);
$vacinaDescriptions = collect();
foreach ($itensPorVacinacao[$vacinacao->id] ?? [] as $item) {
$errosVacina = self::validVacina($item, $vacinacao->paciente_data_nascimento, $vacinacao->hanseniase);
$vacinaDescriptions = $vacinaDescriptions->merge(
self::mapVacinaErrors($errosVacina, $item, $inconsistencias)
);
}
$errorDescriptions = collect($headerDescriptions->all())
->merge($pacienteDescriptions)
->merge($vacinaDescriptions);
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $vacinacao->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' => $vacinacao->paciente_nome,
'paciente_cns' => $vacinacao->paciente_cns ? MaskUtil::getCnsMasked($vacinacao->paciente_cns) : '-',
'paciente_cpf' => $vacinacao->paciente_cpf ? MaskUtil::getCpfMasked($vacinacao->paciente_cpf) : '-',
'paciente_nome_social' => $vacinacao->paciente_nome_social,
'paciente_preferencia_nome_id' => $vacinacao->paciente_preferencia_nome_id,
'paciente_prontuario' => $vacinacao->paciente_prontuario,
'profissional' => $ficha->profissional_nome,
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$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 mapVacinaErrors(Collection $errors, $item, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
if ($error === EstrategiaIsValidForFicha::class) {
$descricao = $inconsistencias[Inconsistencia::ESTRATEGIA_INCORRETA] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao . ' - ' . $item->vacina_descricao);
}
continue;
}
if ($error === FabricanteIsValid::class) {
continue;
}
$inconsistenciaId = self::VACINA_ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function handleVacinaAplicadaExterior($item): bool
{
return $item->registro_anterior && in_array($item->vacina_codigo, VacinaAplicadaExterior::getConstants());
}
private static function validVacinacao($vacinacao, $data_atendimento): Collection
{
$errors = collect();
if (!Cns::isValid($vacinacao->paciente_cns) && !Cpf::isValid($vacinacao->paciente_cpf)) {
$errors->push(CnsPaciente::class);
$errors->push(Cpf::class);
}
if (!DataNascimentoIsRequired::isValid($vacinacao->paciente_data_nascimento)) {
$errors->push(DataNascimentoIsRequired::class);
}
if (!DataNascimentoIsValid::isValid([
'data_nascimento' => $vacinacao->paciente_data_nascimento,
'data_atendimento' => $data_atendimento
])) {
$errors->push(DataNascimentoIsValid::class);
}
if (!LocalAtendimentoIsRequired::isValid($vacinacao->local_atendimento_id)) {
$errors->push(LocalAtendimentoIsRequired::class);
}
if (!LocalAtendimentoIsValid::isValid($vacinacao->local_atendimento_id)) {
$errors->push(LocalAtendimentoIsValid::class);
}
if (!GestanteIsValidForFicha::isValid($vacinacao)) {
$errors->push(GestanteIsValidForFicha::class);
}
if (!PuerperaIsValidForFicha::isValid($vacinacao)) {
$errors->push(PuerperaIsValidForFicha::class);
}
if (PuerperaAndGestanteIsChecked::isValid($vacinacao)) {
$errors->push(PuerperaAndGestanteIsChecked::class);
}
return $errors;
}
private static function validVacina($item, $pacienteDataNascimento, $hanseniase): Collection
{
$errors = collect();
if (!$item->registro_anterior && !EstrategiaIsValid::isValid($item->estrategia_codigo)) {
$errors->push(EstrategiaIsValid::class);
}
if ($item->registro_anterior) {
if (!EstrategiaIsValidForFicha::isValid($item)) {
$errors->push(EstrategiaIsValidForFicha::class);
}
}
if (!DoseIsValid::isValid($item->dose_id)) {
$errors->push(DoseIsValid::class);
}
if (!$item->registro_anterior && !LoteIsRequired::isValid($item->entrada_lote)) {
$errors->push(LoteIsRequired::class);
}
if ($item->registro_anterior && !DataRegistroAnteriorIsRequired::isValid($item->data)) {
$errors->push(DataRegistroAnteriorIsRequired::class);
}
if (!$item->registro_anterior && !FabricanteIsRequired::isValid($item->produtor_id)) {
$errors->push(FabricanteIsRequired::class);
}
if (!DataRegistroAnteriorIsValid::isValid([
'registro_anterior' => $item->registro_anterior,
'data' => $item->data,
'data_nascimento' => $pacienteDataNascimento
])) {
$errors->push(DataRegistroAnteriorIsValid::class);
}
if ($item->produtor_nome && $item->vacina_codigo && !FabricanteIsValid::isValid(['fabricante' => $item->produtor_nome, 'vacina' => $item->vacina_codigo])) {
$errors->push(FabricanteIsValid::class);
}
if (self::handleVacinaAplicadaExterior($item) && !AplicadoExteriorIsRequired::isValid($item->aplicado_exterior)) {
$errors->push(AplicadoExteriorIsRequired::class);
}
if (!$item->registro_anterior && !GrupoIsRequired::isValid($item->grupo_id)) {
$errors->push(GrupoIsRequired::class);
}
if ($item->registro_anterior && !GrupoIsValid::isValid($item->grupo_id)) {
$errors->push(GrupoIsValid::class);
}
if (VacinaIsDuplicate::isValid($item)) {
$errors->push(VacinaIsDuplicate::class);
}
if (!$item->registro_anterior && !VacinaIsValid::isValid($item)) {
$errors->push(VacinaIsValid::class);
}
if ($item->vacina_codigo == Imunobiologico::BCG
&& !ComunicanteHanseniaseIsRequired::isValid($hanseniase)) {
$errors->push(ComunicanteHanseniaseIsRequired::class);
}
return $errors;
}
private static function loadVacinacoes(array $fichaIds): array
{
if (empty($fichaIds)) {
return [];
}
$rows = DB::table('atencao_basica_vacinacao_paciente as vacinacao')
->select(
'vacinacao.id as id',
'vacinacao.vacinacao_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'),
'vacinacao.paciente_id as paciente_id',
'vacinacao.local_atendimento_id as local_atendimento_id',
'vacinacao.gestante as gestante',
'vacinacao.puerpera as puerpera',
'vacinacao.hanseniase as hanseniase',
)
->join('cadastro_paciente', 'cadastro_paciente.id', 'vacinacao.paciente_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_paciente.cidadao_id')
->whereIn('vacinacao.vacinacao_id', $fichaIds)
->get();
$porFicha = [];
foreach ($rows as $row) {
$porFicha[$row->vacinacao_id][] = $row;
}
return $porFicha;
}
private static function loadVacinacaoItens(array $vacinacaoIds): array
{
if (empty($vacinacaoIds)) {
return [];
}
$rows = DB::table('atencao_basica_vacinacao_paciente_item as item')
->select(
'item.estrategia_id as estrategia_id',
'imunizacao_estrategia.codigo_rnds as estrategia_codigo',
'item.vacina_id as vacina_id',
'cadastro_vacina.nome as vacina_descricao',
'item.dose_id as dose_id',
'imunizacao_dose.codigo_esus as dose_codigo',
'item.registro_anterior as registro_anterior',
'item.produtor_id as produtor_id',
'item.grupo_id as grupo_id',
'item.aplicado_exterior as aplicado_exterior',
'item.data as data',
'item.vacinacao_paciente_id as vacinacao_paciente_id',
'cadastro_vacina.codigo as vacina_codigo',
'imunizacao_produtor.nome as produtor_nome',
'imunizacao_entrada_vacina.lote as entrada_lote',
)
->join('cadastro_vacina', 'cadastro_vacina.id', 'item.vacina_id')
->leftJoin('imunizacao_produtor', 'imunizacao_produtor.id', 'item.produtor_id')
->leftJoin('imunizacao_entrada_vacina', 'imunizacao_entrada_vacina.id', 'item.entrada_id')
->leftJoin('imunizacao_estrategia', 'imunizacao_estrategia.id', 'item.estrategia_id')
->leftJoin('imunizacao_dose', 'imunizacao_dose.id', 'item.dose_id')
->whereIn('item.vacinacao_paciente_id', $vacinacaoIds)
->get();
$porVacinacao = [];
foreach ($rows as $row) {
$porVacinacao[$row->vacinacao_paciente_id][] = $row;
}
return $porVacinacao;
}
}

View File

@@ -0,0 +1,274 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\CadastroDomiciliar\ImovelTipo;
use App\Enums\AtencaoBasica\Exportacao\Inconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Rules\AtencaoBasica\Exportacao\AlturaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\CnsPaciente;
use App\Rules\AtencaoBasica\Exportacao\DataAtendimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\DataNascimentoIsValid;
use App\Rules\AtencaoBasica\Exportacao\DesfechoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\GlicemiaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\GlicemiaLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\LatitudeIsRequired;
use App\Rules\AtencaoBasica\Exportacao\LongitudeIsRequired;
use App\Rules\AtencaoBasica\Exportacao\MomentoColetaGlicemiaIsRequired;
use App\Rules\AtencaoBasica\Exportacao\MotivoVisitaIsValid;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\AtencaoBasica\Exportacao\PesoLimitIsValid;
use App\Rules\AtencaoBasica\Exportacao\SexoIsRequired;
use App\Rules\AtencaoBasica\Exportacao\TipoImovelsRequired;
use App\Rules\AtencaoBasica\Exportacao\TurnoIsRequired;
use App\Rules\Cns;
use App\Rules\Cpf;
use App\Services\Service;
use App\Utils\MaskUtil;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class FichaVisitaDomiciliarService extends Service
{
private const ERROR_MAP = [
CnesIsValid::class => 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,
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,
MotivoVisitaIsValid::class => Inconsistencia::MOTIVO_INVALIDO,
TipoImovelsRequired::class => Inconsistencia::TIPO_IMOVEL_SEM_PREENCHIMENTO,
DesfechoIsRequired::class => Inconsistencia::DESFECHO_SEM_PREENCHIMENTO,
LatitudeIsRequired::class => Inconsistencia::LATITUDE_SEM_PREENCHIMENTO,
LongitudeIsRequired::class => Inconsistencia::LONGITUDE_SEM_PREENCHIMENTO,
TurnoIsRequired::class => Inconsistencia::TURNO_SEM_PREENCHIMENTO,
SexoIsRequired::class => Inconsistencia::SEXO_DO_PACIENTE_SEM_PREENCHIMENTO,
GlicemiaLimitIsValid::class => Inconsistencia::GLICEMIA_DO_PACIENTE_MENOR_QUE_0_OU_MAIOR_QUE_800_KG,
GlicemiaIsRequired::class => Inconsistencia::GLICEMIA_SEM_PREENCHIMENTO,
MomentoColetaGlicemiaIsRequired::class => Inconsistencia::MOMENTO_COLETA_GLICEMIA_SEM_PREENCHIMENTO,
PesoLimitIsValid::class => Inconsistencia::PESO_DO_PACIENTE_MENOR_QUE_0_5_OU_MAIOR_QUE_500_KG,
AlturaLimitIsValid::class => Inconsistencia::ALTURA_DO_PACIENTE_MENOR_QUE_20_OU_MAIOR_QUE_250_CM,
];
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');
$fichasVisitaDomiciliar = DB::table('atencao_basica_visita_domiciliar')
->select([
'atencao_basica_visita_domiciliar.id as id',
'atencao_basica_visita_domiciliar.usuario_id as usuario_id',
'atencao_basica_visita_domiciliar.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',
'paciente_cidadao.cns as paciente_cns',
'paciente_cidadao.cpf as paciente_cpf',
'paciente_cidadao.nome as paciente_nome',
'paciente_cidadao.nome_social as paciente_nome_social',
'paciente_cidadao.preferencia_nome_id as paciente_preferencia_nome_id',
'paciente_cidadao.data_nascimento as paciente_data_nascimento',
'paciente_cidadao.sexo_id as paciente_sexo_id',
DB::raw('LPAD(cadastro_paciente.prontuario,7,0) AS paciente_prontuario'),
'atencao_basica_visita_domiciliar.turno_id',
'atencao_basica_visita_domiciliar.peso',
'atencao_basica_visita_domiciliar.altura',
'atencao_basica_visita_domiciliar.paciente_id',
'atencao_basica_visita_domiciliar.imovel_tipo_id',
'atencao_basica_visita_domiciliar.latitude',
'atencao_basica_visita_domiciliar.longitude',
'atencao_basica_visita_domiciliar.visita_compartilhada',
'atencao_basica_visita_domiciliar.temperatura',
'atencao_basica_visita_domiciliar.afericao_pa',
'atencao_basica_visita_domiciliar.glicemia',
'atencao_basica_visita_domiciliar.momento_coleta_id',
'atencao_basica_visita_domiciliar.desfecho_id',
'atencao_basica_cadastro_imovel_tipo.codigo_esus as imovel_tipo_codigo_esus',
])
->join('atencao_basica_parametrizacao_equipe', 'atencao_basica_parametrizacao_equipe.id', 'atencao_basica_visita_domiciliar.equipe_id')
->join('atencao_basica_cadastro_imovel_tipo', 'atencao_basica_cadastro_imovel_tipo.id', 'atencao_basica_visita_domiciliar.imovel_tipo_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')
->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')
->join('cadastro_usuario', 'cadastro_usuario.id', 'atencao_basica_visita_domiciliar.usuario_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->join('sigtap.ocupacao', 'sigtap.ocupacao.id', 'atencao_basica_visita_domiciliar.usuario_ocupacao_id')
->join('cadastro_paciente', 'cadastro_paciente.id', 'atencao_basica_visita_domiciliar.paciente_id')
->join('cadastro_cidadao as paciente_cidadao', 'paciente_cidadao.id', 'cadastro_paciente.cidadao_id')
->whereBetween('atencao_basica_visita_domiciliar.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_visita_domiciliar.usuario_id', request()->usuario_id))
->get();
$groupedList = [];
foreach ($fichasVisitaDomiciliar as $ficha) {
$errors = RelatorioInconsistenciaService::validHeader($ficha, Permissao::VISITA_DOMICILIAR)
->merge(self::validAtendimento($ficha));
$errorDescriptions = self::mapErrorsToDescriptions($errors, $inconsistencias);
if ($errorDescriptions->isEmpty()) {
continue;
}
$key = $ficha->data_atendimento . '|' . $ficha->paciente_id . '|' . $ficha->usuario_id;
if (!isset($groupedList[$key])) {
$groupedList[$key] = [
'data' => (new Carbon($ficha->data_atendimento))->format('d/m/Y'),
'ficha' => 'Visita Domiciliar',
'paciente_nome' => $ficha->paciente_nome,
'paciente_nome_social' => $ficha->paciente_nome_social,
'paciente_preferencia_nome_id' => $ficha->paciente_preferencia_nome_id,
'paciente_prontuario' => $ficha->paciente_prontuario,
'paciente_cns' => $ficha->paciente_cns ? MaskUtil::getCnsMasked($ficha->paciente_cns) : '-',
'paciente_cpf' => $ficha->paciente_cpf ? MaskUtil::getCpfMasked($ficha->paciente_cpf) : '-',
'profissional' => $ficha->profissional_nome,
'erros' => $errorDescriptions,
];
} else {
$groupedList[$key]['erros'] = $groupedList[$key]['erros']->merge($errorDescriptions);
}
}
$result = [];
foreach ($groupedList as $group) {
$unique = $group['erros']->unique();
$group['erros'] = $unique->implode('<br />');
$group['total_erros'] = $unique->count();
$result[] = $group;
}
return $result;
}
private static function mapErrorsToDescriptions(Collection $errors, Collection $inconsistencias): Collection
{
$descriptions = collect();
foreach ($errors as $error) {
$inconsistenciaId = self::ERROR_MAP[$error] ?? null;
if ($inconsistenciaId === null) {
continue;
}
$descricao = $inconsistencias[$inconsistenciaId] ?? null;
if ($descricao !== null) {
$descriptions->push($descricao);
}
}
return $descriptions;
}
private static function validAtendimento($ficha): Collection
{
$errors = collect();
if (!Cns::isValid($ficha->paciente_cns) && !Cpf::isValid($ficha->paciente_cpf)) {
$errors->push(CnsPaciente::class);
$errors->push(Cpf::class);
}
if (self::getTipoImovelValido($ficha->imovel_tipo_codigo_esus) && !DataNascimentoIsRequired::isValid($ficha->paciente_data_nascimento)) {
$errors->push(DataNascimentoIsRequired::class);
}
if (!DataNascimentoIsValid::isValid([
'data_nascimento' => $ficha->paciente_data_nascimento,
'data_atendimento' => $ficha->data_atendimento
])) {
$errors->push(DataNascimentoIsValid::class);
}
if (!MotivoVisitaIsValid::isValid($ficha)) {
$errors->push(MotivoVisitaIsValid::class);
}
if (!TurnoIsRequired::isValid($ficha->turno_id)) {
$errors->push(TurnoIsRequired::class);
}
if ($ficha->longitude && !LatitudeIsRequired::isValid($ficha->latitude)) {
$errors->push(LatitudeIsRequired::class);
}
if ($ficha->latitude && !LongitudeIsRequired::isValid($ficha->longitude)) {
$errors->push(LongitudeIsRequired::class);
}
if (!DesfechoIsRequired::isValid($ficha->desfecho_id)) {
$errors->push(DesfechoIsRequired::class);
}
if (!TipoImovelsRequired::isValid($ficha->imovel_tipo_id)) {
$errors->push(TipoImovelsRequired::class);
}
if (!SexoIsRequired::isValid($ficha->paciente_sexo_id)) {
$errors->push(SexoIsRequired::class);
}
if (!GlicemiaLimitIsValid::isValid($ficha->glicemia)) {
$errors->push(GlicemiaLimitIsValid::class);
}
if ($ficha->momento_coleta_id && !GlicemiaIsRequired::isValid($ficha->glicemia)) {
$errors->push(GlicemiaIsRequired::class);
}
if ($ficha->glicemia && !MomentoColetaGlicemiaIsRequired::isValid($ficha->momento_coleta_id)) {
$errors->push(MomentoColetaGlicemiaIsRequired::class);
}
if (!PesoLimitIsValid::isValid($ficha->peso)) {
$errors->push(PesoLimitIsValid::class);
}
if (!AlturaLimitIsValid::isValid($ficha->altura)) {
$errors->push(AlturaLimitIsValid::class);
}
return $errors;
}
private static function getTipoImovelValido($tipoImovelId): bool
{
return $tipoImovelId == ImovelTipo::DOMICILIO
|| $tipoImovelId == ImovelTipo::ABRIGO
|| $tipoImovelId == ImovelTipo::INSTITUICAO_LONGA_PERMANENCIA_IDOSOS
|| $tipoImovelId == ImovelTipo::UNIDADE_PRISIONAL
|| $tipoImovelId == ImovelTipo::UNIDADE_MEDIDA_SOCIO_EDUCATIVA
|| $tipoImovelId == ImovelTipo::DELEGACIA
|| $tipoImovelId == ImovelTipo::OUTROS;
}
}

View File

@@ -0,0 +1,375 @@
<?php
namespace App\Services\AtencaoBasica\RelatorioInconsistencia;
use App\Enums\AtencaoBasica\Parametrizacao\Permissao;
use App\Models\AtencaoBasica\Parametrizacao\Equipe;
use App\Models\AtencaoBasica\Parametrizacao\OutrosPermissao;
use App\Models\Cadastro\Estabelecimento\Infraestrutura;
use App\Models\Cadastro\Estabelecimento\Unidade;
use App\Models\Cadastro\SujeitoAtencao\Usuario;
use App\Rules\AtencaoBasica\Exportacao\CnesIsValid;
use App\Rules\AtencaoBasica\Exportacao\IbgeIsValid;
use App\Rules\AtencaoBasica\Exportacao\OcupacaoIsAllowed;
use App\Rules\Cns;
use App\Services\Service;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class RelatorioInconsistenciaService extends Service
{
private const ACTIVITY_TABLES = [
['atencao_basica_avaliacao', 'data'],
['atencao_basica_cadastro_domiciliar', 'data_usuario_responsavel'],
['atencao_basica_cadastro_individual', 'data_usuario_responsavel'],
['atencao_basica_ficha_complementar', 'data_atendimento'],
['atencao_basica_consumo_alimentar', 'data_atendimento'],
['atencao_basica_procedimento', 'data_usuario_responsavel'],
['atencao_basica_visita_domiciliar', 'data_usuario_responsavel'],
['atencao_basica_atendimento_domiciliar', 'data'],
['atencao_basica_atendimento_individual', 'data_usuario_responsavel'],
['atencao_basica_atendimento_odontologico', 'data_atendimento'],
['atencao_basica_atividade_coletiva', 'data_usuario_responsavel'],
];
public static function index()
{
return [
'form' => self::getFormData(),
'model' => self::getModelData(),
];
}
private static function getFormData()
{
return [
'fichas' => self::getFichas(),
'data_atual' => Carbon::now()->format('Y-m-d'),
'mesesAno' => self::getPeriodo(),
];
}
public static function getFichas()
{
return DB::table('atencao_basica_parametrizacao_outros_permissao')
->select([
'id',
'descricao'
])
->orderBy('atencao_basica_parametrizacao_outros_permissao.descricao')
->get();
}
private static function getDateRange(string $mes_ano): array
{
$date = Carbon::createFromFormat('mY', $mes_ano);
return [
$date->copy()->startOfMonth()->toDateString(),
$date->copy()->endOfMonth()->toDateString(),
];
}
private static function getActiveEquipeIds(string $start, string $end): array
{
$equipeIds = [];
foreach (self::ACTIVITY_TABLES as [$table, $dateCol]) {
$ids = DB::table($table)
->whereBetween($dateCol, [$start, $end])
->distinct()
->pluck('equipe_id')
->all();
foreach ($ids as $id) {
if ($id !== null) {
$equipeIds[$id] = true;
}
}
}
return array_keys($equipeIds);
}
public static function getUnidades()
{
[$start, $end] = self::getDateRange(request()->mes_ano);
$equipeIds = self::getActiveEquipeIds($start, $end);
if (empty($equipeIds)) {
return collect();
}
return DB::table('cadastro_unidade')
->select(['id', 'nome_fantasia'])
->whereIn('id', function ($q) use ($equipeIds) {
$q->select('unidade_id')
->from('atencao_basica_parametrizacao_equipe')
->whereIn('id', $equipeIds)
->whereNotNull('unidade_id');
})
->orderBy('nome_fantasia')
->get();
}
public static function getInfraestruturas()
{
[$start, $end] = self::getDateRange(request()->mes_ano);
$equipeIds = self::getActiveEquipeIds($start, $end);
if (empty($equipeIds)) {
return [
'infraestruturas' => collect(),
'usuarios' => self::getUsuarios(),
];
}
$infraestruturaIds = DB::table('atencao_basica_parametrizacao_equipe')
->whereIn('id', $equipeIds)
->whereNotNull('infraestrutura_id')
->when(request()->unidade_id, fn ($q) => $q->where('unidade_id', request()->unidade_id))
->distinct()
->pluck('infraestrutura_id')
->all();
$infraestruturas = DB::table('cadastro_infraestrutura')
->select([
'cadastro_infraestrutura.id',
DB::raw('COALESCE(cadastro_infraestrutura.nome, cadastro_unidade.nome_fantasia) AS nome'),
])
->join('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
->whereIn('cadastro_infraestrutura.id', $infraestruturaIds)
->when(request()->unidade_id, fn ($q) => $q->where('cadastro_infraestrutura.unidade_id', request()->unidade_id))
->orderBy('nome')
->get();
return [
'infraestruturas' => $infraestruturas,
'usuarios' => self::getUsuarios(),
];
}
public static function getUsuarios()
{
[$start, $end] = self::getDateRange(request()->mes_ano);
$equipeIds = null;
if (request()->unidade_id || request()->infraestrutura_id) {
$equipeIds = DB::table('atencao_basica_parametrizacao_equipe')
->when(request()->unidade_id, fn ($q) => $q->where('unidade_id', request()->unidade_id))
->when(request()->infraestrutura_id, fn ($q) => $q->where('infraestrutura_id', request()->infraestrutura_id))
->pluck('id')
->all();
if (empty($equipeIds)) {
return collect();
}
}
$usuarioIds = [];
foreach (self::ACTIVITY_TABLES as [$table, $dateCol]) {
$ids = DB::table($table)
->whereBetween($dateCol, [$start, $end])
->when($equipeIds, fn ($q) => $q->whereIn('equipe_id', $equipeIds))
->distinct()
->pluck('usuario_id')
->all();
foreach ($ids as $id) {
if ($id !== null) {
$usuarioIds[$id] = true;
}
}
}
if (empty($usuarioIds)) {
return collect();
}
return DB::table('cadastro_usuario')
->select('cadastro_usuario.id', 'cadastro_cidadao.nome')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
->whereIn('cadastro_usuario.id', array_keys($usuarioIds))
->orderBy('cadastro_cidadao.nome')
->get();
}
public static function getPeriodo()
{
$dataAtual = Carbon::now();
$dataInicial = $dataAtual->copy()->subYear();
$mesesAnos = [];
while ($dataAtual >= $dataInicial) {
$mesesAnos[] = [
'value' => $dataAtual->format('mY'),
'text' => ucfirst($dataAtual->translatedFormat('F/Y')),
];
$dataAtual->subMonth();
}
return $mesesAnos;
}
private static function getModelData()
{
return [
'periodo' => [
'start' => null,
'end' => null,
],
'unidade_id' => null,
'infraestrutura_id' => null,
'usuario_id' => null,
'ficha_id' => null,
'mesAno' => null,
];
}
public static function list()
{
$errors = collect();
if (!request()->ficha_id || request()->ficha_id == Permissao::MARCADORES_DE_CONSUMO_ALIMENTAR) {
$errors = $errors->merge(FichaConsumoAlimentarService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::PROCEDIMENTOS) {
$errors = $errors->merge(FichaProcedimentoService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::ATENDIMENTO_INDIVIDUAL) {
$errors = $errors->merge(FichaAtendimentoIndividualService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::ATENDIMENTO_ODONTOLOGICO) {
$errors = $errors->merge(FichaAtendimentoOdontologicoService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::ATENDIMENTO_DOMICILIAR) {
$errors = $errors->merge(FichaAtendimentoDomiciliarService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::FICHA_COMPLEMENTAR) {
$errors = $errors->merge(FichaComplementarService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::VISITA_DOMICILIAR) {
$errors = $errors->merge(FichaVisitaDomiciliarService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::AVALIACAO_ELEGIBILIDADE_ADMISSAO) {
$errors = $errors->merge(FichaAvaliacaoElegibilidadeService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::CADASTRO_INDIVIDUAL) {
$errors = $errors->merge(FichaCadastroIndividualService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::CADASTRO_DOMICILIAR) {
$errors = $errors->merge(FichaCadastroDomiciliarService::index());
}
if (!request()->ficha_id || request()->ficha_id == Permissao::ATIVIDADE_COLETIVA) {
$errors = $errors->merge(FichaAtividadeColetivaService::index());
}
return $errors;
}
public static function getEquipes()
{
return DB::table('atencao_basica_atendimento_odontologico as atendimento')
->select('equipe.id')
->join('atencao_basica_parametrizacao_equipe as equipe', 'equipe.id', 'atendimento.equipe_id')
->join('cadastro_unidade as unidade', 'unidade.id', 'equipe.unidade_id')
->where('atendimento.data_atendimento', '>=', request()->data_inicial)
->where('atendimento.data_atendimento', '<=', request()->data_final)
->when(request()->unidade_id, function($query) {
$query->where('atendimento.unidade_id', request()->unidade_id);
})
->orderBy('equipe.id')
->groupBy('equipe.id')
->get();
}
public static function getPrintExportData()
{
$items = self::list();
return [
'items' => $items,
'competencia' => self::getCompetencia(request()->mes_ano),
'unidade' => request()->unidade_id ? Unidade::find(request()->unidade_id)->nome_fantasia : 'Todas',
'infraestrutura' => request()->infraestrutura_id ? Infraestrutura::find(request()->infraestrutura_id)->nome : 'Todos',
'usuario' => request()->usuario_id ? Usuario::find(request()->usuario_id)->nome : 'Todos',
'ficha' => request()->ficha_id ? OutrosPermissao::find(request()->ficha_id)->descricao : 'Todas',
'colunas' => collect(request()->selectedCols),
];
}
private static function getCompetencia($date_str)
{
$month_names = [
"Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho",
"Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"
];
$month_index = intval(substr($date_str, 0, 2)) - 1;
$year_str = substr($date_str, 2);
$month_name = $month_names[$month_index];
$formatted_date = "{$month_name}/{$year_str}";
return $formatted_date;
}
public static function validHeader($ficha, $funcionalidade)
{
$errors = collect();
if (!CnesIsValid::isValid($ficha->unidade_cnes)) {
$errors->push(CnesIsValid::class);
}
if (!IbgeIsValid::isValid($ficha->municipio_ibge)) {
$errors->push(IbgeIsValid::class);
}
if (!Cns::isValid($ficha->usuario_cns)) {
$errors->push(Cns::class);
}
if (!OcupacaoIsAllowed::isValid(['permissao_id' => $funcionalidade, 'ocupacao' => $ficha->ocupacao_codigo])) {
$errors->push(OcupacaoIsAllowed::class);
}
return $errors;
}
}