Files
Antonio Lopes dos Santos afec302c3e
All checks were successful
build-api-image / build (push) Successful in 51s
init commit
2026-08-04 16:52:02 -03:00

2753 lines
110 KiB
PHP

<?php
namespace App\Services\Ambulatorio;
use App\Enums\Administracao\Modulo;
use App\Enums\Ambulatorio\AgendamentoStatus as AgendamentoStatusEnum;
use App\Enums\Ambulatorio\AgendamentoTipo as AgendamentoTipoEnum;
use App\Enums\Ambulatorio\JornadaTipo;
use App\Enums\Cadastro\SujeitoAtencao\Classificacao;
use App\Enums\Cadastro\SujeitoAtencao\Subclassificacao;
use App\Enums\Cadastro\Estabelecimento\SetorTipo;
use App\Enums\ExameImagem\Fechamento\Status as ExameImagemAgendamentoStatus;
use App\Enums\ExameLaboratorial\StatusAgendamento as ExameLaboratorialAgendamentoStatus;
use App\Enums\Regulacao\StatusTipo as RegulacaoStatusTipo;
use App\Enums\PainelSenha\Status;
use App\Exceptions\Ambulatorio\AgendamentoConflitanteException;
use App\Exceptions\Ambulatorio\AgendamentoNaoPermitidoJornadaGrupoException;
use App\Exceptions\Ambulatorio\NaoPodeAlterarStatusAgendamentoException;
use App\Exceptions\Ambulatorio\NaoPodeExcluirAgendamentoException;
use App\Http\Controllers\Cadastro\SujeitoAtencao\PacienteController;
use App\Models\Ambulatorio\Agendamento;
use App\Models\Ambulatorio\Atendimento;
use App\Models\Ambulatorio\AgendamentoStatus;
use App\Models\Ambulatorio\AgendamentoTipo;
use App\Models\Ambulatorio\DemandaEspontanea;
use App\Models\Ambulatorio\Jornada;
use App\Models\Ambulatorio\JornadaBloqueio;
use App\Models\Ambulatorio\ParametrizacaoAgendamento;
use App\Models\Ambulatorio\ParametrizacaoIntegracaoAplicativo;
use App\Models\Ambulatorio\ParametrizacaoInfraestrutura;
use App\Models\Ambulatorio\ParametrizacaoInfraestruturaEspecialidade;
use App\Models\Cadastro\Especialidade;
use App\Models\Cadastro\Estabelecimento\Infraestrutura;
use App\Models\Cadastro\Estabelecimento\Unidade;
use App\Models\Cadastro\Feriado\Feriado;
use App\Models\Cadastro\Parametrizacao\Parametrizacao;
use App\Models\Cadastro\SujeitoAtencao\Paciente;
use App\Models\Cadastro\SujeitoAtencao\Usuario;
use App\Models\ExameImagem\Agendamento\Agendamento as ExameImagemAgendamento;
use App\Models\ExameLaboratorial\AgendamentoExame\Agendamento as ExameLaboratorialAgendamento;
use App\Models\Geral\DiaSemana;
use App\Models\PainelSenha\OpcaoEntrada;
use App\Models\Regulacao\Parametrizacao as RegulacaoParametrizacao;
use App\Models\Regulacao\Solicitacao as RegulacaoSolicitacao;
use App\Models\Sigtap\Procedimento;
use App\Models\Cadastro\Estabelecimento\CentroCusto;
use App\Rules\Cns;
use App\Services\Integracao\CnsService;
use App\Services\Regulacao\SolicitacaoService as RegulacaoSolicitacaoService;
use App\Services\Service;
use App\Services\Sigtap\ProcedimentoValidationService;
use App\Services\Ambulatorio\RegistroPacienteAposAgendamento;
use App\Utils\DataUtil;
use App\Utils\MaskUtil;
use Illuminate\Http\Response;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Throwable;
class AgendamentoService extends Service
{
public static function datatable()
{
$parametrizacao = Parametrizacao::first();
$quantidadeDias = $parametrizacao->cadastro_paciente_desatualizado_quantidade_dias ?? 0;
$provisorioBloqueado = $parametrizacao->cadastro_paciente_provisorio_bloqueado ?? false;
$provisorioQuantidadeDias = $parametrizacao->cadastro_paciente_provisorio_quantidade_dias ?? 0;
$pacientes = DB::table('cadastro_paciente as paciente')
->join(
'cadastro_paciente_classificacao as classificacao',
'classificacao.id',
'paciente.classificacao_id'
)
->leftJoin(
'cadastro_cidadao as cidadao',
'cidadao.id',
'paciente.cidadao_id'
)
->leftJoin(
'cadastro_domicilio as domicilio',
'domicilio.id',
'cidadao.domicilio_id'
)
->leftJoin(
'cadastro_endereco as endereco',
'endereco.id',
'domicilio.endereco_id'
)
->leftJoin(
'cadastro_logradouro as logradouro',
'logradouro.id',
'endereco.logradouro_id'
)
->leftJoin(
'intranet.cadastro_municipio as municipio',
'municipio.id',
'logradouro.municipio_id'
)
->leftJoin(
'intranet.cadastro_estado as estado',
'estado.id',
'municipio.estado_id'
)
->leftJoin('cadastro_obito as obito', function ($join) {
$join->on('obito.cidadao_id', '=', 'cidadao.id')
->whereNull('obito.deleted_at');
})
->whereNull('paciente.deleted_at')
->whereNull('paciente.sequencial_desconhecido_em')
->whereNull('obito.id')
->select(DB::raw("
paciente.id,
paciente.classificacao_id,
cidadao.nome,
cidadao.nome_social,
cidadao.preferencia_nome_id,
DATE_FORMAT(cidadao.data_nascimento, '%d/%m/%Y') as data_nascimento,
cidadao.nome_mae,
LPAD(paciente.prontuario, 7, '0') as prontuario,
CONCAT(
SUBSTR(cidadao.cpf, 1, 3),
'.',
SUBSTR(cidadao.cpf, 4, 3),
'.',
SUBSTR(cidadao.cpf, 7, 3),
'-',
SUBSTR(cidadao.cpf, 10, 2)
) as cpf,
CONCAT(
SUBSTR(cidadao.cns, 1, 3),
' ',
SUBSTR(cidadao.cns, 4, 4),
' ',
SUBSTR(cidadao.cns, 8, 4),
' ',
SUBSTR(cidadao.cns, 12, 4)
) as cns,
classificacao.descricao as classificacao_descricao,
IF(paciente.validade_fim IS NULL,
'INDETERMINADO',
DATE_FORMAT(paciente.validade_fim, '%d/%m/%Y')
) as validade_fim,
CASE
WHEN paciente.updated_at IS NULL AND cidadao.updated_at IS NULL THEN 1
WHEN paciente.updated_at IS NOT NULL AND (cidadao.updated_at IS NULL OR paciente.updated_at > cidadao.updated_at) THEN
CASE
WHEN " . ($quantidadeDias > 0
? "DATE_ADD(paciente.updated_at, INTERVAL " . $quantidadeDias . " DAY) < NOW()"
: "false") . "
THEN DATEDIFF(NOW(), DATE_ADD(paciente.updated_at, INTERVAL " . $quantidadeDias . " DAY))
ELSE 0
END
WHEN cidadao.updated_at IS NOT NULL AND (paciente.updated_at IS NULL OR cidadao.updated_at > paciente.updated_at) THEN
CASE
WHEN " . ($quantidadeDias > 0
? "DATE_ADD(cidadao.updated_at, INTERVAL " . $quantidadeDias . " DAY) < NOW()"
: "false") . "
THEN DATEDIFF(NOW(), DATE_ADD(cidadao.updated_at, INTERVAL " . $quantidadeDias . " DAY))
ELSE 0
END
ELSE 0
END as dias_cadastro_desatualizado
"));
if (!DataUtil::isStringTrue(request()->with_paciente_bloqueado)) {
$pacientes->where(function ($query) {
$query
->where('paciente.bloqueado', false)
->orWhereNull('paciente.bloqueado');
});
}
if (!DataUtil::isStringTrue(request()->with_paciente_provisorio) && $provisorioBloqueado) {
$pacientes->where(function ($query) use ($provisorioQuantidadeDias) {
$query->where('paciente.subclassificacao_id', '!=', Subclassificacao::TEMPORARIO)
->orWhere(function ($query) use ($provisorioQuantidadeDias) {
$query->where('paciente.subclassificacao_id', Subclassificacao::TEMPORARIO)
->where(function ($query) use ($provisorioQuantidadeDias) {
$query->where(function ($query) {
$query->whereNull('paciente.validade_inicio')
->orWhereDate('paciente.validade_inicio', '<=', Carbon::now()->format('Y-m-d'));
});
$query->where(function ($query) use ($provisorioQuantidadeDias) {
$query->whereNull('paciente.validade_fim')
->orWhere(function ($query) use ($provisorioQuantidadeDias) {
if (!is_null($provisorioQuantidadeDias) && $provisorioQuantidadeDias > 0) {
$query->whereDate('paciente.validade_fim', '>=', Carbon::now()->format('Y-m-d'))
->orWhereDate('paciente.validade_fim', '>=', Carbon::now()->subDays($provisorioQuantidadeDias)->format('Y-m-d'));
} else {
$query->whereDate('paciente.validade_fim', '>=', Carbon::now()->format('Y-m-d'));
}
});
});
});
})
->orWhereNull('paciente.subclassificacao_id');
});
}
return $pacientes;
}
public static function index($params)
{
$agendamentos = DB::table('ambulatorio_agendamento as agendamento')
->join(
'ambulatorio_agendamento_status as status',
'status.id',
'agendamento.status_id'
)
->join(
'ambulatorio_agendamento_tipo as tipo',
'tipo.id',
'agendamento.tipo_id'
)
->join(
'ambulatorio_jornada as jornada',
'jornada.id',
'agendamento.jornada_id'
)
->join(
'cadastro_lotacao as lotacao',
'lotacao.id',
'jornada.lotacao_id'
)
->join(
'cadastro_infraestrutura as infraestrutura',
'infraestrutura.id',
'lotacao.infraestrutura_id'
)
->join(
'cadastro_unidade as unidade',
'unidade.id',
'infraestrutura.unidade_id'
)
->join(
'cadastro_usuario as usuario',
'usuario.id',
'lotacao.usuario_id'
)
->join(
'cadastro_cidadao as cidadao',
'cidadao.id',
'usuario.cidadao_id'
)
->join(
'sigtap.ocupacao as ocupacao',
'ocupacao.id',
'lotacao.ocupacao_id'
)
->join(
'cadastro_especialidade as especialidade',
'especialidade.id',
'jornada.especialidade_id'
)
->join(
'cadastro_lotacao as lotacao_agendamento',
'lotacao_agendamento.id',
'agendamento.created_lotacao_id'
)
->join(
'cadastro_usuario as usuario_agendamento',
'usuario_agendamento.id',
'lotacao_agendamento.usuario_id'
)
->join(
'cadastro_cidadao as cidadao_agendamento',
'cidadao_agendamento.id',
'usuario_agendamento.cidadao_id'
)
->when($params->paciente_id ?? null, fn ($q, $id) => $q->where('agendamento.paciente_id', $id))
->when($params->periodo ?? null, fn ($q, $periodo) => $q
->whereBetween('jornada.data', [
Carbon::createFromFormat('d/m/Y', $periodo['start'])->startOfDay(),
Carbon::createFromFormat('d/m/Y', $periodo['end'])->endOfDay(),
])
)
->when(isset($params->inicio) && isset($params->fim), fn ($q) => $q
->whereBetween('agendamento.horario', [$params->inicio, $params->fim])
)
->when($params->unidade_id ?? null, fn ($q, $id) => $q->where('unidade.id', $id))
->when($params->infraestrutura_id ?? null, fn ($q, $id) => $q->where('infraestrutura.id', $id))
->when($params->ocupacao_id ?? null, fn ($q, $id) => $q->where('ocupacao.id', $id))
->when($params->especialidade_id ?? null, fn ($q, $id) => $q->where('especialidade.id', $id))
->when($params->usuario_id ?? null, fn ($q, $id) => $q->where('usuario.id', $id))
->when($params->tipo_id ?? null, fn ($q, $id) => $q->where('tipo.id', $id))
->when($params->status_id ?? null, fn ($q, $id) => $q->where('status.id', $id))
->select(DB::raw("
agendamento.id,
agendamento.regulacao_solicitacao_id,
jornada.data,
DATE_FORMAT(agendamento.horario, '%H:%i') as horario,
unidade.nome_fantasia as unidade,
COALESCE(infraestrutura.nome, unidade.nome_fantasia) as infraestrutura,
cidadao.nome as usuario,
CONCAT(ocupacao.codigo, ' - ', ocupacao.nome) as ocupacao,
especialidade.nome as especialidade,
tipo.nome as tipo,
status.id as status_id,
status.nome as status,
cidadao_agendamento.nome as usuario_agendamento,
DATE_FORMAT(agendamento.created_at, '%d/%m/%Y às %H:%i') as data_hora_agendamento
"))
->addSelect([
'has_atendimento' => function ($query) {
$query->select(DB::raw(1))
->from('ambulatorio_atendimento as atendimento')
->whereColumn('atendimento.agendamento_id', 'agendamento.id')
->limit(1);
}
])
->get();
$agendamentos->each(function ($agendamento) {
$isAgendadoSemAtendimento = !$agendamento->has_atendimento
&& $agendamento->status_id === AgendamentoStatusEnum::AGENDADO;
$agendamento->permite_alteracao = $isAgendadoSemAtendimento
&& is_null($agendamento->regulacao_solicitacao_id);
$agendamento->permite_cancelamento_exclusao = $isAgendadoSemAtendimento;
unset($agendamento->regulacao_solicitacao_id, $agendamento->status_id, $agendamento->has_atendimento);
});
return $agendamentos;
}
public static function create($pacienteId)
{
$paciente = Paciente::findOrFail($pacienteId);
switch (request()->origem) {
case 'busca': {
return self::getDadosCreateBusca($paciente);
}
case 'cadastro': {
return self::getDadosCreateCadastro($paciente);
}
}
}
private static function getDadosCreateBusca($paciente)
{
$municipioResidencia = $paciente->cidadao->domicilio->endereco->logradouro->municipio ?? null;
$parametrizacao = ParametrizacaoAgendamento::first();
$agendamentos = self::index((object) [
'paciente_id' => $paciente->id,
'status_id' => AgendamentoStatusEnum::AGENDADO,
'periodo' => [
'start' => Carbon::now()->subYear()->format('d/m/Y'),
'end' => Carbon::now()->format('d/m/Y'),
]
]);
$formFilters = [
'unidades' => [],
'infraestruturas' => [],
'ocupacoes' => [],
'especialidades' => [],
'usuarios' => [],
'tipos' => [],
'status' => [],
];
$modelFilters = [
'periodo' => [
'start' => null,
'end' => null,
],
'inicio' => null,
'fim' => null,
'unidade_id' => null,
'infraestrutura_id' => null,
'ocupacao_id' => null,
'especialidade_id' => null,
'usuario_id' => null,
'tipo_id' => null,
'status_id' => null,
];
if ($agendamentos->isNotEmpty()) {
$agendamentosFilters = self::getAgendamentosFilters($paciente, $agendamentos);
$formFilters['unidades'] = $agendamentosFilters->form['unidades'];
$formFilters['ocupacoes'] = $agendamentosFilters->form['ocupacoes'];
$formFilters['usuarios'] = $agendamentosFilters->form['usuarios'];
$formFilters['tipos'] = $agendamentosFilters->form['tipos'];
$formFilters['status'] = $agendamentosFilters->form['status'];
$modelFilters['periodo'] = $agendamentosFilters->model['periodo'];
$modelFilters['status_id'] = $agendamentosFilters->model['status_id'];
}
$cadastroParametrizacao = Parametrizacao::first();
return [
'additionals' => [
'cadastro_paciente_vencido' => ($parametrizacao->bloqueia_paciente_temporario ?? false)
&& PacienteController::isCadastroPacienteVencido($paciente->id),
'cns_valido' => Cns::isValid($paciente->cidadao->cns),
'avisa_cns_provisorio' => self::avisaCnsProvisorio($parametrizacao, $paciente),
'avisa_cns_temporario_invalido' => $cadastroParametrizacao->exibe_alerta_paciente_cns_temporario_invalido ?? false,
'permissoes_atualizacao_cadastro_paciente' => self::getPermissoesAtualizacaoCadastroPaciente(),
],
'form' => array_merge($formFilters, [
'paciente' => [
'cidadao_id' => $paciente->cidadao->id,
'nome' => $paciente->cidadao->nome,
'nome_social' => $paciente->cidadao->nome_social,
'preferencia_nome_id' => $paciente->cidadao->preferencia_nome_id,
'data_nascimento' => is_null($paciente->cidadao->data_nascimento)
? null
: (new Carbon($paciente->cidadao->data_nascimento))->format('d/m/Y'),
'idade' => $paciente->cidadao->idade_formatada,
'nome_mae' => $paciente->cidadao->nome_mae,
'prontuario' => $paciente->prontuario,
'cpf' => MaskUtil::getCpfMasked($paciente->cidadao->cpf),
'cns' => MaskUtil::getCnsMasked($paciente->cidadao->cns),
'municipio_uf' => is_null($municipioResidencia)
? null
: "{$municipioResidencia->nome} / {$municipioResidencia->estado->uf}",
'telefone' => MaskUtil::getPhoneMasked($paciente->cidadao->telefone_principal->numero ?? null),
'unidade_referencia' => $paciente->unidade->nome_fantasia ?? null,
'classificacao_id' => $paciente->classificacao_id,
'subclassificacao_id' => $paciente->subclassificacao_id,
'classificacao' => $paciente->classificacao->descricao ?? null,
'validade_fim' => is_null($paciente->validade_fim)
? 'INDETERMINADO'
: (new Carbon($paciente->validade_fim))->format('d/m/Y')
],
'especialidades_permitem_nao_municipe' => self::getEspecialidadesPermitemNaoMunicipe($paciente->id),
]),
'model' => $modelFilters,
'agendamentos' => $agendamentos,
];
}
private static function getAgendamentosFilters($paciente, $agendamentos)
{
$datasAgendadas = collect();
$agendamentos->each(fn ($agendamento) => $datasAgendadas
->push(Carbon::createFromFormat('Y-m-d', $agendamento->data))
);
$datasAgendadas = $datasAgendadas->sort();
$periodo = [
'start' => $datasAgendadas->first()->copy()->startOfDay(),
'end' => $datasAgendadas->last()->copy()->endOfDay(),
];
$params = (object) [
'paciente_id' => $paciente->id,
'periodo' => $periodo,
];
return (object) [
'form' => [
'unidades' => self::getBuscaUnidades($params),
'ocupacoes' => self::getBuscaOcupacoes($params),
'usuarios' => self::getBuscaUsuarios($params),
'tipos' => self::getBuscaTipos($params),
'status' => self::getBuscaStatus($params),
],
'model' => [
'periodo' => [
'start' => $periodo['start']->format('d/m/Y'),
'end' => $periodo['end']->format('d/m/Y'),
],
'status_id' => AgendamentoStatusEnum::AGENDADO,
],
];
}
private static function avisaCnsProvisorio($parametrizacao, $paciente)
{
return ($parametrizacao->avisa_cns_provisorio ?? false)
&& Str::startsWith($paciente->cidadao->cns, [5, 7, 8, 9]);
}
private static function getEspecialidadesPermitemNaoMunicipe($pacienteId)
{
return Especialidade::where('permite_agendamento_nao_municipe', true)
->where(fn ($q) => self::setCadastroWhereEspecialidadePaciente($q, $pacienteId))
->whereHas('ambulatorioJornadas', fn ($q) => $q
->whereNull('situacao_id')
->whereDate('data', '>=', Carbon::now())
)
->orderBy('nome')
->get(['id', 'nome']);
}
private static function getDadosCreateCadastro($paciente)
{
$params = (object) [
'paciente_id' => $paciente->id,
'paciente_nao_municipe' => $paciente->classificacao_id !== Classificacao::MUNICIPE,
];
return [
'additionals' => self::getAdditionalsCadastro($paciente),
'form' => [
'feriados' => self::getFeriados(),
'datas_com_agendamento' => self::getCadastroDatasComAgendamento($params),
'unidades' => self::getCadastroUnidades($params),
'infraestruturas' => [],
'ocupacoes' => [],
'especialidades' => [],
'faltas' => [],
'usuarios' => [],
'datas' => [],
'datas_bloqueadas' => [],
'horarios' => [],
'horarios_com_situacao' => [],
'quantidade_demanda_espontanea' => null,
'tipo_nome' => null,
],
'model' => [
'unidade_id' => null,
'infraestrutura_id' => null,
'ocupacao_id' => null,
'especialidade_id' => null,
'usuario_id' => null,
'demanda_espontanea_observa_vaga_atendimento' => null,
'data' => null,
'jornada_id' => null,
'horario' => null,
'observacao' => null,
'motivo_agendamento_excedente' => null,
],
];
}
public static function edit($id)
{
$agendamento = Agendamento::findOrFail($id);
$params = (object) [
'paciente_id' => $agendamento->paciente_id,
'paciente_nao_municipe' => $agendamento->paciente->classificacao_id !== Classificacao::MUNICIPE,
'unidade_id' => $agendamento->jornada->lotacao->infraestrutura->unidade_id,
'infraestrutura_id' => $agendamento->jornada->lotacao->infraestrutura_id,
'ocupacao_id' => $agendamento->jornada->lotacao->ocupacao_id,
'especialidade_id' => $agendamento->jornada->especialidade_id,
'usuario_id' => $agendamento->jornada->lotacao->usuario_id,
'data' => $agendamento->jornada->data->format('d/m/Y'),
'tipo_id' => $agendamento->tipo_id,
];
return [
'additionals' => self::getAdditionalsCadastro(),
'form' => [
'feriados' => self::getFeriados(),
'datas_com_agendamento' => self::getCadastroDatasComAgendamento($params),
'unidades' => self::getCadastroUnidades($params),
'infraestruturas' => self::getCadastroInfraestruturas($params),
'ocupacoes' => self::getCadastroOcupacoes($params, $agendamento->jornada->lotacao->ocupacao_id),
'especialidades' => self::getCadastroEspecialidades($params, $agendamento->jornada->especialidade_id),
'faltas' => self::getFaltas($params),
'usuarios' => self::getCadastroUsuarios($params),
'datas' => self::getCadastroDatas($params),
'datas_bloqueadas' => self::getCadastroDatasBloqueadas($params),
'horarios' => self::getCadastroHorarios($params),
'horarios_com_situacao' => self::getCadastroHorariosComSituacao($params),
'quantidade_demanda_espontanea' => self::getQuantidadeDemandaEspontanea($params),
'tipo_nome' => $agendamento->tipo->nome,
'situacao' => self::getCadastroDatasComSituacao($params)
],
'model' => [
'unidade_id' => $agendamento->jornada->lotacao->infraestrutura->unidade_id,
'infraestrutura_id' => $agendamento->jornada->lotacao->infraestrutura_id,
'ocupacao_id' => $agendamento->jornada->lotacao->ocupacao_id,
'especialidade_id' => $agendamento->jornada->especialidade_id,
'usuario_id' => $agendamento->jornada->lotacao->usuario_id,
'demanda_espontanea_observa_vaga_atendimento' => self::getDemandaEspontaneaObservaVagaAtendimentoAgendamento($agendamento),
'data' => $agendamento->jornada->data->format('d/m/Y'),
'jornada_id' => $agendamento->jornada->id,
'horario' => $agendamento->horario->format('H:i'),
'observacao' => $agendamento->observacao,
'motivo_agendamento_excedente' => $agendamento->motivo_agendamento_excedente,
],
];
}
private static function getAdditionalsCadastro()
{
return [
'data_atual' => Carbon::today()->format('d/m/Y'),
'obriga_motivo_agendamento_excedente' => ParametrizacaoAgendamento::first()
->obriga_motivo_agendamento_excedente ?? false,
];
}
private static function getFeriados()
{
return Feriado::whereHas('modulos', fn ($q) => $q
->where('identificador', Modulo::AMBULATORIO)
)
->orderBy('data')
->get()
->map(fn ($feriado) => (object) [
'data' => (new Carbon($feriado->data))->format('d/m/Y'),
'tipo_descricao' => $feriado->tipo->descricao,
'observacao' => $feriado->observacao,
]);
}
private static function getCadastroDatasComAgendamento($params)
{
$agendamentosExameLaboratorial = ExameLaboratorialAgendamento::where('paciente_id', $params->paciente_id)
->whereHas('agendamentoExame', fn ($q) => $q
->where('status_id', ExameLaboratorialAgendamentoStatus::AGENDADO)
)
->get()
->map(fn ($agendamento) => [
'data' => $agendamento->data->format('d/m/Y'),
'horario' => Carbon::now()->setTimeFromTimeString($agendamento->horario)->format('H:i'),
'unidade_nome_fantasia' => $agendamento->coletaInfraestrutura->unidade->nome_fantasia,
'infraestrutura_nome' => $agendamento->coletaInfraestrutura->nome,
]);
$agendamentosExameImagem = ExameImagemAgendamento::where('paciente_id', $params->paciente_id)
->whereHas('agendamentoExame', fn ($q) => $q
->where('status_id', ExameImagemAgendamentoStatus::AGENDADO)
)
->get()
->map(fn ($agendamento) => [
'data' => $agendamento->data->format('d/m/Y'),
'horario' => Carbon::now()->setTimeFromTimeString($agendamento->hora_exame)->format('H:i'),
'unidade_nome_fantasia' => $agendamento->infraestrutura->unidade->nome_fantasia,
'infraestrutura_nome' => $agendamento->infraestrutura->nome,
]);
return [
'ambulatorio' => self::getAgendamentosPaciente($params->paciente_id),
'exame_laboratorial' => $agendamentosExameLaboratorial,
'exame_imagem' => $agendamentosExameImagem,
];
}
private static function getAgendamentosPaciente($pacienteId)
{
return Agendamento::where('paciente_id', $pacienteId)
->where('status_id', AgendamentoStatusEnum::AGENDADO)
->get()
->map(fn ($agendamento) => [
'id' => $agendamento->id,
'data' => $agendamento->jornada->data->format('d/m/Y'),
'horario' => $agendamento->horario->format('H:i'),
'tipo_nome' => Str::lower($agendamento->tipo->nome),
'unidade_nome_fantasia' => $agendamento->jornada->lotacao->infraestrutura->unidade->nome_fantasia,
'infraestrutura_nome' => $agendamento->jornada->lotacao->infraestrutura->nome,
'especialidade_id' => $agendamento->jornada->especialidade_id,
'especialidade_nome' => $agendamento->jornada->especialidade->nome,
]);
}
public static function list()
{
switch (request()->origem) {
case 'busca': {
return self::getDadosListBusca();
}
case 'cadastro': {
return self::getDadosListCadastro();
}
case 'agendamento-demanda-espontanea': {
return self::getDadosListAgendamentoDemandaEspontanea();
}
}
}
private static function getDadosListBusca()
{
$params = (object) request()->all();
$params->periodo = [
Carbon::createFromFormat('d/m/Y', $params->periodo['start'])->startOfDay(),
Carbon::createFromFormat('d/m/Y', $params->periodo['end'])->endOfDay(),
];
switch (request()->listar) {
case 'unidades': {
return [
'unidades' => self::getBuscaUnidades($params),
'infraestruturas' => [],
'ocupacoes' => self::getBuscaOcupacoes($params),
'especialidades' => [],
'usuarios' => self::getBuscaUsuarios($params),
'tipos' => self::getBuscaTipos($params),
'status' => self::getBuscaStatus($params),
];
}
case 'infraestruturas': {
return [
'infraestruturas' => self::getBuscaInfraestruturas($params),
'ocupacoes' => self::getBuscaOcupacoes($params),
'especialidades' => [],
'usuarios' => self::getBuscaUsuarios($params),
'tipos' => self::getBuscaTipos($params),
'status' => self::getBuscaStatus($params),
];
return self::getBuscaInfraestruturas($params);
}
case 'ocupacoes': {
return [
'ocupacoes' => self::getBuscaOcupacoes($params),
'especialidades' => [],
'usuarios' => self::getBuscaUsuarios($params),
'tipos' => self::getBuscaTipos($params),
'status' => self::getBuscaStatus($params),
];
}
case 'especialidades': {
return [
'especialidades' => self::getBuscaEspecialidades($params),
'usuarios' => self::getBuscaUsuarios($params),
'tipos' => self::getBuscaTipos($params),
'status' => self::getBuscaStatus($params),
];
}
case 'usuarios': {
return [
'usuarios' => self::getBuscaUsuarios($params),
'tipos' => self::getBuscaTipos($params),
'status' => self::getBuscaStatus($params),
];
}
case 'tipos': {
return [
'tipos' => self::getBuscaTipos($params),
'status' => self::getBuscaStatus($params),
];
}
case 'status': {
return [
'status' => self::getBuscaStatus($params),
];
}
}
}
private static function getBuscaUnidades($params)
{
return Unidade::whereHas('infraestruturas', fn ($q) => $q
->whereHas('lotacoes', fn ($q) => $q
->whereHas('ambulatorioJornadas', fn ($q) => $q
->whereBetween('data', $params->periodo)
->whereHas('agendamentos', fn ($q) =>
self::setBuscaWhereAgendamento($q, $params)
)
)
)
)
->orderBy('nome_fantasia')
->get(['id', 'nome_fantasia']);
}
private static function getBuscaInfraestruturas($params)
{
return Infraestrutura::setEagerLoads([])
->where('unidade_id', $params->unidade_id)
->whereHas('lotacoes', fn ($q) => $q
->whereHas('ambulatorioJornadas', fn ($q) => $q
->whereBetween('data', $params->periodo)
->whereHas('agendamentos', fn ($q) =>
self::setBuscaWhereAgendamento($q, $params)
)
)
)
->get(['id', 'nome', 'principal', 'unidade_id'])
->map(fn ($infraestrutura) => [
'id' => $infraestrutura->id,
'nome' => $infraestrutura->nome
])
->sortBy('nome')
->values();
}
private static function getBuscaOcupacoes($params)
{
return DB::table('sigtap.ocupacao as ocupacao')
->whereExists(function ($query) use ($params) {
$query->select(DB::raw(1))
->from('cadastro_lotacao as lotacao')
->join(
'cadastro_infraestrutura as infraestrutura',
'infraestrutura.id',
'lotacao.infraestrutura_id'
)
->whereColumn('lotacao.ocupacao_id', 'ocupacao.id')
->when($params->infraestrutura_id ?? null,
fn ($q, $id) => $q->where('lotacao.infraestrutura_id', $id),
fn ($q) => $q->when($params->unidade_id ?? null, fn ($q, $id) => $q
->where('infraestrutura.unidade_id', $id)
)
)
->whereExists(function ($query) use ($params) {
$query->select(DB::raw(1))
->from('ambulatorio_agendamento as agendamento')
->join(
'ambulatorio_jornada as jornada',
'jornada.id',
'agendamento.jornada_id'
)
->whereColumn('jornada.lotacao_id', 'lotacao.id')
->whereBetween('jornada.data', $params->periodo)
->where(fn ($q) => self::setBuscaWhereAgendamento($q, $params, 'agendamento'));
});
})
->select(DB::raw("
ocupacao.id,
CONCAT(ocupacao.codigo, ' - ', ocupacao.nome) as descricao
"))
->orderByRaw("CONCAT(ocupacao.codigo, ' - ', ocupacao.nome)")
->get();
}
private static function getBuscaEspecialidades($params)
{
return Especialidade::whereHas('ambulatorioJornadas', fn ($q) => $q
->whereBetween('data', $params->periodo)
->whereHas('lotacao', fn ($q) => $q
->when($params->infraestrutura_id ?? null,
fn ($q, $id) => $q->where('infraestrutura_id', $id),
fn ($q) => $q->when($params->unidade_id ?? null, fn ($q, $id) => $q
->whereHas('infraestrutura', fn ($q) => $q->where('unidade_id', $id))
)
)
->where('ocupacao_id', $params->ocupacao_id)
)
->whereHas('agendamentos', fn ($q) =>
self::setBuscaWhereAgendamento($q, $params)
)
)
->orderBy('nome')
->get(['id', 'nome']);
}
private static function getBuscaUsuarios($params)
{
return Usuario::setEagerLoads([])
->whereHas('lotacoes', fn ($q) => $q
->whereHas('ambulatorioJornadas', fn ($q) => $q
->whereBetween('data', $params->periodo)
->whereHas('agendamentos', fn ($q) =>
self::setBuscaWhereAgendamento($q, $params)
)
->when($params->especialidade_id ?? null, fn ($q, $id) => $q->where('especialidade_id', $id))
)
->when($params->infraestrutura_id ?? null,
fn ($q, $id) => $q->where('infraestrutura_id', $id),
fn ($q) => $q->when($params->unidade_id ?? null, fn ($q, $id) => $q
->whereHas('infraestrutura', fn ($q) => $q->where('unidade_id', $id))
)
)
->when($params->ocupacao_id ?? null, fn ($q, $id) => $q->where('ocupacao_id', $id))
)
->get(['id', 'cidadao_id'])
->map(fn ($usuario) => [
'id' => $usuario->id,
'nome' => $usuario->cidadao()->setEagerLoads([])->first(['nome'])->nome,
])
->sortBy('nome')
->values();
}
private static function getBuscaTipos($params)
{
return AgendamentoTipo::whereHas('agendamentos', fn ($q) =>
self::setBuscaWhereAgendamento($q, $params)
->whereHas('jornada', fn ($q) => $q
->whereBetween('data', $params->periodo)
->whereHas('lotacao', fn ($q) => $q
->when($params->infraestrutura_id ?? null,
fn ($q, $id) => $q->where('infraestrutura_id', $id),
fn ($q) => $q->when($params->unidade_id ?? null, fn ($q, $id) => $q
->whereHas('infraestrutura', fn ($q) => $q->where('unidade_id', $id))
)
)
->when($params->usuario_id ?? null, fn ($q, $id) => $q->where('usuario_id', $id))
->when($params->ocupacao_id ?? null, fn ($q, $id) => $q->where('ocupacao_id', $id))
)
->when($params->especialidade_id ?? null, fn ($q, $id) => $q->where('especialidade_id', $id))
)
)
->orderBy('nome')
->get(['id', 'nome']);
}
private static function getBuscaStatus($params)
{
return AgendamentoStatus::whereHas('agendamentos', fn ($q) =>
self::setBuscaWhereAgendamento($q, $params)
->when($params->tipo_id ?? null, fn ($q, $id) => $q->where('tipo_id', $id))
->whereHas('jornada', fn ($q) => $q
->whereBetween('data', $params->periodo)
->whereHas('lotacao', fn ($q) => $q
->when($params->infraestrutura_id ?? null,
fn ($q, $id) => $q->where('infraestrutura_id', $id),
fn ($q) => $q->when($params->unidade_id ?? null, fn ($q, $id) => $q
->whereHas('infraestrutura', fn ($q) => $q->where('unidade_id', $id))
)
)
->when($params->usuario_id ?? null, fn ($q, $id) => $q->where('usuario_id', $id))
->when($params->ocupacao_id ?? null, fn ($q, $id) => $q->where('ocupacao_id', $id))
)
->when($params->especialidade_id ?? null, fn ($q, $id) => $q->where('especialidade_id', $id))
)
)
->orderBy('nome')
->get(['id', 'nome']);
}
private static function setBuscaWhereAgendamento($query, $params, $tableAlias = '')
{
if (!empty($tableAlias)) {
$tableAlias .= '.';
}
return $query->where("{$tableAlias}.paciente_id", $params->paciente_id)
->when(!is_null($params->inicio ?? null) && !is_null($params->fim ?? null), fn ($q) => $q
->whereBetween("{$tableAlias}.horario", [$params->inicio, $params->fim])
);
}
private static function getDadosListCadastro()
{
$params = (object) request()->all();
$agendamento = Agendamento::find($params->agendamento_id ?? null);
$paciente = Paciente::setEagerLoads([])->where('id', $params->paciente_id)->first();
$tipo = $agendamento->tipo ?? (isset($params->especialidade_id)
? self::getTipo($params->paciente_id, $params->infraestrutura_id, $params->especialidade_id)
: null);
$params->paciente_nao_municipe = $paciente->classificacao_id !== Classificacao::MUNICIPE;
$params->tipo_id = $tipo->id ?? null;
switch (request()->listar) {
case 'unidades': {
return [
'unidades' => self::getCadastroUnidades($params),
'infraestruturas' => [],
'ocupacoes' => [],
'especialidades' => [],
'usuarios' => [],
'datas' => [],
'horarios' => [],
];
}
case 'infraestruturas': {
return [
'infraestruturas' => self::getCadastroInfraestruturas($params),
'ocupacoes' => [],
'especialidades' => [],
'usuarios' => [],
'datas' => [],
'horarios' => [],
];
}
case 'ocupacoes': {
return [
'ocupacoes' => self::getCadastroOcupacoes(
$params,
$agendamento->jornada->lotacao->ocupacao_id ?? null
),
'especialidades' => [],
'usuarios' => [],
'datas' => [],
'horarios' => [],
];
}
case 'especialidades': {
return [
'especialidades' => self::getCadastroEspecialidades(
$params,
$agendamento->jornada->especialidade_id ?? null
),
'usuarios' => [],
'datas' => [],
'horarios' => [],
];
}
case 'usuarios': {
return [
'faltas' => self::getFaltas($params),
'usuarios' => self::getCadastroUsuarios($params),
'datas' => [],
'horarios' => [],
];
}
case 'datas': {
return [
'datas' => self::getCadastroDatas($params),
'datas_bloqueadas' => self::getCadastroDatasBloqueadas($params),
'datas_com_agendamento' => self::getCadastroDatasComAgendamento($params),
'situacao' => self::getCadastroDatasComSituacao($params),
'horarios' => [],
];
}
case 'horarios': {
return [
'horarios' => self::getCadastroHorarios($params),
'horarios_com_situacao' => self::getCadastroHorariosComSituacao($params),
'quantidade_demanda_espontanea' => self::getQuantidadeDemandaEspontanea($params),
'model' => [
'demanda_espontanea_observa_vaga_atendimento' => self::getDemandaEspontaneaObservaVagaAtendimento(
$params->infraestrutura_id,
$params->especialidade_id,
),
],
'tipo_nome' => $tipo->nome,
];
}
}
}
private static function getCadastroUnidades($params)
{
$infraestruturaLogada = self::getCurrentCentroCusto()->infraestrutura;
return Unidade::whereHas('infraestruturas', fn ($q) => $q
->where(fn ($q) => $q
->where('id', $infraestruturaLogada->id)
->orWhereHas('ambulatorioParametrizacaoJornadaInfraestrutura', fn ($q) => $q
->whereHas('infraestruturasAgendadoras', fn ($q) => $q
->where('infraestrutura_id', $infraestruturaLogada->id)
)
)
)
->whereHas('lotacoes', fn ($q) => $q
->whereHas('ambulatorioJornadas', fn ($q) => $q
->whereNull('situacao_id')
->whereDate('data', '>=', Carbon::now())
->whereHas('especialidade', fn ($q) =>
self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
)
)
)
->orderBy('nome_fantasia')
->get(['id', 'nome_fantasia']);
}
private static function getCadastroInfraestruturas($params)
{
$infraestruturaLogada = self::getCurrentCentroCusto()->infraestrutura;
return Infraestrutura::setEagerLoads([])
->where('unidade_id', $params->unidade_id)
->where(fn ($q) => $q
->where('id', $infraestruturaLogada->id)
->orWhereHas('ambulatorioParametrizacaoJornadaInfraestrutura', fn ($q) => $q
->whereHas('infraestruturasAgendadoras', fn ($q) => $q
->where('infraestrutura_id', $infraestruturaLogada->id)
)
)
)
->whereHas('lotacoes', fn ($q) => $q
->whereHas('ambulatorioJornadas', fn ($q) => $q
->whereNull('situacao_id')
->whereDate('data', '>=', Carbon::now())
->whereHas('especialidade', fn ($q) =>
self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
)
)
->get(['id', 'nome', 'principal', 'unidade_id'])
->map(fn ($infraestrutura) => [
'id' => $infraestrutura->id,
'nome' => $infraestrutura->nome
])
->sortBy('nome')
->values();
}
private static function getCadastroOcupacoes($params, $ocupacaoId = null)
{
return DB::table('sigtap.ocupacao as ocupacao')
->when($ocupacaoId, fn ($q, $id) => $q->where('id', $id))
->whereExists(function ($query) use ($params) {
$query->select(DB::raw(1))
->from('cadastro_lotacao as lotacao')
->whereColumn('lotacao.ocupacao_id', 'ocupacao.id')
->where('lotacao.infraestrutura_id', $params->infraestrutura_id)
->whereExists(function ($query) use ($params) {
$query->select(DB::raw(1))
->from('ambulatorio_jornada as jornada')
->join(
'cadastro_especialidade as especialidade',
'especialidade.id',
'jornada.especialidade_id'
)
->whereColumn('jornada.lotacao_id', 'lotacao.id')
->whereNull('jornada.situacao_id')
->whereDate('jornada.data', '>=', Carbon::now())
->where(fn ($query) => self::setCadastroWhereEspecialidadePaciente(
$query,
$params->paciente_id,
'especialidade'
))
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('especialidade.permite_agendamento_nao_municipe', true)
);
});
})
->orderByRaw("CONCAT(ocupacao.codigo, ' - ', ocupacao.nome)")
->select(DB::raw("
ocupacao.id,
CONCAT(ocupacao.codigo, ' - ', ocupacao.nome) as descricao
"))
->get();
}
private static function getCadastroEspecialidades($params, $especialidadeId = null)
{
return Especialidade::when($especialidadeId, fn ($q, $id) => $q->where('id', $id))
->where(fn ($q) => self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id))
->whereHas('ambulatorioJornadas', fn ($q) => $q
->whereNull('situacao_id')
->whereDate('data', '>=', Carbon::now())
->whereHas('lotacao', fn ($q) => $q
->where('infraestrutura_id', $params->infraestrutura_id)
->where('ocupacao_id', $params->ocupacao_id)
)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
->orderBy('nome')
->get(['id', 'nome', 'tratamento', 'permite_agendamento_mesmo_dia','is_regulado']);
}
private static function getFaltas($params)
{
$parametrizacao = ParametrizacaoInfraestrutura::where('infraestrutura_id', $params->infraestrutura_id)
->first();
$parametrizacaoEspecialidade = $parametrizacao->especialidades()
->where('especialidade_id', $params->especialidade_id)
->first();
if (is_null($parametrizacaoEspecialidade)
|| is_null($parametrizacaoEspecialidade->avisa_falta_quantidade)
) {
return collect();
}
$dataAtual = Carbon::today()->endOfDay();
$data = $dataAtual->copy()->subDays($parametrizacaoEspecialidade->avisa_falta_dias)->startOfDay();
$faltas = Agendamento::where('paciente_id', $params->paciente_id)
->where('status_id', AgendamentoStatusEnum::FALTA_DO_PACIENTE)
->whereHas('jornada', fn ($q) => $q
->whereBetween('data', [$data, $dataAtual])
)
->get()
->map(fn ($agendamento) => [
'data' => $agendamento->jornada->data->format('d/m/Y'),
'horario' => $agendamento->horario->format('H:i'),
'especialidade' => $agendamento->jornada->especialidade->nome,
'usuario' => $agendamento->jornada->lotacao->usuario->cidadao()
->setEagerLoads([])
->first(['nome'])
->nome,
]);
return $faltas->count() >= $parametrizacaoEspecialidade->avisa_falta_quantidade ? $faltas : collect();
}
private static function getCadastroUsuarios($params)
{
$campoQuantidade = self::getCampoQuantidade($params->tipo_id);
return Usuario::setEagerLoads([])
->whereHas('lotacoes', fn ($q) => $q
->where('infraestrutura_id', $params->infraestrutura_id)
->where('ocupacao_id', $params->ocupacao_id)
->whereHas('ambulatorioJornadas', fn ($q) => $q
->whereNotNull($campoQuantidade)
->whereNull('situacao_id')
->whereDate('data', '>=', Carbon::now())
->where('especialidade_id', $params->especialidade_id)
->whereHas('especialidade', fn ($q) =>
self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
)
)
->get(['id', 'cidadao_id'])
->map(fn ($usuario) => [
'id' => $usuario->id,
'nome' => $usuario->cidadao()->setEagerLoads([])->first(['nome'])->nome,
])
->sortBy('nome')
->values();
}
private static function getCadastroDatas($params)
{
$campoQuantidade = self::getCampoQuantidade($params->tipo_id);
$campoQuantidadeCidadao = self::getCampoQuantidadeConectasusCidadao($params->tipo_id);
$feriados = self::getFeriados()->map(fn ($f) => Carbon::createFromFormat('d/m/Y', $f->data)->setTime(0, 0));
$jornadasAgrupadas = Jornada::whereNotNull($campoQuantidade)
->whereNull('situacao_id')
->whereDate('data', '>=', Carbon::now())
->whereHas('lotacao', fn ($q) => $q
->where('infraestrutura_id', $params->infraestrutura_id)
->where('ocupacao_id', $params->ocupacao_id)
->where('usuario_id', $params->usuario_id)
)
->where('especialidade_id', $params->especialidade_id)
->whereHas('especialidade', fn ($q) =>
self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
->groupBy('data')
->select(
'data',
DB::raw("SUM({$campoQuantidade}) + SUM(COALESCE({$campoQuantidadeCidadao}, 0)) as quantidade_total")
)
->get()
->reject(function ($jornadasAgrupadas) use ($feriados) {
return $feriados->contains($jornadasAgrupadas->data);
})
->reject(function ($jornadasAgrupadas) use ($params) {
return JornadaBloqueio::where('infraestrutura_id', $params->infraestrutura_id)
->where('especialidade_id', $params->especialidade_id)
->whereDate('inicio', '<=', $jornadasAgrupadas->data)
->whereDate('fim', '>=', $jornadasAgrupadas->data)
->where(function ($query) use ($params) {
$query->whereNull('usuario_id')
->orWhere('usuario_id', $params->usuario_id);
})
->exists();
})
->when(
self::getDataMinimaOcorrencia($params->paciente_id, $params->especialidade_id),
fn ($collection, $dataMinima) => $collection->reject(
fn ($jornada) => $jornada->data->lt($dataMinima)
)
)
->values();
$agendados = Agendamento::where('tipo_id', $params->tipo_id)
->whereNotIn('status_id', self::getStatusLiberamHorario())
->whereHas('jornada', fn ($q) => $q
->whereDate('data', '>=', Carbon::now())
->whereHas('lotacao', fn ($q) => $q
->where('infraestrutura_id', $params->infraestrutura_id)
->where('ocupacao_id', $params->ocupacao_id)
->where('usuario_id', $params->usuario_id)
)
->where('especialidade_id', $params->especialidade_id)
->whereHas('especialidade', fn ($q) =>
self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
)
->get()
->map(fn ($agendamento) => [
'data' => $agendamento->jornada->data->format('d/m/Y'),
]);
$demandas = DemandaEspontanea::whereHas('jornada', fn ($q) => $q
->whereDate('data', '>=', Carbon::now())
->whereHas('lotacao', fn ($q) => $q
->where('infraestrutura_id', $params->infraestrutura_id)
->where('ocupacao_id', $params->ocupacao_id)
->where('usuario_id', $params->usuario_id)
)
->where('especialidade_id', $params->especialidade_id)
->whereHas('especialidade', fn ($q) =>
self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
)
->get()
->map(fn ($demanda) => [
'data' => $demanda->jornada->data->format('d/m/Y'),
]);
$contagens = [];
foreach ($agendados as $agendamento) {
$data = $agendamento["data"];
$contagens[$data] = isset($contagens[$data]) ? $contagens[$data] + 1 : 1;
}
foreach ($demandas as $demanda) {
$data = $demanda["data"];
$contagens[$data] = isset($contagens[$data]) ? $contagens[$data] + 1 : 1;
}
foreach ($jornadasAgrupadas as &$jornada) {
$data = $jornada["data"]->format("d/m/Y");
$jornada["quantidade_agendada"] = $contagens[$data] ?? 0;
}
return $jornadasAgrupadas;
}
private static function getCadastroDatasBloqueadas($params)
{
$datas = collect();
JornadaBloqueio::where('infraestrutura_id', $params->infraestrutura_id)
->where('especialidade_id', $params->especialidade_id)
->where(fn ($q) => $q
->whereDate('inicio', '>=', Carbon::today())
->orWhereDate('fim', '>=', Carbon::today())
)
->where(function ($query) use ($params) {
$query->whereNull('usuario_id')
->orWhere('usuario_id', $params->usuario_id);
})
->get(['inicio', 'fim'])
->each(function ($parametrizacao) use ($datas) {
$dataAtual = Carbon::today();
$data = $parametrizacao->inicio;
while ($data <= $parametrizacao->fim) {
if ($data >= $dataAtual) {
$datas->push($data->format('d/m/Y'));
}
$data->addDay();
}
});
return $datas;
}
private static function getCadastroHorarios($params)
{
$horarios = collect();
$data = Carbon::createFromFormat('d/m/Y', $params->data);
$dataMinima = self::getDataMinimaOcorrencia($params->paciente_id, $params->especialidade_id);
if ($dataMinima && $data->lt($dataMinima)) {
return $horarios;
}
Jornada::whereDate('data', $data)
->whereNotNull(self::getCampoQuantidade($params->tipo_id))
->whereNull('situacao_id')
->whereHas('lotacao', fn ($q) => $q
->where('infraestrutura_id', $params->infraestrutura_id)
->where('ocupacao_id', $params->ocupacao_id)
->where('usuario_id', $params->usuario_id)
)
->where('especialidade_id', $params->especialidade_id)
->whereHas('especialidade', fn ($q) =>
self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
->get([
'id',
'inicio',
'tipo_id',
'quantidade_consulta',
'quantidade_retorno',
'tempo_consulta',
'tempo_retorno',
'quantidade_consulta_conectasus_cidadao',
'quantidade_retorno_conectasus_cidadao',
'permite_agendamento_somente_supervisor',
])
->each(function ($jornada) use ($horarios, $data, $params) {
$jornada->inicio = $data->copy()->setTimeFromTimeString($jornada->inicio);
if ($jornada->tipo_id === JornadaTipo::FIXA) {
self::setHorarioJornadaFixa($horarios, $jornada, $params->tipo_id);
}
else if ($jornada->tipo_id === JornadaTipo::DINAMICA) {
self::setHorariosJornadaDinamica($horarios, $jornada, $params->tipo_id);
}
});
return $horarios->sortBy('horario')->values();
}
private static function setHorarioJornadaFixa($horarios, $jornada, $tipoId)
{
$horarios->push((object) [
'jornada_id' => $jornada->id,
'horario' => $jornada->inicio->format('H:i'),
'quantidade_agendada' => Agendamento::where('jornada_id', $jornada->id)
->where('tipo_id', $tipoId)
->whereTime('horario', $jornada->inicio)
->whereNotIn('status_id', self::getStatusLiberamHorario())
->count(),
'quantidade_total' => $jornada->{self::getCampoQuantidade($tipoId)}
+ ($jornada->{self::getCampoQuantidadeConectasusCidadao($tipoId)} ?? 0),
'obrigatorio_autenticacao_supervisor' => $jornada->permite_agendamento_somente_supervisor
&& !SupervisorService::isUsuarioLogadoSupervisor(),
]);
}
private static function setHorariosJornadaDinamica($horarios, $jornada, $tipoId)
{
$agendamentos = self::getAgendamentos($jornada->id);
$agendamentos->each(fn ($a) => $a->horario = $jornada->inicio->copy()->setTimeFromTimeString($a->horario));
$agendamentos->where('tipo_id', $tipoId)
->each(fn ($agendamento) => $horarios->push((object) [
'jornada_id' => $jornada->id,
'horario' => $agendamento->horario->format('H:i'),
'quantidade_agendada' => $agendamento->quantidade_agendada,
'quantidade_total' => 1,
'obrigatorio_autenticacao_supervisor' => $jornada->permite_agendamento_somente_supervisor
&& !SupervisorService::isUsuarioLogadoSupervisor(),
]));
$quantidade = $agendamentos->where('tipo_id', $tipoId)->count();
$quantidadeTotal = $jornada->{self::getCampoQuantidade($tipoId)}
+ ($jornada->{self::getCampoQuantidadeConectasusCidadao($tipoId)} ?? 0);
if ($quantidade < $quantidadeTotal) {
$data = $jornada->inicio->copy();
self::setHorarioJornadaDinamica(
$horarios,
$agendamentos,
$jornada,
$tipoId,
$quantidade,
$data,
$quantidadeTotal
);
}
}
private static function getAgendamentos($jornadaId)
{
return Agendamento::where('jornada_id', $jornadaId)
->whereNotIn('status_id', self::getStatusLiberamHorario())
->groupBy([
'tipo_id',
'horario',
])
->orderBy('horario')
->select(
'tipo_id',
'horario',
DB::raw("COUNT(id) as quantidade_agendada")
)
->get();
}
private static function setHorarioJornadaDinamica($horarios, $agendamentos, $jornada, $tipoId, $quantidade, $data, $quantidadeTotal = null)
{
if ($quantidadeTotal === null) {
$quantidadeTotal = $jornada->{self::getCampoQuantidade($tipoId)}
+ ($jornada->{self::getCampoQuantidadeConectasusCidadao($tipoId)} ?? 0);
}
$agendamento = $agendamentos->where('horario', $data)->first();
if (is_null($agendamento)) {
$final = $data->copy()->addMinutes($jornada->{self::getCampoTempo($tipoId)});
$agendamento = $agendamentos
->where('horario', '>', $data)
->where('horario', '<', $final)
->first();
if ($agendamento) {
self::setHorarioJornadaDinamica(
$horarios,
$agendamentos,
$jornada,
$tipoId,
$quantidade,
$agendamento->horario,
$quantidadeTotal
);
}
else {
$horarios->push((object) [
'jornada_id' => $jornada->id,
'horario' => $data->format('H:i'),
'quantidade_agendada' => 0,
'quantidade_total' => 1,
'obrigatorio_autenticacao_supervisor' => $jornada->permite_agendamento_somente_supervisor
&& !SupervisorService::isUsuarioLogadoSupervisor(),
]);
if (++$quantidade < $quantidadeTotal) {
$data->addMinutes($jornada->{self::getCampoTempo($tipoId)});
self::setHorarioJornadaDinamica($horarios, $agendamentos, $jornada, $tipoId, $quantidade, $data, $quantidadeTotal);
}
}
}
else {
if ($agendamento->tipo_id == $tipoId) {
$data->addMinutes($jornada->{self::getCampoTempo($tipoId)});
self::setHorarioJornadaDinamica($horarios, $agendamentos, $jornada, $tipoId, $quantidade, $data, $quantidadeTotal);
}
else {
$data = $agendamento->horario->copy()->addMinutes($jornada->{self::getCampoTempo($tipoId, true)});
self::setHorarioJornadaDinamica($horarios, $agendamentos, $jornada, $tipoId, $quantidade, $data, $quantidadeTotal);
}
}
}
private static function getCadastroHorariosComSituacao($params)
{
return Jornada::whereDate('data', Carbon::createFromFormat('d/m/Y', $params->data))
->whereNotNull(self::getCampoQuantidade($params->tipo_id))
->whereNotNull('situacao_id')
->whereHas('lotacao', fn ($q) => $q
->where('infraestrutura_id', $params->infraestrutura_id)
->where('ocupacao_id', $params->ocupacao_id)
->where('usuario_id', $params->usuario_id)
)
->where('especialidade_id', $params->especialidade_id)
->whereHas('especialidade', fn ($q) =>
self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
->orderBy('inicio')
->get([
'situacao_id',
'inicio',
'fim',
'data'
])
->map(fn ($jornada) => [
'inicio' => $jornada->inicio->format('H:i'),
'fim' => $jornada->fim->format('H:i'),
'situacao_nome' => $jornada->situacao->nome,
]);
}
private static function getCadastroDatasComSituacao($params){
return Jornada::whereNotNull('situacao_id')
->whereHas('lotacao', fn ($q) => $q
->where('infraestrutura_id', $params->infraestrutura_id)
->where('usuario_id', $params->usuario_id)
)
->join('ambulatorio_jornada_situacao', 'ambulatorio_jornada_situacao.id', 'ambulatorio_jornada.situacao_id')
->groupBy(['ambulatorio_jornada_situacao.nome', 'ambulatorio_jornada.data'])
->get([
'ambulatorio_jornada_situacao.nome as situacao_nome',
'ambulatorio_jornada.data'
])
->map(fn($jornada) =>[
'data' => $jornada->data->format('d/m/Y'),
'situacao_nome'=> $jornada->situacao_nome
]);
}
private static function setCadastroWhereEspecialidadePaciente($query, $pacienteId, $tableAlias = '')
{
if (!empty($tableAlias)) {
$tableAlias .= '.';
}
$paciente = Paciente::find($pacienteId);
$idade = $paciente->cidadao->idade;
return $query->where(fn ($q) => $q
->when($idade, fn ($q) => $q
->where(fn ($q) => $q
->whereNull("{$tableAlias}idade_minima")
->orWhere("{$tableAlias}idade_minima", '<=', $idade['anos'])
)
->where(fn ($q) => $q
->whereNull("{$tableAlias}idade_maxima")
->orWhere("{$tableAlias}idade_maxima", '>=', $idade['anos'])
)
)
->where(fn ($q) => $q
->whereNull("{$tableAlias}sexo_id")
->orWhere("{$tableAlias}sexo_id", $paciente->cidadao->sexo_id)
)
);
}
private static function getDadosListAgendamentoDemandaEspontanea()
{
$params = (object) request()->all();
$params->periodo = [
Carbon::createFromFormat('d/m/Y', $params->periodo['start'])->startOfDay(),
Carbon::createFromFormat('d/m/Y', $params->periodo['end'])->endOfDay(),
];
switch (request()->listar) {
case 'origens': {
return [
'origens' => self::getAgendamentoDemandaEspontaneaOrigens($params),
];
}
}
}
private static function getAgendamentoDemandaEspontaneaOrigens($params)
{
$origens = collect();
$hasAgendamentos = DB::table('ambulatorio_agendamento AS agendamento')
->join('ambulatorio_jornada AS jornada', 'jornada.id', 'agendamento.jornada_id')
->where('agendamento.paciente_id', $params->paciente_id)
->whereBetween('jornada.data', $params->periodo)
->exists();
if ($hasAgendamentos) {
$origens->push((object) ['value' => 'consulta-agendada', 'text' => 'Consulta agendada']);
}
$hasDemandasEspontaneas = DB::table('ambulatorio_demanda_espontanea AS demanda_espontanea')
->where('demanda_espontanea.paciente_id', $params->paciente_id)
->whereBetween('demanda_espontanea.data', $params->periodo)
->exists();
if ($hasDemandasEspontaneas) {
$origens->push((object) ['value' => 'demanda-espontanea', 'text' => 'Demanda espontânea']);
}
return $origens;
}
public static function getSugestao($pacienteId, $infraestruturaId, $ocupacaoId, $especialidadeId, $usuarioId)
{
$agendamento = Agendamento::find(request()->agendamento_id ?? null);
$paciente = Paciente::setEagerLoads([])->where('id', $pacienteId)->first();
$tipo = $agendamento->tipo ?? self::getTipo($pacienteId, $infraestruturaId, $especialidadeId);
$datasAgendamentosPaciente = self::getAgendamentosPaciente($pacienteId)
->map(fn ($agendamento) => Carbon::createFromFormat('d/m/Y', $agendamento['data'])->startOfDay());
$params = (object) [
'paciente_id' => $pacienteId,
'paciente_nao_municipe' => $paciente->classificacao_id !== Classificacao::MUNICIPE,
'infraestrutura_id' => $infraestruturaId,
'ocupacao_id' => $ocupacaoId,
'especialidade_id' => $especialidadeId,
'usuario_id' => $usuarioId,
'tipo_id' => $tipo->id,
];
$horarioDisponivel = null;
self::getCadastroDatas($params)
->each(function ($data) use (&$horarioDisponivel, $params, $pacienteId, $datasAgendamentosPaciente) {
if($data->quantidade_agendada >= $data->quantidade_total){
return true;
}
if ($datasAgendamentosPaciente->contains($data->data->startOfDay())) {
return true;
}
$params->data = $data->data->format('d/m/Y');
$encontrado = false;
self::getCadastroHorarios($params)
->each(function ($horario) use ($data, &$encontrado, &$horarioDisponivel, $pacienteId) {
if ($horario->quantidade_agendada < $horario->quantidade_total) {
try {
self::validateAgendamento($pacienteId, $horario->jornada_id, $horario->horario);
$horarioDisponivel = (object) [
'jornada_id' => $horario->jornada_id,
'horario' => $horario->horario,
'data' => $data->data->format('d/m/Y'),
'dia_semana' => DiaSemana::find($data->dia_semana)->descricao,
'obrigatorio_autenticacao_supervisor' => $horario->obrigatorio_autenticacao_supervisor,
];
$encontrado = true;
return false;
}
catch (AgendamentoConflitanteException
| AgendamentoNaoPermitidoJornadaGrupoException $e
) { }
}
});
if ($encontrado) {
return false;
}
});
if (!is_null($horarioDisponivel)) {
$horarioDisponivel->tipo_nome = Str::lower($tipo->nome);
}
return $horarioDisponivel;
}
public static function unidadePaciente($pacienteId, $infraestruturaId)
{
$possuiJornadaUnidadePaciente = false;
$parametrizacao = ParametrizacaoAgendamento::first();
if ($parametrizacao->avisa_paciente_outra_unidade) {
$paciente = Paciente::findOrFail($pacienteId);
$unidadeId = Infraestrutura::findOrFail($infraestruturaId)->unidade_id;
if ($unidadeId !== $paciente->unidade_id) {
$possuiJornadaUnidadePaciente = Unidade::where('id', $paciente->unidade_id)->exists();
}
}
return [
'unidade_com_jornada' => $possuiJornadaUnidadePaciente,
'unidade' => $paciente->unidade->nome_fantasia ?? null,
];
}
private static function getTipo($pacienteId, $infraestruturaId, $especialidadeId)
{
$ultimaData = self::getUltimoDataAgendamento($pacienteId, $infraestruturaId, $especialidadeId);
$diasParaRetorno = ParametrizacaoService::getQuantidadeDiasRetorno($infraestruturaId, $especialidadeId);
$tipoId = $diasParaRetorno === 0 || is_null($ultimaData) || Carbon::today()->diff($ultimaData)->days > $diasParaRetorno
? AgendamentoTipoEnum::CONSULTA
: AgendamentoTipoEnum::RETORNO;
return AgendamentoTipo::find($tipoId);
}
private static function getUltimoDataAgendamento($pacienteId, $infraestruturaId, $especialidadeId)
{
$ultimaJornada = Jornada::where('especialidade_id', $especialidadeId)
->whereHas('lotacao', fn ($q) => $q
->where('infraestrutura_id', $infraestruturaId)
)
->whereHas('agendamentos', fn ($q) => $q
->where('paciente_id', $pacienteId)
->where('status_id', AgendamentoStatusEnum::REALIZADO)
)
->orderByDesc('data')
->first(['data']);
return is_null($ultimaJornada) ? null : $ultimaJornada->data->startOfDay();
}
private static function getCampoQuantidade($tipoId)
{
if ($tipoId === AgendamentoTipoEnum::CONSULTA) {
return 'quantidade_consulta';
}
else if ($tipoId === AgendamentoTipoEnum::RETORNO) {
return 'quantidade_retorno';
}
}
private static function getCampoQuantidadeConectasusCidadao($tipoId)
{
if ($tipoId === AgendamentoTipoEnum::CONSULTA) {
return 'quantidade_consulta_conectasus_cidadao';
}
else if ($tipoId === AgendamentoTipoEnum::RETORNO) {
return 'quantidade_retorno_conectasus_cidadao';
}
}
private static function getCampoTempo($tipoId, $inverso = false)
{
if ($tipoId === AgendamentoTipoEnum::CONSULTA) {
return $inverso ? 'tempo_retorno' : 'tempo_consulta';
}
else if ($tipoId === AgendamentoTipoEnum::RETORNO) {
return $inverso ? 'tempo_consulta' : 'tempo_retorno';
}
}
private static function getDemandaEspontaneaObservaVagaAtendimento($infraestruturaId, $especialidadeId)
{
if (is_null($infraestruturaId) || is_null($especialidadeId)) {
return false;
}
$parametrizacao = ParametrizacaoInfraestruturaEspecialidade::whereHas('parametrizacaoInfraestrutura', fn ($q) => $q
->where('infraestrutura_id', $infraestruturaId)
)
->where('especialidade_id', $especialidadeId)
->first(['demanda_espontanea_observa_vaga_atendimento']);
return $parametrizacao->demanda_espontanea_observa_vaga_atendimento ?? false;
}
private static function getDemandaEspontaneaObservaVagaAtendimentoAgendamento($agendamento)
{
return self::getDemandaEspontaneaObservaVagaAtendimento(
$agendamento->jornada->lotacao->infraestrutura_id,
$agendamento->jornada->especialidade_id,
);
}
public static function store()
{
self::validateStore();
$agendamento = new Agendamento();
$deveIntegrarSafecare = false;
DB::transaction(function () use ($agendamento, &$deveIntegrarSafecare) {
$jornada = Jornada::findOrFail(request()->jornada_id);
self::validateAgendamento(
request()->paciente_id,
$jornada->id,
request()->horario,
request()->motivo_agendamento_excedente
);
$agendamento->fill([
'jornada_id' => $jornada->id,
'tipo_id' => self::getTipo(
request()->paciente_id,
$jornada->lotacao->infraestrutura_id,
$jornada->especialidade_id
)->id,
'horario' => request()->horario,
'paciente_id' => request()->paciente_id,
'motivo_agendamento_excedente' => request()->motivo_agendamento_excedente,
'observacao' => request()->observacao,
'status_id' => AgendamentoStatusEnum::AGENDADO,
'regulacao_solicitacao_id' => request()->regulacao_solicitacao_id,
]);
$agendamento->save();
(new SmsService($agendamento->id))->send();
if (optional(ParametrizacaoIntegracaoAplicativo::first())->habilita_integracao_aplicativo_feridas) {
$infraestruturaId = $jornada->lotacao->infraestrutura_id;
$ocupacaoCodigo = $jornada->lotacao->ocupacao->codigo ?? '';
$isEnfermagem = substr($ocupacaoCodigo, 0, 4) == '2235' || substr($ocupacaoCodigo, 0, 4) == '3222';
$setorTipo = $isEnfermagem ? SetorTipo::ENFERMAGEM : SetorTipo::CONSULTORIO;
$centroCusto = CentroCusto::where('infraestrutura_id', $infraestruturaId)
->whereHas('setor', function ($query) use ($setorTipo) {
$query->where('tipo_id', $setorTipo);
})
->first();
if (!$centroCusto) {
$setorNome = $isEnfermagem ? 'enfermagem' : 'consultório';
abort(
Response::HTTP_UNPROCESSABLE_ENTITY,
"Não foi encontrado um centro de custo de {$setorNome} para a unidade da jornada."
);
}
$opcaoEntrada = OpcaoEntrada::whereHas('modulo', function ($query) {
$query->where('identificador', Modulo::AMBULATORIO);
})
->whereHas('centroCusto', function ($query) use ($infraestruturaId) {
$query->where('infraestrutura_id', $infraestruturaId)
->whereHas('setor', function ($subQuery) {
$subQuery->where('tipo_id', SetorTipo::RECEPCAO);
});
})
->where('prioridade_obrigatoria', 0)
->first();
if (!$opcaoEntrada) {
abort(
Response::HTTP_UNPROCESSABLE_ENTITY,
'Não há opção de entrada parametrizada para a recepção da unidade da jornada.'
);
}
$payload = [
'paciente_id' => request('paciente_id'),
'status_id' => Status::EM_ESPERA,
'opcao_entrada_id' => $opcaoEntrada->id,
'prioridade_id' => null,
'centro_custo_id' => $centroCusto->id,
'data_jornada' => $jornada->data,
'habilita_integracao_aplicativo_feridas' => optional(ParametrizacaoIntegracaoAplicativo::first())->habilita_integracao_aplicativo_feridas,
];
$fila = FilaService::store((object) $payload);
$filaId = is_array($fila) ? ($fila['id'] ?? null) : ($fila->id ?? null);
$atendimento = new Atendimento();
$atendimento->fill([
'fila_id' => $filaId,
'paciente_id' => request('paciente_id'),
'agendamento_id' => $agendamento->id,
'demanda_espontanea_id' => null,
]);
$atendimento->save();
$deveIntegrarSafecare = true;
}
});
if ($deveIntegrarSafecare) {
DB::afterCommit(fn() => self::integrarSafecare($agendamento));
}
$agendamento->refresh();
$agendamento->data = $agendamento->jornada()->first('data')->data->format('d/m/Y');
return $agendamento;
}
private static function integrarSafecare(Agendamento $agendamento): void
{
try {
app(RegistroPacienteAposAgendamento::class)->integracaoPaciente($agendamento);
} catch (Throwable $e) {
logger()->error('Falha na integração Safecare', [
'agendamento_id' => $agendamento->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
}
}
private static function validateStore()
{
request()->validate([
'paciente_id' => [
'required',
Rule::exists(Paciente::class, 'id'),
],
'jornada_id' => [
'required',
Rule::exists(Jornada::class, 'id'),
],
'horario' => [
'required',
'date_format:H:i',
],
'observacao' => [
'nullable',
'string',
'max:500',
],
'motivo_agendamento_excedente' => [
'nullable',
'string',
'max:500',
],
'regulacao_solicitacao_id' => [
'nullable',
'integer',
Rule::exists(RegulacaoSolicitacao::class, 'id'),
],
]);
}
public static function update($id)
{
self::validateUpdate();
$agendamentoAtual = Agendamento::findOrFail($id);
$agendamentoNovo = new Agendamento();
DB::transaction(function () use ($agendamentoAtual, $agendamentoNovo) {
if ($agendamentoAtual->status_id !== AgendamentoStatusEnum::AGENDADO) {
throw new NaoPodeAlterarStatusAgendamentoException();
}
$jornada = Jornada::findOrFail(request()->jornada_id);
self::validateAgendamento(
$agendamentoAtual->paciente_id,
$jornada->id,
request()->horario,
request()->motivo_agendamento_excedente,
$agendamentoAtual
);
$agendamentoNovo->fill([
'agendamento_anterior_id' => $agendamentoAtual->id,
'jornada_id' => $jornada->id,
'tipo_id' => $agendamentoAtual->tipo_id,
'horario' => request()->horario,
'paciente_id' => $agendamentoAtual->paciente_id,
'motivo_agendamento_excedente' => request()->motivo_agendamento_excedente,
'observacao' => request()->observacao,
'status_id' => AgendamentoStatusEnum::AGENDADO,
'regulacao_solicitacao_id' => $agendamentoAtual->regulacao_solicitacao_id,
]);
$agendamentoNovo->save();
$agendamentoAtual->update(['status_id' => AgendamentoStatusEnum::REAGENDADO_PELO_PACIENTE]);
(new SmsService($agendamentoAtual->id))->cancel();
(new SmsService($agendamentoNovo->id))->send();
});
$agendamentoNovo->refresh();
$agendamentoNovo->data = $agendamentoNovo->jornada()->first('data')->data->format('d/m/Y');
return $agendamentoNovo;
}
private static function validateUpdate()
{
request()->validate([
'jornada_id' => [
'required',
Rule::exists(Jornada::class, 'id'),
],
'horario' => [
'required',
'date_format:H:i',
],
'observacao' => [
'nullable',
'string',
'max:500',
],
'motivo_agendamento_excedente' => [
'nullable',
'string',
'max:500',
]
]);
}
private static function validateAgendamento(
$pacienteId,
$jornadaId,
$horario,
$motivoAgendamentoExcedente = null,
$agendamento = null
) {
self::validateProcedimentoGrupo($pacienteId, $jornadaId);
self::validateConflito($pacienteId, $jornadaId, $horario, $motivoAgendamentoExcedente, $agendamento);
}
private static function validateProcedimentoGrupo($pacienteId, $jornadaId)
{
$especialidade = Jornada::findOrFail($jornadaId)->especialidade()
->first(['procedimento_grupo_codigo']);
if (is_null($especialidade->procedimento_grupo_codigo)) {
return;
}
$paciente = Paciente::findOrFail($pacienteId);
$procedimento = Procedimento::where('codigo', $especialidade->procedimento_grupo_codigo)
->orderByDesc('competencia_id')
->first();
$validator = new ProcedimentoValidationService($procedimento);
if (!$validator->isSexoValid($paciente->cidadao->sexo)
|| !$validator->isIdadeValid($paciente->cidadao->idade['anos'])
) {
throw new AgendamentoNaoPermitidoJornadaGrupoException();
}
}
private static function validateConflito(
$pacienteId,
$jornadaId,
$horario,
$motivoAgendamentoExcedente = null,
$agendamento = null
) {
if (!empty($motivoAgendamentoExcedente)) {
return;
}
if (!is_null($agendamento)
&& $agendamento->jornada_id == $jornadaId
&& $agendamento->horario->format('H:i') == $horario
) {
return;
}
$paciente = Paciente::findOrFail($pacienteId);
$jornada = Jornada::findOrFail($jornadaId);
$tipo = $agendamento->tipo ?? self::getTipo(
$paciente->id,
$jornada->lotacao->infraestrutura_id,
$jornada->especialidade_id
);
$params = (object) [
'paciente_id' => $paciente->id,
'paciente_nao_municipe' => $paciente->classificacao_id !== Classificacao::MUNICIPE,
'unidade_id' => $jornada->lotacao->infraestrutura->unidade_id,
'infraestrutura_id' => $jornada->lotacao->infraestrutura_id,
'ocupacao_id' => $jornada->lotacao->ocupacao_id,
'especialidade_id' => $jornada->especialidade_id,
'usuario_id' => $jornada->lotacao->usuario_id,
'data' => $jornada->data->format('d/m/Y'),
'tipo_id' => $tipo->id,
];
$horarioJornada = self::getCadastroHorarios($params)->where('horario', $horario)->first();
$agendamento = Agendamento::where('jornada_id', $jornadaId)
->where('horario', $horario)
->whereNotIn('status_id', self::getStatusLiberamHorario())
->first();
if (!is_null($agendamento)) {
if ($jornada->tipo_id === JornadaTipo::FIXA
&& $horarioJornada->quantidade_agendada < $horarioJornada->quantidade_total
) {
return;
}
throw new AgendamentoConflitanteException();
}
}
public static function getStatusLiberamHorario()
{
return [
AgendamentoStatusEnum::REAGENDADO_PELO_PACIENTE,
AgendamentoStatusEnum::REAGENDADO_PELO_MEDICO,
AgendamentoStatusEnum::CANCELADO_PELO_PACIENTE,
AgendamentoStatusEnum::CANCELADO_PELO_MEDICO,
];
}
private static function getDataMinimaOcorrencia($pacienteId, $especialidadeId)
{
$especialidade = DB::table('cadastro_especialidade')
->where('id', $especialidadeId)
->first(['limite_ocorrencias_paciente', 'periodo_monitoramento', 'tempo_liberacao']);
if (is_null($especialidade)
|| is_null($especialidade->limite_ocorrencias_paciente)
|| is_null($especialidade->periodo_monitoramento)
) {
return null;
}
$dataInicio = Carbon::today()->subDays($especialidade->periodo_monitoramento);
$baseQuery = fn () => DB::table('ambulatorio_agendamento as agendamento')
->join('ambulatorio_jornada as jornada', 'jornada.id', 'agendamento.jornada_id')
->where('agendamento.paciente_id', $pacienteId)
->where('jornada.especialidade_id', $especialidadeId)
->whereDate('jornada.data', '>=', $dataInicio)
->whereIn('agendamento.status_id', [
AgendamentoStatusEnum::FALTA_DO_PACIENTE,
AgendamentoStatusEnum::CANCELADO_PELO_PACIENTE,
]);
$ocorrencias = $baseQuery()->count();
if ($ocorrencias < $especialidade->limite_ocorrencias_paciente) {
return null;
}
$tempoLiberacao = $especialidade->tempo_liberacao ?? 0;
if ($tempoLiberacao === 0) {
return null;
}
$ultimaOcorrencia = $baseQuery()->max('jornada.data');
if (is_null($ultimaOcorrencia)) {
return null;
}
return Carbon::parse($ultimaOcorrencia)->addDays($tempoLiberacao);
}
public static function cancel($id, $params)
{
self::validateCancel($params);
$agendamento = Agendamento::findOrFail($id);
if ($agendamento->status_id !== AgendamentoStatusEnum::AGENDADO) {
throw new NaoPodeAlterarStatusAgendamentoException();
}
DB::transaction(function () use ($agendamento, $params) {
$agendamento->update([
'status_id' => AgendamentoStatusEnum::CANCELADO_PELO_PACIENTE,
'motivo_cancelamento' => $params['motivo'],
]);
(new SmsService($agendamento->id))->cancel();
if (!is_null($agendamento->regulacao_solicitacao_id) && !($params['is_from_regulacao'] ?? false)) {
RegulacaoSolicitacaoService::updateStatus(
$agendamento->regulacao_solicitacao_id,
RegulacaoStatusTipo::CANCELADO,
['justificativa' => $params['motivo']],
);
}
});
return $agendamento->fresh();
}
private static function validateCancel($params)
{
Validator::make($params, [
'motivo' => [
'required',
'string',
'max:500',
]
])->validate();
}
public static function destroy($id)
{
$agendamento = Agendamento::findOrFail($id);
if ($agendamento->status_id !== AgendamentoStatusEnum::AGENDADO) {
throw new NaoPodeExcluirAgendamentoException();
}
DB::transaction(function () use ($agendamento) {
if (!is_null($agendamento->regulacao_solicitacao_id)) {
RegulacaoSolicitacaoService::updateStatus(
$agendamento->regulacao_solicitacao_id,
RegulacaoStatusTipo::CANCELADO,
['justificativa' => 'Cancelado automaticamente pela funcionalidade Agendamento'],
);
}
(new SmsService($agendamento->id))->cancel();
$agendamento->delete();
});
}
public static function absent($id, $params = [])
{
$agendamento = Agendamento::findOrFail($id);
DB::transaction(function () use ($agendamento, $params) {
$agendamento->update(['status_id' => AgendamentoStatusEnum::FALTA_DO_PACIENTE]);
if (!is_null($agendamento->regulacao_solicitacao_id) && !($params['is_from_regulacao'] ?? false)) {
RegulacaoSolicitacaoService::updateStatus(
$agendamento->regulacao_solicitacao_id,
RegulacaoStatusTipo::FALTA
);
}
});
}
public static function undoAbsent($id)
{
$agendamento = Agendamento::findOrFail($id);
if ($agendamento->status_id === AgendamentoStatusEnum::FALTA_DO_PACIENTE) {
DB::transaction(function () use ($agendamento) {
$agendamento->update(['status_id' => AgendamentoStatusEnum::AGENDADO]);
});
}
}
public static function getPrintData($id)
{
$agendamento = Agendamento::findOrFail($id);
return [
'isPrintExterno' => false,
'unidade' => [
'nome_fantasia' => $agendamento->jornada->lotacao->infraestrutura->unidade->nome_fantasia
],
'infraestrutura' => [
'nome' => $agendamento->jornada->lotacao->infraestrutura->nome,
'endereco' => $agendamento->jornada->lotacao->infraestrutura->endereco->completo,
'telefone' => MaskUtil::getPhoneMasked($agendamento->jornada->lotacao->infraestrutura->telefone),
],
'paciente' => [
'nome' => $agendamento->paciente->cidadao->nome,
'nome_social' => $agendamento->paciente->cidadao->nome_social,
'prontuario' => $agendamento->paciente->prontuario,
],
'tipo' => [
'nome' => $agendamento->tipo->nome,
],
'data' => $agendamento->jornada->data->format('d/m/Y'),
'diaSemana' => DiaSemana::findOrFail($agendamento->jornada->dia_semana)->descricao,
'horario' => $agendamento->horario->format('H:i'),
'usuario' => [
'nome' => $agendamento->jornada->lotacao->usuario->cidadao->nome,
],
'especialidade' => [
'nome' => $agendamento->jornada->especialidade->nome,
'mensagem_protocolo_agendamento' => $agendamento->jornada->especialidade->mensagem_protocolo_agendamento,
],
'regulacaoMensagemComprovanteAgendamento' => is_null($agendamento->regulacaoSolicitacao)
? null
: RegulacaoParametrizacao::first()->mensagem_comprovante_agendamento ?? null,
'regulacaoProtocoloFechamento' => is_null($agendamento->regulacaoSolicitacao)
? null
: $agendamento->regulacaoSolicitacao->numero ?? null,
'observacao' => $agendamento->observacao,
'usuarioAgendamento' => [
'nome' => $agendamento->createdLotacao->usuario->cidadao->nome,
'nome_social' => $agendamento->createdLotacao->usuario->cidadao->nome_social,
'data_hora' => $agendamento->created_at->format('d/m/Y \à\s H:i'),
'unidade' => $agendamento->createdCentroCusto->infraestrutura->unidade->nome_fantasia,
'infraestrutura' => $agendamento->createdCentroCusto->infraestrutura->nome,
'setor' => $agendamento->createdCentroCusto->setor->nome,
],
];
}
public static function getPrintDataExterno($id)
{
$solicitacao = RegulacaoSolicitacao::findOrFail($id);
$tipo = $solicitacao->prestadorExterno->destino->tipo->descricao ?? '';
$logradouro = $solicitacao->prestadorExterno->destino->nome_logradouro ?? '';
$numero = $solicitacao->prestadorExterno->destino->numero_logradouro ? $solicitacao->prestadorExterno->destino->numero_logradouro : 'S/N';
$bairro = $solicitacao->prestadorExterno->destino->nome_bairro;
$municipio = $solicitacao->prestadorExterno->destino->municipio->nome ?? '';
$uf = $solicitacao->prestadorExterno->destino->municipio->estado->uf ?? '';
$completo = "{$tipo} {$logradouro}, {$numero} - {$bairro}, {$municipio} - {$uf}";
return [
'isPrintExterno' => true,
'unidade' => [
'nome_fantasia' => null,
],
'infraestrutura' => [
'nome' => $solicitacao->prestadorExterno->destino->descricao_destino,
'endereco' => $completo,
'telefone' => MaskUtil::getPhoneMasked($solicitacao->prestadorExterno->destino->numero_telefone_comercial),
],
'paciente' => [
'nome' => $solicitacao->paciente->cidadao->nome,
'nome_social' => $solicitacao->paciente->cidadao->nome_social,
'prontuario' => $solicitacao->paciente->prontuario,
],
'tipo' => [
'nome' => 'CONSULTA',
],
'data' => $solicitacao->data_agendada->format('d/m/Y'),
'diaSemana' => DiaSemana::findOrFail($solicitacao->data_agendada->dayOfWeek + 1)->descricao,
'horario' => $solicitacao->hora_agendada->format('H:i'),
'usuario' => [
'nome' => $solicitacao->lotacaUsuario->usuario->cidadao->nome,
],
'especialidade' => [
'nome' => $solicitacao->itens()->first()->especialidade->nome,
'mensagem_protocolo_agendamento' => $solicitacao->itens()->first()->especialidade->mensagem_protocolo_agendamento,
],
'regulacaoMensagemComprovanteAgendamento' => RegulacaoParametrizacao::first()->mensagem_comprovante_agendamento ?? null,
'regulacaoProtocoloFechamento' => $solicitacao->numero ?? null,
'observacao' => $solicitacao->observacao,
'usuarioAgendamento' => [
'nome' => $solicitacao->createdLotacao->usuario->cidadao->nome,
'data_hora' => $solicitacao->created_at->format('d/m/Y \à\s H:i'),
'unidade' => $solicitacao->createdCentroCusto->infraestrutura->unidade->nome_fantasia,
'infraestrutura' => $solicitacao->createdCentroCusto->infraestrutura->nome,
'setor' => $solicitacao->createdCentroCusto->setor->nome,
],
];
}
private static function getPermissoesAtualizacaoCadastroPaciente()
{
$parametrizacao = CnsService::getParametrizacao();
if (!is_null($parametrizacao)) {
$lotacao = self::getCurrentLotacao();
return DB::table('cadastro_parametrizacao_permissao_usuario')
->select([
'municipe',
'provisorio',
'outro_municipio',
])
->where('infraestrutura_id', $lotacao->infraestrutura_id)
->where('usuario_id', $lotacao->usuario_id)
->get()
->first();
}
return null;
}
private static function getQuantidadeDemandaEspontanea($params)
{
return DemandaEspontanea::whereHas('jornada', fn ($q) =>
$q->whereDate('data', '>=', Carbon::now())
->whereHas('lotacao', fn ($q) => $q
->where('infraestrutura_id', $params->infraestrutura_id)
->where('ocupacao_id', $params->ocupacao_id)
->where('usuario_id', $params->usuario_id)
)
->where('especialidade_id', $params->especialidade_id)
->whereHas('especialidade', fn ($q) =>
self::setCadastroWhereEspecialidadePaciente($q, $params->paciente_id)
->when($params->paciente_nao_municipe, fn ($q) => $q
->where('permite_agendamento_nao_municipe', true)
)
)
)
->count();
}
public static function createAgendamentoDemandaEspontanea($pacienteId)
{
$agendamentosDemandas = self::getAgendamentosDemandasEspontaneas((object) [
'paciente_id' => $pacienteId,
'periodo' => [
'start' => Carbon::now()->subYear()->format('d/m/Y'),
'end' => Carbon::now()->format('d/m/Y'),
],
'origem' => null,
]);
$itens = $agendamentosDemandas['itens'];
$formFilters = [
'origens' => [],
];
$modelFilters = [
'periodo' => ['start' => null, 'end' => null],
'origem' => null
];
if ($itens->isNotEmpty()) {
$filters = self::getAgendamentoDemandaEspontaneaFilters($pacienteId, $itens);
$formFilters['origens'] = $filters->form['origens'];
$modelFilters['periodo'] = $filters->model['periodo'];
}
return [
'form' => $formFilters,
'model' => $modelFilters,
'itens' => $itens,
'totalizadores' => $agendamentosDemandas['totalizadores'],
];
}
private static function getAgendamentoDemandaEspontaneaFilters($pacienteId, $itens)
{
$datas = collect();
$itens->each(fn ($agendamento) => $datas
->push(Carbon::createFromFormat('Y-m-d', $agendamento->data))
);
$datas = $datas->sort();
$periodo = [
'start' => $datas->first()->copy()->startOfDay(),
'end' => $datas->last()->copy()->endOfDay(),
];
$params = (object) [
'paciente_id' => $pacienteId,
'periodo' => $periodo,
];
return (object) [
'form' => [
'origens' => self::getAgendamentoDemandaEspontaneaOrigens($params),
],
'model' => [
'periodo' => [
'start' => $periodo['start']->format('d/m/Y'),
'end' => $periodo['end']->format('d/m/Y'),
],
],
];
}
public static function getAgendamentosDemandasEspontaneas($params)
{
$params->periodo = [
Carbon::createFromFormat('d/m/Y', $params->periodo['start'])->startOfDay(),
Carbon::createFromFormat('d/m/Y', $params->periodo['end'])->endOfDay(),
];
$getAgendamentosBuilder = fn () => DB::table('ambulatorio_agendamento as agendamento')
->join('ambulatorio_agendamento_status as status', 'status.id', 'agendamento.status_id')
->join('ambulatorio_agendamento_tipo as tipo', 'tipo.id', 'agendamento.tipo_id')
->join('ambulatorio_jornada as jornada', 'jornada.id', 'agendamento.jornada_id')
->join('cadastro_lotacao as lotacao', 'lotacao.id', 'jornada.lotacao_id')
->join('cadastro_infraestrutura as infraestrutura', 'infraestrutura.id', 'lotacao.infraestrutura_id')
->join('cadastro_unidade as unidade', 'unidade.id', 'infraestrutura.unidade_id')
->join('cadastro_usuario as usuario', 'usuario.id', 'lotacao.usuario_id')
->join('cadastro_cidadao as cidadao', 'cidadao.id', 'usuario.cidadao_id')
->join('sigtap.ocupacao as ocupacao', 'ocupacao.id', 'lotacao.ocupacao_id')
->join('cadastro_especialidade as especialidade', 'especialidade.id', 'jornada.especialidade_id')
->where('agendamento.paciente_id', $params->paciente_id)
->whereBetween('jornada.data', $params->periodo)
->select(DB::raw("
'Consulta agendada' as origem,
unidade.nome_fantasia as unidade,
COALESCE(infraestrutura.nome, unidade.nome_fantasia) as infraestrutura,
cidadao.nome as usuario,
CONCAT(ocupacao.codigo, ' - ', ocupacao.nome) as ocupacao,
especialidade.nome as especialidade,
tipo.nome as tipo,
jornada.data,
DATE_FORMAT(agendamento.horario, '%H:%i') as horario,
status.id as status_id,
status.nome as status
"));
$getDemandasEspontaneasBuilder = fn () => DB::table('ambulatorio_demanda_espontanea as demanda_espontanea')
->join('ambulatorio_atendimento as atendimento', 'atendimento.demanda_espontanea_id', 'demanda_espontanea.id')
->join('painel_senha_fila as fila', 'fila.id', 'atendimento.fila_id')
->join('cadastro_lotacao as lotacao', 'lotacao.id', 'demanda_espontanea.created_lotacao_id')
->join('cadastro_infraestrutura as infraestrutura', 'infraestrutura.id', 'lotacao.infraestrutura_id')
->join('cadastro_unidade as unidade', 'unidade.id', 'infraestrutura.unidade_id')
->leftJoin('cadastro_usuario as usuario', 'usuario.id', 'demanda_espontanea.usuario_id')
->leftJoin('cadastro_cidadao as cidadao', 'cidadao.id', 'usuario.cidadao_id')
->leftJoin('sigtap.ocupacao as ocupacao', 'ocupacao.id', 'demanda_espontanea.ocupacao_id')
->leftJoin('cadastro_especialidade as especialidade', 'especialidade.id', 'demanda_espontanea.especialidade_id')
->where('demanda_espontanea.paciente_id', $params->paciente_id)
->whereBetween('demanda_espontanea.data', $params->periodo)
->select(DB::raw("
'Demanda espontânea' as origem,
unidade.nome_fantasia as unidade,
COALESCE(infraestrutura.nome, unidade.nome_fantasia) as infraestrutura,
cidadao.nome as usuario,
CONCAT(ocupacao.codigo, ' - ', ocupacao.nome) as ocupacao,
especialidade.nome as especialidade,
'CONSULTA' as tipo,
demanda_espontanea.data,
DATE_FORMAT(demanda_espontanea.created_at, '%H:%i') as horario,
IF(fila.evadido = 1,
" . AgendamentoStatusEnum::EVADIDO . ",
IF(atendimento.finalizado_em IS NULL, 'em-aberto', " . AgendamentoStatusEnum::REALIZADO . ")
) as status_id,
IF(fila.evadido = 1, 'EVADIDO', IF(atendimento.finalizado_em IS NULL, 'EM ABERTO', 'REALIZADO')) as status
"));
$builder = null;
if ($params->origem === 'consulta-agendada') {
$builder = $getAgendamentosBuilder();
}
else if ($params->origem === 'demanda-espontanea') {
$builder = $getDemandasEspontaneasBuilder();
}
else {
$builder = $getAgendamentosBuilder()->union($getDemandasEspontaneasBuilder());
}
self::setAgendamentosDemandasEspontaneasOrderBy($builder);
$itens = $builder->get();
$totalizadores = (object) [
'agendado' => $itens->where('status_id', AgendamentoStatusEnum::AGENDADO)->count(),
'realizado' => $itens->where('status_id', AgendamentoStatusEnum::REALIZADO)->count(),
'reagendado_pelo_paciente' => $itens->where('status_id', AgendamentoStatusEnum::REAGENDADO_PELO_PACIENTE)->count(),
'reagendado_pelo_medico' => $itens->where('status_id', AgendamentoStatusEnum::REAGENDADO_PELO_MEDICO)->count(),
'cancelado_pelo_paciente' => $itens->where('status_id', AgendamentoStatusEnum::CANCELADO_PELO_PACIENTE)->count(),
'cancelado_pelo_medico' => $itens->where('status_id', AgendamentoStatusEnum::CANCELADO_PELO_MEDICO)->count(),
'falta_paciente' => $itens->where('status_id', AgendamentoStatusEnum::FALTA_DO_PACIENTE)->count(),
'falta_medico' => $itens->where('status_id', AgendamentoStatusEnum::FALTA_DO_MEDICO)->count(),
'em_aberto' => $itens->where('status_id', 'em-aberto')->count(),
'evadido' => $itens->where('status_id', AgendamentoStatusEnum::EVADIDO)->count(),
'geral' => $itens->count(),
];
return compact('itens', 'totalizadores');
}
private static function setAgendamentosDemandasEspontaneasOrderBy($builder)
{
$columns = [
'origem' => 'origem',
'unidade' => 'unidade',
'infraestrutura' => 'infraestrutura',
'usuario' => 'usuario',
'ocupacao' => 'ocupacao',
'especialidade' => 'especialidade',
'tipo' => 'tipo',
'data' => 'data',
'horario' => 'horario',
'status' => 'status',
];
if (is_null(request()->sort_by)) {
$builder->orderByRaw(join(', ', $columns));
return;
}
$column = $columns[request()->sort_by];
$direction = DataUtil::getBooleanValueFromStringBoolean(request()->sort_desc) ? 'DESC' : 'ASC';
unset($columns[request()->sort_by]);
$builder->orderByRaw(
"{$column} {$direction}, " . join(', ', $columns)
);
}
}