This commit is contained in:
@@ -0,0 +1,708 @@
|
||||
<?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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user