This commit is contained in:
@@ -0,0 +1,618 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\Agendamento;
|
||||
|
||||
use App\Enums\Administracao\Modulo as ModuloEnum;
|
||||
use App\Enums\Ambulatorio\AgendamentoStatus as AgendamentoStatusEnum;
|
||||
use App\Enums\Ambulatorio\AgendamentoTipo as AgendamentoTipoEnum;
|
||||
use App\Models\Ambulatorio\Agendamento;
|
||||
use App\Models\Ambulatorio\Jornada;
|
||||
use App\Models\Cadastro\Feriado\Feriado;
|
||||
use App\Services\Ambulatorio\AgendamentoService as AmbulatorioAgendamentoService;
|
||||
use App\Services\Ambulatorio\ParametrizacaoService;
|
||||
use App\Services\Service;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class AgendamentoService extends Service
|
||||
{
|
||||
public static function especialidades()
|
||||
{
|
||||
request()->validate(
|
||||
[
|
||||
'unidade_id' => 'required|integer',
|
||||
]
|
||||
);
|
||||
|
||||
$unidadeId = request()->unidade_id;
|
||||
$paciente = auth('mobile')->user();
|
||||
|
||||
$especialidades = DB::table('cadastro_especialidade as especialidade')
|
||||
->where('especialidade.permite_integracao_conectasus_cidadao', true)
|
||||
->whereExists(function ($query) use ($unidadeId) {
|
||||
$query->select(DB::raw(1))
|
||||
->from('ambulatorio_jornada as jornada')
|
||||
->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')
|
||||
->whereColumn('jornada.especialidade_id', 'especialidade.id')
|
||||
->where('unidade.id', $unidadeId)
|
||||
->whereNull('jornada.situacao_id')
|
||||
->whereDate('jornada.data', '>=', Carbon::today())
|
||||
->where(function ($q) {
|
||||
$q->where('jornada.quantidade_consulta_conectasus_cidadao', '>', 0)
|
||||
->orWhere('jornada.quantidade_retorno_conectasus_cidadao', '>', 0);
|
||||
});
|
||||
})
|
||||
->where(fn ($q) => self::filtrarEspecialidadePorPaciente($q, $paciente, 'especialidade'))
|
||||
->orderBy('especialidade.nome')
|
||||
->get(['especialidade.id', 'especialidade.nome']);
|
||||
|
||||
return $especialidades->map(function ($especialidade) use ($paciente) {
|
||||
$bloqueadoAte = self::validarBloqueioFaltas($paciente->id, $especialidade->id);
|
||||
$especialidade->bloqueado_ate = $bloqueadoAte ? $bloqueadoAte->format('Y-m-d') : null;
|
||||
|
||||
return $especialidade;
|
||||
});
|
||||
}
|
||||
|
||||
public static function vagas()
|
||||
{
|
||||
request()->validate(
|
||||
[
|
||||
'unidade_id' => 'required|integer',
|
||||
'especialidade_id' => 'required|integer',
|
||||
]
|
||||
);
|
||||
|
||||
$unidadeId = request()->unidade_id;
|
||||
$especialidadeId = request()->especialidade_id;
|
||||
$paciente = auth('mobile')->user();
|
||||
|
||||
$vagas = collect();
|
||||
|
||||
$feriadoDatas = self::getFeriadoDatas();
|
||||
|
||||
$jornadas = Jornada::where('especialidade_id', $especialidadeId)
|
||||
->whereNull('situacao_id')
|
||||
->whereDate('data', '>=', Carbon::today())
|
||||
->whereNotIn('data', $feriadoDatas)
|
||||
->where(function ($q) {
|
||||
$q->where('quantidade_consulta_conectasus_cidadao', '>', 0)
|
||||
->orWhere('quantidade_retorno_conectasus_cidadao', '>', 0);
|
||||
})
|
||||
->whereHas('lotacao', function ($q) use ($unidadeId) {
|
||||
$q->whereHas('infraestrutura', function ($q2) use ($unidadeId) {
|
||||
$q2->where('unidade_id', $unidadeId);
|
||||
});
|
||||
})
|
||||
->whereHas('especialidade', fn ($q) => self::filtrarEspecialidadePorPaciente($q, $paciente))
|
||||
->with(['lotacao.usuario.cidadao'])
|
||||
->orderBy('data')
|
||||
->orderBy('inicio')
|
||||
->get();
|
||||
|
||||
foreach ($jornadas as $jornada) {
|
||||
|
||||
self::calcularVagasJornada($vagas, $jornada);
|
||||
}
|
||||
|
||||
return [
|
||||
'vagas' => $vagas->sortBy('data_hora')->values(),
|
||||
'feriados' => self::getFeriados(),
|
||||
];
|
||||
}
|
||||
|
||||
public static function store()
|
||||
{
|
||||
request()->validate(
|
||||
[
|
||||
'jornada_id' => 'required|integer|exists:ambulatorio_jornada,id',
|
||||
'horario' => 'required|date_format:H:i',
|
||||
'observacao' => 'nullable|string|max:500',
|
||||
]
|
||||
);
|
||||
|
||||
$paciente = auth('mobile')->user();
|
||||
$jornada = Jornada::findOrFail(request()->jornada_id);
|
||||
$unidadeJornada = $jornada->lotacao->infraestrutura->unidade;
|
||||
|
||||
if ($unidadeJornada->id !== $paciente->unidade_id) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'A unidade informada não é a sua unidade de referência.');
|
||||
}
|
||||
|
||||
$especialidadePermitida = self::filtrarEspecialidadePorPaciente(
|
||||
$jornada->especialidade()->getQuery(),
|
||||
$paciente
|
||||
)->exists();
|
||||
|
||||
if (!$especialidadePermitida) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Você não está elegível para agendar nesta especialidade.');
|
||||
}
|
||||
|
||||
$bloqueadoAte = self::validarBloqueioFaltas($paciente->id, $jornada->especialidade_id);
|
||||
|
||||
if ($bloqueadoAte) {
|
||||
|
||||
abort(response()->json([
|
||||
'message' => 'Você está temporariamente bloqueado para agendamento nesta especialidade devido a faltas ou cancelamentos.',
|
||||
'bloqueado_ate' => $bloqueadoAte->format('Y-m-d'),
|
||||
], Response::HTTP_UNPROCESSABLE_ENTITY));
|
||||
}
|
||||
$dataHoraAgendamento = Carbon::parse($jornada->data->format('Y-m-d') . ' ' . request()->horario);
|
||||
|
||||
if ($dataHoraAgendamento->isPast()) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Não é possível agendar em horário retroativo.');
|
||||
}
|
||||
|
||||
$ehFeriado = Feriado::whereHas('modulos', fn($q) => $q->where('identificador', ModuloEnum::AMBULATORIO))
|
||||
->whereDate('data', $jornada->data->format('Y-m-d'))
|
||||
->exists();
|
||||
|
||||
if ($ehFeriado) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Não é possível agendar em dias de feriado.');
|
||||
}
|
||||
|
||||
self::validarAgendamentoDuplicado($paciente->id, $jornada);
|
||||
self::validarVagaDisponivel($jornada, request()->horario);
|
||||
|
||||
/** @var Agendamento $agendamento */
|
||||
$agendamento = DB::transaction(function () use ($jornada, $paciente) {
|
||||
$agendamento = new Agendamento();
|
||||
|
||||
$agendamento->fill([
|
||||
'jornada_id' => $jornada->id,
|
||||
'tipo_id' => self::getTipoAgendamento(
|
||||
$paciente->id,
|
||||
$jornada->lotacao->infraestrutura_id,
|
||||
$jornada->especialidade_id
|
||||
),
|
||||
'horario' => request()->horario,
|
||||
'paciente_id' => $paciente->id,
|
||||
'status_id' => AgendamentoStatusEnum::AGENDADO,
|
||||
'origem_agendamento' => 'conectasus_cidadao',
|
||||
'observacao' => request()->input('observacao'),
|
||||
]);
|
||||
|
||||
$agendamento->created_lotacao_id = $jornada->lotacao_id;
|
||||
$agendamento->created_centro_custo_id = $jornada->created_centro_custo_id;
|
||||
$agendamento->updated_lotacao_id = $jornada->lotacao_id;
|
||||
$agendamento->updated_centro_custo_id = $jornada->created_centro_custo_id;
|
||||
|
||||
$agendamento->save();
|
||||
|
||||
return $agendamento;
|
||||
});
|
||||
|
||||
$agendamento->refresh();
|
||||
|
||||
$profissionalNome = $jornada->lotacao->usuario->cidadao->nome ?? null;
|
||||
|
||||
return [
|
||||
'id' => $agendamento->id,
|
||||
'data_hora' => self::formatarDataHora($jornada->data, $agendamento->horario),
|
||||
'especialidade' => $jornada->especialidade->nome,
|
||||
'unidade' => $unidadeJornada->nome_fantasia,
|
||||
'profissional' => $profissionalNome,
|
||||
'status' => 'confirmado',
|
||||
];
|
||||
}
|
||||
|
||||
public static function cancelar($id)
|
||||
{
|
||||
$paciente = auth('mobile')->user();
|
||||
$agendamento = Agendamento::findOrFail($id);
|
||||
|
||||
if ($agendamento->paciente_id !== $paciente->id) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Este agendamento não pertence ao cidadão autenticado.');
|
||||
}
|
||||
|
||||
if ($agendamento->status_id !== AgendamentoStatusEnum::AGENDADO) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Apenas agendamentos com status "Agendado" podem ser cancelados.');
|
||||
}
|
||||
|
||||
$jornada = $agendamento->jornada;
|
||||
$dataHoraConsulta = Carbon::parse($jornada->data->format('Y-m-d') . ' ' . $agendamento->horario->format('H:i:s'));
|
||||
$horasAntecedencia = 48;
|
||||
|
||||
if (Carbon::now()->diffInHours($dataHoraConsulta, false) < $horasAntecedencia) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Cancelamento permitido apenas com no mínimo 48 horas de antecedência.');
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($agendamento) {
|
||||
$agendamento->update([
|
||||
'status_id' => AgendamentoStatusEnum::CANCELADO_PELO_PACIENTE,
|
||||
'motivo_cancelamento' => 'Cancelado pelo cidadão via ConectaSUS Cidadão.',
|
||||
]);
|
||||
});
|
||||
|
||||
return ['message' => 'Agendamento cancelado com sucesso.'];
|
||||
}
|
||||
|
||||
public static function historico()
|
||||
{
|
||||
$paciente = auth('mobile')->user();
|
||||
$perPage = request()->per_page ?? 15;
|
||||
$status = request()->status;
|
||||
|
||||
$statusMap = [
|
||||
'confirmado' => AgendamentoStatusEnum::AGENDADO,
|
||||
'cancelado' => [AgendamentoStatusEnum::CANCELADO_PELO_PACIENTE, AgendamentoStatusEnum::CANCELADO_PELO_MEDICO],
|
||||
'realizado' => AgendamentoStatusEnum::REALIZADO,
|
||||
'falta' => AgendamentoStatusEnum::FALTA_DO_PACIENTE,
|
||||
];
|
||||
|
||||
$query = DB::table('ambulatorio_agendamento as agendamento')
|
||||
->join('ambulatorio_jornada as jornada', 'jornada.id', 'agendamento.jornada_id')
|
||||
->join('cadastro_especialidade as especialidade', 'especialidade.id', 'jornada.especialidade_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')
|
||||
->leftJoin('cadastro_usuario as usuario', 'usuario.id', 'lotacao.usuario_id')
|
||||
->leftJoin('cadastro_cidadao as cidadao', 'cidadao.id', 'usuario.cidadao_id')
|
||||
->join('ambulatorio_agendamento_status as agendamento_status', 'agendamento_status.id', 'agendamento.status_id')
|
||||
->where('agendamento.paciente_id', $paciente->id)
|
||||
->when($status && isset($statusMap[$status]), function ($q) use ($status, $statusMap) {
|
||||
$valor = $statusMap[$status];
|
||||
|
||||
if (is_array($valor)) {
|
||||
|
||||
$q->whereIn('agendamento.status_id', $valor);
|
||||
} else {
|
||||
|
||||
$q->where('agendamento.status_id', $valor);
|
||||
}
|
||||
})
|
||||
->select([
|
||||
'agendamento.id',
|
||||
DB::raw("CONCAT(DATE_FORMAT(jornada.data, '%Y-%m-%d'), 'T', DATE_FORMAT(agendamento.horario, '%H:%i:%s')) as data_hora"),
|
||||
'especialidade.id as especialidade_id',
|
||||
'especialidade.nome as especialidade_nome',
|
||||
'unidade.id as unidade_id',
|
||||
'unidade.nome_fantasia as unidade_nome',
|
||||
'cidadao.id as profissional_id',
|
||||
'cidadao.nome as profissional_nome',
|
||||
'agendamento.status_id',
|
||||
'agendamento.origem_agendamento',
|
||||
'jornada.data as jornada_data',
|
||||
'agendamento.horario',
|
||||
])
|
||||
->orderByDesc('jornada.data')
|
||||
->orderByDesc('agendamento.horario');
|
||||
|
||||
$paginator = $query->paginate((int) $perPage);
|
||||
|
||||
$items = collect($paginator->items())->map(function ($item) {
|
||||
$statusLabel = self::statusLabel($item->status_id);
|
||||
$podeCancelar = self::podeCancelar($item->status_id, $item->jornada_data, $item->horario);
|
||||
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'data_hora' => $item->data_hora,
|
||||
'especialidade' => [
|
||||
'id' => $item->especialidade_id,
|
||||
'nome' => $item->especialidade_nome,
|
||||
],
|
||||
'unidade' => [
|
||||
'id' => $item->unidade_id,
|
||||
'nome' => $item->unidade_nome,
|
||||
],
|
||||
'profissional' => $item->profissional_id ? [
|
||||
'id' => $item->profissional_id,
|
||||
'nome' => $item->profissional_nome,
|
||||
] : null,
|
||||
'status' => $statusLabel,
|
||||
'origem' => $item->origem_agendamento,
|
||||
'pode_cancelar' => $podeCancelar,
|
||||
];
|
||||
});
|
||||
|
||||
return [
|
||||
'data' => $items->toArray(),
|
||||
'current_page' => $paginator->currentPage(),
|
||||
'last_page' => $paginator->lastPage(),
|
||||
'total' => $paginator->total(),
|
||||
];
|
||||
}
|
||||
|
||||
private static function calcularVagasJornada($vagas, Jornada $jornada): void
|
||||
{
|
||||
$data = $jornada->data;
|
||||
$inicio = Carbon::parse($data->format('Y-m-d') . ' ' . $jornada->inicio->format('H:i:s'));
|
||||
|
||||
$profissional = null;
|
||||
$usuario = $jornada->lotacao->usuario ?? null;
|
||||
|
||||
if ($usuario && $usuario->cidadao) {
|
||||
|
||||
$profissional = [
|
||||
'id' => $usuario->cidadao->id,
|
||||
'nome' => $usuario->cidadao->nome,
|
||||
];
|
||||
}
|
||||
|
||||
$quantidadeSistemaConsulta = $jornada->quantidade_consulta ?? 0;
|
||||
$tempoSistemaConsulta = $jornada->tempo_consulta ?? 0;
|
||||
$quantidadeSistemaRetorno = $jornada->quantidade_retorno ?? 0;
|
||||
$tempoSistemaRetorno = $jornada->tempo_retorno ?? 0;
|
||||
|
||||
$quantidadeConsulta = $jornada->quantidade_consulta_conectasus_cidadao ?? 0;
|
||||
$tempoConsulta = $jornada->tempo_consulta_conectasus_cidadao ?? 0;
|
||||
$quantidadeRetorno = $jornada->quantidade_retorno_conectasus_cidadao ?? 0;
|
||||
$tempoRetorno = $jornada->tempo_retorno_conectasus_cidadao ?? 0;
|
||||
|
||||
$statusLiberamHorario = AmbulatorioAgendamentoService::getStatusLiberamHorario();
|
||||
|
||||
$offsetSistemaConsulta = $quantidadeSistemaConsulta * $tempoSistemaConsulta;
|
||||
$offsetSistemaRetorno = $quantidadeSistemaRetorno * $tempoSistemaRetorno;
|
||||
|
||||
if ($quantidadeConsulta > 0 && $tempoConsulta > 0) {
|
||||
|
||||
$inicioConsultaApp = $inicio->copy()->addMinutes($offsetSistemaConsulta);
|
||||
|
||||
self::gerarVagas(
|
||||
$vagas,
|
||||
$jornada,
|
||||
$inicioConsultaApp,
|
||||
$quantidadeConsulta,
|
||||
$tempoConsulta,
|
||||
$profissional,
|
||||
$statusLiberamHorario
|
||||
);
|
||||
}
|
||||
|
||||
if ($quantidadeRetorno > 0 && $tempoRetorno > 0) {
|
||||
|
||||
$inicioRetornoApp = $inicio->copy()->addMinutes($offsetSistemaConsulta + $quantidadeConsulta * $tempoConsulta + $offsetSistemaRetorno);
|
||||
|
||||
self::gerarVagas(
|
||||
$vagas,
|
||||
$jornada,
|
||||
$inicioRetornoApp,
|
||||
$quantidadeRetorno,
|
||||
$tempoRetorno,
|
||||
$profissional,
|
||||
$statusLiberamHorario
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static function gerarVagas($vagas, Jornada $jornada, Carbon $inicio, int $quantidade, int $tempo, ?array $profissional, array $statusLiberamHorario): void
|
||||
{
|
||||
$horarioAtual = $inicio->copy();
|
||||
|
||||
for ($i = 0; $i < $quantidade; $i++) {
|
||||
|
||||
if ($horarioAtual->isPast()) {
|
||||
|
||||
$horarioAtual->addMinutes($tempo);
|
||||
continue;
|
||||
}
|
||||
|
||||
$horarioStr = $horarioAtual->format('H:i');
|
||||
|
||||
$agendado = Agendamento::where('jornada_id', $jornada->id)
|
||||
->whereTime('horario', $horarioStr)
|
||||
->where('origem_agendamento', 'conectasus_cidadao')
|
||||
->whereNotIn('status_id', $statusLiberamHorario)
|
||||
->exists();
|
||||
|
||||
if (!$agendado) {
|
||||
|
||||
$vagas->push([
|
||||
'jornada_id' => $jornada->id,
|
||||
'horario' => $horarioStr,
|
||||
'data_hora' => $jornada->data->format('Y-m-d') . 'T' . $horarioStr . ':00',
|
||||
'profissional' => $profissional,
|
||||
]);
|
||||
}
|
||||
|
||||
$horarioAtual->addMinutes($tempo);
|
||||
}
|
||||
}
|
||||
|
||||
private static function validarBloqueioFaltas(int $pacienteId, int $especialidadeId): ?Carbon
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
$dataBase = Carbon::parse($ultimaOcorrencia)->min(Carbon::today());
|
||||
$dataMinima = $dataBase->addDays($tempoLiberacao);
|
||||
|
||||
return Carbon::today()->lt($dataMinima) ? $dataMinima : null;
|
||||
}
|
||||
|
||||
private static function validarAgendamentoDuplicado(int $pacienteId, Jornada $jornada): void
|
||||
{
|
||||
$especialidade = $jornada->especialidade;
|
||||
|
||||
if ($especialidade->tratamento || $especialidade->permite_agendamento_mesmo_dia) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$existe = Agendamento::where('paciente_id', $pacienteId)
|
||||
->where('status_id', AgendamentoStatusEnum::AGENDADO)
|
||||
->whereHas('jornada', function ($q) use ($jornada) {
|
||||
$q->where('especialidade_id', $jornada->especialidade_id)
|
||||
->whereDate('data', '>=', Carbon::today());
|
||||
})
|
||||
->exists();
|
||||
|
||||
if ($existe) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Você já possui um agendamento ativo para esta especialidade.');
|
||||
}
|
||||
}
|
||||
|
||||
private static function validarVagaDisponivel(Jornada $jornada, string $horario): void
|
||||
{
|
||||
$statusLiberamHorario = AmbulatorioAgendamentoService::getStatusLiberamHorario();
|
||||
|
||||
$agendado = Agendamento::where('jornada_id', $jornada->id)
|
||||
->whereTime('horario', $horario)
|
||||
->where('origem_agendamento', 'conectasus_cidadao')
|
||||
->whereNotIn('status_id', $statusLiberamHorario)
|
||||
->exists();
|
||||
|
||||
if ($agendado) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Esta vaga não está mais disponível. Selecione outro horário.');
|
||||
}
|
||||
}
|
||||
|
||||
private static function getTipoAgendamento(int $pacienteId, int $infraestruturaId, int $especialidadeId): int
|
||||
{
|
||||
$ultimaData = self::getUltimoDataAgendamento($pacienteId, $infraestruturaId, $especialidadeId);
|
||||
$diasParaRetorno = ParametrizacaoService::getQuantidadeDiasRetorno($infraestruturaId, $especialidadeId);
|
||||
|
||||
return $diasParaRetorno === 0 || is_null($ultimaData) || Carbon::today()->diff($ultimaData)->days > $diasParaRetorno
|
||||
? AgendamentoTipoEnum::CONSULTA
|
||||
: AgendamentoTipoEnum::RETORNO;
|
||||
}
|
||||
|
||||
private static function getUltimoDataAgendamento(int $pacienteId, int $infraestruturaId, int $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 statusLabel(int $statusId): string
|
||||
{
|
||||
switch ($statusId) {
|
||||
case AgendamentoStatusEnum::AGENDADO:
|
||||
return 'confirmado';
|
||||
case AgendamentoStatusEnum::CANCELADO_PELO_PACIENTE:
|
||||
case AgendamentoStatusEnum::CANCELADO_PELO_MEDICO:
|
||||
return 'cancelado';
|
||||
case AgendamentoStatusEnum::FALTA_DO_PACIENTE:
|
||||
return 'falta';
|
||||
case AgendamentoStatusEnum::REALIZADO:
|
||||
return 'realizado';
|
||||
default:
|
||||
return 'outro';
|
||||
}
|
||||
}
|
||||
|
||||
private static function podeCancelar(int $statusId, $jornadaData, $horario): bool
|
||||
{
|
||||
if ($statusId !== AgendamentoStatusEnum::AGENDADO) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dataHora = Carbon::parse($jornadaData . ' ' . $horario);
|
||||
|
||||
return Carbon::now()->diffInHours($dataHora, false) >= 48;
|
||||
}
|
||||
|
||||
private static function formatarDataHora($data, $horario): string
|
||||
{
|
||||
return Carbon::parse($data->format('Y-m-d') . ' ' . $horario->format('H:i:s'))->format('Y-m-d\TH:i:s');
|
||||
}
|
||||
|
||||
private static function getFeriados(): array
|
||||
{
|
||||
return Feriado::whereHas('modulos', fn($q) => $q->where('identificador', ModuloEnum::AMBULATORIO))
|
||||
->whereDate('data', '>=', Carbon::today())
|
||||
->orderBy('data')
|
||||
->get()
|
||||
->map(fn($f) => [
|
||||
'data' => Carbon::parse($f->data)->format('Y-m-d'),
|
||||
'observacao' => $f->observacao,
|
||||
])
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private static function getFeriadoDatas(): Collection
|
||||
{
|
||||
return Feriado::whereHas('modulos', fn($q) => $q->where('identificador', ModuloEnum::AMBULATORIO))
|
||||
->whereDate('data', '>=', Carbon::today())
|
||||
->pluck('data');
|
||||
}
|
||||
|
||||
private static function filtrarEspecialidadePorPaciente($query, $paciente, string $tableAlias = '')
|
||||
{
|
||||
$alias = $tableAlias ? "{$tableAlias}." : '';
|
||||
$cidadao = $paciente->cidadao;
|
||||
$idadeAnos = $cidadao->idade['anos'] ?? null;
|
||||
$sexoId = $cidadao->sexo_id;
|
||||
|
||||
return $query
|
||||
->when($idadeAnos !== null, fn ($q) => $q
|
||||
->where(fn ($q) => $q
|
||||
->whereNull("{$alias}idade_minima")
|
||||
->orWhere("{$alias}idade_minima", '<=', $idadeAnos)
|
||||
)
|
||||
->where(fn ($q) => $q
|
||||
->whereNull("{$alias}idade_maxima")
|
||||
->orWhere("{$alias}idade_maxima", '>=', $idadeAnos)
|
||||
)
|
||||
)
|
||||
->where(fn ($q) => $q
|
||||
->whereNull("{$alias}sexo_id")
|
||||
->orWhere("{$alias}sexo_id", $sexoId)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\Autenticacao;
|
||||
|
||||
use App\Models\Cadastro\SujeitoAtencao\Cidadao;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class AutenticacaoService
|
||||
{
|
||||
public function login(array $dados): array
|
||||
{
|
||||
$cidadao = Cidadao::where('cpf', $dados['cpf'])
|
||||
->whereHas('paciente', fn ($q) => $q->whereNotNull('senha'))
|
||||
->first();
|
||||
|
||||
if (is_null($cidadao) || !Hash::check($dados['senha'], $cidadao->paciente->senha)) {
|
||||
|
||||
throw new \InvalidArgumentException('CPF e/ou senha incorreto(s).', Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$usuario = $cidadao->paciente;
|
||||
|
||||
$token = auth('mobile')->setTTL(15)->login($usuario);
|
||||
|
||||
$refreshToken = auth('mobile')
|
||||
->claims(['type' => 'refresh'])
|
||||
->setTTL(10080)
|
||||
->tokenById($usuario->id);
|
||||
|
||||
return [
|
||||
'expiration' => time() + 15 * 60,
|
||||
'token' => $token,
|
||||
'refresh_token' => $refreshToken,
|
||||
'usuario' => [
|
||||
'cpf' => $dados['cpf'],
|
||||
'nome' => $cidadao->nome,
|
||||
'paciente_id' => $cidadao->paciente->id,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function refresh()
|
||||
{
|
||||
$payload = auth('mobile')->payload();
|
||||
|
||||
if ($payload->get('type') !== 'refresh') {
|
||||
return response()->json(['error' => 'Token inválido'], 401);
|
||||
}
|
||||
|
||||
$user = auth('mobile')->user();
|
||||
auth('mobile')->invalidate();
|
||||
|
||||
$accessToken = auth('mobile')
|
||||
->claims(['type' => 'access'])
|
||||
->setTTL(15)
|
||||
->tokenById($user->id);
|
||||
|
||||
$refreshToken = auth('mobile')
|
||||
->claims(['type' => 'refresh'])
|
||||
->setTTL(10080)
|
||||
->tokenById($user->id);
|
||||
|
||||
return [
|
||||
'token' => $accessToken,
|
||||
'refresh_token' => $refreshToken,
|
||||
'expiration' => time() + 15 * 60,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\Autenticacao;
|
||||
|
||||
use App\Enums\Mobile\ConectaCidadao\PrimeiroAcesso;
|
||||
use App\Models\Cadastro\SujeitoAtencao\Cidadao;
|
||||
use App\Models\Mobile\ConectaCidadao\ConectaCidadaoSmsCode;
|
||||
use App\Services\Sms\SmsService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class PrimeiroAcessoService
|
||||
{
|
||||
public function iniciar(array $dados): array
|
||||
{
|
||||
$cidadao = Cidadao::where('cpf', $dados['cpf'])->first();
|
||||
$telefonePrincipal = $cidadao ? $cidadao->telefone_principal : null;
|
||||
$paciente = $cidadao ? $cidadao->paciente : null;
|
||||
|
||||
if (!is_null($paciente) && !is_null($paciente->senha)) {
|
||||
|
||||
throw new \InvalidArgumentException('Primeiro acesso já realizado. Utilize a tela de login.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
if (
|
||||
is_null($cidadao) ||
|
||||
is_null($paciente) ||
|
||||
is_null($telefonePrincipal) ||
|
||||
$cidadao->data_nascimento->format('Y-m-d') !== $dados['data_nascimento'] ||
|
||||
$telefonePrincipal->numero !== $dados['telefone']
|
||||
) {
|
||||
throw new \InvalidArgumentException('Dados não conferem.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$codigo = (string) random_int(100000, 999999);
|
||||
$agora = Carbon::now();
|
||||
|
||||
$registro = ConectaCidadaoSmsCode::updateOrCreate(
|
||||
[
|
||||
'cidadao_id' => $cidadao->id,
|
||||
'telefone' => $telefonePrincipal->numero,
|
||||
'usado' => false,
|
||||
],
|
||||
[
|
||||
'codigo' => $codigo,
|
||||
'expires_at' => $agora->copy()->addMinutes(PrimeiroAcesso::SMS_CODE_TTL_MINUTOS),
|
||||
'last_sent_at' => $agora,
|
||||
'tentativas_envio' => 1,
|
||||
'tentativas_validacao' => 0,
|
||||
]
|
||||
);
|
||||
|
||||
$mensagem = "Seu código de segurança ConectaSus Cidadão é: {$codigo}";
|
||||
|
||||
if (app()->environment('production')) {
|
||||
|
||||
(new SmsService())->send($registro->telefone, $mensagem);
|
||||
}
|
||||
|
||||
$retorno = [
|
||||
'token' => $registro->id,
|
||||
];
|
||||
|
||||
if (!app()->environment('production')) {
|
||||
|
||||
$retorno['codigo'] = $codigo;
|
||||
}
|
||||
|
||||
return $retorno;
|
||||
}
|
||||
|
||||
public function reenviarCodigo(int $token): void
|
||||
{
|
||||
$registro = ConectaCidadaoSmsCode::findOrFail($token);
|
||||
|
||||
if ($registro->usado || $registro->expires_at->isPast()) {
|
||||
|
||||
throw new \InvalidArgumentException('Código expirado. Inicie novamente o processo.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$agora = Carbon::now();
|
||||
|
||||
if (!is_null($registro->last_sent_at) && $agora->diffInSeconds($registro->last_sent_at) < PrimeiroAcesso::SMS_REENVIO_INTERVALO_SEGUNDOS) {
|
||||
|
||||
throw new \InvalidArgumentException('Aguarde antes de solicitar um novo código.', Response::HTTP_TOO_MANY_REQUESTS);
|
||||
}
|
||||
|
||||
if ($registro->tentativas_envio >= PrimeiroAcesso::SMS_REENVIO_LIMITE) {
|
||||
|
||||
throw new \InvalidArgumentException('Limite de reenvios atingido.', Response::HTTP_TOO_MANY_REQUESTS);
|
||||
}
|
||||
|
||||
$registro->tentativas_envio++;
|
||||
$registro->last_sent_at = $agora;
|
||||
$registro->save();
|
||||
|
||||
$mensagem = "Seu código de segurança ConectaSus Cidadão é: {$registro->codigo}";
|
||||
|
||||
if (app()->environment('production')) {
|
||||
|
||||
(new SmsService())->send($registro->telefone, $mensagem);
|
||||
}
|
||||
}
|
||||
|
||||
public function validarCodigo(array $dados): void
|
||||
{
|
||||
$registro = ConectaCidadaoSmsCode::findOrFail($dados['token']);
|
||||
|
||||
if ($registro->usado || $registro->expires_at->isPast()) {
|
||||
|
||||
throw new \InvalidArgumentException('Código expirado. Inicie novamente o processo.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
if ($registro->codigo !== $dados['codigo']) {
|
||||
$registro->tentativas_validacao++;
|
||||
$registro->save();
|
||||
|
||||
throw new \InvalidArgumentException('Código inválido.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$registro->usado = true;
|
||||
$registro->save();
|
||||
}
|
||||
|
||||
public function definirSenha(array $dados): void
|
||||
{
|
||||
$registro = ConectaCidadaoSmsCode::findOrFail($dados['token']);
|
||||
|
||||
if (!$registro->usado || $registro->expires_at->isPast()) {
|
||||
|
||||
throw new \InvalidArgumentException('Fluxo inválido. Valide o código de segurança novamente.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$cidadao = Cidadao::findOrFail($registro->cidadao_id);
|
||||
|
||||
if (is_null($cidadao->paciente)) {
|
||||
|
||||
throw new \InvalidArgumentException('Cidadão não encontrado.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$cidadao->paciente->senha = Hash::make($dados['senha']);
|
||||
$cidadao->paciente->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\Autenticacao;
|
||||
|
||||
use App\Enums\Mobile\ConectaCidadao\PrimeiroAcesso;
|
||||
use App\Models\Cadastro\SujeitoAtencao\Cidadao;
|
||||
use App\Models\Mobile\ConectaCidadao\ConectaCidadaoSmsCode;
|
||||
use App\Services\Sms\SmsService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class RecuperacaoSenhaService
|
||||
{
|
||||
public function iniciar(string $cpf): array
|
||||
{
|
||||
$cidadao = Cidadao::where('cpf', $cpf)->first();
|
||||
|
||||
if ($cidadao && empty($cidadao->paciente->senha)) {
|
||||
|
||||
throw new \InvalidArgumentException('Cidadão não possui senha cadastrada.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$telefonePrincipal = $cidadao ? $cidadao->telefone_principal : null;
|
||||
$paciente = $cidadao ? $cidadao->paciente : null;
|
||||
|
||||
if (is_null($cidadao) || is_null($paciente) || is_null($telefonePrincipal)) {
|
||||
|
||||
throw new \InvalidArgumentException('Cidadão não encontrado ou sem telefone principal cadastrado.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$codigo = (string) random_int(100000, 999999);
|
||||
$agora = Carbon::now();
|
||||
|
||||
$registro = ConectaCidadaoSmsCode::updateOrCreate(
|
||||
[
|
||||
'cidadao_id' => $cidadao->id,
|
||||
'telefone' => $telefonePrincipal->numero,
|
||||
'usado' => false,
|
||||
],
|
||||
[
|
||||
'codigo' => $codigo,
|
||||
'expires_at' => $agora->copy()->addMinutes(PrimeiroAcesso::SMS_CODE_TTL_MINUTOS),
|
||||
'last_sent_at' => $agora,
|
||||
'tentativas_envio' => 1,
|
||||
'tentativas_validacao' => 0,
|
||||
]
|
||||
);
|
||||
|
||||
$mensagem = "Seu código de recuperação de senha ConectaSus Cidadão é: {$codigo}";
|
||||
|
||||
if (app()->environment('production')) {
|
||||
|
||||
(new SmsService())->send($registro->telefone, $mensagem);
|
||||
}
|
||||
|
||||
$retorno = [
|
||||
'token' => $registro->id,
|
||||
'telefone' => $this->mascararTelefone($telefonePrincipal->numero),
|
||||
];
|
||||
|
||||
if (!app()->environment('production')) {
|
||||
|
||||
$retorno['codigo'] = $codigo;
|
||||
}
|
||||
|
||||
return $retorno;
|
||||
}
|
||||
|
||||
public function reenviarCodigo(int $token): void
|
||||
{
|
||||
$registro = ConectaCidadaoSmsCode::findOrFail($token);
|
||||
|
||||
if ($registro->usado || $registro->expires_at->isPast()) {
|
||||
|
||||
throw new \InvalidArgumentException('Código expirado. Inicie novamente o processo.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$agora = Carbon::now();
|
||||
|
||||
if (!is_null($registro->last_sent_at) && $agora->diffInSeconds($registro->last_sent_at) < PrimeiroAcesso::SMS_REENVIO_INTERVALO_SEGUNDOS) {
|
||||
|
||||
throw new \InvalidArgumentException('Aguarde antes de solicitar um novo código.', Response::HTTP_TOO_MANY_REQUESTS);
|
||||
}
|
||||
|
||||
if ($registro->tentativas_envio >= PrimeiroAcesso::SMS_REENVIO_LIMITE) {
|
||||
|
||||
throw new \InvalidArgumentException('Limite de reenvios atingido.', Response::HTTP_TOO_MANY_REQUESTS);
|
||||
}
|
||||
|
||||
$registro->tentativas_envio++;
|
||||
$registro->last_sent_at = $agora;
|
||||
$registro->save();
|
||||
|
||||
$mensagem = "Seu código de recuperação de senha ConectaSus Cidadão é: {$registro->codigo}";
|
||||
|
||||
if (app()->environment('production')) {
|
||||
|
||||
(new SmsService())->send($registro->telefone, $mensagem);
|
||||
}
|
||||
}
|
||||
|
||||
public function validarCodigo(array $dados): void
|
||||
{
|
||||
$registro = ConectaCidadaoSmsCode::findOrFail($dados['token']);
|
||||
|
||||
if ($registro->usado || $registro->expires_at->isPast()) {
|
||||
|
||||
throw new \InvalidArgumentException('Código expirado. Inicie novamente o processo.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
if ($registro->codigo !== $dados['codigo']) {
|
||||
$registro->tentativas_validacao++;
|
||||
$registro->save();
|
||||
|
||||
throw new \InvalidArgumentException('Código inválido.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$registro->usado = true;
|
||||
$registro->save();
|
||||
}
|
||||
|
||||
public function redefinirSenha(array $dados): void
|
||||
{
|
||||
$registro = ConectaCidadaoSmsCode::findOrFail($dados['token']);
|
||||
|
||||
if (!$registro->usado || $registro->expires_at->isPast()) {
|
||||
|
||||
throw new \InvalidArgumentException('Fluxo inválido. Valide o código de segurança novamente.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$cidadao = Cidadao::findOrFail($registro->cidadao_id);
|
||||
|
||||
if (is_null($cidadao->paciente)) {
|
||||
|
||||
throw new \InvalidArgumentException('Cidadão não encontrado.', Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
$cidadao->paciente->senha = Hash::make($dados['senha']);
|
||||
$cidadao->paciente->save();
|
||||
}
|
||||
|
||||
private function mascararTelefone(string $telefone): string
|
||||
{
|
||||
$apenasNumeros = preg_replace('/[^0-9]/', '', $telefone);
|
||||
$tamanho = strlen($apenasNumeros);
|
||||
|
||||
if ($tamanho < 8) {
|
||||
|
||||
return $telefone;
|
||||
}
|
||||
|
||||
$fimVisivel = substr($apenasNumeros, -4);
|
||||
$ddd = substr($apenasNumeros, 0, 2);
|
||||
|
||||
return "({$ddd}) *****-{$fimVisivel}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\CarteiraVacinacao;
|
||||
|
||||
use App\Enums\Cadastro\Vacina as VacinaEnum;
|
||||
use App\Enums\Imunizacao\Estrategia;
|
||||
use App\Enums\Mobile\ConectaCidadao\CarteiraVacinacao\Mensagem;
|
||||
use App\Enums\Mobile\ConectaCidadao\CarteiraVacinacao\Prazo;
|
||||
use App\Models\Cadastro\SujeitoAtencao\Paciente;
|
||||
use App\Services\Service;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CarteiraVacinacaoService extends Service
|
||||
{
|
||||
private const VACINAS_COVID = [
|
||||
VacinaEnum::COVID_19_ASTRAZENECA_FIOCRUZ,
|
||||
VacinaEnum::COVID_19_SINOVAC_BUTANTAN,
|
||||
VacinaEnum::COVID_19_PFIZER,
|
||||
VacinaEnum::COVID_19_JANSSEN,
|
||||
VacinaEnum::COVID_19_ASTRAZENECA,
|
||||
VacinaEnum::COVID_19_GAMALEYA,
|
||||
VacinaEnum::COVID_19_BHARAT,
|
||||
VacinaEnum::COVID_19_MODERNA_SPIKEVAX,
|
||||
VacinaEnum::COVID_19_SINOVAC_CORONAVAC,
|
||||
VacinaEnum::COVID_19_PFIZER_COMIRNATY_PEDIATRICA,
|
||||
VacinaEnum::COVID_19_PFIZER_COMIRNATY_PEDIATRICA_MENOR_DE_5_ANOS,
|
||||
VacinaEnum::COVID_19_PFIZER_COMIRNATYBIVALENTE,
|
||||
];
|
||||
|
||||
public static function vacinas(): array
|
||||
{
|
||||
$paciente = auth('mobile')->user();
|
||||
$dataNascimento = $paciente->cidadao->data_nascimento;
|
||||
|
||||
$regrasAtuais = $dataNascimento
|
||||
? self::getRegrasAtuais($dataNascimento)
|
||||
: collect();
|
||||
|
||||
$aplicacoes = self::getAplicacoes($paciente);
|
||||
$calendario = self::getCalendarioVacinal($paciente, $regrasAtuais);
|
||||
|
||||
$alertas = array_values(
|
||||
array_filter(
|
||||
$calendario,
|
||||
fn($v) => in_array(
|
||||
$v['status'],
|
||||
[
|
||||
'em_atraso',
|
||||
'proximo_do_prazo'
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return [
|
||||
'paciente' => self::getDadosPaciente($paciente),
|
||||
'aplicacoes' => $aplicacoes,
|
||||
'calendario_vacinal' => $calendario,
|
||||
'alertas' => $alertas,
|
||||
'mensagem_orientativa' => self::getMensagemOrientativa($calendario),
|
||||
'mensagem_civp' => Mensagem::CIVP,
|
||||
];
|
||||
}
|
||||
|
||||
private static function getDadosPaciente(Paciente $paciente): array
|
||||
{
|
||||
$cidadao = $paciente->cidadao;
|
||||
$dataNascimento = $cidadao->data_nascimento;
|
||||
$hoje = Carbon::now();
|
||||
$diff = $dataNascimento->diff($hoje);
|
||||
|
||||
return [
|
||||
'nome' => $cidadao->nome,
|
||||
'cpf' => $cidadao->cpf,
|
||||
'cns' => $cidadao->cns,
|
||||
'nome_mae' => $cidadao->nome_mae,
|
||||
'sexo' => $cidadao->sexo ? $cidadao->sexo->descricao : null,
|
||||
'nacionalidade' => $cidadao->nacionalidade ? $cidadao->nacionalidade->descricao : null,
|
||||
'data_nascimento' => $dataNascimento->format('Y-m-d'),
|
||||
'idade' => $dataNascimento->diff($hoje)->format('%ya, %mm e %dd'),
|
||||
'idade_anos' => (int) $diff->y,
|
||||
'idade_meses' => (int) $diff->m,
|
||||
'idade_dias' => (int) $diff->d,
|
||||
];
|
||||
}
|
||||
|
||||
private static function getRegrasAtuais(Carbon $dataNascimento): Collection
|
||||
{
|
||||
$hoje = Carbon::now();
|
||||
$idade = $dataNascimento->diff($hoje);
|
||||
|
||||
return DB::table('imunizacao_esquema_vacinal_regra as regra')
|
||||
->join('imunizacao_esquema_vacinal_faixa_etaria as faixa', 'faixa.id', 'regra.faixa_etaria_id')
|
||||
->join('cadastro_vacina as vacina', 'vacina.id', 'regra.vacina_id')
|
||||
->join('imunizacao_dose as dose', 'dose.id', 'regra.dose_id')
|
||||
->join('imunizacao_estrategia as estrategia', 'estrategia.id', 'regra.estrategia_id')
|
||||
->where('regra.estrategia_id', Estrategia::ROTINA)
|
||||
->select(
|
||||
[
|
||||
'vacina.id as vacina_id',
|
||||
'vacina.codigo as vacina_codigo',
|
||||
'vacina.nome as vacina_nome',
|
||||
'vacina.abreviacao as vacina_abreviacao',
|
||||
'vacina.sigla as vacina_sigla',
|
||||
'dose.id as dose_id',
|
||||
'dose.nome as dose_nome',
|
||||
'estrategia.nome as estrategia_nome',
|
||||
'faixa.inicio_ano',
|
||||
'faixa.inicio_mes',
|
||||
'faixa.inicio_dia',
|
||||
'faixa.fim_ano',
|
||||
'faixa.fim_mes',
|
||||
'faixa.fim_dia'
|
||||
]
|
||||
)
|
||||
->where(
|
||||
fn($q) => $q
|
||||
->where(
|
||||
fn($q) => $q
|
||||
->where('faixa.inicio_ano', '<', $idade->y)
|
||||
->orWhere(
|
||||
fn($q) => $q
|
||||
->where('faixa.inicio_ano', '=', $idade->y)
|
||||
->where('faixa.inicio_mes', '<', $idade->m)
|
||||
)
|
||||
->orWhere(
|
||||
fn($q) => $q
|
||||
->where('faixa.inicio_ano', '=', $idade->y)
|
||||
->where('faixa.inicio_mes', '=', $idade->m)
|
||||
->where('faixa.inicio_dia', '<=', $idade->d)
|
||||
)
|
||||
)
|
||||
->where(
|
||||
fn($q) => $q
|
||||
->where('faixa.fim_ano', '>', $idade->y)
|
||||
->orWhere(
|
||||
fn($q) => $q
|
||||
->where('faixa.fim_ano', '=', $idade->y)
|
||||
->where('faixa.fim_mes', '>', $idade->m)
|
||||
)
|
||||
->orWhere(
|
||||
fn($q) => $q
|
||||
->where('faixa.fim_ano', '=', $idade->y)
|
||||
->where('faixa.fim_mes', '=', $idade->m)
|
||||
->where('faixa.fim_dia', '>=', $idade->d)
|
||||
)
|
||||
)
|
||||
)
|
||||
->get()
|
||||
->keyBy(fn($r) => "{$r->vacina_id}_{$r->dose_id}");
|
||||
}
|
||||
|
||||
private static function getAplicacoes(Paciente $paciente): array
|
||||
{
|
||||
$aplicacoes = DB::table('imunizacao_registro_aplicacao as ira')
|
||||
->select(
|
||||
[
|
||||
'ira.id',
|
||||
'vacina.id as vacina_id',
|
||||
'vacina.codigo as vacina_codigo',
|
||||
'vacina.nome as vacina_nome',
|
||||
'vacina.abreviacao as vacina_abreviacao',
|
||||
'vacina.sigla as vacina_sigla',
|
||||
'dose.id as dose_id',
|
||||
'dose.nome as dose_nome',
|
||||
'estrategia_reg.nome as estrategia_nome',
|
||||
'ira.data_aplicacao',
|
||||
'iev.lote',
|
||||
'iev.data_validade_lote',
|
||||
'produtor.nome as produtor_nome',
|
||||
'unidade.cnes as unidade_cnes',
|
||||
'unidade.nome_fantasia as unidade_nome',
|
||||
DB::raw("CASE WHEN infra.principal THEN unidade.nome_fantasia ELSE infra.nome END AS sala_vacinacao_nome"),
|
||||
'aplicador_cidadao.nome as aplicador_nome',
|
||||
'mun.nome as municipio_nome',
|
||||
'est.uf as estado_uf'
|
||||
]
|
||||
)
|
||||
->leftJoin('imunizacao_entrada_vacina as iev', 'iev.id', 'ira.entrada_vacina_id')
|
||||
->leftJoin('imunizacao_entrada as entrada', 'entrada.id', 'iev.entrada_id')
|
||||
->leftJoin('imunizacao_sala_vacinacao as sala', 'sala.id', 'entrada.sala_vacinacao_id')
|
||||
->leftJoin('cadastro_infraestrutura as infra', 'infra.id', 'sala.infraestrutura_id')
|
||||
->leftJoin('cadastro_unidade as unidade', 'unidade.id', 'infra.unidade_id')
|
||||
->leftJoin('cadastro_endereco as end_infra', 'end_infra.id', 'infra.endereco_id')
|
||||
->leftJoin('cadastro_logradouro as logr_infra', 'logr_infra.id', 'end_infra.logradouro_id')
|
||||
->leftJoin('intranet.cadastro_municipio as mun', 'mun.id', 'logr_infra.municipio_id')
|
||||
->leftJoin('intranet.cadastro_estado as est', 'est.id', 'mun.estado_id')
|
||||
->leftJoin('cadastro_vacina as vacina', 'vacina.id', 'iev.vacina_id')
|
||||
->leftJoin('imunizacao_produtor as produtor', 'produtor.id', 'iev.produtor_id')
|
||||
->leftJoin('imunizacao_dose as dose', 'dose.id', 'ira.dose_id')
|
||||
->leftJoin('imunizacao_estrategia as estrategia_reg', 'estrategia_reg.id', 'ira.estrategia_id')
|
||||
->leftJoin('cadastro_usuario as aplicador', 'aplicador.id', 'ira.aplicador_id')
|
||||
->leftJoin('cadastro_cidadao as aplicador_cidadao', 'aplicador_cidadao.id', 'aplicador.cidadao_id')
|
||||
->where('ira.paciente_id', $paciente->id)
|
||||
->get()
|
||||
->map(
|
||||
fn($item) => [
|
||||
'id' => $item->id,
|
||||
'tipo' => 'aplicacao',
|
||||
'vacina' => [
|
||||
'id' => $item->vacina_id,
|
||||
'nome' => $item->vacina_nome,
|
||||
'abreviacao' => $item->vacina_abreviacao,
|
||||
'sigla' => $item->vacina_sigla,
|
||||
'is_covid' => in_array($item->vacina_codigo, self::VACINAS_COVID),
|
||||
],
|
||||
'dose' => [
|
||||
'id' => $item->dose_id,
|
||||
'nome' => $item->dose_nome
|
||||
],
|
||||
'estrategia' => $item->estrategia_nome,
|
||||
'data_aplicacao' => $item->data_aplicacao,
|
||||
'lote' => $item->lote,
|
||||
'data_validade_lote' => $item->data_validade_lote,
|
||||
'fabricante' => $item->produtor_nome,
|
||||
'cnes' => $item->unidade_cnes,
|
||||
'unidade_saude' => $item->unidade_nome,
|
||||
'sala_vacinacao' => $item->sala_vacinacao_nome,
|
||||
'municipio' => $item->municipio_nome,
|
||||
'uf' => $item->estado_uf,
|
||||
'profissional' => $item->aplicador_nome
|
||||
]
|
||||
);
|
||||
|
||||
$atualizacoes = DB::table('imunizacao_registro_atualizacao as ira')
|
||||
->select(
|
||||
[
|
||||
'ira.id',
|
||||
'vacina.id as vacina_id',
|
||||
'vacina.codigo as vacina_codigo',
|
||||
'vacina.nome as vacina_nome',
|
||||
'vacina.abreviacao as vacina_abreviacao',
|
||||
'vacina.sigla as vacina_sigla',
|
||||
'dose.id as dose_id',
|
||||
'dose.nome as dose_nome',
|
||||
'estrategia_reg.nome as estrategia_nome',
|
||||
'ira.data_atualizacao as data_aplicacao',
|
||||
'ira.lote',
|
||||
'unidade.cnes as unidade_cnes',
|
||||
'unidade.nome_fantasia as unidade_nome',
|
||||
DB::raw("CASE WHEN infra.principal THEN unidade.nome_fantasia ELSE infra.nome END AS sala_vacinacao_nome"),
|
||||
'aplicador_cidadao.nome as aplicador_nome',
|
||||
'mun.nome as municipio_nome',
|
||||
'est.uf as estado_uf'
|
||||
]
|
||||
)
|
||||
->leftJoin('cadastro_vacina as vacina', 'vacina.id', 'ira.vacina_id')
|
||||
->leftJoin('imunizacao_dose as dose', 'dose.id', 'ira.dose_id')
|
||||
->leftJoin('imunizacao_estrategia as estrategia_reg', 'estrategia_reg.id', 'ira.estrategia_id')
|
||||
->leftJoin('imunizacao_sala_vacinacao as sala', 'sala.id', 'ira.sala_vacinacao_id')
|
||||
->leftJoin('cadastro_infraestrutura as infra', 'infra.id', 'sala.infraestrutura_id')
|
||||
->leftJoin('cadastro_unidade as unidade', 'unidade.id', 'infra.unidade_id')
|
||||
->leftJoin('cadastro_endereco as end_infra', 'end_infra.id', 'infra.endereco_id')
|
||||
->leftJoin('cadastro_logradouro as logr_infra', 'logr_infra.id', 'end_infra.logradouro_id')
|
||||
->leftJoin('intranet.cadastro_municipio as mun', 'mun.id', 'logr_infra.municipio_id')
|
||||
->leftJoin('intranet.cadastro_estado as est', 'est.id', 'mun.estado_id')
|
||||
->leftJoin('cadastro_usuario as aplicador', 'aplicador.id', 'ira.aplicador_id')
|
||||
->leftJoin('cadastro_cidadao as aplicador_cidadao', 'aplicador_cidadao.id', 'aplicador.cidadao_id')
|
||||
->where('ira.paciente_id', $paciente->id)
|
||||
->get()
|
||||
->map(
|
||||
fn($item) => [
|
||||
'id' => $item->id,
|
||||
'tipo' => 'atualizacao',
|
||||
'vacina' => [
|
||||
'id' => $item->vacina_id,
|
||||
'nome' => $item->vacina_nome,
|
||||
'abreviacao' => $item->vacina_abreviacao,
|
||||
'sigla' => $item->vacina_sigla,
|
||||
'is_covid' => in_array($item->vacina_codigo, self::VACINAS_COVID),
|
||||
],
|
||||
'dose' => [
|
||||
'id' => $item->dose_id,
|
||||
'nome' => $item->dose_nome
|
||||
],
|
||||
'estrategia' => $item->estrategia_nome,
|
||||
'data_aplicacao' => $item->data_aplicacao,
|
||||
'lote' => $item->lote,
|
||||
'data_validade_lote' => null,
|
||||
'fabricante' => null,
|
||||
'cnes' => $item->unidade_cnes,
|
||||
'unidade_saude' => $item->unidade_nome,
|
||||
'sala_vacinacao' => $item->sala_vacinacao_nome,
|
||||
'municipio' => $item->municipio_nome,
|
||||
'uf' => $item->estado_uf,
|
||||
'profissional' => $item->aplicador_nome,
|
||||
]
|
||||
);
|
||||
|
||||
return $aplicacoes
|
||||
->merge($atualizacoes)
|
||||
->sortByDesc('data_aplicacao')
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private static function getCalendarioVacinal(Paciente $paciente, Collection $regrasAtuais): array
|
||||
{
|
||||
if ($regrasAtuais->isEmpty()) {
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
$hoje = Carbon::now();
|
||||
|
||||
$aplicacoes = DB::table('imunizacao_registro_aplicacao as ira')
|
||||
->join('imunizacao_entrada_vacina as iev', 'iev.id', 'ira.entrada_vacina_id')
|
||||
->where('ira.paciente_id', $paciente->id)
|
||||
->select(
|
||||
[
|
||||
'iev.vacina_id',
|
||||
'ira.dose_id',
|
||||
'ira.data_aplicacao'
|
||||
]
|
||||
)
|
||||
->get()
|
||||
->merge(
|
||||
DB::table('imunizacao_registro_atualizacao as ira')
|
||||
->where('ira.paciente_id', $paciente->id)
|
||||
->select(
|
||||
[
|
||||
'ira.vacina_id',
|
||||
'ira.dose_id',
|
||||
'ira.data_atualizacao as data_aplicacao'
|
||||
]
|
||||
)
|
||||
->get()
|
||||
)
|
||||
->keyBy(fn($a) => "{$a->vacina_id}_{$a->dose_id}");
|
||||
|
||||
$aprazamentos = DB::table('imunizacao_registro_aprazamento')
|
||||
->where('paciente_id', $paciente->id)
|
||||
->whereNull('registro_aplicacao_id')
|
||||
->select(
|
||||
[
|
||||
'vacina_id',
|
||||
'dose_id',
|
||||
'data_aprazamento'
|
||||
]
|
||||
)
|
||||
->get()
|
||||
->keyBy(fn($a) => "{$a->vacina_id}_{$a->dose_id}");
|
||||
|
||||
return $regrasAtuais->map(
|
||||
function ($regra) use ($aplicacoes, $aprazamentos, $hoje) {
|
||||
|
||||
$key = "{$regra->vacina_id}_{$regra->dose_id}";
|
||||
$aplicacao = $aplicacoes->get($key);
|
||||
$aprazamento = $aprazamentos->get($key);
|
||||
$status = self::calcularStatus($aplicacao, $aprazamento, $hoje);
|
||||
|
||||
return [
|
||||
'vacina' => [
|
||||
'id' => $regra->vacina_id,
|
||||
'nome' => $regra->vacina_nome,
|
||||
'abreviacao' => $regra->vacina_abreviacao,
|
||||
'sigla' => $regra->vacina_sigla,
|
||||
'is_covid' => in_array($regra->vacina_codigo, self::VACINAS_COVID),
|
||||
],
|
||||
'dose' => [
|
||||
'id' => $regra->dose_id,
|
||||
'nome' => $regra->dose_nome,
|
||||
],
|
||||
'faixa_etaria' => self::formatarFaixaEtaria($regra),
|
||||
'estrategia' => $regra->estrategia_nome,
|
||||
'data_aprazamento' => $aprazamento ? $aprazamento->data_aprazamento : null,
|
||||
'data_aplicacao' => $aplicacao ? $aplicacao->data_aplicacao : null,
|
||||
'status' => $status,
|
||||
'alerta' => in_array($status, ['em_atraso', 'proximo_do_prazo']),
|
||||
];
|
||||
}
|
||||
)->values()->toArray();
|
||||
}
|
||||
|
||||
private static function calcularStatus(?object $aplicacao, ?object $aprazamento, Carbon $hoje): string
|
||||
{
|
||||
if ($aprazamento) {
|
||||
|
||||
$dataAprazamento = Carbon::parse($aprazamento->data_aprazamento)->startOfDay();
|
||||
$hoje = $hoje->copy()->startOfDay();
|
||||
|
||||
if ($dataAprazamento->lt($hoje)) {
|
||||
|
||||
return 'em_atraso';
|
||||
}
|
||||
|
||||
if ($dataAprazamento->lte($hoje->addDays(Prazo::DIAS_PROXIMO_DO_PRAZO))) {
|
||||
|
||||
return 'proximo_do_prazo';
|
||||
}
|
||||
|
||||
return 'em_dia';
|
||||
}
|
||||
|
||||
if ($aplicacao) {
|
||||
|
||||
return 'em_dia';
|
||||
}
|
||||
|
||||
return 'sem_registro';
|
||||
}
|
||||
|
||||
private static function getMensagemOrientativa(array $calendario): ?string
|
||||
{
|
||||
$statuses = array_column($calendario, 'status');
|
||||
|
||||
if (in_array('em_atraso', $statuses)) {
|
||||
|
||||
return Mensagem::CARTEIRA_EM_ATRASO;
|
||||
}
|
||||
|
||||
if (in_array('proximo_do_prazo', $statuses)) {
|
||||
|
||||
return Mensagem::CARTEIRA_PROXIMO_PRAZO;
|
||||
}
|
||||
|
||||
if (in_array('sem_registro', $statuses)) {
|
||||
|
||||
return Mensagem::CARTEIRA_SEM_REGISTRO;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function formatarFaixaEtaria(object $regra): string
|
||||
{
|
||||
$inicio = self::formatarIdade($regra->inicio_ano, $regra->inicio_mes, $regra->inicio_dia);
|
||||
$fim = self::formatarIdade($regra->fim_ano, $regra->fim_mes, $regra->fim_dia);
|
||||
|
||||
return "{$inicio} a {$fim}";
|
||||
}
|
||||
|
||||
private static function formatarIdade(int $anos, int $meses, int $dias): string
|
||||
{
|
||||
$partes = [];
|
||||
|
||||
if ($anos > 0) {
|
||||
|
||||
$partes[] = $anos . ($anos === 1 ? ' ano' : ' anos');
|
||||
}
|
||||
|
||||
if ($meses > 0) {
|
||||
|
||||
$partes[] = $meses . ($meses === 1 ? ' mês' : ' meses');
|
||||
}
|
||||
|
||||
if ($dias > 0) {
|
||||
|
||||
$partes[] = $dias . ($dias === 1 ? ' dia' : ' dias');
|
||||
}
|
||||
|
||||
return implode(' e ', $partes) ?: '0 dias';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,707 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\EncaminhamentoRegulado;
|
||||
|
||||
use App\Enums\Parametrizacao\Exame\Tipo as ExameTipo;
|
||||
use App\Enums\Regulacao\ParametrizacaoDivulgacaoAba;
|
||||
use App\Enums\Regulacao\ParametrizacaoDivulgacaoColuna;
|
||||
use App\Enums\Regulacao\StatusTipo;
|
||||
use App\Models\Regulacao\Parametrizacao;
|
||||
use App\Models\Regulacao\Solicitacao;
|
||||
use App\Services\Service;
|
||||
use App\Utils\MaskUtil;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class EncaminhamentoReguladoService extends Service
|
||||
{
|
||||
public static function meusEncaminhamentos(): array
|
||||
{
|
||||
$paciente = auth('mobile')->user();
|
||||
$perPage = max(1, min((int) request()->input('per_page', 15), 50));
|
||||
$page = max((int) request()->input('page', 1), 1);
|
||||
|
||||
$colunasVisiveis = self::getColunasVisiveis();
|
||||
$query = self::getSolicitacoesQuery($paciente->id);
|
||||
|
||||
$total = $query->count();
|
||||
$solicitacoes = (clone $query)
|
||||
->limit($perPage)
|
||||
->offset(($page - 1) * $perPage)
|
||||
->get();
|
||||
|
||||
if ($solicitacoes->isEmpty()) {
|
||||
|
||||
return [
|
||||
'data' => [],
|
||||
'total' => 0,
|
||||
'page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'last_page' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
$ids = $solicitacoes->pluck('id');
|
||||
$exibeStatus = $colunasVisiveis->contains(ParametrizacaoDivulgacaoColuna::STATUS);
|
||||
$itensPorSolicitacao = self::getItensPorSolicitacao($ids);
|
||||
$prestadoresInternos = self::getPrestadoresInternos($solicitacoes->whereNotNull('prestador_interno_id')->pluck('prestador_interno_id')->unique());
|
||||
$prestadoresExternos = self::getPrestadoresExternos($solicitacoes->whereNotNull('prestador_externo_id')->pluck('prestador_externo_id')->unique());
|
||||
|
||||
return [
|
||||
'data' => $solicitacoes->map(
|
||||
fn($s) => self::formatarSolicitacao($s, $itensPorSolicitacao, $prestadoresInternos, $prestadoresExternos, $exibeStatus)
|
||||
)->values()->toArray(),
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'last_page' => (int) ceil($total / $perPage),
|
||||
];
|
||||
}
|
||||
|
||||
public static function documentos(int $id): array
|
||||
{
|
||||
$paciente = auth('mobile')->user();
|
||||
|
||||
$solicitacao = Solicitacao::where('id', $id)
|
||||
->where('paciente_id', $paciente->id)
|
||||
->whereNotNull('confirmado_em')
|
||||
->with('itens')
|
||||
->firstOrFail();
|
||||
|
||||
$tipos = [['tipo' => 'solicitacao', 'descricao' => 'Protocolo de Solicitação']];
|
||||
|
||||
if (self::hasPreparos($solicitacao)) {
|
||||
|
||||
$tipos[] = ['tipo' => 'preparo', 'descricao' => 'Preparos'];
|
||||
}
|
||||
|
||||
$isAgendado = in_array($solicitacao->status->tipo_id, [StatusTipo::AGENDADO, StatusTipo::REAGENDADO]);
|
||||
|
||||
if ($isAgendado) {
|
||||
$tipoExame = self::hasExame($solicitacao);
|
||||
|
||||
if ($tipoExame === ExameTipo::LABORATORIAL) {
|
||||
|
||||
$tipos[] = ['tipo' => 'protocolo_agendamento_exame_laboratorial', 'descricao' => 'Protocolo de Agendamento de Exame Laboratorial'];
|
||||
} else if ($tipoExame === ExameTipo::IMAGEM) {
|
||||
|
||||
$tipos[] = ['tipo' => 'protocolo_agendamento_exame_imagem', 'descricao' => 'Protocolo de Agendamento de Exame de Imagem'];
|
||||
} else if (self::hasProcedimentoCirurgico($solicitacao) && !$solicitacao->prestador_externo_id) {
|
||||
|
||||
$tipos[] = ['tipo' => 'protocolo_agendamento_procedimento_cirurgico', 'descricao' => 'Protocolo de Agendamento de Procedimento Cirúrgico'];
|
||||
} else if (self::hasEspecialidade($solicitacao)) {
|
||||
|
||||
$tipos[] = ['tipo' => 'protocolo_agendamento_especialidade', 'descricao' => 'Protocolo de Agendamento de Consulta'];
|
||||
}
|
||||
}
|
||||
|
||||
return $tipos;
|
||||
}
|
||||
|
||||
public static function documentoPorTipo(int $id, string $tipo): array
|
||||
{
|
||||
$paciente = auth('mobile')->user();
|
||||
|
||||
$solicitacao = Solicitacao::where('id', $id)
|
||||
->where('paciente_id', $paciente->id)
|
||||
->whereNotNull('confirmado_em')
|
||||
->firstOrFail();
|
||||
|
||||
$parametrizacao = Parametrizacao::first();
|
||||
|
||||
if ($tipo === 'solicitacao') {
|
||||
|
||||
return self::documentoSolicitacao($solicitacao, $parametrizacao);
|
||||
} else if ($tipo === 'preparo') {
|
||||
|
||||
return self::documentoPreparo($solicitacao);
|
||||
} else if (in_array($tipo, ['protocolo_agendamento_exame_laboratorial', 'protocolo_agendamento_exame_imagem'])) {
|
||||
|
||||
return self::documentoProtocoloAgendamentoExame($solicitacao, $tipo, $parametrizacao);
|
||||
} else if ($tipo === 'protocolo_agendamento_procedimento_cirurgico') {
|
||||
|
||||
return self::documentoProtocoloAgendamentoProcedimentoCirurgico($solicitacao, $parametrizacao);
|
||||
} else if ($tipo === 'protocolo_agendamento_especialidade') {
|
||||
|
||||
return self::documentoProtocoloAgendamentoEspecialidade($solicitacao, $parametrizacao);
|
||||
}
|
||||
|
||||
abort(404, 'Tipo de documento não encontrado.');
|
||||
}
|
||||
|
||||
private static function getColunasVisiveis(): Collection
|
||||
{
|
||||
return DB::table('regulacao_parametrizacao_divulgacao_aba_coluna')
|
||||
->whereIn('aba_id', [ParametrizacaoDivulgacaoAba::SOLICITACOES_EM_ESPERA, ParametrizacaoDivulgacaoAba::SOLICITACOES_ATENDIDAS])
|
||||
->where('visivel', true)
|
||||
->pluck('coluna_id');
|
||||
}
|
||||
|
||||
private static function getSolicitacoesQuery(int $pacienteId)
|
||||
{
|
||||
$statusTiposVisiveis = DB::table('regulacao_parametrizacao_divulgacao_aba_status_tipo')
|
||||
->whereIn('aba_id', [ParametrizacaoDivulgacaoAba::SOLICITACOES_EM_ESPERA, ParametrizacaoDivulgacaoAba::SOLICITACOES_ATENDIDAS])
|
||||
->where('visivel', true)
|
||||
->pluck('status_tipo_id');
|
||||
|
||||
return DB::table('regulacao_solicitacao as solicitacao')
|
||||
->join('regulacao_status as status', 'status.id', 'solicitacao.status_id')
|
||||
->join('regulacao_status_tipo as status_tipo', 'status_tipo.id', 'status.tipo_id')
|
||||
->join('regulacao_solicitacao_prioridade as prioridade', 'prioridade.id', 'solicitacao.prioridade_id')
|
||||
->join('cadastro_usuario as usuario_solicitante', 'usuario_solicitante.id', 'solicitacao.usuario_solicitante_id')
|
||||
->join('cadastro_cidadao as cidadao_solicitante', 'cidadao_solicitante.id', 'usuario_solicitante.cidadao_id')
|
||||
->join(
|
||||
'regulacao_parametrizacao_divulgacao_regulador as regulador',
|
||||
'regulador.regulacao_parametrizacao_regulador_id',
|
||||
'solicitacao.regulador_id'
|
||||
)
|
||||
->leftJoin('sigtap.cid as cid', 'cid.id', 'solicitacao.cid_id')
|
||||
->where('solicitacao.paciente_id', $pacienteId)
|
||||
->whereNotNull('solicitacao.confirmado_em')
|
||||
->where('regulador.ativo', true)
|
||||
->whereIn('status.tipo_id', $statusTiposVisiveis)
|
||||
->selectRaw("
|
||||
solicitacao.id,
|
||||
DATE_FORMAT(solicitacao.data, '%d/%m/%Y') as data_pedido,
|
||||
cidadao_solicitante.nome as medico_solicitante,
|
||||
prioridade.nome as prioridade,
|
||||
IF(cid.id IS NOT NULL, CONCAT(cid.codigo, ' - ', cid.nome), NULL) as cid,
|
||||
TIMESTAMPDIFF(
|
||||
DAY,
|
||||
(
|
||||
SELECT STR_TO_DATE(CONCAT(sh.data, ' 00:00:00'), '%Y-%m-%d %H:%i:%s')
|
||||
FROM regulacao_solicitacao_historico sh
|
||||
JOIN regulacao_status hs ON hs.id = sh.status_id
|
||||
WHERE sh.solicitacao_id = solicitacao.id
|
||||
AND hs.tipo_id = " . StatusTipo::SOLICITADO . "
|
||||
ORDER BY sh.created_at
|
||||
LIMIT 1
|
||||
),
|
||||
COALESCE((
|
||||
SELECT sh2.created_at
|
||||
FROM regulacao_solicitacao_historico sh2
|
||||
JOIN regulacao_status hs2 ON hs2.id = sh2.status_id
|
||||
WHERE sh2.solicitacao_id = solicitacao.id
|
||||
AND hs2.tipo_id IN (" . StatusTipo::AGENDADO . ", " . StatusTipo::REAGENDADO . ")
|
||||
ORDER BY sh2.created_at
|
||||
LIMIT 1
|
||||
), CURRENT_TIMESTAMP)
|
||||
) as tempo_espera_dias,
|
||||
status_tipo.id as status_tipo_id,
|
||||
status_tipo.nome as status_nome,
|
||||
solicitacao.prestador_interno_id,
|
||||
solicitacao.prestador_externo_id,
|
||||
DATE_FORMAT(solicitacao.data_agendada, '%d/%m/%Y') as data_agendada,
|
||||
DATE_FORMAT(solicitacao.hora_agendada, '%H:%i') as hora_agendada
|
||||
")
|
||||
->orderByDesc('solicitacao.data')
|
||||
->orderByDesc('solicitacao.id');
|
||||
}
|
||||
|
||||
private static function getItensPorSolicitacao(Collection $ids): Collection
|
||||
{
|
||||
return DB::table('regulacao_solicitacao_item as item')
|
||||
->leftJoin('parametrizacao_exame as exame', 'exame.id', 'item.exame_id')
|
||||
->leftJoin('parametrizacao_procedimento_cirurgico as procedimento_cirurgico', 'procedimento_cirurgico.id', 'item.procedimento_cirurgico_id')
|
||||
->leftJoin('cadastro_especialidade as especialidade', 'especialidade.id', 'item.especialidade_id')
|
||||
->whereIn('item.solicitacao_id', $ids)
|
||||
->selectRaw("
|
||||
item.solicitacao_id,
|
||||
COALESCE(exame.nome, procedimento_cirurgico.nome, especialidade.nome) as descricao
|
||||
")
|
||||
->get()
|
||||
->groupBy('solicitacao_id');
|
||||
}
|
||||
|
||||
private static function getPrestadoresInternos(Collection $ids): Collection
|
||||
{
|
||||
if ($ids->isEmpty()) {
|
||||
|
||||
return collect();
|
||||
}
|
||||
|
||||
return DB::table('regulacao_parametrizacao_prestador_interno as prestador_interno')
|
||||
->join('cadastro_infraestrutura as infraestrutura', 'infraestrutura.id', 'prestador_interno.infraestrutura_id')
|
||||
->join('cadastro_unidade as unidade', 'unidade.id', 'infraestrutura.unidade_id')
|
||||
->leftJoin('cadastro_endereco as endereco', 'endereco.id', 'infraestrutura.endereco_id')
|
||||
->leftJoin('cadastro_logradouro as logradouro', 'logradouro.id', 'endereco.logradouro_id')
|
||||
->whereIn('prestador_interno.id', $ids)
|
||||
->selectRaw("
|
||||
prestador_interno.id,
|
||||
IF(infraestrutura.principal = 1, unidade.nome_fantasia, COALESCE(infraestrutura.nome, unidade.nome_fantasia)) as nome,
|
||||
infraestrutura.telefone,
|
||||
logradouro.nome as logradouro_nome,
|
||||
IF(endereco.numero IS NULL, 'S/N', endereco.numero) as logradouro_numero,
|
||||
endereco.complemento as logradouro_complemento
|
||||
")
|
||||
->get()
|
||||
->keyBy('id');
|
||||
}
|
||||
|
||||
private static function getPrestadoresExternos(Collection $ids): Collection
|
||||
{
|
||||
if ($ids->isEmpty()) {
|
||||
|
||||
return collect();
|
||||
}
|
||||
|
||||
return DB::table('regulacao_parametrizacao_prestador_externo as prestador_externo')
|
||||
->join('transporte_paciente_destino as destino', 'destino.id', 'prestador_externo.destino_id')
|
||||
->whereIn('prestador_externo.id', $ids)
|
||||
->selectRaw("
|
||||
prestador_externo.id,
|
||||
destino.descricao_destino as nome,
|
||||
COALESCE(destino.numero_telefone_comercial, destino.numero_telefone_fax) as telefone,
|
||||
TRIM(CONCAT_WS(', ',
|
||||
NULLIF(destino.nome_logradouro, ''),
|
||||
NULLIF(destino.numero_logradouro, '')
|
||||
)) as endereco
|
||||
")
|
||||
->get()
|
||||
->keyBy('id');
|
||||
}
|
||||
|
||||
private static function formatarSolicitacao(
|
||||
object $s,
|
||||
Collection $itensPorSolicitacao,
|
||||
Collection $prestadoresInternos,
|
||||
Collection $prestadoresExternos,
|
||||
bool $exibeStatus
|
||||
): array {
|
||||
$result = [
|
||||
'id' => $s->id,
|
||||
'procedimentos' => self::formatarItens($itensPorSolicitacao[$s->id] ?? collect()),
|
||||
'data_pedido' => $s->data_pedido,
|
||||
'medico_solicitante' => $s->medico_solicitante,
|
||||
'prioridade' => $s->prioridade,
|
||||
'cid' => $s->cid,
|
||||
'tempo_espera_dias' => is_null($s->tempo_espera_dias) ? null : (int) $s->tempo_espera_dias,
|
||||
];
|
||||
|
||||
if ($exibeStatus) {
|
||||
|
||||
$result['status'] = $s->status_nome;
|
||||
}
|
||||
|
||||
if (in_array($s->status_tipo_id, [StatusTipo::AGENDADO, StatusTipo::REAGENDADO])) {
|
||||
|
||||
$result['agendamento'] = self::formatarAgendamento($s, $prestadoresInternos, $prestadoresExternos);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function formatarItens(Collection $itens): array
|
||||
{
|
||||
return $itens->pluck('descricao')->filter()->unique()->values()->toArray();
|
||||
}
|
||||
|
||||
private static function formatarAgendamento(
|
||||
object $s,
|
||||
Collection $prestadoresInternos,
|
||||
Collection $prestadoresExternos
|
||||
): array {
|
||||
$prestador = null;
|
||||
|
||||
if (!is_null($s->prestador_interno_id) && $prestadoresInternos->has($s->prestador_interno_id)) {
|
||||
|
||||
$p = $prestadoresInternos->get($s->prestador_interno_id);
|
||||
$prestador = [
|
||||
'nome' => $p->nome,
|
||||
'telefone' => $p->telefone,
|
||||
'endereco' => self::formatarEnderecoInterno($p),
|
||||
];
|
||||
} else if (!is_null($s->prestador_externo_id) && $prestadoresExternos->has($s->prestador_externo_id)) {
|
||||
|
||||
$p = $prestadoresExternos->get($s->prestador_externo_id);
|
||||
$prestador = [
|
||||
'nome' => $p->nome,
|
||||
'telefone' => $p->telefone,
|
||||
'endereco' => $p->endereco ?: null,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'data' => $s->data_agendada,
|
||||
'hora' => $s->hora_agendada,
|
||||
'prestador' => $prestador,
|
||||
];
|
||||
}
|
||||
|
||||
private static function formatarEnderecoInterno(object $prestador): ?string
|
||||
{
|
||||
if (!$prestador->logradouro_nome) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return trim(
|
||||
$prestador->logradouro_nome
|
||||
. ($prestador->logradouro_numero ? ', ' . $prestador->logradouro_numero : '')
|
||||
. ($prestador->logradouro_complemento ? ' - ' . $prestador->logradouro_complemento : '')
|
||||
);
|
||||
}
|
||||
|
||||
private static function getNomeAtendente(Solicitacao $solicitacao): ?string
|
||||
{
|
||||
$lotacao = $solicitacao->createdLotacao()->withTrashed()->first();
|
||||
|
||||
if (!$lotacao || !$lotacao->usuario || !$lotacao->usuario->cidadao) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $lotacao->usuario->cidadao->nome;
|
||||
}
|
||||
|
||||
private static function documentoSolicitacao(Solicitacao $solicitacao, ?Parametrizacao $parametrizacao): array
|
||||
{
|
||||
$itens = $solicitacao->itens()->with(
|
||||
[
|
||||
'exame',
|
||||
'procedimentoCirurgico',
|
||||
'especialidade.ocupacao',
|
||||
]
|
||||
)->get();
|
||||
|
||||
$examesLaboratoriais = $itens
|
||||
->filter(fn($i) => !is_null($i->exame_id) && $i->exame->tipo_id === ExameTipo::LABORATORIAL)
|
||||
->map(fn($i) => self::formatarItemDocumentoExame($i->exame))
|
||||
->sortBy('descricao')
|
||||
->values();
|
||||
|
||||
$exameImagem = $itens
|
||||
->filter(fn($i) => !is_null($i->exame_id) && $i->exame->tipo_id === ExameTipo::IMAGEM)
|
||||
->first();
|
||||
|
||||
$procedimentoCirurgico = $itens
|
||||
->filter(fn($i) => !is_null($i->procedimento_cirurgico_id))
|
||||
->first();
|
||||
|
||||
$especialidade = $itens
|
||||
->filter(fn($i) => !is_null($i->especialidade_id))
|
||||
->first();
|
||||
|
||||
return [
|
||||
'tipo' => 'solicitacao',
|
||||
'data_solicitacao' => $solicitacao->data->format('d/m/Y'),
|
||||
'hipotese_diagnostica' => $solicitacao->hipotese_diagnostica_justificativa,
|
||||
'peso' => $solicitacao->peso ? str_replace('.', ',', $solicitacao->peso) . ' kg' : null,
|
||||
'altura' => $solicitacao->altura ? str_replace('.', ',', $solicitacao->altura) . ' cm' : null,
|
||||
'exames_laboratoriais' => $examesLaboratoriais->toArray(),
|
||||
'exame_imagem' => $exameImagem ? self::formatarItemDocumentoExame($exameImagem->exame) : null,
|
||||
'procedimento_cirurgico' => $procedimentoCirurgico ? self::formatarItemDocumentoProcedimento($procedimentoCirurgico->procedimentoCirurgico) : null,
|
||||
'especialidade' => $especialidade ? self::formatarItemDocumentoEspecialidade($especialidade->especialidade) : null,
|
||||
'solicitante' => [
|
||||
'medico' => $solicitacao->usuarioSolicitante->cidadao->nome,
|
||||
'unidade' => $solicitacao->infraestruturaSolicitante->unidade->nome_fantasia,
|
||||
'infraestrutura' => $solicitacao->infraestruturaSolicitante->nome,
|
||||
],
|
||||
'atendente' => self::getNomeAtendente($solicitacao),
|
||||
'mensagem' => $parametrizacao ? $parametrizacao->mensagem_protocolo_solicitacao : null,
|
||||
];
|
||||
}
|
||||
|
||||
private static function formatarItemDocumentoExame(object $exame): array
|
||||
{
|
||||
$codigo = empty($exame->codigo) ? null : MaskUtil::getProcedimentoMasked($exame->codigo);
|
||||
|
||||
return [
|
||||
'codigo' => $codigo,
|
||||
'descricao' => $exame->nome,
|
||||
];
|
||||
}
|
||||
|
||||
private static function formatarItemDocumentoProcedimento(object $procedimento): array
|
||||
{
|
||||
return [
|
||||
'codigo' => MaskUtil::getProcedimentoMasked($procedimento->procedimento_codigo),
|
||||
'descricao' => $procedimento->nome,
|
||||
];
|
||||
}
|
||||
|
||||
private static function formatarItemDocumentoEspecialidade(object $especialidade): array
|
||||
{
|
||||
return [
|
||||
'codigo' => $especialidade->ocupacao->codigo ?? null,
|
||||
'descricao' => $especialidade->nome,
|
||||
];
|
||||
}
|
||||
|
||||
private static function hasPreparos(Solicitacao $solicitacao): bool
|
||||
{
|
||||
if (is_null($solicitacao->prestador_interno_id) && is_null($solicitacao->prestador_externo_id)) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasPreparo = false;
|
||||
$prestadorRelationName = is_null($solicitacao->prestador_interno_id) ? 'prestadorExterno' : 'prestadorInterno';
|
||||
|
||||
$solicitacao->itens->each(function ($item) use ($solicitacao, &$hasPreparo, $prestadorRelationName) {
|
||||
|
||||
if ($hasPreparo) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasPreparoExame = fn() => !is_null($item->exame_id)
|
||||
&& $solicitacao->{$prestadorRelationName}->exames()
|
||||
->where('id', $item->exame_id)
|
||||
->wherePivotNotNull('observacoes')
|
||||
->exists();
|
||||
|
||||
$hasPreparoProcedimentoCirurgico = fn() => !is_null($item->procedimento_cirurgico_id)
|
||||
&& $solicitacao->{$prestadorRelationName}->procedimentosCirurgicos()
|
||||
->where('id', $item->procedimento_cirurgico_id)
|
||||
->wherePivotNotNull('observacoes')
|
||||
->exists();
|
||||
|
||||
$hasPreparoEspecialidade = fn() => !is_null($item->especialidade_id)
|
||||
&& $solicitacao->{$prestadorRelationName}->especialidades()
|
||||
->where('id', $item->especialidade_id)
|
||||
->wherePivotNotNull('observacoes')
|
||||
->exists();
|
||||
|
||||
if ($hasPreparoExame() || $hasPreparoProcedimentoCirurgico() || $hasPreparoEspecialidade()) {
|
||||
|
||||
$hasPreparo = true;
|
||||
}
|
||||
});
|
||||
|
||||
return $hasPreparo;
|
||||
}
|
||||
|
||||
private static function hasExame(Solicitacao $solicitacao)
|
||||
{
|
||||
$item = $solicitacao->itens()->with('exame')->whereNotNull('exame_id')->first();
|
||||
|
||||
return ($item && $item->exame) ? $item->exame->tipo_id : false;
|
||||
}
|
||||
|
||||
private static function hasProcedimentoCirurgico(Solicitacao $solicitacao): bool
|
||||
{
|
||||
return $solicitacao->itens()->whereNotNull('procedimento_cirurgico_id')->exists();
|
||||
}
|
||||
|
||||
private static function hasEspecialidade(Solicitacao $solicitacao): bool
|
||||
{
|
||||
return $solicitacao->itens()->whereNotNull('especialidade_id')->exists();
|
||||
}
|
||||
|
||||
private static function documentoPreparo(Solicitacao $solicitacao): array
|
||||
{
|
||||
$itens = collect();
|
||||
$prestadorRelationName = is_null($solicitacao->prestador_interno_id) ? 'prestadorExterno' : 'prestadorInterno';
|
||||
|
||||
$solicitacao->itens->each(function ($item) use ($solicitacao, $prestadorRelationName, $itens) {
|
||||
|
||||
if (!is_null($item->exame_id)) {
|
||||
|
||||
$solicitacao->{$prestadorRelationName}->exames()
|
||||
->where('id', $item->exame_id)
|
||||
->wherePivotNotNull('observacoes')
|
||||
->get()
|
||||
->each(function ($exame) use ($itens) {
|
||||
$itens->push(
|
||||
[
|
||||
'descricao' => empty($exame->codigo)
|
||||
? $exame->nome
|
||||
: MaskUtil::getProcedimentoMasked($exame->codigo) . " - {$exame->nome}",
|
||||
'observacoes' => $exame->pivotPrestadorExame->observacoes,
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (!is_null($item->procedimento_cirurgico_id)) {
|
||||
|
||||
$solicitacao->{$prestadorRelationName}->procedimentosCirurgicos()
|
||||
->where('id', $item->procedimento_cirurgico_id)
|
||||
->wherePivotNotNull('observacoes')
|
||||
->get()
|
||||
->each(function ($procedimento) use ($itens) {
|
||||
$itens->push(
|
||||
[
|
||||
'descricao' => MaskUtil::getProcedimentoMasked($procedimento->procedimento_codigo) . " - {$procedimento->nome}",
|
||||
'observacoes' => $procedimento->pivotPrestadorProcedimentoCirurgico->observacoes,
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (!is_null($item->especialidade_id)) {
|
||||
|
||||
$solicitacao->{$prestadorRelationName}->especialidades()
|
||||
->where('id', $item->especialidade_id)
|
||||
->wherePivotNotNull('observacoes')
|
||||
->get()
|
||||
->each(function ($especialidade) use ($itens) {
|
||||
$itens->push(
|
||||
[
|
||||
'descricao' => "{$especialidade->ocupacao->codigo} - {$especialidade->nome}",
|
||||
'observacoes' => $especialidade->pivotPrestadorEspecialidade->observacoes,
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
'tipo' => 'preparo',
|
||||
'itens' => $itens->sortBy('descricao')->values()->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
private static function formatarPrestadorProtocolo(Solicitacao $solicitacao): ?array
|
||||
{
|
||||
if ($solicitacao->prestadorInterno) {
|
||||
|
||||
$infra = $solicitacao->prestadorInterno->infraestrutura;
|
||||
|
||||
return [
|
||||
'nome' => $infra->principal ? $infra->unidade->nome_fantasia : ($infra->nome ?? $infra->unidade->nome_fantasia),
|
||||
'telefone' => $infra->telefone,
|
||||
'endereco' => $infra->endereco ? $infra->endereco->enderecoFormatado : null,
|
||||
];
|
||||
}
|
||||
|
||||
if ($solicitacao->prestadorExterno) {
|
||||
|
||||
$destino = $solicitacao->prestadorExterno->destino;
|
||||
|
||||
return [
|
||||
'nome' => $destino->descricao_destino,
|
||||
'telefone' => $destino->numero_telefone_comercial ?? $destino->numero_telefone_fax,
|
||||
'endereco' => trim(implode(', ', array_filter(
|
||||
[
|
||||
$destino->nome_logradouro,
|
||||
$destino->numero_logradouro,
|
||||
$destino->nome_bairro,
|
||||
]
|
||||
))) ?: null,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function formatarPacienteProtocolo(Solicitacao $solicitacao): array
|
||||
{
|
||||
$cidadao = $solicitacao->paciente->cidadao;
|
||||
return [
|
||||
'nome' => $cidadao->nome,
|
||||
'nome_social' => $cidadao->nome_social,
|
||||
'prontuario' => $solicitacao->paciente->prontuario,
|
||||
'data_nascimento' => $cidadao->data_nascimento
|
||||
? \Carbon\Carbon::parse($cidadao->data_nascimento)->format('d/m/Y')
|
||||
: null,
|
||||
];
|
||||
}
|
||||
|
||||
private static function documentoProtocoloAgendamentoExame(
|
||||
Solicitacao $solicitacao,
|
||||
string $tipo,
|
||||
?Parametrizacao $parametrizacao
|
||||
): array {
|
||||
|
||||
$tipoExame = $tipo === 'protocolo_agendamento_exame_laboratorial' ? ExameTipo::LABORATORIAL : ExameTipo::IMAGEM;
|
||||
|
||||
$itens = $solicitacao->itens()->with('exame')
|
||||
->whereHas('exame', function ($q) use ($tipoExame) {
|
||||
$q->where('tipo_id', $tipoExame);
|
||||
})
|
||||
->get()
|
||||
->map(function ($item) {
|
||||
return [
|
||||
'codigo' => empty($item->exame->codigo) ? null : MaskUtil::getProcedimentoMasked($item->exame->codigo),
|
||||
'descricao' => $item->exame->nome,
|
||||
];
|
||||
})
|
||||
->sortBy('descricao')
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
return [
|
||||
'tipo' => $tipo,
|
||||
'paciente' => self::formatarPacienteProtocolo($solicitacao),
|
||||
'solicitante' => [
|
||||
'medico' => $solicitacao->usuarioSolicitante->cidadao->nome,
|
||||
'unidade' => $solicitacao->infraestruturaSolicitante->unidade->nome_fantasia,
|
||||
],
|
||||
'data_agendamento' => $solicitacao->data_agendada ? $solicitacao->data_agendada->format('d/m/Y') : null,
|
||||
'hora_agendamento' => $solicitacao->hora_agendada ? $solicitacao->hora_agendada->format('H:i') : null,
|
||||
'prestador' => self::formatarPrestadorProtocolo($solicitacao),
|
||||
'itens' => $itens,
|
||||
'mensagem' => $parametrizacao ? $parametrizacao->mensagem_comprovante_agendamento : null,
|
||||
];
|
||||
}
|
||||
|
||||
private static function documentoProtocoloAgendamentoProcedimentoCirurgico(
|
||||
Solicitacao $solicitacao,
|
||||
?Parametrizacao $parametrizacao
|
||||
): array {
|
||||
|
||||
$item = $solicitacao->itens()->with('procedimentoCirurgico')
|
||||
->whereNotNull('procedimento_cirurgico_id')
|
||||
->first();
|
||||
|
||||
$procedimento = null;
|
||||
|
||||
if ($item && $item->procedimentoCirurgico) {
|
||||
|
||||
$procedimento = [
|
||||
'codigo' => MaskUtil::getProcedimentoMasked($item->procedimentoCirurgico->procedimento_codigo),
|
||||
'descricao' => $item->procedimentoCirurgico->nome,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'tipo' => 'protocolo_agendamento_procedimento_cirurgico',
|
||||
'paciente' => self::formatarPacienteProtocolo($solicitacao),
|
||||
'solicitante' => [
|
||||
'medico' => $solicitacao->usuarioSolicitante->cidadao->nome,
|
||||
'unidade' => $solicitacao->infraestruturaSolicitante->unidade->nome_fantasia,
|
||||
],
|
||||
'data_agendamento' => $solicitacao->data_agendada ? $solicitacao->data_agendada->format('d/m/Y') : null,
|
||||
'hora_agendamento' => $solicitacao->hora_agendada ? $solicitacao->hora_agendada->format('H:i') : null,
|
||||
'prestador' => self::formatarPrestadorProtocolo($solicitacao),
|
||||
'procedimento' => $procedimento,
|
||||
'mensagem' => $parametrizacao ? $parametrizacao->mensagem_comprovante_agendamento : null,
|
||||
];
|
||||
}
|
||||
|
||||
private static function documentoProtocoloAgendamentoEspecialidade(
|
||||
Solicitacao $solicitacao,
|
||||
?Parametrizacao $parametrizacao
|
||||
): array {
|
||||
|
||||
$item = $solicitacao->itens()->with('especialidade.ocupacao')
|
||||
->whereNotNull('especialidade_id')
|
||||
->first();
|
||||
|
||||
$especialidade = null;
|
||||
|
||||
if ($item && $item->especialidade) {
|
||||
|
||||
$especialidade = [
|
||||
'codigo' => $item->especialidade->ocupacao->codigo ?? null,
|
||||
'descricao' => $item->especialidade->nome,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'tipo' => 'protocolo_agendamento_especialidade',
|
||||
'paciente' => self::formatarPacienteProtocolo($solicitacao),
|
||||
'solicitante' => [
|
||||
'medico' => $solicitacao->usuarioSolicitante->cidadao->nome,
|
||||
'unidade' => $solicitacao->infraestruturaSolicitante->unidade->nome_fantasia,
|
||||
],
|
||||
'data_agendamento' => $solicitacao->data_agendada ? $solicitacao->data_agendada->format('d/m/Y') : null,
|
||||
'hora_agendamento' => $solicitacao->hora_agendada ? $solicitacao->hora_agendada->format('H:i') : null,
|
||||
'prestador' => self::formatarPrestadorProtocolo($solicitacao),
|
||||
'especialidade' => $especialidade,
|
||||
'mensagem' => $parametrizacao ? $parametrizacao->mensagem_comprovante_agendamento : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,630 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\ExameImagem;
|
||||
|
||||
use App\Enums\ExameImagem\Fechamento\Status;
|
||||
use App\Enums\ExameImagem\Parametrizacao\ControleJornada\Tipo as ControleJornadaTipo;
|
||||
use App\Enums\ExameImagem\Tipo;
|
||||
use App\Enums\Parametrizacao\Exame\Tipo as ExameTipo;
|
||||
use App\Models\Ambulatorio\AtendimentoSoapSolicitacaoExameItem;
|
||||
use App\Models\Cadastro\Feriado\Feriado;
|
||||
use App\Models\ExameImagem\Agendamento\Agendamento;
|
||||
use App\Models\ExameImagem\Agendamento\AgendamentoExame;
|
||||
use App\Services\Service;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AgendamentoImagemService extends Service
|
||||
{
|
||||
public static function elegibilidade()
|
||||
{
|
||||
$habilitado = DB::table('exame_imagem_parametrizacao_integracao_aplicativo')
|
||||
->value('habilita_agendamento_conectasus_cidadao');
|
||||
|
||||
return ['habilitado' => (bool) $habilitado];
|
||||
}
|
||||
|
||||
public static function prestadores()
|
||||
{
|
||||
self::validarParametrizacaoHabilitada();
|
||||
|
||||
$agora = now();
|
||||
|
||||
return DB::table('exame_imagem_parametrizacao_unidade as parametrizacao')
|
||||
->leftJoin('cadastro_unidade as unidade', 'unidade.id', 'parametrizacao.unidade_id')
|
||||
->leftJoin('cadastro_infraestrutura as infraestrutura', 'infraestrutura.id', 'parametrizacao.infraestrutura_id')
|
||||
->where(function ($q) {
|
||||
$q->where('parametrizacao.executante', true)->orWhere('parametrizacao.prestador', true);
|
||||
})
|
||||
->whereNull('parametrizacao.deleted_at')
|
||||
->whereExists(function ($q) use ($agora) {
|
||||
$q->select(DB::raw(1))
|
||||
->from('exame_imagem_jornada as jornada')
|
||||
->whereColumn('jornada.infraestrutura_id', 'parametrizacao.infraestrutura_id')
|
||||
->whereNotNull('jornada.vagas_app')
|
||||
->where('jornada.vagas_app', '>', 0)
|
||||
->where(function ($q2) use ($agora) {
|
||||
$q2->whereDate('jornada.data', '>', $agora->toDateString())
|
||||
->orWhere(function ($q3) use ($agora) {
|
||||
$q3->whereDate('jornada.data', $agora->toDateString())
|
||||
->whereTime('jornada.hora_fim', '>', $agora->format('H:i:s'));
|
||||
});
|
||||
})
|
||||
->whereNull('jornada.deleted_at');
|
||||
})
|
||||
->get([
|
||||
'parametrizacao.unidade_id',
|
||||
'unidade.nome_fantasia',
|
||||
'parametrizacao.infraestrutura_id',
|
||||
'infraestrutura.nome as infraestrutura_nome',
|
||||
])
|
||||
->map(
|
||||
fn($param) =>
|
||||
[
|
||||
'unidade_id' => $param->unidade_id,
|
||||
'nome_fantasia' => $param->nome_fantasia,
|
||||
'infraestrutura_id' => $param->infraestrutura_id,
|
||||
'infraestrutura_nome' => $param->infraestrutura_nome,
|
||||
]
|
||||
)
|
||||
->unique(fn($item) => $item['unidade_id'] . '-' . $item['infraestrutura_id'])
|
||||
->sortBy('nome_fantasia')
|
||||
->values();
|
||||
}
|
||||
|
||||
public static function exames(): array
|
||||
{
|
||||
self::validarParametrizacaoHabilitada();
|
||||
|
||||
$pacienteId = auth('mobile')->user()->id;
|
||||
|
||||
$statusRealizados = [
|
||||
Status::REALIZADO,
|
||||
Status::RESULTADO,
|
||||
];
|
||||
|
||||
$exameIdsBloqueadosPorValidade = DB::table('parametrizacao_exame as exame')
|
||||
->join('exame_imagem_agendamento_exame as ae', 'ae.exame_id', 'exame.id')
|
||||
->join('exame_imagem_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();
|
||||
|
||||
$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')
|
||||
->leftJoin('cadastro_grupo', 'cadastro_grupo.id', 'parametrizacao_exame.grupo_tipo_id')
|
||||
->where('parametrizacao_exame.tipo_id', ExameTipo::IMAGEM)
|
||||
->whereNull('parametrizacao_exame.regulacao_tipo_id')
|
||||
->whereNull('parametrizacao_exame.deleted_at')
|
||||
->where('ambulatorio_atendimento_soap_solicitacao_exame_item.descartado_exame_imagem', '!=', true)
|
||||
->where('ambulatorio_atendimento_soap_solicitacao_exame_item.agendado_exame_imagem', '!=', true)
|
||||
->whereNotIn('parametrizacao_exame.id', $exameIdsBloqueadosPorValidade)
|
||||
->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',
|
||||
'parametrizacao_exame.codigo',
|
||||
'parametrizacao_exame.nome',
|
||||
'parametrizacao_exame.observacao',
|
||||
'parametrizacao_exame.sexo_id',
|
||||
'cadastro_grupo.id as grupo_id',
|
||||
'cadastro_cidadao.nome as profissional_nome',
|
||||
'profissional.id as profissional_id',
|
||||
]
|
||||
);
|
||||
|
||||
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($exame) => [
|
||||
'exame_item_id' => $exame->exame_item_id,
|
||||
'id' => $exame->id,
|
||||
'codigo' => $exame->codigo,
|
||||
'nome' => $exame->nome,
|
||||
'observacao' => $exame->observacao,
|
||||
'sexo_id' => $exame->sexo_id,
|
||||
'grupo_id' => $exame->grupo_id,
|
||||
])->values()->toArray(),
|
||||
])
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public static function jornadas(): array
|
||||
{
|
||||
self::validarParametrizacaoHabilitada();
|
||||
|
||||
request()->validate(
|
||||
[
|
||||
'infraestrutura_id' => 'required|integer|exists:cadastro_infraestrutura,id',
|
||||
'exame_id' => 'required|integer|exists:parametrizacao_exame,id',
|
||||
]
|
||||
);
|
||||
|
||||
$infraestruturaId = (int) request()->infraestrutura_id;
|
||||
$exameId = (int) request()->exame_id;
|
||||
|
||||
$grupoId = DB::table('parametrizacao_exame')
|
||||
->where('id', $exameId)
|
||||
->value('grupo_tipo_id');
|
||||
|
||||
$hoje = Carbon::today();
|
||||
$fim = Carbon::today()->addMonths(3);
|
||||
$agora = now();
|
||||
|
||||
$jornadasRaw = DB::table('exame_imagem_jornada as jornada')
|
||||
->where('jornada.infraestrutura_id', $infraestruturaId)
|
||||
->when($grupoId, fn($q) => $q->where('jornada.exame_id', $grupoId))
|
||||
->whereNotNull('jornada.vagas_app')
|
||||
->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'));
|
||||
});
|
||||
})
|
||||
->whereDate('jornada.data', '<=', $fim->toDateString())
|
||||
->whereNull('jornada.deleted_at')
|
||||
->orderBy('jornada.data')
|
||||
->orderBy('jornada.hora_inicial')
|
||||
->get(
|
||||
[
|
||||
'jornada.id',
|
||||
'jornada.data',
|
||||
'jornada.hora_inicial',
|
||||
'jornada.hora_fim',
|
||||
'jornada.vagas_app',
|
||||
'jornada.exame_id as grupo_id',
|
||||
'jornada.controle_jornada_id',
|
||||
'jornada.tempo_atendimento',
|
||||
]
|
||||
);
|
||||
|
||||
$jornadaIds = $jornadasRaw->pluck('id')->toArray();
|
||||
|
||||
$statusExcluidos = [
|
||||
Status::CANCELADO,
|
||||
Status::FALTOU,
|
||||
Status::FALTA_MEDICA,
|
||||
];
|
||||
|
||||
$consumoPorJornada = DB::table('exame_imagem_agendamento as ag')
|
||||
->join('exame_imagem_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
||||
->whereIn('ag.jornada_id', $jornadaIds)
|
||||
->where('ag.app_conectasus_cidadao', true)
|
||||
->whereNotIn('ag_exame.status_id', $statusExcluidos)
|
||||
->groupBy('ag.jornada_id')
|
||||
->select(
|
||||
'ag.jornada_id',
|
||||
DB::raw('COUNT(DISTINCT ag.id) as visitas'),
|
||||
DB::raw('COUNT(ag_exame.id) as exames')
|
||||
)
|
||||
->get()
|
||||
->keyBy('jornada_id');
|
||||
|
||||
$horariosOcupadosPorJornada = DB::table('exame_imagem_agendamento as ag')
|
||||
->join('exame_imagem_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
||||
->whereIn('ag.jornada_id', $jornadaIds)
|
||||
->whereNotIn('ag_exame.status_id', $statusExcluidos)
|
||||
->select('ag.jornada_id', DB::raw("DATE_FORMAT(ag.hora_exame, '%H:%i') as horario"))
|
||||
->distinct()
|
||||
->get()
|
||||
->groupBy('jornada_id')
|
||||
->map(fn($rows) => $rows->pluck('horario')->toArray());
|
||||
|
||||
$jornadas = $jornadasRaw->map(function ($jornada) use ($consumoPorJornada, $horariosOcupadosPorJornada) {
|
||||
$consumo = $consumoPorJornada[$jornada->id] ?? null;
|
||||
|
||||
$consumido = $jornada->controle_jornada_id == ControleJornadaTipo::EXAME
|
||||
? ($consumo->exames ?? 0)
|
||||
: ($consumo->visitas ?? 0);
|
||||
|
||||
$disponivel = max(0, $jornada->vagas_app - $consumido);
|
||||
|
||||
return [
|
||||
'id' => $jornada->id,
|
||||
'data' => Carbon::parse($jornada->data)->format('Y-m-d'),
|
||||
'hora_inicial' => $jornada->hora_inicial,
|
||||
'hora_fim' => $jornada->hora_fim,
|
||||
'tempo_atendimento' => $jornada->tempo_atendimento,
|
||||
'vagas_app' => $jornada->vagas_app,
|
||||
'consumido' => $consumido,
|
||||
'disponivel' => $disponivel,
|
||||
'controle_jornada_id' => $jornada->controle_jornada_id,
|
||||
'horarios' => self::gerarHorariosDisponiveis(
|
||||
$jornada->data,
|
||||
$jornada->hora_inicial,
|
||||
$jornada->hora_fim,
|
||||
$jornada->tempo_atendimento,
|
||||
$horariosOcupadosPorJornada[$jornada->id] ?? []
|
||||
),
|
||||
];
|
||||
})->values()->toArray();
|
||||
|
||||
$feriados = Feriado::whereDate('data', '>=', $hoje->toDateString())
|
||||
->whereDate('data', '<=', $fim->toDateString())
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('data')
|
||||
->get(['data', 'observacao'])
|
||||
->map(
|
||||
fn($f) =>
|
||||
[
|
||||
'data' => Carbon::parse($f->data)->format('Y-m-d'),
|
||||
'observacao' => $f->observacao,
|
||||
]
|
||||
)
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
return [
|
||||
'jornadas' => $jornadas,
|
||||
'feriados' => $feriados,
|
||||
'cota' => self::computarCotaApp($infraestruturaId),
|
||||
];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static function agendar(): array
|
||||
{
|
||||
self::validarParametrizacaoHabilitada();
|
||||
|
||||
request()->validate(
|
||||
[
|
||||
'jornada_id' => 'required|integer|exists:exame_imagem_jornada,id',
|
||||
'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',
|
||||
'gestante' => 'nullable|boolean',
|
||||
'nome_responsavel' => 'nullable|string',
|
||||
]
|
||||
);
|
||||
|
||||
$paciente = auth('mobile')->user();
|
||||
$jornada = DB::table('exame_imagem_jornada')->where('id', request()->jornada_id)->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);
|
||||
$dataHoraExame = Carbon::parse($dataJornada . ' ' . request()->horario);
|
||||
|
||||
if ($dataHoraExame->isPast()) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Não é possível agendar em horário retroativo.');
|
||||
}
|
||||
|
||||
if ($dataHoraExame->lt($horaInicialJornada) || $dataHoraExame->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_imagem_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)
|
||||
->where('ae.status_id', Status::AGENDADO)
|
||||
->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, count(request()->exames));
|
||||
self::verificarCotaApp((int) request()->infraestrutura_id);
|
||||
|
||||
$solicitanteInfraId = DB::table('cadastro_infraestrutura')
|
||||
->where('unidade_id', $paciente->unidade_id)
|
||||
->where('principal', true)
|
||||
->value('id');
|
||||
|
||||
$solicitanteUnidadeId = $paciente->unidade_id;
|
||||
|
||||
$ultimoNumero = DB::table('exame_imagem_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->profissional_id = $jornada->usuario_id;
|
||||
$agendamento->data = $jornada->data;
|
||||
$agendamento->exame_id = $jornada->exame_id;
|
||||
$agendamento->hora_exame = request()->horario;
|
||||
$agendamento->unidade_id = $jornada->unidade_id;
|
||||
$agendamento->infraestrutura_id = $jornada->infraestrutura_id;
|
||||
$agendamento->unidade_solicitante_id = $solicitanteUnidadeId;
|
||||
$agendamento->infraestrutura_solicitante_id = $solicitanteInfraId;
|
||||
$agendamento->observacao = request()->observacao;
|
||||
$agendamento->gestante = request()->gestante ?? false;
|
||||
$agendamento->urgente = false;
|
||||
$agendamento->nome_responsavel = request()->nome_responsavel;
|
||||
$agendamento->retroativo = false;
|
||||
$agendamento->app_conectasus_cidadao = true;
|
||||
$agendamento->save();
|
||||
|
||||
foreach (request()->exames as $exameData) {
|
||||
|
||||
$agendamentoExame = new AgendamentoExame();
|
||||
$agendamentoExame->agendamento_id = $agendamento->id;
|
||||
$agendamentoExame->paciente_id = $paciente->id;
|
||||
$agendamentoExame->exame_id = $exameData['exame_id'];
|
||||
$agendamentoExame->status_id = Status::AGENDADO;
|
||||
$agendamentoExame->soap_solicitacao_exame_item_id = $exameData['exame_item_id'];
|
||||
$agendamentoExame->save();
|
||||
|
||||
AtendimentoSoapSolicitacaoExameItem::findOrFail($exameData['exame_item_id'])
|
||||
->update(['agendado_exame_imagem' => true]);
|
||||
}
|
||||
|
||||
return [
|
||||
'agendamento_id' => $agendamento->id,
|
||||
'agendamento_numero' => $agendamento->agendamento_numero,
|
||||
];
|
||||
}
|
||||
|
||||
private static function validarHorarioDisponivel(object $jornada, string $horario): void
|
||||
{
|
||||
$statusExcluidos = [
|
||||
Status::CANCELADO,
|
||||
Status::FALTOU,
|
||||
Status::FALTA_MEDICA,
|
||||
];
|
||||
|
||||
$ocupado = DB::table('exame_imagem_agendamento as ag')
|
||||
->join('exame_imagem_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
||||
->where('ag.jornada_id', $jornada->id)
|
||||
->whereTime('ag.hora_exame', $horario)
|
||||
->whereNotIn('ag_exame.status_id', $statusExcluidos)
|
||||
->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, int $quantidadeExames): void
|
||||
{
|
||||
$statusExcluidos = [
|
||||
Status::CANCELADO,
|
||||
Status::FALTOU,
|
||||
Status::FALTA_MEDICA,
|
||||
];
|
||||
|
||||
$query = DB::table('exame_imagem_agendamento as ag')
|
||||
->join('exame_imagem_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
||||
->where('ag.jornada_id', $jornada->id)
|
||||
->where('ag.app_conectasus_cidadao', true)
|
||||
->whereNotIn('ag_exame.status_id', $statusExcluidos);
|
||||
|
||||
if ($jornada->controle_jornada_id == ControleJornadaTipo::EXAME) {
|
||||
|
||||
$consumido = $query->count('ag_exame.id');
|
||||
$necessario = $quantidadeExames;
|
||||
} else {
|
||||
|
||||
$consumido = $query->distinct()->count('ag.id');
|
||||
$necessario = 1;
|
||||
}
|
||||
|
||||
if ($consumido + $necessario > $jornada->vagas_app) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Não há vagas disponíveis para o aplicativo nesta jornada.');
|
||||
}
|
||||
}
|
||||
|
||||
private static function verificarCotaApp(int $infraestruturaId): void
|
||||
{
|
||||
$cota = self::computarCotaApp($infraestruturaId);
|
||||
|
||||
if ($cota && $cota['bloqueado']) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'O limite de agendamentos pelo aplicativo foi atingido para este período.');
|
||||
}
|
||||
}
|
||||
|
||||
private static function computarCotaApp(int $infraestruturaId): ?array
|
||||
{
|
||||
$appConfig = DB::table('exame_imagem_parametrizacao_app_conectasus_cidadao_prestador')
|
||||
->where('infraestrutura_id', $infraestruturaId)
|
||||
->whereNull('deleted_at')
|
||||
->first();
|
||||
|
||||
if (!$appConfig) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$hoje = now()->toDateString();
|
||||
$statusExcluidos = [Status::CANCELADO, Status::FALTOU, Status::FALTA_MEDICA];
|
||||
|
||||
$cotaQuantidade = null;
|
||||
$cotaValor = null;
|
||||
|
||||
if ($appConfig->percentual_quantitativo) {
|
||||
|
||||
$prestadorQtd = DB::table('exame_imagem_parametrizacao_cota_prestador')
|
||||
->where('infraestrutura_id', $infraestruturaId)
|
||||
->where('cota_tipo_id', Tipo::QUANTIDADE)
|
||||
->where('inicio', '<=', $hoje)
|
||||
->where('fim', '>=', $hoje)
|
||||
->whereNull('deleted_at')
|
||||
->first();
|
||||
|
||||
if ($prestadorQtd) {
|
||||
|
||||
$limite = (int) floor($prestadorQtd->cota_unidade * $appConfig->percentual_quantitativo / 100);
|
||||
|
||||
$consumido = DB::table('exame_imagem_agendamento as ag')
|
||||
->join('exame_imagem_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
||||
->where('ag.infraestrutura_id', $infraestruturaId)
|
||||
->where('ag.app_conectasus_cidadao', true)
|
||||
->whereDate('ag.data', '>=', $prestadorQtd->inicio)
|
||||
->whereDate('ag.data', '<=', $prestadorQtd->fim)
|
||||
->whereNotIn('ag_exame.status_id', $statusExcluidos)
|
||||
->distinct()
|
||||
->count('ag.id');
|
||||
|
||||
$cotaQuantidade = [
|
||||
'percentual_limite' => (float) $appConfig->percentual_quantitativo,
|
||||
'limite' => $limite,
|
||||
'consumido' => $consumido,
|
||||
'percentual_consumido' => $limite > 0 ? round($consumido / $limite * 100, 2) : 0,
|
||||
'bloqueado' => $consumido >= $limite,
|
||||
'periodo_inicio' => $prestadorQtd->inicio,
|
||||
'periodo_fim' => $prestadorQtd->fim,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($appConfig->percentual_financeiro) {
|
||||
|
||||
$prestadorValor = DB::table('exame_imagem_parametrizacao_cota_prestador')
|
||||
->where('infraestrutura_id', $infraestruturaId)
|
||||
->where('cota_tipo_id', Tipo::VALOR)
|
||||
->where('inicio', '<=', $hoje)
|
||||
->where('fim', '>=', $hoje)
|
||||
->whereNull('deleted_at')
|
||||
->first();
|
||||
|
||||
if ($prestadorValor) {
|
||||
|
||||
$limite = round($prestadorValor->cota_valor * $appConfig->percentual_financeiro / 100, 2);
|
||||
|
||||
$consumido = DB::table('exame_imagem_agendamento as ag')
|
||||
->join('exame_imagem_agendamento_exame as ag_exame', 'ag_exame.agendamento_id', '=', 'ag.id')
|
||||
->join('parametrizacao_exame as exame', 'exame.id', '=', 'ag_exame.exame_id')
|
||||
->where('ag.infraestrutura_id', $infraestruturaId)
|
||||
->where('ag.app_conectasus_cidadao', true)
|
||||
->whereDate('ag.data', '>=', $prestadorValor->inicio)
|
||||
->whereDate('ag.data', '<=', $prestadorValor->fim)
|
||||
->whereNotIn('ag_exame.status_id', $statusExcluidos)
|
||||
->sum('exame.valor');
|
||||
|
||||
$consumido = round((float) $consumido, 2);
|
||||
|
||||
$cotaValor = [
|
||||
'percentual_limite' => (float) $appConfig->percentual_financeiro,
|
||||
'limite' => $limite,
|
||||
'consumido' => $consumido,
|
||||
'percentual_consumido' => $limite > 0 ? round($consumido / $limite * 100, 2) : 0,
|
||||
'bloqueado' => $consumido >= $limite,
|
||||
'periodo_inicio' => $prestadorValor->inicio,
|
||||
'periodo_fim' => $prestadorValor->fim,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$cotaQuantidade && !$cotaValor) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$bloqueado = ($cotaQuantidade['bloqueado'] ?? false) || ($cotaValor['bloqueado'] ?? false);
|
||||
|
||||
return [
|
||||
'bloqueado' => $bloqueado,
|
||||
'quantidade' => $cotaQuantidade,
|
||||
'valor' => $cotaValor,
|
||||
];
|
||||
}
|
||||
|
||||
private static function validarParametrizacaoHabilitada(): void
|
||||
{
|
||||
$habilitado = DB::table('exame_imagem_parametrizacao_integracao_aplicativo')
|
||||
->value('habilita_agendamento_conectasus_cidadao');
|
||||
|
||||
if (!$habilitado) {
|
||||
|
||||
abort(Response::HTTP_FORBIDDEN, 'O agendamento de exame de imagem pelo ConectaSUS Cidadão não está habilitado.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\ExameImagem;
|
||||
|
||||
use App\Enums\ExameImagem\Fechamento\Status;
|
||||
use App\Models\ExameImagem\Parametrizacao\IntegracaoAplicativo;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class LaudoService extends Service
|
||||
{
|
||||
public static function laudos(): array
|
||||
{
|
||||
self::validarVisualizacaoHabilitada();
|
||||
|
||||
$paciente = auth('mobile')->user();
|
||||
|
||||
return DB::table('exame_imagem_agendamento as agendamento')
|
||||
->join('exame_imagem_jornada as jornada', 'jornada.id', 'agendamento.jornada_id')
|
||||
->join('cadastro_infraestrutura as infraestrutura', 'infraestrutura.id', 'agendamento.infraestrutura_id')
|
||||
->join('cadastro_unidade as unidade', 'unidade.id', 'infraestrutura.unidade_id')
|
||||
->leftJoin('cadastro_infraestrutura as infra_sol', 'infra_sol.id', 'agendamento.infraestrutura_solicitante_id')
|
||||
->leftJoin('cadastro_unidade as unidade_sol', 'unidade_sol.id', 'infra_sol.unidade_id')
|
||||
->leftJoin('cadastro_usuario as medico_usuario', 'medico_usuario.id', 'agendamento.profissional_id')
|
||||
->leftJoin('cadastro_cidadao as medico_cidadao', 'medico_cidadao.id', 'medico_usuario.cidadao_id')
|
||||
->where('agendamento.paciente_id', $paciente->id)
|
||||
->whereExists(function ($sub) {
|
||||
$sub->select(DB::raw(1))
|
||||
->from('exame_imagem_agendamento_exame as exame')
|
||||
->whereColumn('exame.agendamento_id', 'agendamento.id')
|
||||
->where('exame.status_id', Status::REALIZADO);
|
||||
})
|
||||
->orderByDesc('jornada.data')
|
||||
->get([
|
||||
'agendamento.id',
|
||||
'jornada.data',
|
||||
'agendamento.hora_exame',
|
||||
'unidade.nome_fantasia as unidade_nome',
|
||||
'unidade_sol.nome_fantasia as unidade_solicitante',
|
||||
'medico_cidadao.nome as medico_nome',
|
||||
])
|
||||
->map(
|
||||
fn($item) =>
|
||||
[
|
||||
'id' => $item->id,
|
||||
'data' => $item->data,
|
||||
'hora_exame' => $item->hora_exame,
|
||||
'unidade' => $item->unidade_nome,
|
||||
'unidade_solicitante' => $item->unidade_solicitante,
|
||||
'medico' => $item->medico_nome,
|
||||
'exames' => self::getExamesComResultado($item->id),
|
||||
]
|
||||
)
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private static function getExamesComResultado(int $agendamentoId): array
|
||||
{
|
||||
return DB::table('exame_imagem_agendamento_exame as exame')
|
||||
->join('parametrizacao_exame as param_exame', 'param_exame.id', 'exame.exame_id')
|
||||
->leftJoin('cadastro_usuario as usuario', 'usuario.id', 'exame.usuario_id')
|
||||
->leftJoin('cadastro_cidadao as cidadao_usuario', 'cidadao_usuario.id', 'usuario.cidadao_id')
|
||||
->where('exame.agendamento_id', $agendamentoId)
|
||||
->where('exame.status_id', Status::REALIZADO)
|
||||
->get(
|
||||
[
|
||||
'exame.id',
|
||||
'param_exame.nome',
|
||||
'param_exame.codigo',
|
||||
'exame.arquivo',
|
||||
'exame.resultado',
|
||||
'exame.laudo_nome',
|
||||
'cidadao_usuario.nome as lancado_por',
|
||||
]
|
||||
)
|
||||
->map(
|
||||
fn($exame) =>
|
||||
[
|
||||
'id' => $exame->id,
|
||||
'nome' => $exame->nome,
|
||||
'codigo' => $exame->codigo,
|
||||
'resultado' => $exame->resultado,
|
||||
'laudo_nome' => $exame->laudo_nome,
|
||||
'lancado_por' => $exame->lancado_por,
|
||||
'url_download' => $exame->arquivo
|
||||
? url("api/mobile/conecta-cidadao/exame-imagem/laudos/{$exame->id}/download")
|
||||
: null,
|
||||
]
|
||||
)
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public static function download(int $exameId): array
|
||||
{
|
||||
$paciente = auth('mobile')->user();
|
||||
|
||||
$exame = DB::table('exame_imagem_agendamento_exame as exame')
|
||||
->join('exame_imagem_agendamento as agendamento', 'agendamento.id', 'exame.agendamento_id')
|
||||
->where('exame.id', $exameId)
|
||||
->where('agendamento.paciente_id', $paciente->id)
|
||||
->whereNotNull('exame.arquivo')
|
||||
->first(['exame.arquivo']);
|
||||
|
||||
abort_if(is_null($exame), Response::HTTP_NOT_FOUND, 'Laudo não encontrado.');
|
||||
|
||||
$caminho = 'exame-imagem' . DIRECTORY_SEPARATOR . 'laudos' . DIRECTORY_SEPARATOR . app('config')->get('database.default') . DIRECTORY_SEPARATOR . $exame->arquivo;
|
||||
|
||||
abort_if(!Storage::exists($caminho), Response::HTTP_NOT_FOUND, 'Arquivo do laudo não encontrado.');
|
||||
|
||||
return [
|
||||
'nome' => $exame->arquivo,
|
||||
'caminho' => Storage::path($caminho),
|
||||
];
|
||||
}
|
||||
|
||||
private static function validarVisualizacaoHabilitada(): void
|
||||
{
|
||||
$integracao = IntegracaoAplicativo::first();
|
||||
|
||||
if (!($integracao->habilita_consulta_imagem_conectasus_cidadao ?? false)) {
|
||||
|
||||
abort(Response::HTTP_FORBIDDEN, 'A consulta de exames de imagem pelo ConectaSUS Cidadão não está habilitada.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\ExameLaboratorial;
|
||||
|
||||
use App\Enums\ExameLaboratorial\StatusAgendamento;
|
||||
use App\Models\ExameLaboratorial\Parametrizacao\IntegracaoAplicativo;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class LaudoService extends Service
|
||||
{
|
||||
public static function laudos(): array
|
||||
{
|
||||
self::validarVisualizacaoHabilitada();
|
||||
|
||||
$paciente = auth('mobile')->user();
|
||||
|
||||
return DB::table('exame_laboratorial_agendamento as agendamento')
|
||||
->join('cadastro_unidade as unidade_sol', 'unidade_sol.id', 'agendamento.unidade_id')
|
||||
->join('cadastro_infraestrutura as infra_sol', 'infra_sol.id', 'agendamento.infraestrutura_id')
|
||||
->join('cadastro_unidade as unidade_col', 'unidade_col.id', 'agendamento.coleta_unidade_id')
|
||||
->leftJoin('cadastro_usuario as medico', 'medico.id', 'agendamento.medico_id')
|
||||
->leftJoin('cadastro_cidadao as cidadao_medico', 'cidadao_medico.id', 'medico.cidadao_id')
|
||||
->where('agendamento.paciente_id', $paciente->id)
|
||||
->where(function ($q) {
|
||||
$q->whereExists(function ($sub) {
|
||||
$sub->select(DB::raw(1))
|
||||
->from('exame_laboratorial_agendamento_exame as exame')
|
||||
->whereColumn('exame.agendamento_id', 'agendamento.id')
|
||||
->where('exame.status_id', StatusAgendamento::ASSINADO);
|
||||
})
|
||||
->orWhereExists(function ($sub) {
|
||||
$sub->select(DB::raw(1))
|
||||
->from('exame_laboratorial_agendamento_laudo as laudo')
|
||||
->whereColumn('laudo.agendamento_id', 'agendamento.id')
|
||||
->where(function ($l) {
|
||||
$l->whereNotNull('laudo.arquivo')
|
||||
->orWhereNotNull('laudo.url');
|
||||
});
|
||||
});
|
||||
})
|
||||
->orderByDesc('agendamento.data')
|
||||
->get(
|
||||
[
|
||||
'agendamento.id',
|
||||
'agendamento.data',
|
||||
'unidade_sol.nome_fantasia as unidade_solicitante',
|
||||
'unidade_col.nome_fantasia as unidade_coleta',
|
||||
'cidadao_medico.nome as medico_nome',
|
||||
]
|
||||
)
|
||||
->map(
|
||||
fn($item) =>
|
||||
[
|
||||
'id' => $item->id,
|
||||
'data' => $item->data,
|
||||
'unidade_solicitante' => $item->unidade_solicitante,
|
||||
'unidade_coleta' => $item->unidade_coleta,
|
||||
'medico' => $item->medico_nome,
|
||||
'exames' => self::getExamesComLaudo($item->id),
|
||||
]
|
||||
)
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private static function getExamesComLaudo(int $agendamentoId): array
|
||||
{
|
||||
$examesAssinados = DB::table('exame_laboratorial_agendamento_exame as exame')
|
||||
->join('parametrizacao_exame as param_exame', 'param_exame.id', 'exame.exame_id')
|
||||
->leftJoin('cadastro_usuario as prof_assinatura', 'prof_assinatura.id', 'exame.assinatura_profissional_id')
|
||||
->leftJoin('cadastro_cidadao as cidadao_assinatura', 'cidadao_assinatura.id', 'prof_assinatura.cidadao_id')
|
||||
->where('exame.agendamento_id', $agendamentoId)
|
||||
->where('exame.status_id', StatusAgendamento::ASSINADO)
|
||||
->get(
|
||||
[
|
||||
'exame.id',
|
||||
'param_exame.nome',
|
||||
'param_exame.sigla_exame',
|
||||
'exame.data_assinatura',
|
||||
'cidadao_assinatura.nome as assinado_por',
|
||||
'exame.entregue',
|
||||
'exame.impressao',
|
||||
]
|
||||
)
|
||||
->map(
|
||||
fn($exame) =>
|
||||
[
|
||||
'id' => $exame->id,
|
||||
'nome' => $exame->nome,
|
||||
'sigla_exame' => $exame->sigla_exame,
|
||||
'data_assinatura' => $exame->data_assinatura,
|
||||
'assinado_por' => $exame->assinado_por,
|
||||
'status_laudo' => self::resolverStatusLaudo($exame->entregue, $exame->impressao),
|
||||
'origem' => 'assinatura_eletronica',
|
||||
]
|
||||
)
|
||||
->toArray();
|
||||
|
||||
$laudosAnexados = DB::table('exame_laboratorial_agendamento_laudo as laudo')
|
||||
->where('laudo.agendamento_id', $agendamentoId)
|
||||
->where(function ($q) {
|
||||
$q->whereNotNull('laudo.arquivo')
|
||||
->orWhereNotNull('laudo.url');
|
||||
})
|
||||
->orderBy('laudo.ordem')
|
||||
->get(
|
||||
[
|
||||
'laudo.id',
|
||||
'laudo.arquivo',
|
||||
'laudo.url',
|
||||
'laudo.is_enviado_webservice',
|
||||
'laudo.ordem',
|
||||
]
|
||||
)
|
||||
->map(
|
||||
fn($laudo) =>
|
||||
[
|
||||
'id' => $laudo->id,
|
||||
'ordem' => $laudo->ordem,
|
||||
'origem' => $laudo->is_enviado_webservice ? 'externo_prestador' : 'importacao_manual',
|
||||
'url_download' => $laudo->url
|
||||
?? ($laudo->arquivo
|
||||
? url("api/mobile/conecta-cidadao/exame-laboratorial/laudo/{$laudo->id}/download")
|
||||
: null),
|
||||
]
|
||||
)
|
||||
->toArray();
|
||||
|
||||
return [
|
||||
'assinados' => $examesAssinados,
|
||||
'laudos_anexados' => $laudosAnexados,
|
||||
];
|
||||
}
|
||||
|
||||
public static function download(int $laudoId): array
|
||||
{
|
||||
$paciente = auth('mobile')->user();
|
||||
|
||||
$laudo = DB::table('exame_laboratorial_agendamento_laudo as laudo')
|
||||
->join('exame_laboratorial_agendamento as agendamento', 'agendamento.id', 'laudo.agendamento_id')
|
||||
->where('laudo.id', $laudoId)
|
||||
->where('agendamento.paciente_id', $paciente->id)
|
||||
->whereNotNull('laudo.arquivo')
|
||||
->first(['laudo.arquivo']);
|
||||
|
||||
abort_if(is_null($laudo), Response::HTTP_NOT_FOUND, 'Laudo não encontrado.');
|
||||
|
||||
$caminho = 'exame-laboratorial' . DIRECTORY_SEPARATOR . 'laudos' . DIRECTORY_SEPARATOR . app('config')->get('database.default') . DIRECTORY_SEPARATOR . $laudo->arquivo;
|
||||
|
||||
abort_if(!Storage::exists($caminho), Response::HTTP_NOT_FOUND, 'Arquivo do laudo não encontrado.');
|
||||
|
||||
return [
|
||||
'nome' => $laudo->arquivo,
|
||||
'caminho' => Storage::path($caminho),
|
||||
];
|
||||
}
|
||||
|
||||
private static function resolverStatusLaudo(bool $entregue, bool $impressao): string
|
||||
{
|
||||
if ($entregue) {
|
||||
|
||||
return 'entregue';
|
||||
}
|
||||
|
||||
return $impressao ? 'pendente_entrega' : 'pendente_impressao';
|
||||
}
|
||||
|
||||
private static function validarVisualizacaoHabilitada(): void
|
||||
{
|
||||
$parametrizacao = IntegracaoAplicativo::first();
|
||||
|
||||
if (!($parametrizacao->habilita_visualizacao_laudo_conectasus_cidadao ?? false)) {
|
||||
|
||||
abort(Response::HTTP_FORBIDDEN, 'A visualização de laudos não está habilitada para o aplicativo.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\ExclusaoConta;
|
||||
|
||||
use App\Models\Cadastro\SujeitoAtencao\Paciente;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class ExclusaoContaService extends Service
|
||||
{
|
||||
public static function excluirConta(int $id): array
|
||||
{
|
||||
$paciente = Paciente::find($id);
|
||||
|
||||
if (!$paciente) {
|
||||
|
||||
abort(Response::HTTP_NOT_FOUND, 'Paciente não encontrado.');
|
||||
}
|
||||
|
||||
if (auth('mobile')->user()->id !== $paciente->id) {
|
||||
|
||||
abort(Response::HTTP_FORBIDDEN, 'Acesso negado.');
|
||||
}
|
||||
|
||||
$paciente->update(['senha' => null]);
|
||||
|
||||
return [
|
||||
'message' => 'Conta excluída com sucesso.'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,475 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\Farmacia;
|
||||
|
||||
use App\Enums\Geral\MedidaTempo;
|
||||
use App\Enums\Mobile\ConectaCidadao\Farmacia\MonitoramentoPosologia;
|
||||
use App\Models\Farmacia\AppCidadaoLog;
|
||||
use App\Models\Farmacia\ParametrizacaoAppCidadao;
|
||||
use App\Models\Farmacia\ReceitaItem;
|
||||
use App\Models\Farmacia\ReceitaMonitoramentoPosologia;
|
||||
use App\Models\Farmacia\ReceitaTratamentoInicio;
|
||||
use App\Services\Service;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class MonitoramentoPosologiaService extends Service
|
||||
{
|
||||
public static function medicamentos(): array
|
||||
{
|
||||
self::verificarParametrizacao();
|
||||
|
||||
$pacienteId = auth('mobile')->user()->id;
|
||||
|
||||
$rows = DB::table('farmacia_receita_item as fri')
|
||||
->select(
|
||||
'fri.id as item_id',
|
||||
'fr.id as receita_id',
|
||||
'fr.data_receita',
|
||||
'cm.descricao as material_descricao',
|
||||
'um.nome as unidade_medida_nome',
|
||||
'fri.quantidade',
|
||||
'fri.intervalo_tempo',
|
||||
'fri.intervalo_medida_tempo_id',
|
||||
'mt.nome as intervalo_medida_tempo_nome',
|
||||
'fri.durante_tempo',
|
||||
'ti.inicio_em',
|
||||
'ti.interrompido_em',
|
||||
DB::raw("(SELECT COUNT(*) FROM farmacia_receita_monitoramento_posologia WHERE receita_item_id = fri.id AND paciente_id = {$pacienteId}) as total_confirmadas")
|
||||
)
|
||||
->join('farmacia_receita as fr', 'fr.id', 'fri.receita_id')
|
||||
->join('cadastro_material as cm', 'cm.id', 'fri.material_id')
|
||||
->leftJoin('cadastro_unidade_medida as um', 'um.id', 'cm.apresentacao_unidade_medida_id')
|
||||
->leftJoin('geral_medida_tempo as mt', 'mt.id', 'fri.intervalo_medida_tempo_id')
|
||||
->leftJoin('farmacia_receita_tratamento_inicio as ti', function ($join) use ($pacienteId) {
|
||||
$join->on('ti.receita_item_id', '=', 'fri.id')
|
||||
->where('ti.paciente_id', '=', $pacienteId);
|
||||
})
|
||||
->where('fr.paciente_id', $pacienteId)
|
||||
->where(function ($q) {
|
||||
$q->whereDate('fri.proxima_retirada', '>=', now()->toDateString())
|
||||
->orWhereNotNull('ti.inicio_em');
|
||||
})
|
||||
->orderBy('fr.data_receita', 'desc')
|
||||
->orderBy('fri.id', 'asc')
|
||||
->get();
|
||||
|
||||
$receitas = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
|
||||
$receitaId = $row->receita_id;
|
||||
|
||||
if (!isset($receitas[$receitaId])) {
|
||||
|
||||
$receitas[$receitaId] = [
|
||||
'id' => $receitaId,
|
||||
'data_receita' => $row->data_receita,
|
||||
'itens' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$inicioEm = $row->inicio_em ? Carbon::parse($row->inicio_em) : null;
|
||||
$interrompidoEm = $row->interrompido_em ? Carbon::parse($row->interrompido_em) : null;
|
||||
|
||||
$receitas[$receitaId]['itens'][] = [
|
||||
'id' => $row->item_id,
|
||||
'medicamento' => $row->material_descricao,
|
||||
'posologia_texto' => self::montarPosologiaTexto($row),
|
||||
'posologia_incompleta' => is_null($row->intervalo_tempo) || is_null($row->durante_tempo),
|
||||
'status' => self::calcularStatus(
|
||||
$row->durante_tempo,
|
||||
$row->intervalo_tempo,
|
||||
$row->intervalo_medida_tempo_id,
|
||||
$inicioEm,
|
||||
$interrompidoEm,
|
||||
(int) $row->total_confirmadas
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'habilitado' => true,
|
||||
'data' => array_values($receitas),
|
||||
];
|
||||
}
|
||||
|
||||
public static function iniciar(int $receitaItemId): array
|
||||
{
|
||||
self::verificarParametrizacao();
|
||||
|
||||
$pacienteId = auth('mobile')->user()->id;
|
||||
|
||||
$item = ReceitaItem::findOrFail($receitaItemId);
|
||||
abort_if($item->receita->paciente_id !== $pacienteId, Response::HTTP_FORBIDDEN);
|
||||
|
||||
request()->validate([
|
||||
'inicio_em' => ['required', 'date'],
|
||||
'aderiu_tratamento' => ['nullable', 'boolean'],
|
||||
]);
|
||||
|
||||
$inicioEm = Carbon::parse(request()->input('inicio_em'));
|
||||
|
||||
ReceitaTratamentoInicio::updateOrCreate(
|
||||
['receita_item_id' => $receitaItemId, 'paciente_id' => $pacienteId],
|
||||
[
|
||||
'inicio_em' => $inicioEm,
|
||||
'interrompido_em' => null,
|
||||
'aderiu_tratamento' => request()->boolean('aderiu_tratamento'),
|
||||
]
|
||||
);
|
||||
|
||||
if ($inicioEm->isPast()) {
|
||||
|
||||
ReceitaMonitoramentoPosologia::firstOrCreate(
|
||||
['receita_item_id' => $receitaItemId, 'paciente_id' => $pacienteId, 'dose_numero' => 1],
|
||||
['confirmado_em' => $inicioEm, 'observacao' => null]
|
||||
);
|
||||
}
|
||||
|
||||
self::log($pacienteId, 'INICIO_TRATAMENTO', $item->receita_id, $receitaItemId);
|
||||
|
||||
return ['registrado' => true];
|
||||
}
|
||||
|
||||
public static function index(int $receitaItemId): array
|
||||
{
|
||||
self::verificarParametrizacao();
|
||||
|
||||
$pacienteId = auth('mobile')->user()->id;
|
||||
|
||||
$item = ReceitaItem::findOrFail($receitaItemId);
|
||||
abort_if($item->receita->paciente_id !== $pacienteId, Response::HTTP_FORBIDDEN);
|
||||
|
||||
$inicio = ReceitaTratamentoInicio::where('receita_item_id', $receitaItemId)
|
||||
->where('paciente_id', $pacienteId)
|
||||
->first();
|
||||
|
||||
$confirmacoes = ReceitaMonitoramentoPosologia::where('receita_item_id', $receitaItemId)
|
||||
->where('paciente_id', $pacienteId)
|
||||
->orderBy('dose_numero')
|
||||
->get();
|
||||
|
||||
$inicioEm = $inicio ? $inicio->inicio_em : null;
|
||||
$interrompidoEm = $inicio ? $inicio->interrompido_em : null;
|
||||
|
||||
$status = self::calcularStatus(
|
||||
$item->durante_tempo,
|
||||
$item->intervalo_tempo,
|
||||
$item->intervalo_medida_tempo_id,
|
||||
$inicioEm,
|
||||
$interrompidoEm,
|
||||
$confirmacoes->count()
|
||||
);
|
||||
|
||||
$cronograma = self::calcularCronograma($item, $inicio, $confirmacoes);
|
||||
$adesao = self::calcularAdesao($item, $inicioEm, $confirmacoes->count());
|
||||
|
||||
self::log($pacienteId, 'CONSULTA_MONITORAMENTO', $item->receita_id, $receitaItemId);
|
||||
|
||||
return [
|
||||
'receita_item_id' => $receitaItemId,
|
||||
'medicamento' => $item->material->descricao,
|
||||
'posologia_incompleta' => is_null($item->intervalo_tempo) || is_null($item->durante_tempo),
|
||||
'status' => $status,
|
||||
'inicio_em' => $inicioEm,
|
||||
'interrompido_em' => $interrompidoEm,
|
||||
'adesao' => $adesao,
|
||||
'cronograma' => $cronograma,
|
||||
];
|
||||
}
|
||||
|
||||
public static function store(int $receitaItemId): array
|
||||
{
|
||||
self::verificarParametrizacao();
|
||||
|
||||
$pacienteId = auth('mobile')->user()->id;
|
||||
|
||||
$item = ReceitaItem::findOrFail($receitaItemId);
|
||||
abort_if($item->receita->paciente_id !== $pacienteId, Response::HTTP_FORBIDDEN);
|
||||
|
||||
$inicio = ReceitaTratamentoInicio::where('receita_item_id', $receitaItemId)
|
||||
->where('paciente_id', $pacienteId)
|
||||
->first();
|
||||
|
||||
abort_if(is_null($inicio), Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
abort_if(!is_null($inicio->interrompido_em), Response::HTTP_FORBIDDEN);
|
||||
|
||||
request()->validate([
|
||||
'confirmado_em' => ['required', 'date'],
|
||||
'dose_numero' => ['required', 'integer', 'min:1'],
|
||||
'observacao' => ['nullable', 'string', 'max:500'],
|
||||
]);
|
||||
|
||||
$monitoramento = ReceitaMonitoramentoPosologia::create([
|
||||
'receita_item_id' => $receitaItemId,
|
||||
'paciente_id' => $pacienteId,
|
||||
'confirmado_em' => request()->input('confirmado_em'),
|
||||
'dose_numero' => request()->input('dose_numero'),
|
||||
'observacao' => request()->input('observacao'),
|
||||
]);
|
||||
|
||||
self::log($pacienteId, 'CONFIRMAR_DOSE', $item->receita_id, $receitaItemId);
|
||||
|
||||
return ['id' => $monitoramento->id];
|
||||
}
|
||||
|
||||
public static function destroy(int $id): array
|
||||
{
|
||||
self::verificarParametrizacao();
|
||||
|
||||
$pacienteId = auth('mobile')->user()->id;
|
||||
|
||||
$monitoramento = ReceitaMonitoramentoPosologia::findOrFail($id);
|
||||
abort_if($monitoramento->paciente_id !== $pacienteId, Response::HTTP_FORBIDDEN);
|
||||
|
||||
$receitaId = $monitoramento->receitaItem->receita_id;
|
||||
$receitaItemId = $monitoramento->receita_item_id;
|
||||
|
||||
$monitoramento->delete();
|
||||
|
||||
self::log($pacienteId, 'CANCELAR_DOSE', $receitaId, $receitaItemId);
|
||||
|
||||
return ['removido' => true];
|
||||
}
|
||||
|
||||
public static function interromper(int $receitaItemId): array
|
||||
{
|
||||
self::verificarParametrizacao();
|
||||
|
||||
$pacienteId = auth('mobile')->user()->id;
|
||||
|
||||
$item = ReceitaItem::findOrFail($receitaItemId);
|
||||
abort_if($item->receita->paciente_id !== $pacienteId, Response::HTTP_FORBIDDEN);
|
||||
|
||||
$inicio = ReceitaTratamentoInicio::where('receita_item_id', $receitaItemId)
|
||||
->where('paciente_id', $pacienteId)
|
||||
->whereNull('interrompido_em')
|
||||
->firstOrFail();
|
||||
|
||||
$inicio->update(['interrompido_em' => Carbon::now()]);
|
||||
|
||||
self::log($pacienteId, 'INTERROMPER_MONITORAMENTO', $item->receita_id, $receitaItemId);
|
||||
|
||||
return ['interrompido' => true];
|
||||
}
|
||||
|
||||
private static function calcularStatus(
|
||||
?int $duranteTempo,
|
||||
?int $intervaloTempo,
|
||||
?int $intervaloMedidaTempoId,
|
||||
?Carbon $inicioEm,
|
||||
?Carbon $interrompidoEm,
|
||||
int $totalConfirmadas
|
||||
): string {
|
||||
|
||||
if (is_null($inicioEm)) {
|
||||
|
||||
return MonitoramentoPosologia::STATUS_NAO_INICIADO;
|
||||
}
|
||||
|
||||
if (!is_null($interrompidoEm)) {
|
||||
|
||||
return MonitoramentoPosologia::STATUS_INTERROMPIDO;
|
||||
}
|
||||
|
||||
if (is_null($duranteTempo) || is_null($intervaloTempo)) {
|
||||
|
||||
return MonitoramentoPosologia::STATUS_EM_ANDAMENTO;
|
||||
}
|
||||
|
||||
$intervaloMinutos = self::calcularIntervaloEmMinutos($intervaloTempo, $intervaloMedidaTempoId);
|
||||
|
||||
if ($intervaloMinutos > 0) {
|
||||
|
||||
$totalDosesPrevistas = (int) ceil(($duranteTempo * 24 * 60) / $intervaloMinutos);
|
||||
|
||||
if ($totalConfirmadas >= $totalDosesPrevistas) {
|
||||
|
||||
return MonitoramentoPosologia::STATUS_FINALIZADO;
|
||||
}
|
||||
}
|
||||
|
||||
$fimTratamento = $inicioEm->copy()->addDays($duranteTempo);
|
||||
|
||||
if (Carbon::now()->lessThanOrEqualTo($fimTratamento)) {
|
||||
|
||||
return MonitoramentoPosologia::STATUS_EM_ANDAMENTO;
|
||||
}
|
||||
|
||||
return $totalConfirmadas > 0 ? MonitoramentoPosologia::STATUS_FINALIZADO : MonitoramentoPosologia::STATUS_EXPIRADO;
|
||||
}
|
||||
|
||||
private static function calcularCronograma(ReceitaItem $item, ?ReceitaTratamentoInicio $inicio, Collection $confirmacoes): array
|
||||
{
|
||||
if (is_null($inicio) || is_null($item->intervalo_tempo) || is_null($item->durante_tempo)) {
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
$intervaloMinutos = self::calcularIntervaloEmMinutos($item->intervalo_tempo, $item->intervalo_medida_tempo_id);
|
||||
|
||||
if ($intervaloMinutos <= 0) {
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
$duracaoMinutos = $item->durante_tempo * 24 * 60;
|
||||
$totalDoses = (int) ceil($duracaoMinutos / $intervaloMinutos);
|
||||
|
||||
$confirmacoesPorNumero = $confirmacoes->keyBy('dose_numero');
|
||||
|
||||
$cronograma = [];
|
||||
|
||||
for ($i = 0; $i < $totalDoses; $i++) {
|
||||
|
||||
$doseNumero = $i + 1;
|
||||
$esperadoEm = $inicio->inicio_em->copy()->addMinutes($i * $intervaloMinutos);
|
||||
$confirmacao = $confirmacoesPorNumero->get($doseNumero);
|
||||
|
||||
if ($confirmacao) {
|
||||
|
||||
$statusDose = MonitoramentoPosologia::STATUS_DOSE_CONFIRMADA;
|
||||
} elseif ($esperadoEm->copy()->addMinutes(MonitoramentoPosologia::TOLERANCIA_ATRASO_MINUTOS)->isPast()) {
|
||||
|
||||
$statusDose = MonitoramentoPosologia::STATUS_DOSE_ATRASADA;
|
||||
} else {
|
||||
|
||||
$statusDose = MonitoramentoPosologia::STATUS_DOSE_PENDENTE;
|
||||
}
|
||||
|
||||
$cronograma[] = [
|
||||
'id' => $confirmacao ? $confirmacao->id : null,
|
||||
'numero' => $doseNumero,
|
||||
'esperado_em' => $esperadoEm->toDateTimeString(),
|
||||
'confirmado_em' => $confirmacao ? $confirmacao->confirmado_em : null,
|
||||
'status' => $statusDose,
|
||||
'observacao' => $confirmacao ? $confirmacao->observacao : null,
|
||||
];
|
||||
}
|
||||
|
||||
return $cronograma;
|
||||
}
|
||||
|
||||
private static function calcularIntervaloEmMinutos(int $intervalo, ?int $medidaTempoId): int
|
||||
{
|
||||
if ($medidaTempoId === MedidaTempo::MINUTOS) {
|
||||
|
||||
return $intervalo;
|
||||
}
|
||||
|
||||
if ($medidaTempoId === MedidaTempo::DIAS) {
|
||||
|
||||
return $intervalo * 24 * 60;
|
||||
}
|
||||
|
||||
if ($medidaTempoId === MedidaTempo::SEMANAS) {
|
||||
|
||||
return $intervalo * 7 * 24 * 60;
|
||||
}
|
||||
|
||||
return $intervalo * 60;
|
||||
}
|
||||
|
||||
private static function calcularAdesao(ReceitaItem $item, ?Carbon $inicioEm, int $confirmadas): array
|
||||
{
|
||||
if (is_null($inicioEm) || is_null($item->intervalo_tempo) || is_null($item->durante_tempo)) {
|
||||
|
||||
return [
|
||||
'doses_previstas' => null,
|
||||
'doses_confirmadas' => $confirmadas,
|
||||
'percentual' => null,
|
||||
];
|
||||
}
|
||||
|
||||
$intervaloMinutos = self::calcularIntervaloEmMinutos($item->intervalo_tempo, $item->intervalo_medida_tempo_id);
|
||||
$duracaoMinutos = $item->durante_tempo * 24 * 60;
|
||||
$decorridoMinutos = min($inicioEm->diffInMinutes(Carbon::now()), $duracaoMinutos);
|
||||
|
||||
$previstas = $intervaloMinutos > 0 ? (int) ceil($decorridoMinutos / $intervaloMinutos) : 0;
|
||||
$percentual = $previstas > 0 ? round(($confirmadas / $previstas) * 100, 1) : null;
|
||||
|
||||
return [
|
||||
'doses_previstas' => $previstas,
|
||||
'doses_confirmadas' => $confirmadas,
|
||||
'percentual' => $percentual,
|
||||
];
|
||||
}
|
||||
|
||||
private static function montarPosologiaTexto(object $row): ?string
|
||||
{
|
||||
$texto = '';
|
||||
|
||||
if (!is_null($row->quantidade)) {
|
||||
|
||||
$quantidade = (float) $row->quantidade;
|
||||
$texto .= fmod($quantidade, 1) == 0 ? (int) $quantidade : number_format($quantidade, 2, ',', '.');
|
||||
|
||||
if ($row->unidade_medida_nome) {
|
||||
|
||||
$texto .= ' ' . strtolower($row->unidade_medida_nome);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($row->intervalo_tempo)) {
|
||||
|
||||
$intervalo = (int) $row->intervalo_tempo;
|
||||
|
||||
if ($row->intervalo_medida_tempo_nome) {
|
||||
|
||||
$unidade = strtolower(self::pluralizar($row->intervalo_medida_tempo_nome, $intervalo));
|
||||
$texto .= $intervalo === 1
|
||||
? " a cada {$unidade}"
|
||||
: " a cada {$intervalo} {$unidade}";
|
||||
} else {
|
||||
|
||||
$texto .= " a cada {$intervalo}";
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($row->durante_tempo)) {
|
||||
|
||||
$dias = (int) $row->durante_tempo;
|
||||
$texto .= $dias === 1 ? ' durante 1 dia' : " durante {$dias} dias";
|
||||
}
|
||||
|
||||
return empty($texto) ? null : $texto;
|
||||
}
|
||||
|
||||
private static function pluralizar(string $nome, int $valor): string
|
||||
{
|
||||
if (strpos($nome, '(es)') !== false) {
|
||||
|
||||
return $valor === 1
|
||||
? str_replace('(es)', '', $nome)
|
||||
: str_replace('(es)', 'es', $nome);
|
||||
}
|
||||
|
||||
if (strpos($nome, '(s)') !== false) {
|
||||
|
||||
return $valor === 1
|
||||
? str_replace('(s)', '', $nome)
|
||||
: str_replace('(s)', 's', $nome);
|
||||
}
|
||||
|
||||
return $nome;
|
||||
}
|
||||
|
||||
private static function verificarParametrizacao(): void
|
||||
{
|
||||
$parametrizacao = ParametrizacaoAppCidadao::first();
|
||||
abort_if(!($parametrizacao->permite_envio_receitas_medicas ?? false), Response::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
private static function log(int $pacienteId, string $tipoAcao, ?int $receitaId = null, ?int $receitaItemId = null): void
|
||||
{
|
||||
AppCidadaoLog::create([
|
||||
'paciente_id' => $pacienteId,
|
||||
'receita_id' => $receitaId,
|
||||
'receita_item_id' => $receitaItemId,
|
||||
'tipo_acao' => $tipoAcao,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\Farmacia;
|
||||
|
||||
use App\Enums\Geral\MedidaTempo;
|
||||
use App\Models\Farmacia\AppCidadaoLog;
|
||||
use App\Models\Farmacia\ParametrizacaoAppCidadao;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ReceitaService extends Service
|
||||
{
|
||||
public static function index(): array
|
||||
{
|
||||
$parametrizacao = ParametrizacaoAppCidadao::first();
|
||||
|
||||
if (!($parametrizacao->permite_envio_receitas_medicas ?? false)) {
|
||||
|
||||
return ['habilitado' => false];
|
||||
}
|
||||
|
||||
$pacienteId = auth('mobile')->user()->id;
|
||||
$perPage = (int) request()->input('per_page', 15);
|
||||
$page = (int) request()->input('page', 1);
|
||||
|
||||
$todos = self::queryAmbulatorioIds($pacienteId)->get()->map(
|
||||
function ($r) {
|
||||
return (array) $r;
|
||||
}
|
||||
);
|
||||
|
||||
$total = $todos->count();
|
||||
$lastPage = max(1, (int) ceil($total / $perPage));
|
||||
$page = max(1, min($page, $lastPage));
|
||||
$slice = $todos->sortByDesc('data')->slice(($page - 1) * $perPage, $perPage)->values();
|
||||
$ids = $slice->pluck('id')->toArray();
|
||||
|
||||
$receitas = collect();
|
||||
|
||||
if (!empty($ids)) {
|
||||
|
||||
$rows = self::queryAmbulatorioDetalhes($ids)->get();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$receitas->push(
|
||||
[
|
||||
'id' => $row->prescricao_id,
|
||||
'data_emissao' => $row->data_receita,
|
||||
'validade' => null,
|
||||
'situacao' => 'ativa',
|
||||
'unidade' => $row->unidade_nome,
|
||||
'prescritor' => $row->prescritor_nome,
|
||||
'total_itens' => $row->total_itens,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self::log($pacienteId, 'LISTA_RECEITAS');
|
||||
|
||||
return [
|
||||
'habilitado' => true,
|
||||
'data' => $receitas->sortByDesc('data_emissao')->values(),
|
||||
'current_page' => $page,
|
||||
'last_page' => $lastPage,
|
||||
'total' => $total,
|
||||
'per_page' => $perPage,
|
||||
];
|
||||
}
|
||||
|
||||
public static function show(int $id): array
|
||||
{
|
||||
$parametrizacao = ParametrizacaoAppCidadao::first();
|
||||
|
||||
if (!($parametrizacao->permite_envio_receitas_medicas ?? false)) {
|
||||
|
||||
return ['habilitado' => false];
|
||||
}
|
||||
|
||||
$pacienteId = auth('mobile')->user()->id;
|
||||
|
||||
$pacienteNome = DB::table('cadastro_paciente')
|
||||
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_paciente.cidadao_id')
|
||||
->where('cadastro_paciente.id', $pacienteId)
|
||||
->value('cadastro_cidadao.nome');
|
||||
|
||||
$header = DB::table('ambulatorio_atendimento_soap_prescricao_medicamento as prescricao')
|
||||
->select(
|
||||
'prescricao.id',
|
||||
'prescricao.created_at as data_receita',
|
||||
'prescritor_cidadao.nome as prescritor_nome',
|
||||
'cadastro_unidade.nome_fantasia as unidade_nome',
|
||||
'cid.codigo as cid_codigo',
|
||||
'cid.nome as cid_nome'
|
||||
)
|
||||
->join('ambulatorio_atendimento_soap as soap', 'soap.id', 'prescricao.soap_id')
|
||||
->join('ambulatorio_atendimento as atendimento', 'atendimento.id', 'soap.atendimento_id')
|
||||
->leftJoin('cadastro_lotacao', 'cadastro_lotacao.id', 'prescricao.created_lotacao_id')
|
||||
->leftJoin('cadastro_usuario', 'cadastro_usuario.id', 'cadastro_lotacao.usuario_id')
|
||||
->leftJoin('cadastro_cidadao as prescritor_cidadao', 'prescritor_cidadao.id', 'cadastro_usuario.cidadao_id')
|
||||
->leftJoin('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'cadastro_lotacao.infraestrutura_id')
|
||||
->leftJoin('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
|
||||
->leftJoin(
|
||||
DB::raw('(SELECT soap_id, MIN(cid_id) as cid_id FROM ambulatorio_atendimento_soap_problema_condicao WHERE cid_id IS NOT NULL GROUP BY soap_id) as cond'),
|
||||
'cond.soap_id',
|
||||
'=',
|
||||
'soap.id'
|
||||
)
|
||||
->leftJoin('sigtap.cid as cid', 'cid.id', 'cond.cid_id')
|
||||
->where('prescricao.id', $id)
|
||||
->where(function ($q) use ($pacienteId) {
|
||||
$q->whereExists(function ($q2) use ($pacienteId) {
|
||||
$q2->select(DB::raw(1))
|
||||
->from('ambulatorio_agendamento as ag')
|
||||
->whereColumn('ag.id', 'atendimento.agendamento_id')
|
||||
->where('ag.paciente_id', $pacienteId);
|
||||
})->orWhereExists(function ($q2) use ($pacienteId) {
|
||||
$q2->select(DB::raw(1))
|
||||
->from('ambulatorio_demanda_espontanea as de')
|
||||
->whereColumn('de.id', 'atendimento.demanda_espontanea_id')
|
||||
->where('de.paciente_id', $pacienteId);
|
||||
});
|
||||
})
|
||||
->first();
|
||||
|
||||
abort_if(!$header, Response::HTTP_NOT_FOUND);
|
||||
|
||||
$itens = DB::table('ambulatorio_atendimento_soap_prescricao_medicamento_item as item')
|
||||
->select(
|
||||
'item.id',
|
||||
'item.material_id',
|
||||
'item.descricao as item_descricao',
|
||||
'item.justificativa',
|
||||
'item.dose',
|
||||
'item.intervalo',
|
||||
'item.duracao',
|
||||
'item.duracao_medida_tempo_id',
|
||||
'item.recomendacoes',
|
||||
'medida_tempo.nome as intervalo_medida_tempo_nome',
|
||||
'unidade_medida.nome as apresentacao_unidade_medida',
|
||||
'cadastro_via_administracao.nome as via_administracao',
|
||||
DB::raw("CASE WHEN item.material_id IS NOT NULL THEN cadastro_material.descricao ELSE item.descricao END AS material_descricao")
|
||||
)
|
||||
->leftJoin('cadastro_material', 'cadastro_material.id', 'item.material_id')
|
||||
->leftJoin('cadastro_unidade_medida AS unidade_medida', 'unidade_medida.id', 'item.unidade_medida_id')
|
||||
->leftJoin('geral_medida_tempo as medida_tempo', 'medida_tempo.id', 'item.intervalo_medida_tempo_id')
|
||||
->leftJoin('cadastro_via_administracao', 'cadastro_via_administracao.id', 'cadastro_material.via_administracao_id')
|
||||
->where('item.prescricao_medicamento_id', $id)
|
||||
->get()
|
||||
->map(
|
||||
function ($item) {
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'tipo' => is_null($item->material_id) ? 'nao_padronizado' : 'padronizado',
|
||||
'medicamento' => $item->material_descricao,
|
||||
'justificativa' => is_null($item->material_id) ? $item->justificativa : null,
|
||||
'posologia_texto' => self::montarPosologiaTexto($item),
|
||||
'posologia_incompleta' => is_null($item->intervalo),
|
||||
'unidade_medida' => $item->apresentacao_unidade_medida,
|
||||
'intervalo_tempo' => $item->intervalo,
|
||||
'intervalo_medida_tempo' => $item->intervalo_medida_tempo_nome,
|
||||
'durante_tempo' => self::calcularDuracaoEmDias($item->duracao, $item->duracao_medida_tempo_id),
|
||||
'via_administracao' => $item->via_administracao,
|
||||
'recomendacoes' => $item->recomendacoes,
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
self::log($pacienteId, 'DETALHE_RECEITA', $id);
|
||||
|
||||
return [
|
||||
'id' => $header->id,
|
||||
'paciente' => $pacienteNome,
|
||||
'data_emissao' => $header->data_receita,
|
||||
'validade' => null,
|
||||
'situacao' => 'ativa',
|
||||
'cid' => [
|
||||
'codigo' => $header->cid_codigo,
|
||||
'descricao' => $header->cid_nome,
|
||||
],
|
||||
'prescritor' => $header->prescritor_nome,
|
||||
'unidade' => $header->unidade_nome,
|
||||
'itens' => $itens,
|
||||
];
|
||||
}
|
||||
|
||||
private static function queryAmbulatorioIds(int $pacienteId)
|
||||
{
|
||||
return DB::table('ambulatorio_atendimento_soap_prescricao_medicamento as prescricao')
|
||||
->select('prescricao.id', 'prescricao.created_at as data')
|
||||
->join('ambulatorio_atendimento_soap as soap', 'soap.id', 'prescricao.soap_id')
|
||||
->join('ambulatorio_atendimento as atendimento', 'atendimento.id', 'soap.atendimento_id')
|
||||
->where('prescricao.solicitacao_fechada', false)
|
||||
->where(function ($q) use ($pacienteId) {
|
||||
$q->whereExists(function ($q2) use ($pacienteId) {
|
||||
$q2->select(DB::raw(1))
|
||||
->from('ambulatorio_agendamento as ag')
|
||||
->whereColumn('ag.id', 'atendimento.agendamento_id')
|
||||
->where('ag.paciente_id', $pacienteId);
|
||||
})->orWhereExists(function ($q2) use ($pacienteId) {
|
||||
$q2->select(DB::raw(1))
|
||||
->from('ambulatorio_demanda_espontanea as de')
|
||||
->whereColumn('de.id', 'atendimento.demanda_espontanea_id')
|
||||
->where('de.paciente_id', $pacienteId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static function queryAmbulatorioDetalhes(array $ids)
|
||||
{
|
||||
return DB::table('ambulatorio_atendimento_soap_prescricao_medicamento as prescricao')
|
||||
->select(
|
||||
'prescricao.id as prescricao_id',
|
||||
'prescricao.created_at as data_receita',
|
||||
'prescritor_cidadao.nome as prescritor_nome',
|
||||
'cadastro_unidade.nome_fantasia as unidade_nome',
|
||||
DB::raw('(SELECT COUNT(*) FROM ambulatorio_atendimento_soap_prescricao_medicamento_item WHERE prescricao_medicamento_id = prescricao.id) as total_itens')
|
||||
)
|
||||
->leftJoin('cadastro_lotacao', 'cadastro_lotacao.id', 'prescricao.created_lotacao_id')
|
||||
->leftJoin('cadastro_usuario', 'cadastro_usuario.id', 'cadastro_lotacao.usuario_id')
|
||||
->leftJoin('cadastro_cidadao as prescritor_cidadao', 'prescritor_cidadao.id', 'cadastro_usuario.cidadao_id')
|
||||
->leftJoin('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'cadastro_lotacao.infraestrutura_id')
|
||||
->leftJoin('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
|
||||
->whereIn('prescricao.id', $ids);
|
||||
}
|
||||
|
||||
private static function calcularDuracaoEmDias(?int $duracao, ?int $medidaTempoId): ?int
|
||||
{
|
||||
if (is_null($duracao)) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($medidaTempoId === MedidaTempo::HORAS) {
|
||||
|
||||
return max(1, (int) ceil($duracao / 24));
|
||||
}
|
||||
|
||||
if ($medidaTempoId === MedidaTempo::SEMANAS) {
|
||||
|
||||
return $duracao * 7;
|
||||
}
|
||||
|
||||
if ($medidaTempoId === MedidaTempo::MESES) {
|
||||
|
||||
return $duracao * 30;
|
||||
}
|
||||
|
||||
return $duracao;
|
||||
}
|
||||
|
||||
private static function pluralizar(string $nome, int $valor): string
|
||||
{
|
||||
if (strpos($nome, '(es)') !== false) {
|
||||
|
||||
return $valor === 1
|
||||
? str_replace('(es)', '', $nome)
|
||||
: str_replace('(es)', 'es', $nome);
|
||||
}
|
||||
|
||||
if (strpos($nome, '(s)') !== false) {
|
||||
|
||||
return $valor === 1
|
||||
? str_replace('(s)', '', $nome)
|
||||
: str_replace('(s)', 's', $nome);
|
||||
}
|
||||
|
||||
return $nome;
|
||||
}
|
||||
|
||||
private static function montarPosologiaTexto($row): ?string
|
||||
{
|
||||
$texto = '';
|
||||
|
||||
if (!is_null($row->dose)) {
|
||||
|
||||
$dose = (float) trim($row->dose);
|
||||
$texto .= fmod($dose, 1) == 0 ? (int) $dose : number_format($dose, 2, ',', '.');
|
||||
|
||||
if ($row->apresentacao_unidade_medida) {
|
||||
|
||||
$texto .= ' ' . strtolower($row->apresentacao_unidade_medida);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($row->intervalo)) {
|
||||
|
||||
$intervalo = (int) $row->intervalo;
|
||||
|
||||
if ($row->intervalo_medida_tempo_nome) {
|
||||
|
||||
$unidade = strtolower(self::pluralizar($row->intervalo_medida_tempo_nome, $intervalo));
|
||||
$texto .= $intervalo === 1
|
||||
? " a cada {$unidade}"
|
||||
: " a cada {$intervalo} {$unidade}";
|
||||
} else {
|
||||
|
||||
$texto .= " a cada {$intervalo}";
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($row->duracao)) {
|
||||
|
||||
$dias = self::calcularDuracaoEmDias((int) $row->duracao, $row->duracao_medida_tempo_id);
|
||||
|
||||
if (!is_null($dias)) {
|
||||
|
||||
$texto .= $dias === 1 ? ' durante 1 dia' : " durante {$dias} dias";
|
||||
}
|
||||
}
|
||||
|
||||
return empty($texto) ? null : $texto;
|
||||
}
|
||||
|
||||
private static function log(int $pacienteId, string $tipoAcao, ?int $receitaId = null): void
|
||||
{
|
||||
AppCidadaoLog::create(
|
||||
[
|
||||
'paciente_id' => $pacienteId,
|
||||
'receita_id' => $receitaId,
|
||||
'tipo_acao' => $tipoAcao,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\HistoricoAtendimento;
|
||||
|
||||
use App\Services\Service;
|
||||
use App\Services\Ambulatorio\AtendimentoHistoricoService as AmbulatorioHistoricoService;
|
||||
use App\Services\ProntoAtendimento\HistoricoService as ProntoAtendimentoHistoricoService;
|
||||
use App\Models\Cadastro\SujeitoAtencao\Paciente;
|
||||
use App\Models\Mobile\ConectaCidadao\ConectaCidadaoAvaliacaoAtendimento;
|
||||
use App\Models\Ambulatorio\AtendimentoPreAtendimento;
|
||||
use App\Models\Ambulatorio\AtendimentoSoap;
|
||||
use App\Models\Ambulatorio\AtendimentoPosConsulta;
|
||||
use App\Models\ProntoAtendimento\Atendimento\Atendimento;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
|
||||
class HistoricoAtendimentoService extends Service
|
||||
{
|
||||
public function getHistorico(Paciente $paciente, $limit = 15)
|
||||
{
|
||||
$preAtendimentos = self::getPreAtendimentoQuery($paciente->id);
|
||||
$atendimentosSoap = self::getAtendimentoSoapQuery($paciente->id);
|
||||
$posConsulta = self::getPosConsultaQuery($paciente->id);
|
||||
$prontoAtendimento = self::getProntoAtendimentosQuery($paciente->id);
|
||||
|
||||
$query = $preAtendimentos
|
||||
->unionAll($atendimentosSoap)
|
||||
->unionAll($posConsulta)
|
||||
->unionAll($prontoAtendimento)
|
||||
->orderBy('data_atendimento', 'desc');
|
||||
|
||||
$paginator = $query->paginate((int) $limit);
|
||||
|
||||
return [
|
||||
'current_page' => $paginator->currentPage(),
|
||||
'data' => array_map(function ($item) {
|
||||
return self::formatItem($item);
|
||||
}, $paginator->items()),
|
||||
'first_page_url' => $paginator->url(1),
|
||||
'from' => $paginator->firstItem(),
|
||||
'last_page' => $paginator->lastPage(),
|
||||
'last_page_url' => $paginator->url($paginator->lastPage()),
|
||||
'next_page_url' => $paginator->nextPageUrl(),
|
||||
'path' => $paginator->path(),
|
||||
'per_page' => $paginator->perPage(),
|
||||
'prev_page_url' => $paginator->previousPageUrl(),
|
||||
'to' => $paginator->lastItem(),
|
||||
'total' => $paginator->total(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getDetalhes($id, $sourceType)
|
||||
{
|
||||
switch ($sourceType) {
|
||||
case 'pre-atendimento':
|
||||
return AmbulatorioHistoricoService::getPreAtendimento($id);
|
||||
case 'soap':
|
||||
return AmbulatorioHistoricoService::getAtendimentoSoap($id);
|
||||
case 'pos-consulta':
|
||||
return AmbulatorioHistoricoService::getPosConsulta($id);
|
||||
case 'pronto-atendimento':
|
||||
return ProntoAtendimentoHistoricoService::getAtendimento($id);
|
||||
default:
|
||||
throw new Exception('Tipo de atendimento inválido.');
|
||||
}
|
||||
}
|
||||
|
||||
public function avaliar(string $sourceType, int $id, array $params): void
|
||||
{
|
||||
$avaliacao = new ConectaCidadaoAvaliacaoAtendimento();
|
||||
|
||||
switch ($sourceType) {
|
||||
case 'pre-atendimento':
|
||||
AtendimentoPreAtendimento::findOrFail($id);
|
||||
$avaliacao->ambulatorio_atendimento_pre_atendimento_id = $id;
|
||||
break;
|
||||
|
||||
case 'soap':
|
||||
AtendimentoSoap::findOrFail($id);
|
||||
$avaliacao->ambulatorio_atendimento_soap_id = $id;
|
||||
break;
|
||||
|
||||
case 'pos-consulta':
|
||||
AtendimentoPosConsulta::findOrFail($id);
|
||||
$avaliacao->ambulatorio_atendimento_pos_consulta_id = $id;
|
||||
break;
|
||||
|
||||
case 'pronto-atendimento':
|
||||
Atendimento::findOrFail($id);
|
||||
$avaliacao->pronto_atendimento_atendimento_id = $id;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \InvalidArgumentException('Tipo de origem inválido.');
|
||||
}
|
||||
|
||||
$avaliacao->nota = $params['nota'] ?? null;
|
||||
$avaliacao->comentario = $params['comentario'] ?? null;
|
||||
|
||||
$avaliacao->save();
|
||||
}
|
||||
|
||||
private static function formatItem($item)
|
||||
{
|
||||
$item->created_at = Carbon::create($item->data_atendimento);
|
||||
|
||||
$dadosHistorico = [
|
||||
'tipo' => $item->source_type,
|
||||
'data_hora' => $item->created_at->format('d/m/Y \à\s H:i'),
|
||||
'lotacao' => [
|
||||
'ocupacao' => [
|
||||
'codigo' => $item->ocupacao_codigo,
|
||||
'nome' => $item->ocupacao_nome,
|
||||
],
|
||||
'profissional' => [
|
||||
'nome' => $item->cidadao_nome,
|
||||
],
|
||||
],
|
||||
'centro_custo' => [
|
||||
'setor' => [
|
||||
'nome' => $item->setor_nome,
|
||||
],
|
||||
'unidade' => [
|
||||
'nome_fantasia' => $item->unidade_nome_fantasia,
|
||||
],
|
||||
],
|
||||
'tipo_atendimento' => $item->tipo_atendimento,
|
||||
'id' => $item->id,
|
||||
'atendimento_id' => $item->atendimento_id,
|
||||
'is_atendimento_enfermagem' => (bool) $item->is_atendimento_enfermagem,
|
||||
'is_atendimento_soap_assinado' => (bool) $item->is_atendimento_soap_assinado,
|
||||
];
|
||||
|
||||
return (object) $dadosHistorico;
|
||||
}
|
||||
|
||||
private static function getProntoAtendimentosQuery($pacienteId)
|
||||
{
|
||||
return DB::table('pronto_atendimento_atendimento')
|
||||
->select([
|
||||
'pronto_atendimento_atendimento.id',
|
||||
'pronto_atendimento_atendimento.id as atendimento_id',
|
||||
'pronto_atendimento_atendimento_recepcao.atendido_em as data_atendimento',
|
||||
'ocupacao.id AS ocupacao_id',
|
||||
'ocupacao.codigo AS ocupacao_codigo',
|
||||
'ocupacao.nome AS ocupacao_nome',
|
||||
'cadastro_cidadao.nome AS cidadao_nome',
|
||||
'cadastro_setor.nome AS setor_nome',
|
||||
'cadastro_centro_custo.infraestrutura_id AS centro_custo_infraestrutura_id',
|
||||
'cadastro_unidade.nome_fantasia AS unidade_nome_fantasia',
|
||||
DB::raw("'Pronto Atendimento' AS tipo_atendimento"),
|
||||
DB::raw("false as is_atendimento_enfermagem"),
|
||||
DB::raw("false as is_atendimento_soap_assinado"),
|
||||
DB::raw("'pronto-atendimento' as source_type")
|
||||
])
|
||||
->join('pronto_atendimento_faa', 'pronto_atendimento_faa.id', 'pronto_atendimento_atendimento.faa_id')
|
||||
->join('pronto_atendimento_atendimento_recepcao', 'pronto_atendimento_atendimento_recepcao.atendimento_id', 'pronto_atendimento_atendimento.id')
|
||||
->join('cadastro_lotacao', 'cadastro_lotacao.id', 'pronto_atendimento_faa.lotacao_id')
|
||||
->join('cadastro_usuario', 'cadastro_usuario.id', 'cadastro_lotacao.usuario_id')
|
||||
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
|
||||
->join('sigtap.ocupacao', 'ocupacao.id', 'cadastro_lotacao.ocupacao_id')
|
||||
->join('cadastro_centro_custo', 'cadastro_centro_custo.id', 'pronto_atendimento_faa.created_centro_custo_id')
|
||||
->join('cadastro_setor', 'cadastro_setor.id', 'cadastro_centro_custo.setor_id')
|
||||
->join('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'cadastro_centro_custo.infraestrutura_id')
|
||||
->join('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
|
||||
->where('pronto_atendimento_faa.paciente_id', $pacienteId)
|
||||
->whereNull('pronto_atendimento_atendimento.deleted_at');
|
||||
}
|
||||
|
||||
private static function getPreAtendimentoQuery($pacienteId)
|
||||
{
|
||||
return DB::table('ambulatorio_atendimento_pre_atendimento')
|
||||
->select([
|
||||
'ambulatorio_atendimento_pre_atendimento.id',
|
||||
'ambulatorio_atendimento_pre_atendimento.atendimento_id',
|
||||
'ambulatorio_atendimento_pre_atendimento.created_at as data_atendimento',
|
||||
'ocupacao.id AS ocupacao_id',
|
||||
'ocupacao.codigo AS ocupacao_codigo',
|
||||
'ocupacao.nome AS ocupacao_nome',
|
||||
'cadastro_cidadao.nome AS cidadao_nome',
|
||||
'cadastro_setor.nome AS setor_nome',
|
||||
'cadastro_centro_custo.infraestrutura_id AS centro_custo_infraestrutura_id',
|
||||
'cadastro_unidade.nome_fantasia AS unidade_nome_fantasia',
|
||||
DB::raw("'Pré-atendimento' AS tipo_atendimento"),
|
||||
DB::raw("false as is_atendimento_enfermagem"),
|
||||
DB::raw("false as is_atendimento_soap_assinado"),
|
||||
DB::raw("'pre-atendimento' as source_type")
|
||||
])
|
||||
->join('ambulatorio_atendimento', 'ambulatorio_atendimento.id', 'ambulatorio_atendimento_pre_atendimento.atendimento_id')
|
||||
->join('ambulatorio_agendamento', 'ambulatorio_agendamento.id', 'ambulatorio_atendimento.agendamento_id')
|
||||
->join('cadastro_lotacao', 'cadastro_lotacao.id', 'ambulatorio_atendimento_pre_atendimento.created_lotacao_id')
|
||||
->join('cadastro_usuario', 'cadastro_usuario.id', 'cadastro_lotacao.usuario_id')
|
||||
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
|
||||
->join('sigtap.ocupacao', 'ocupacao.id', 'cadastro_lotacao.ocupacao_id')
|
||||
->join('cadastro_centro_custo', 'cadastro_centro_custo.id', 'ambulatorio_atendimento_pre_atendimento.created_centro_custo_id')
|
||||
->join('cadastro_setor', 'cadastro_setor.id', 'cadastro_centro_custo.setor_id')
|
||||
->join('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'cadastro_centro_custo.infraestrutura_id')
|
||||
->join('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
|
||||
->where('ambulatorio_agendamento.paciente_id', $pacienteId)
|
||||
->unionAll(
|
||||
DB::table('ambulatorio_atendimento_pre_atendimento')
|
||||
->select([
|
||||
'ambulatorio_atendimento_pre_atendimento.id',
|
||||
'ambulatorio_atendimento_pre_atendimento.atendimento_id',
|
||||
'ambulatorio_atendimento_pre_atendimento.created_at as data_atendimento',
|
||||
'ocupacao.id AS ocupacao_id',
|
||||
'ocupacao.codigo AS ocupacao_codigo',
|
||||
'ocupacao.nome AS ocupacao_nome',
|
||||
'cadastro_cidadao.nome AS cidadao_nome',
|
||||
'cadastro_setor.nome AS setor_nome',
|
||||
'cadastro_centro_custo.infraestrutura_id AS centro_custo_infraestrutura_id',
|
||||
'cadastro_unidade.nome_fantasia AS unidade_nome_fantasia',
|
||||
DB::raw("'Pré-atendimento' AS tipo_atendimento"),
|
||||
DB::raw("false as is_atendimento_enfermagem"),
|
||||
DB::raw("false as is_atendimento_soap_assinado"),
|
||||
DB::raw("'pre-atendimento' as source_type")
|
||||
])
|
||||
->join('ambulatorio_atendimento', 'ambulatorio_atendimento.id', 'ambulatorio_atendimento_pre_atendimento.atendimento_id')
|
||||
->join('ambulatorio_demanda_espontanea', 'ambulatorio_demanda_espontanea.id', 'ambulatorio_atendimento.demanda_espontanea_id')
|
||||
->join('cadastro_lotacao', 'cadastro_lotacao.id', 'ambulatorio_atendimento_pre_atendimento.created_lotacao_id')
|
||||
->join('cadastro_usuario', 'cadastro_usuario.id', 'cadastro_lotacao.usuario_id')
|
||||
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
|
||||
->join('sigtap.ocupacao', 'ocupacao.id', 'cadastro_lotacao.ocupacao_id')
|
||||
->join('cadastro_centro_custo', 'cadastro_centro_custo.id', 'ambulatorio_atendimento_pre_atendimento.created_centro_custo_id')
|
||||
->join('cadastro_setor', 'cadastro_setor.id', 'cadastro_centro_custo.setor_id')
|
||||
->join('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'cadastro_centro_custo.infraestrutura_id')
|
||||
->join('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
|
||||
->where('ambulatorio_demanda_espontanea.paciente_id', $pacienteId)
|
||||
);
|
||||
}
|
||||
|
||||
private static function getAtendimentoSoapQuery($pacienteId)
|
||||
{
|
||||
return DB::table('ambulatorio_atendimento_soap')
|
||||
->select([
|
||||
'ambulatorio_atendimento_soap.id',
|
||||
'ambulatorio_atendimento_soap.atendimento_id',
|
||||
'jornada.data as data_atendimento',
|
||||
'ocupacao.id AS ocupacao_id',
|
||||
'ocupacao.codigo AS ocupacao_codigo',
|
||||
'ocupacao.nome AS ocupacao_nome',
|
||||
'cadastro_cidadao.nome AS cidadao_nome',
|
||||
'cadastro_setor.nome AS setor_nome',
|
||||
'cadastro_centro_custo.infraestrutura_id AS centro_custo_infraestrutura_id',
|
||||
'cadastro_unidade.nome_fantasia AS unidade_nome_fantasia',
|
||||
DB::raw("'Atendimento' AS tipo_atendimento"),
|
||||
'ambulatorio_atendimento_soap.is_atendimento_enfermagem',
|
||||
'ambulatorio_atendimento_soap.is_atendimento_soap_assinado',
|
||||
DB::raw("'soap' as source_type")
|
||||
])
|
||||
->join('ambulatorio_atendimento', 'ambulatorio_atendimento.id', 'ambulatorio_atendimento_soap.atendimento_id')
|
||||
->join('ambulatorio_agendamento', 'ambulatorio_agendamento.id', 'ambulatorio_atendimento.agendamento_id')
|
||||
->join('ambulatorio_jornada as jornada', 'jornada.id', 'ambulatorio_agendamento.jornada_id')
|
||||
->join('cadastro_lotacao', 'cadastro_lotacao.id', 'jornada.lotacao_id')
|
||||
->join('cadastro_usuario', 'cadastro_usuario.id', 'cadastro_lotacao.usuario_id')
|
||||
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
|
||||
->join('sigtap.ocupacao', 'ocupacao.id', 'cadastro_lotacao.ocupacao_id')
|
||||
->join('cadastro_centro_custo', 'cadastro_centro_custo.id', 'ambulatorio_atendimento_soap.created_centro_custo_id')
|
||||
->join('cadastro_setor', 'cadastro_setor.id', 'cadastro_centro_custo.setor_id')
|
||||
->join('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'cadastro_centro_custo.infraestrutura_id')
|
||||
->join('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
|
||||
->where('ambulatorio_atendimento_soap.is_rascunho', false)
|
||||
->where('ambulatorio_agendamento.paciente_id', $pacienteId)
|
||||
->unionAll(
|
||||
DB::table('ambulatorio_atendimento_soap')
|
||||
->select([
|
||||
'ambulatorio_atendimento_soap.id',
|
||||
'ambulatorio_atendimento_soap.atendimento_id',
|
||||
'ambulatorio_demanda_espontanea.data as data_atendimento',
|
||||
'ocupacao.id AS ocupacao_id',
|
||||
'ocupacao.codigo AS ocupacao_codigo',
|
||||
'ocupacao.nome AS ocupacao_nome',
|
||||
'cadastro_cidadao.nome AS cidadao_nome',
|
||||
'cadastro_setor.nome AS setor_nome',
|
||||
'cadastro_centro_custo.infraestrutura_id AS centro_custo_infraestrutura_id',
|
||||
'cadastro_unidade.nome_fantasia AS unidade_nome_fantasia',
|
||||
DB::raw("'Atendimento' AS tipo_atendimento"),
|
||||
'ambulatorio_atendimento_soap.is_atendimento_enfermagem',
|
||||
'ambulatorio_atendimento_soap.is_atendimento_soap_assinado',
|
||||
DB::raw("'soap' as source_type")
|
||||
])
|
||||
->join('ambulatorio_atendimento', 'ambulatorio_atendimento.id', 'ambulatorio_atendimento_soap.atendimento_id')
|
||||
->join('ambulatorio_demanda_espontanea', 'ambulatorio_demanda_espontanea.id', 'ambulatorio_atendimento.demanda_espontanea_id')
|
||||
->join('cadastro_lotacao', 'cadastro_lotacao.id', 'ambulatorio_atendimento_soap.created_lotacao_id')
|
||||
->join('cadastro_usuario', 'cadastro_usuario.id', 'cadastro_lotacao.usuario_id')
|
||||
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
|
||||
->join('sigtap.ocupacao', 'ocupacao.id', 'cadastro_lotacao.ocupacao_id')
|
||||
->join('cadastro_centro_custo', 'cadastro_centro_custo.id', 'ambulatorio_atendimento_soap.created_centro_custo_id')
|
||||
->join('cadastro_setor', 'cadastro_setor.id', 'cadastro_centro_custo.setor_id')
|
||||
->join('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'cadastro_centro_custo.infraestrutura_id')
|
||||
->join('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
|
||||
->where('ambulatorio_atendimento_soap.is_rascunho', false)
|
||||
->where('ambulatorio_demanda_espontanea.paciente_id', $pacienteId)
|
||||
);
|
||||
}
|
||||
|
||||
private static function getPosConsultaQuery($pacienteId)
|
||||
{
|
||||
return DB::table('ambulatorio_atendimento_pos_consulta')
|
||||
->select([
|
||||
'ambulatorio_atendimento_pos_consulta.id',
|
||||
'ambulatorio_atendimento_pos_consulta.atendimento_id',
|
||||
'ambulatorio_atendimento_pos_consulta.created_at as data_atendimento',
|
||||
'ocupacao.id AS ocupacao_id',
|
||||
'ocupacao.codigo AS ocupacao_codigo',
|
||||
'ocupacao.nome AS ocupacao_nome',
|
||||
'cadastro_cidadao.nome AS cidadao_nome',
|
||||
'cadastro_setor.nome AS setor_nome',
|
||||
'cadastro_centro_custo.infraestrutura_id AS centro_custo_infraestrutura_id',
|
||||
'cadastro_unidade.nome_fantasia AS unidade_nome_fantasia',
|
||||
DB::raw("'Pós-consulta' AS tipo_atendimento"),
|
||||
DB::raw("false as is_atendimento_enfermagem"),
|
||||
DB::raw("false as is_atendimento_soap_assinado"),
|
||||
DB::raw("'pos-consulta' as source_type")
|
||||
])
|
||||
->join('ambulatorio_atendimento', 'ambulatorio_atendimento.id', 'ambulatorio_atendimento_pos_consulta.atendimento_id')
|
||||
->join('ambulatorio_agendamento', 'ambulatorio_agendamento.id', 'ambulatorio_atendimento.agendamento_id')
|
||||
->join('cadastro_lotacao', 'cadastro_lotacao.id', 'ambulatorio_atendimento_pos_consulta.created_lotacao_id')
|
||||
->join('cadastro_usuario', 'cadastro_usuario.id', 'cadastro_lotacao.usuario_id')
|
||||
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
|
||||
->join('sigtap.ocupacao', 'ocupacao.id', 'cadastro_lotacao.ocupacao_id')
|
||||
->join('cadastro_centro_custo', 'cadastro_centro_custo.id', 'ambulatorio_atendimento_pos_consulta.created_centro_custo_id')
|
||||
->join('cadastro_setor', 'cadastro_setor.id', 'cadastro_centro_custo.setor_id')
|
||||
->join('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'cadastro_centro_custo.infraestrutura_id')
|
||||
->join('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
|
||||
->where('ambulatorio_agendamento.paciente_id', $pacienteId)
|
||||
->unionAll(
|
||||
DB::table('ambulatorio_atendimento_pos_consulta')
|
||||
->select([
|
||||
'ambulatorio_atendimento_pos_consulta.id',
|
||||
'ambulatorio_atendimento_pos_consulta.atendimento_id',
|
||||
'ambulatorio_atendimento_pos_consulta.created_at as data_atendimento',
|
||||
'ocupacao.id AS ocupacao_id',
|
||||
'ocupacao.codigo AS ocupacao_codigo',
|
||||
'ocupacao.nome AS ocupacao_nome',
|
||||
'cadastro_cidadao.nome AS cidadao_nome',
|
||||
'cadastro_setor.nome AS setor_nome',
|
||||
'cadastro_centro_custo.infraestrutura_id AS centro_custo_infraestrutura_id',
|
||||
'cadastro_unidade.nome_fantasia AS unidade_nome_fantasia',
|
||||
DB::raw("'Pós-consulta' AS tipo_atendimento"),
|
||||
DB::raw("false as is_atendimento_enfermagem"),
|
||||
DB::raw("false as is_atendimento_soap_assinado"),
|
||||
DB::raw("'pos-consulta' as source_type")
|
||||
])
|
||||
->join('ambulatorio_atendimento', 'ambulatorio_atendimento.id', 'ambulatorio_atendimento_pos_consulta.atendimento_id')
|
||||
->join('ambulatorio_demanda_espontanea', 'ambulatorio_demanda_espontanea.id', 'ambulatorio_atendimento.demanda_espontanea_id')
|
||||
->join('cadastro_lotacao', 'cadastro_lotacao.id', 'ambulatorio_atendimento_pos_consulta.created_lotacao_id')
|
||||
->join('cadastro_usuario', 'cadastro_usuario.id', 'cadastro_lotacao.usuario_id')
|
||||
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_usuario.cidadao_id')
|
||||
->join('sigtap.ocupacao', 'ocupacao.id', 'cadastro_lotacao.ocupacao_id')
|
||||
->join('cadastro_centro_custo', 'cadastro_centro_custo.id', 'ambulatorio_atendimento_pos_consulta.created_centro_custo_id')
|
||||
->join('cadastro_setor', 'cadastro_setor.id', 'cadastro_centro_custo.setor_id')
|
||||
->join('cadastro_infraestrutura', 'cadastro_infraestrutura.id', 'cadastro_centro_custo.infraestrutura_id')
|
||||
->join('cadastro_unidade', 'cadastro_unidade.id', 'cadastro_infraestrutura.unidade_id')
|
||||
->where('ambulatorio_demanda_espontanea.paciente_id', $pacienteId)
|
||||
);
|
||||
}
|
||||
|
||||
public function avaliacao(string $sourceType, int $id, array $params)
|
||||
{
|
||||
$campoMap = [
|
||||
'pre-atendimento' => 'ambulatorio_atendimento_pre_atendimento_id',
|
||||
'soap' => 'ambulatorio_atendimento_soap_id',
|
||||
'pos-consulta' => 'ambulatorio_atendimento_pos_consulta_id',
|
||||
'pronto-atendimento' => 'pronto_atendimento_atendimento_id',
|
||||
];
|
||||
|
||||
if (!isset($campoMap[$sourceType])) {
|
||||
|
||||
throw new \InvalidArgumentException("Tipo de atendimento inválido: {$sourceType}");
|
||||
}
|
||||
|
||||
$campo = $campoMap[$sourceType];
|
||||
|
||||
$avaliacao = DB::table('conecta_cidadao_avaliacao_atendimento')
|
||||
->where($campo, $id)
|
||||
->first();
|
||||
|
||||
if (!$avaliacao) {
|
||||
|
||||
throw new ModelNotFoundException('Avaliação não encontrada.');
|
||||
}
|
||||
|
||||
return $avaliacao;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\PerfilCidadao;
|
||||
|
||||
use App\Services\Service;
|
||||
use App\Models\Cadastro\SujeitoAtencao\Paciente;
|
||||
|
||||
class PerfilCidadaoService extends Service
|
||||
{
|
||||
public function getDadosCidadao(Paciente $paciente): array
|
||||
{
|
||||
$cidadao = $paciente->cidadao;
|
||||
|
||||
$telefones = $cidadao->telefones->map(function ($telefone) {
|
||||
return [
|
||||
'numero' => $telefone->numero,
|
||||
'principal' => (bool) $telefone->principal,
|
||||
'autoriza_sms' => (bool) $telefone->autoriza_sms,
|
||||
];
|
||||
})->values();
|
||||
|
||||
$endereco = null;
|
||||
$domicilio = $cidadao->domicilio;
|
||||
|
||||
if ($domicilio && $domicilio->endereco) {
|
||||
$end = $domicilio->endereco;
|
||||
$logradouro = $end->logradouro;
|
||||
|
||||
$endereco = [
|
||||
'logradouro' => $logradouro ? $logradouro->nome : null,
|
||||
'tipo_logradouro' => ($logradouro && $logradouro->tipo) ? $logradouro->tipo->descricao : null,
|
||||
'cep' => $logradouro ? $logradouro->cep : null,
|
||||
'numero' => $end->numero ?? null,
|
||||
'complemento' => $end->complemento ?? null,
|
||||
'referencia' => $end->referencia ?? null,
|
||||
'bairro' => ($logradouro && $logradouro->bairro) ? $logradouro->bairro->nome : null,
|
||||
'municipio' => ($logradouro && $logradouro->municipio) ? $logradouro->municipio->nome : null,
|
||||
'uf' => ($logradouro && $logradouro->municipio && $logradouro->municipio->estado) ? $logradouro->municipio->estado->uf : null,
|
||||
];
|
||||
}
|
||||
|
||||
$cns = $cidadao->cns;
|
||||
$nome = $cidadao->nome_social ? $cidadao->nome_social : $cidadao->nome;
|
||||
|
||||
$unidade = $paciente->unidade;
|
||||
|
||||
return [
|
||||
'paciente_id' => $paciente->id,
|
||||
'prontuario' => $paciente->prontuario,
|
||||
'nome' => $nome,
|
||||
'nome_completo' => $cidadao->nome,
|
||||
'nome_social' => $cidadao->nome_social ?? null,
|
||||
'cpf' => $cidadao->cpf,
|
||||
'cns' => $cns,
|
||||
'data_nascimento' => $cidadao->data_nascimento,
|
||||
'idade' => $cidadao->idade_formatada,
|
||||
'sexo' => ($cidadao->sexo) ? $cidadao->sexo->descricao : null,
|
||||
'email' => $paciente->email ?? null,
|
||||
'telefones' => $telefones,
|
||||
'endereco' => $endereco,
|
||||
'unidade_referencia' => $unidade ? [
|
||||
'id' => $unidade->id,
|
||||
'nome' => $unidade->nome_fantasia,
|
||||
'cnes' => $unidade->cnes,
|
||||
] : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
114
api/app/Services/Mobile/ConectaCidadao/Termos/TermoService.php
Normal file
114
api/app/Services/Mobile/ConectaCidadao/Termos/TermoService.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mobile\ConectaCidadao\Termos;
|
||||
|
||||
use App\Enums\Mobile\ConectaCidadao\TermoTipo;
|
||||
use App\Models\Mobile\ConectaCidadao\ConectaCidadaoAceitacaoCidadao;
|
||||
use App\Models\Mobile\ConectaCidadao\ConectaCidadaoTermo;
|
||||
use App\Models\Mobile\ConectaCidadao\ConectaCidadaoTermoTipo;
|
||||
use App\Services\Service;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class TermoService extends Service
|
||||
{
|
||||
|
||||
public static function getAtivoByTipo(string $tipo): ?ConectaCidadaoTermo
|
||||
{
|
||||
$tipoId = ConectaCidadaoTermoTipo::where('codigo', $tipo)->value('id');
|
||||
|
||||
return ConectaCidadaoTermo::where('tipo_id', $tipoId)
|
||||
->where('ativo', true)
|
||||
->latest('data_publicacao')
|
||||
->first();
|
||||
}
|
||||
|
||||
public static function getAtivo(): ConectaCidadaoTermo
|
||||
{
|
||||
$tipo = request()->validate(
|
||||
[
|
||||
'tipo' => 'required|string|in:' . TermoTipo::TERMO_USO,
|
||||
]
|
||||
)['tipo'];
|
||||
|
||||
$tipoId = ConectaCidadaoTermoTipo::where('codigo', $tipo)->value('id');
|
||||
|
||||
$termo = ConectaCidadaoTermo::where('tipo_id', $tipoId)
|
||||
->where('ativo', true)
|
||||
->latest('data_publicacao')
|
||||
->first();
|
||||
|
||||
if (!$termo) {
|
||||
|
||||
abort(Response::HTTP_NOT_FOUND, "Nenhum termo ativo encontrado para o tipo '{$tipo}'.");
|
||||
}
|
||||
|
||||
return $termo;
|
||||
}
|
||||
|
||||
public static function getById(int $id): ConectaCidadaoTermo
|
||||
{
|
||||
return ConectaCidadaoTermo::findOrFail($id);
|
||||
}
|
||||
|
||||
public static function getPendentes(): array
|
||||
{
|
||||
$cidadaoId = self::getCidadaoId();
|
||||
|
||||
return ConectaCidadaoTermo::where('ativo', true)
|
||||
->where('obrigatorio', true)
|
||||
->whereNotIn('id', function ($query) use ($cidadaoId) {
|
||||
$query->select('termo_id')
|
||||
->from('conecta_cidadao_aceitacao_cidadao')
|
||||
->where('cidadao_id', $cidadaoId);
|
||||
})
|
||||
->get(
|
||||
[
|
||||
'id',
|
||||
'tipo_id',
|
||||
'versao',
|
||||
'data_publicacao',
|
||||
'obrigatorio'
|
||||
]
|
||||
)
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public static function aceitar(int $id): ConectaCidadaoAceitacaoCidadao
|
||||
{
|
||||
ConectaCidadaoTermo::findOrFail($id);
|
||||
|
||||
$cidadaoId = self::getCidadaoId();
|
||||
|
||||
$jaAceito = ConectaCidadaoAceitacaoCidadao::where('cidadao_id', $cidadaoId)
|
||||
->where('termo_id', $id)
|
||||
->exists();
|
||||
|
||||
if ($jaAceito) {
|
||||
|
||||
abort(Response::HTTP_UNPROCESSABLE_ENTITY, 'Este termo já foi aceito anteriormente.');
|
||||
}
|
||||
|
||||
return ConectaCidadaoAceitacaoCidadao::create([
|
||||
'cidadao_id' => $cidadaoId,
|
||||
'termo_id' => $id,
|
||||
'aceitado_em' => Carbon::now(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getAceitacoes(): array
|
||||
{
|
||||
$cidadaoId = self::getCidadaoId();
|
||||
|
||||
return ConectaCidadaoAceitacaoCidadao::where('cidadao_id', $cidadaoId)
|
||||
->with('termo:id,tipo_id,versao,data_publicacao')
|
||||
->orderByDesc('aceitado_em')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private static function getCidadaoId(): int
|
||||
{
|
||||
return auth('mobile')->user()->cidadao_id;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user