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, ]); } }