709 lines
30 KiB
PHP
709 lines
30 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Mobile\ConectaCidadao\ExameLaboratorial;
|
|
|
|
use App\Enums\ExameLaboratorial\Acao;
|
|
use App\Enums\ExameLaboratorial\StatusAgendamento;
|
|
use App\Enums\ExameLaboratorial\TipoCota;
|
|
use App\Enums\ExameLaboratorial\TipoUnidade;
|
|
use App\Enums\Parametrizacao\Exame\Tipo;
|
|
use App\Enums\WebService\Laboratorio\Cliente\PedidoStatus;
|
|
use App\Models\Ambulatorio\AtendimentoSoapSolicitacaoExameItem;
|
|
use App\Models\Cadastro\Feriado\Feriado;
|
|
use App\Models\ExameLaboratorial\AgendamentoExame\Agendamento;
|
|
use App\Models\ExameLaboratorial\AgendamentoExame\AgendamentoExame;
|
|
use App\Services\ExameLaboratorial\AgendamentoExameHistoricoService;
|
|
use App\Services\Service;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Http\Response;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class AgendamentoExameService extends Service
|
|
{
|
|
public static function elegibilidade(): array
|
|
{
|
|
$parametrizacao = DB::table('exame_laboratorial_parametrizacao_integracao_aplicativo')->first();
|
|
|
|
return [
|
|
'agendamento' => [
|
|
'habilitado' => (bool) ($parametrizacao->habilita_agendamento_conectasus_cidadao ?? false),
|
|
],
|
|
'visualizacao_laudo' => [
|
|
'habilitada' => (bool) ($parametrizacao->habilita_visualizacao_laudo_conectasus_cidadao ?? false),
|
|
],
|
|
];
|
|
}
|
|
|
|
public static function unidadesColeta(): array
|
|
{
|
|
self::validarParametrizacaoHabilitada();
|
|
|
|
$pacienteUnidadeId = auth('mobile')->user()->unidade_id;
|
|
|
|
$agora = now();
|
|
|
|
return DB::table('cadastro_infraestrutura as infra_coleta')
|
|
->join('cadastro_unidade as unidade_coleta', 'unidade_coleta.id', 'infra_coleta.unidade_id')
|
|
->join('exame_laboratorial_parametrizacao_unidade as param_unidade', function ($join) {
|
|
$join->on('param_unidade.infraestrutura_id', 'infra_coleta.id')
|
|
->where('param_unidade.unidade_coleta', true)
|
|
->whereNull('param_unidade.deleted_at');
|
|
})
|
|
->join('exame_laboratorial_parametrizacao_unidade_solicitante as sol', function ($join) {
|
|
$join->on('sol.infraestrutura_id', 'infra_coleta.id')
|
|
->whereNull('sol.deleted_at');
|
|
})
|
|
->join('cadastro_infraestrutura as infra_sol', 'infra_sol.id', 'sol.unidade_solicitante_infraestrutura_id')
|
|
->where('infra_sol.unidade_id', $pacienteUnidadeId)
|
|
->whereNull('infra_coleta.deleted_at')
|
|
->whereExists(function ($q) use ($agora) {
|
|
$q->select(DB::raw(1))
|
|
->from('exame_laboratorial_jornada as jornada_coleta')
|
|
->whereColumn('jornada_coleta.infraestrutura_id', 'infra_coleta.id')
|
|
->where('jornada_coleta.vagas_app', '>', 0)
|
|
->where(function ($q2) use ($agora) {
|
|
$q2->whereDate('jornada_coleta.data', '>', $agora->toDateString())
|
|
->orWhere(function ($q3) use ($agora) {
|
|
$q3->whereDate('jornada_coleta.data', $agora->toDateString())
|
|
->whereTime('jornada_coleta.hora_fim', '>', $agora->format('H:i:s'));
|
|
});
|
|
})
|
|
->whereNull('jornada_coleta.deleted_at');
|
|
})
|
|
->orderBy('unidade_coleta.nome_fantasia')
|
|
->orderBy('infra_coleta.nome')
|
|
->get(
|
|
[
|
|
'infra_coleta.id as infraestrutura_id',
|
|
'unidade_coleta.id as unidade_id',
|
|
'unidade_coleta.nome_fantasia',
|
|
DB::raw(
|
|
"CASE WHEN infra_coleta.nome IS NOT NULL
|
|
THEN infra_coleta.nome
|
|
ELSE unidade_coleta.nome_fantasia
|
|
END AS infraestrutura_nome"
|
|
),
|
|
'sol.unidade_solicitante_infraestrutura_id',
|
|
]
|
|
)
|
|
->unique('infraestrutura_id')
|
|
->map(
|
|
fn($item) => [
|
|
'infraestrutura_id' => $item->infraestrutura_id,
|
|
'unidade_id' => $item->unidade_id,
|
|
'nome_fantasia' => $item->nome_fantasia,
|
|
'infraestrutura_nome' => $item->infraestrutura_nome,
|
|
'unidade_solicitante_infraestrutura_id' => $item->unidade_solicitante_infraestrutura_id,
|
|
]
|
|
)
|
|
->values()
|
|
->toArray();
|
|
}
|
|
|
|
public static function exames(): array
|
|
{
|
|
self::validarParametrizacaoHabilitada();
|
|
|
|
$pacienteId = auth('mobile')->user()->id;
|
|
|
|
$statusAtivos = [
|
|
StatusAgendamento::AGENDADO,
|
|
StatusAgendamento::PENDENTE_ASSINATURA,
|
|
StatusAgendamento::NOVA_COLETA,
|
|
StatusAgendamento::PENDENTE_ENTREGA,
|
|
];
|
|
|
|
$statusRealizados = [
|
|
StatusAgendamento::REALIZADO,
|
|
StatusAgendamento::ASSINADO,
|
|
StatusAgendamento::ENTREGUE,
|
|
];
|
|
|
|
$exameIdsAtivos = DB::table('exame_laboratorial_agendamento_exame')
|
|
->where('paciente_id', $pacienteId)
|
|
->whereIn('status_id', $statusAtivos)
|
|
->pluck('exame_id')
|
|
->unique()
|
|
->toArray();
|
|
|
|
$exameIdsBloqueadosPorValidade = DB::table('parametrizacao_exame as exame')
|
|
->join('exame_laboratorial_agendamento_exame as ae', 'ae.exame_id', 'exame.id')
|
|
->join('exame_laboratorial_agendamento as ag', 'ag.id', 'ae.agendamento_id')
|
|
->where('ae.paciente_id', $pacienteId)
|
|
->whereIn('ae.status_id', $statusRealizados)
|
|
->whereNotNull('exame.validade')
|
|
->where('exame.validade', '>', 0)
|
|
->where(fn($q) => $q->where('exame.permite_repeticao', false)->orWhereNull('exame.permite_repeticao'))
|
|
->whereRaw('ag.data >= DATE_SUB(CURDATE(), INTERVAL exame.validade DAY)')
|
|
->pluck('exame.id')
|
|
->unique()
|
|
->toArray();
|
|
|
|
$exameIdsBloqueados = array_unique(array_merge($exameIdsAtivos, $exameIdsBloqueadosPorValidade));
|
|
|
|
$pedidosBloqueados = DB::table('ambulatorio_atendimento_soap_solicitacao_exame_item')
|
|
->where(function ($q) use ($exameIdsBloqueados) {
|
|
$q->where('agendado_exame_laboratorial', true);
|
|
|
|
if (!empty($exameIdsBloqueados)) {
|
|
$q->orWhereIn('exame_id', $exameIdsBloqueados);
|
|
}
|
|
})
|
|
->pluck('solicitacao_exame_id')
|
|
->unique()
|
|
->toArray();
|
|
|
|
$rows = DB::table('ambulatorio_atendimento_soap_solicitacao_exame_item')
|
|
->join('ambulatorio_atendimento_soap_solicitacao_exame', 'ambulatorio_atendimento_soap_solicitacao_exame.id', '=', 'ambulatorio_atendimento_soap_solicitacao_exame_item.solicitacao_exame_id')
|
|
->join('parametrizacao_exame', 'parametrizacao_exame.id', '=', 'ambulatorio_atendimento_soap_solicitacao_exame_item.exame_id')
|
|
->join('ambulatorio_atendimento_soap', 'ambulatorio_atendimento_soap.id', '=', 'ambulatorio_atendimento_soap_solicitacao_exame.soap_id')
|
|
->join('ambulatorio_atendimento', 'ambulatorio_atendimento.id', '=', 'ambulatorio_atendimento_soap.atendimento_id')
|
|
->join('painel_senha_fila', 'painel_senha_fila.id', '=', 'ambulatorio_atendimento.fila_id')
|
|
->join('cadastro_lotacao', 'cadastro_lotacao.id', 'ambulatorio_atendimento_soap_solicitacao_exame.created_lotacao_id')
|
|
->join('cadastro_usuario as profissional', 'profissional.id', 'cadastro_lotacao.usuario_id')
|
|
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'profissional.cidadao_id')
|
|
->where('parametrizacao_exame.tipo_id', Tipo::LABORATORIAL)
|
|
->whereNull('parametrizacao_exame.regulacao_tipo_id')
|
|
->whereNull('parametrizacao_exame.deleted_at')
|
|
->where('ambulatorio_atendimento_soap_solicitacao_exame_item.descartado_exame_laboratorial', '!=', true)
|
|
->whereNotIn('ambulatorio_atendimento_soap_solicitacao_exame_item.solicitacao_exame_id', $pedidosBloqueados)
|
|
->where('painel_senha_fila.paciente_id', $pacienteId)
|
|
->where('ambulatorio_atendimento_soap.is_rascunho', false)
|
|
->orderBy('ambulatorio_atendimento_soap_solicitacao_exame.id')
|
|
->orderBy('parametrizacao_exame.nome')
|
|
->get([
|
|
'ambulatorio_atendimento_soap_solicitacao_exame.id as solicitacao_id',
|
|
'ambulatorio_atendimento_soap_solicitacao_exame.created_at as data_solicitacao',
|
|
'ambulatorio_atendimento_soap_solicitacao_exame_item.id as exame_item_id',
|
|
'parametrizacao_exame.id as exame_id',
|
|
'parametrizacao_exame.codigo',
|
|
'parametrizacao_exame.nome',
|
|
'parametrizacao_exame.sigla_exame',
|
|
'parametrizacao_exame.prazo_entrega',
|
|
'parametrizacao_exame.observacao',
|
|
'parametrizacao_exame.sexo_id',
|
|
'profissional.id as profissional_id',
|
|
'cadastro_cidadao.nome as profissional_nome',
|
|
]);
|
|
|
|
return $rows
|
|
->groupBy('solicitacao_id')
|
|
->map(fn($group) => [
|
|
'solicitacao_id' => $group->first()->solicitacao_id,
|
|
'data_solicitacao' => Carbon::parse($group->first()->data_solicitacao)->format('Y-m-d'),
|
|
'profissional_id' => $group->first()->profissional_id,
|
|
'profissional_nome' => $group->first()->profissional_nome,
|
|
'exames' => $group->map(fn($row) => [
|
|
'exame_item_id' => $row->exame_item_id,
|
|
'id' => $row->exame_id,
|
|
'codigo' => $row->codigo,
|
|
'nome' => $row->nome,
|
|
'sigla_exame' => $row->sigla_exame,
|
|
'prazo_entrega' => $row->prazo_entrega,
|
|
'observacao' => $row->observacao,
|
|
'sexo_id' => $row->sexo_id,
|
|
])->values()->toArray(),
|
|
])
|
|
->values()
|
|
->toArray();
|
|
}
|
|
|
|
public static function jornadas(): array
|
|
{
|
|
self::validarParametrizacaoHabilitada();
|
|
|
|
request()->validate([
|
|
'coleta_infraestrutura_id' => 'required|integer|exists:cadastro_infraestrutura,id',
|
|
'unidade_solicitante_infraestrutura_id' => 'required|integer|exists:cadastro_infraestrutura,id',
|
|
'exame_ids' => 'nullable|array',
|
|
'exame_ids.*' => 'integer|exists:parametrizacao_exame,id',
|
|
]);
|
|
|
|
$coletaInfraId = (int) request()->coleta_infraestrutura_id;
|
|
$unidadeSolInfraId = (int) request()->unidade_solicitante_infraestrutura_id;
|
|
$exameIds = request()->exame_ids ?? [];
|
|
|
|
$statusPermitidos = [
|
|
StatusAgendamento::AGENDADO,
|
|
StatusAgendamento::REALIZADO,
|
|
StatusAgendamento::PENDENTE_ASSINATURA,
|
|
StatusAgendamento::ASSINADO,
|
|
StatusAgendamento::NOVA_COLETA,
|
|
StatusAgendamento::PENDENTE_ENTREGA,
|
|
StatusAgendamento::ENTREGUE,
|
|
];
|
|
|
|
$agora = now();
|
|
|
|
$jornadasRaw = DB::table('exame_laboratorial_jornada as jornada')
|
|
->where('jornada.infraestrutura_id', $coletaInfraId)
|
|
->where('jornada.vagas_app', '>', 0)
|
|
->where(function ($q) use ($agora) {
|
|
$q->whereDate('jornada.data', '>', $agora->toDateString())
|
|
->orWhere(function ($q2) use ($agora) {
|
|
$q2->whereDate('jornada.data', $agora->toDateString())
|
|
->whereTime('jornada.hora_fim', '>', $agora->format('H:i:s'));
|
|
});
|
|
})
|
|
->whereNull('jornada.deleted_at')
|
|
->orderBy('jornada.data')
|
|
->orderBy('jornada.hora_inicial')
|
|
->get([
|
|
'jornada.id',
|
|
'jornada.data',
|
|
'jornada.hora_inicial',
|
|
'jornada.hora_fim',
|
|
'jornada.tempo_atendimento',
|
|
'jornada.vagas_app',
|
|
'jornada.laboratorio_prestador_infraestrutura_id',
|
|
]);
|
|
|
|
$jornadaIds = $jornadasRaw->pluck('id')->toArray();
|
|
|
|
$consumoPorJornada = DB::table('exame_laboratorial_agendamento as ag')
|
|
->join('exame_laboratorial_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
|
->whereIn('ag.jornada_id', $jornadaIds)
|
|
->where('ag.app_conectasus_cidadao', true)
|
|
->whereIn('ag_exame.status_id', $statusPermitidos)
|
|
->groupBy('ag.jornada_id')
|
|
->select('ag.jornada_id', DB::raw('COUNT(DISTINCT ag.id) as visitas'))
|
|
->get()
|
|
->keyBy('jornada_id');
|
|
|
|
$horariosOcupadosPorJornada = DB::table('exame_laboratorial_agendamento as ag')
|
|
->join('exame_laboratorial_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
|
->whereIn('ag.jornada_id', $jornadaIds)
|
|
->whereIn('ag_exame.status_id', $statusPermitidos)
|
|
->select('ag.jornada_id', DB::raw("DATE_FORMAT(ag.horario, '%H:%i') as horario"))
|
|
->distinct()
|
|
->get()
|
|
->groupBy('jornada_id')
|
|
->map(fn($rows) => $rows->pluck('horario')->toArray());
|
|
|
|
$jornadas = $jornadasRaw->map(function ($jornada) use ($consumoPorJornada, $horariosOcupadosPorJornada) {
|
|
$consumido = $consumoPorJornada[$jornada->id]->visitas ?? 0;
|
|
|
|
return [
|
|
'jornada_id' => $jornada->id,
|
|
'data' => Carbon::parse($jornada->data)->format('Y-m-d'),
|
|
'hora_inicial' => Carbon::parse($jornada->hora_inicial)->format('H:i'),
|
|
'hora_fim' => Carbon::parse($jornada->hora_fim)->format('H:i'),
|
|
'tempo_atendimento' => $jornada->tempo_atendimento,
|
|
'vagas_app' => $jornada->vagas_app,
|
|
'vagas_app_disponiveis' => max(0, $jornada->vagas_app - $consumido),
|
|
'laboratorio_prestador_infraestrutura_id' => $jornada->laboratorio_prestador_infraestrutura_id,
|
|
'horarios' => self::gerarHorariosDisponiveis(
|
|
$jornada->data,
|
|
$jornada->hora_inicial,
|
|
$jornada->hora_fim,
|
|
$jornada->tempo_atendimento,
|
|
$horariosOcupadosPorJornada[$jornada->id] ?? []
|
|
),
|
|
];
|
|
})->values()->toArray();
|
|
|
|
$feriados = Feriado::orderBy('data')
|
|
->get()
|
|
->map(
|
|
fn($feriado) =>
|
|
[
|
|
'data' => (new Carbon($feriado->data))->format('Y-m-d'),
|
|
'tipo_id' => $feriado->tipo_id,
|
|
'tipo_nome' => $feriado->tipo->descricao,
|
|
'observacao' => $feriado->observacao,
|
|
]
|
|
)
|
|
->values()
|
|
->toArray();
|
|
|
|
return [
|
|
'jornadas' => $jornadas,
|
|
'feriados' => $feriados,
|
|
'cotas' => self::computarCotasApp($coletaInfraId, $unidadeSolInfraId, $exameIds),
|
|
];
|
|
}
|
|
|
|
private static function gerarHorariosDisponiveis(
|
|
string $data,
|
|
string $horaInicial,
|
|
string $horaFim,
|
|
?int $tempoAtendimento,
|
|
array $horariosOcupados
|
|
): array {
|
|
if (!$tempoAtendimento || $tempoAtendimento <= 0) {
|
|
|
|
return [];
|
|
}
|
|
|
|
$dataStr = Carbon::parse($data)->format('Y-m-d');
|
|
$horarioAtual = Carbon::parse($dataStr . ' ' . $horaInicial);
|
|
$fim = Carbon::parse($dataStr . ' ' . $horaFim);
|
|
|
|
$horarios = [];
|
|
|
|
while ($horarioAtual->lt($fim)) {
|
|
|
|
if ($horarioAtual->isPast()) {
|
|
|
|
$horarioAtual->addMinutes($tempoAtendimento);
|
|
continue;
|
|
}
|
|
|
|
$horarioStr = $horarioAtual->format('H:i');
|
|
|
|
if (!in_array($horarioStr, $horariosOcupados)) {
|
|
|
|
$horarios[] = [
|
|
'horario' => $horarioStr,
|
|
'data_hora' => $dataStr . 'T' . $horarioStr . ':00',
|
|
];
|
|
}
|
|
|
|
$horarioAtual->addMinutes($tempoAtendimento);
|
|
}
|
|
|
|
return $horarios;
|
|
}
|
|
|
|
private static function computarCotasApp(int $coletaInfraId, int $unidadeSolInfraId, array $exameIds): array
|
|
{
|
|
$hoje = now()->toDateString();
|
|
|
|
$cotaLaboratorio = null;
|
|
|
|
$labInfraIds = DB::table('exame_laboratorial_jornada')
|
|
->where('infraestrutura_id', $coletaInfraId)
|
|
->whereNotNull('laboratorio_prestador_infraestrutura_id')
|
|
->whereNull('deleted_at')
|
|
->distinct()
|
|
->pluck('laboratorio_prestador_infraestrutura_id');
|
|
|
|
foreach ($labInfraIds as $labInfraId) {
|
|
|
|
$cota = DB::table('exame_laboratorial_parametrizacao_cota')
|
|
->where('infraestrutura_id', $labInfraId)
|
|
->where('unidade_tipo_id', TipoUnidade::LABORATORIO)
|
|
->where('cota_tipo_id', TipoCota::QUANTIDADE)
|
|
->where('inicio', '<=', $hoje)
|
|
->where('fim', '>=', $hoje)
|
|
->whereNull('deleted_at')
|
|
->first();
|
|
|
|
$appLab = DB::table('exame_laboratorial_param_app_conectasus_cidadao_laboratorio')
|
|
->where('infraestrutura_id', $labInfraId)
|
|
->whereNull('deleted_at')
|
|
->first();
|
|
|
|
if (!$cota || !$appLab || $appLab->percentual_quantitativo <= 0) {
|
|
|
|
continue;
|
|
}
|
|
|
|
$limite = (int) round($cota->cota_quantidade * $appLab->percentual_quantitativo / 100);
|
|
$consumido = DB::table('exame_laboratorial_agendamento')
|
|
->where('laboratorio_infraestrutura_id', $labInfraId)
|
|
->where('app_conectasus_cidadao', true)
|
|
->where('data', '>=', $cota->inicio)
|
|
->where('data', '<=', $cota->fim)
|
|
->count();
|
|
|
|
$cotaLaboratorio = [
|
|
'infraestrutura_id' => $labInfraId,
|
|
'percentual_limite' => (float) $appLab->percentual_quantitativo,
|
|
'limite_vagas' => $limite,
|
|
'consumido' => $consumido,
|
|
'percentual_consumido' => $limite > 0 ? round($consumido / $limite * 100, 2) : 0,
|
|
'bloqueado' => $consumido >= $limite,
|
|
];
|
|
|
|
break;
|
|
}
|
|
|
|
$cotasExames = [];
|
|
|
|
foreach ($exameIds as $exameId) {
|
|
|
|
$periodo = DB::table('exame_laboratorial_parametrizacao_cota_exame_periodo')
|
|
->where('infraestrutura_id', $coletaInfraId)
|
|
->where('exame_id', $exameId)
|
|
->where('cota_tipo_id', TipoCota::QUANTIDADE)
|
|
->where('inicio', '<=', $hoje)
|
|
->where('fim', '>=', $hoje)
|
|
->whereNull('deleted_at')
|
|
->first();
|
|
|
|
if (!$periodo) {
|
|
|
|
continue;
|
|
}
|
|
|
|
$appExame = DB::table('exame_laboratorial_parametrizacao_app_conectasus_cidadao_exame')
|
|
->where('cota_exame_periodo_id', $periodo->id)
|
|
->whereNull('deleted_at')
|
|
->first();
|
|
|
|
if (!$appExame || $appExame->percentual_quantitativo <= 0) {
|
|
|
|
continue;
|
|
}
|
|
|
|
$limite = (int) round($periodo->cota_quantidade * $appExame->percentual_quantitativo / 100);
|
|
$consumido = DB::table('exame_laboratorial_agendamento_exame as ae')
|
|
->join('exame_laboratorial_agendamento as ag', 'ag.id', '=', 'ae.agendamento_id')
|
|
->where('ae.exame_id', $exameId)
|
|
->where('ag.app_conectasus_cidadao', true)
|
|
->where('ag.coleta_infraestrutura_id', $coletaInfraId)
|
|
->where('ag.data', '>=', $periodo->inicio)
|
|
->where('ag.data', '<=', $periodo->fim)
|
|
->count();
|
|
|
|
$cotasExames[] = [
|
|
'exame_id' => (int) $exameId,
|
|
'percentual_limite' => (float) $appExame->percentual_quantitativo,
|
|
'limite_vagas' => $limite,
|
|
'consumido' => $consumido,
|
|
'percentual_consumido' => $limite > 0 ? round($consumido / $limite * 100, 2) : 0,
|
|
'bloqueado' => $consumido >= $limite,
|
|
];
|
|
}
|
|
|
|
$cotaUnidadeSolicitante = null;
|
|
|
|
$appSol = DB::table('exame_laboratorial_param_app_conectasus_cidadao_unidade_sol')
|
|
->where('infraestrutura_id', $unidadeSolInfraId)
|
|
->whereNull('deleted_at')
|
|
->first();
|
|
|
|
if ($appSol && $appSol->percentual_quantitativo > 0) {
|
|
|
|
$cotaSol = DB::table('exame_laboratorial_parametrizacao_cota_unidade_solicitante as sol')
|
|
->join('exame_laboratorial_parametrizacao_cota as cota', 'cota.id', '=', 'sol.cota_id')
|
|
->where('sol.infraestrutura_solicitante_id', $unidadeSolInfraId)
|
|
->where('cota.cota_tipo_id', TipoCota::QUANTIDADE)
|
|
->where('cota.inicio', '<=', $hoje)
|
|
->where('cota.fim', '>=', $hoje)
|
|
->whereNull('cota.deleted_at')
|
|
->whereNull('sol.deleted_at')
|
|
->first(['sol.cota_quantidade', 'cota.inicio', 'cota.fim']);
|
|
|
|
if ($cotaSol) {
|
|
|
|
$limite = (int) round($cotaSol->cota_quantidade * $appSol->percentual_quantitativo / 100);
|
|
$consumido = DB::table('exame_laboratorial_agendamento')
|
|
->where('infraestrutura_id', $unidadeSolInfraId)
|
|
->where('app_conectasus_cidadao', true)
|
|
->where('data', '>=', $cotaSol->inicio)
|
|
->where('data', '<=', $cotaSol->fim)
|
|
->count();
|
|
|
|
$cotaUnidadeSolicitante = [
|
|
'infraestrutura_id' => $unidadeSolInfraId,
|
|
'percentual_limite' => (float) $appSol->percentual_quantitativo,
|
|
'limite_vagas' => $limite,
|
|
'consumido' => $consumido,
|
|
'percentual_consumido' => $limite > 0 ? round($consumido / $limite * 100, 2) : 0,
|
|
'bloqueado' => $consumido >= $limite,
|
|
];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'laboratorio' => $cotaLaboratorio,
|
|
'exames' => $cotasExames,
|
|
'unidade_solicitante' => $cotaUnidadeSolicitante,
|
|
];
|
|
}
|
|
|
|
public static function agendar(): array
|
|
{
|
|
self::validarParametrizacaoHabilitada();
|
|
|
|
request()->validate([
|
|
'jornada_id' => 'required|integer|exists:exame_laboratorial_jornada,id',
|
|
'coleta_infraestrutura_id' => 'required|integer|exists:cadastro_infraestrutura,id',
|
|
'unidade_solicitante_infraestrutura_id' => 'required|integer|exists:cadastro_infraestrutura,id',
|
|
'horario' => 'required|date_format:H:i',
|
|
'exames' => 'required|array|min:1',
|
|
'exames.*.exame_item_id' => 'required|integer|exists:ambulatorio_atendimento_soap_solicitacao_exame_item,id',
|
|
'exames.*.exame_id' => 'required|integer|exists:parametrizacao_exame,id',
|
|
'exames.*.profissional_id' => 'required|integer|exists:cadastro_usuario,id',
|
|
'observacao' => 'nullable|string',
|
|
]);
|
|
|
|
$paciente = auth('mobile')->user();
|
|
$jornada = DB::table('exame_laboratorial_jornada')
|
|
->where('id', request()->jornada_id)
|
|
->whereNull('deleted_at')
|
|
->first();
|
|
|
|
if (!$jornada) {
|
|
|
|
abort(Response::HTTP_NOT_FOUND, 'Jornada não encontrada.');
|
|
}
|
|
|
|
$dataJornada = Carbon::parse($jornada->data)->format('Y-m-d');
|
|
$horaInicialJornada = Carbon::parse($dataJornada . ' ' . $jornada->hora_inicial);
|
|
$horaFimJornada = Carbon::parse($dataJornada . ' ' . $jornada->hora_fim);
|
|
$dataHoraAgendamento = Carbon::parse($dataJornada . ' ' . request()->horario);
|
|
|
|
if ($dataHoraAgendamento->isPast()) {
|
|
|
|
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Não é possível agendar em horário retroativo.');
|
|
}
|
|
|
|
if ($dataHoraAgendamento->lt($horaInicialJornada) || $dataHoraAgendamento->gte($horaFimJornada)) {
|
|
|
|
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'O horário informado está fora do intervalo da jornada.');
|
|
}
|
|
|
|
self::validarHorarioDisponivel($jornada, request()->horario);
|
|
|
|
$exameIds = collect(request()->exames)->pluck('exame_id')->toArray();
|
|
|
|
$examesAtivos = DB::table('exame_laboratorial_agendamento_exame as ae')
|
|
->leftJoin('parametrizacao_exame as exame', 'exame.id', 'ae.exame_id')
|
|
->where('ae.paciente_id', $paciente->id)
|
|
->whereIn('ae.exame_id', $exameIds)
|
|
->whereIn('ae.status_id', [
|
|
StatusAgendamento::AGENDADO,
|
|
StatusAgendamento::PENDENTE_ASSINATURA,
|
|
StatusAgendamento::NOVA_COLETA,
|
|
StatusAgendamento::PENDENTE_ENTREGA,
|
|
])
|
|
->get(['ae.exame_id', 'exame.nome as exame_nome']);
|
|
|
|
if ($examesAtivos->isNotEmpty()) {
|
|
|
|
$nomes = $examesAtivos->map(fn($ae) => $ae->exame_nome ?? "Exame #{$ae->exame_id}")->unique()->values();
|
|
|
|
abort(response()->json([
|
|
'message' => 'Um ou mais exames do pedido já possuem agendamento ativo.',
|
|
'exames' => $nomes,
|
|
], Response::HTTP_UNPROCESSABLE_ENTITY));
|
|
}
|
|
|
|
self::verificarVagasApp($jornada);
|
|
|
|
$coletaUnidadeId = DB::table('cadastro_infraestrutura')
|
|
->where('id', request()->coleta_infraestrutura_id)
|
|
->value('unidade_id');
|
|
|
|
$solicitanteUnidadeId = DB::table('cadastro_infraestrutura')
|
|
->where('id', request()->unidade_solicitante_infraestrutura_id)
|
|
->value('unidade_id');
|
|
|
|
$labInfraId = $jornada->laboratorio_prestador_infraestrutura_id;
|
|
$labUnidadeId = $labInfraId
|
|
? DB::table('cadastro_infraestrutura')->where('id', $labInfraId)->value('unidade_id')
|
|
: null;
|
|
|
|
$ultimoNumero = DB::table('exame_laboratorial_agendamento')
|
|
->orderByDesc('agendamento_numero')
|
|
->value('agendamento_numero');
|
|
|
|
$agendamentoNumero = $ultimoNumero ? $ultimoNumero + 1 : 1;
|
|
|
|
$agendamento = new Agendamento();
|
|
$agendamento->agendamento_numero = $agendamentoNumero;
|
|
$agendamento->paciente_id = $paciente->id;
|
|
$agendamento->jornada_id = $jornada->id;
|
|
$agendamento->medico_id = request()->exames[0]['profissional_id'];
|
|
$agendamento->data = $jornada->data;
|
|
$agendamento->horario = request()->horario;
|
|
$agendamento->unidade_id = $solicitanteUnidadeId;
|
|
$agendamento->infraestrutura_id = (int) request()->unidade_solicitante_infraestrutura_id;
|
|
$agendamento->coleta_unidade_id = $coletaUnidadeId;
|
|
$agendamento->coleta_infraestrutura_id = (int) request()->coleta_infraestrutura_id;
|
|
$agendamento->laboratorio_unidade_id = $labUnidadeId;
|
|
$agendamento->laboratorio_infraestrutura_id = $labInfraId;
|
|
$agendamento->observacao = request()->observacao;
|
|
$agendamento->app_conectasus_cidadao = true;
|
|
$agendamento->web_service_laboratorio_cliente_pedido_status_id = PedidoStatus::AGUARDANDO_PROCESSAMENTO;
|
|
$agendamento->save();
|
|
|
|
foreach (request()->exames as $exameData) {
|
|
|
|
$exame = new AgendamentoExame();
|
|
$exame->agendamento_id = $agendamento->id;
|
|
$exame->paciente_id = $paciente->id;
|
|
$exame->exame_id = $exameData['exame_id'];
|
|
$exame->status_id = StatusAgendamento::AGENDADO;
|
|
$exame->soap_solicitacao_exame_item_id = $exameData['exame_item_id'];
|
|
$exame->quantidade = 1;
|
|
$exame->save();
|
|
|
|
AtendimentoSoapSolicitacaoExameItem::findOrFail($exameData['exame_item_id'])
|
|
->update(['agendado_exame_laboratorial' => true]);
|
|
|
|
AgendamentoExameHistoricoService::store($exame, $agendamento, Acao::INSERCAO);
|
|
}
|
|
|
|
return [
|
|
'agendamento_id' => $agendamento->id,
|
|
'agendamento_numero' => $agendamento->agendamento_numero,
|
|
];
|
|
}
|
|
|
|
private static function validarHorarioDisponivel(object $jornada, string $horario): void
|
|
{
|
|
$statusPermitidos = [
|
|
StatusAgendamento::AGENDADO,
|
|
StatusAgendamento::REALIZADO,
|
|
StatusAgendamento::PENDENTE_ASSINATURA,
|
|
StatusAgendamento::ASSINADO,
|
|
StatusAgendamento::NOVA_COLETA,
|
|
StatusAgendamento::PENDENTE_ENTREGA,
|
|
StatusAgendamento::ENTREGUE,
|
|
];
|
|
|
|
$ocupado = DB::table('exame_laboratorial_agendamento as ag')
|
|
->join('exame_laboratorial_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
|
->where('ag.jornada_id', $jornada->id)
|
|
->whereTime('ag.horario', $horario)
|
|
->whereIn('ag_exame.status_id', $statusPermitidos)
|
|
->exists();
|
|
|
|
if ($ocupado) {
|
|
|
|
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Este horário não está mais disponível. Selecione outro horário.');
|
|
}
|
|
}
|
|
|
|
private static function verificarVagasApp(object $jornada): void
|
|
{
|
|
$statusPermitidos = [
|
|
StatusAgendamento::AGENDADO,
|
|
StatusAgendamento::REALIZADO,
|
|
StatusAgendamento::PENDENTE_ASSINATURA,
|
|
StatusAgendamento::ASSINADO,
|
|
StatusAgendamento::NOVA_COLETA,
|
|
StatusAgendamento::PENDENTE_ENTREGA,
|
|
StatusAgendamento::ENTREGUE,
|
|
];
|
|
|
|
$agendamentosApp = DB::table('exame_laboratorial_agendamento as ag')
|
|
->join('exame_laboratorial_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
|
->where('ag.jornada_id', $jornada->id)
|
|
->where('ag.app_conectasus_cidadao', true)
|
|
->whereIn('ag_exame.status_id', $statusPermitidos)
|
|
->distinct()
|
|
->count('ag.id');
|
|
|
|
if ($agendamentosApp >= $jornada->vagas_app) {
|
|
|
|
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Não há vagas disponíveis para o aplicativo nesta jornada.');
|
|
}
|
|
}
|
|
|
|
private static function validarParametrizacaoHabilitada(): void
|
|
{
|
|
$habilitado = DB::table('exame_laboratorial_parametrizacao_integracao_aplicativo')
|
|
->value('habilita_agendamento_conectasus_cidadao');
|
|
|
|
if (!$habilitado) {
|
|
|
|
abort(Response::HTTP_FORBIDDEN, 'O agendamento de exames laboratoriais não está habilitado para o aplicativo.');
|
|
}
|
|
}
|
|
}
|