docboard-api / app /routers /procedures.py
yCamelow's picture
Upload app folder
29cbab9 verified
Raw
History Blame Contribute Delete
6.89 kB
from fastapi import APIRouter, Depends, HTTPException, status
from sqlmodel import Session, select
import uuid
import datetime
from app.core.database import get_session
from app.api.deps import get_current_user
from app.models.user import User
from app.models.procedure import Procedure
from app.models.procedure_type import ProcedureType
from app.models.team_procedure_value import TeamProcedureValue
from app.models.membership import Membership
from app.schemas.procedure import ProcedureCreate, ProcedureRead, ProcedureUpdate
router = APIRouter(prefix="/procedures", tags=["Procedures"])
@router.post("/", response_model=ProcedureRead, status_code=status.HTTP_201_CREATED)
def create_procedure(
procedure_in: ProcedureCreate,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
"""
Cria um novo procedimento ("A Rota de Escrita").
Aplica as regras de negócio:
- Valida o médico a partir do token (via `get_current_user`).
- Identifica o time associado ao médico (caso não pertença a nenhum, retorna erro 400).
- Congela o valor: busca na tabela de valores de procedimentos do time qual o preço
do procedimento neste exato momento e salva no campo `value_at_time`.
"""
# 1. Identificar o Time: O backend busca a qual team_id esse médico pertence
membership = session.exec(select(Membership).where(Membership.user_id == current_user.id)).first()
if not membership:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="O médico não pertence a nenhum time."
)
# 2. Congelar o Valor: O código busca na tabela team_procedure_values qual é o preço daquele procedimento para aquele time específico
team_procedure_value = session.exec(
select(TeamProcedureValue)
.where(TeamProcedureValue.team_id == membership.team_id)
.where(TeamProcedureValue.procedure_type_id == procedure_in.procedure_type_id)
).first()
# Define o valor do procedimento (usa o custom_value, se não existir mapeamento usa 0 ou dispara erro)
# Por segurança, caso não esteja mapeado, vamos preencher com 0 e o admin depois ajusta,
# Ou podemos rejeitar a criação se não tiver preço. Adotaremos 0 como fallback coerente.
value_at_time = team_procedure_value.custom_value if team_procedure_value else 0
# 3. Criar a Procedure associando todos os dados
# Se o cliente enviou um doctor_id (via form), usamos ele. Caso contrário, usamos o logado.
final_doctor_id = procedure_in.doctor_id or current_user.id
procedure = Procedure(
**procedure_in.model_dump(exclude={"doctor_id"}),
doctor_id=final_doctor_id,
team_id=membership.team_id,
value_at_time=value_at_time
)
session.add(procedure)
session.commit()
session.refresh(procedure)
return procedure
@router.patch("/{procedure_id}", response_model=ProcedureRead)
def update_procedure(
*,
session: Session = Depends(get_session),
procedure_id: uuid.UUID,
procedure_in: ProcedureUpdate,
current_user: User = Depends(get_current_user)
):
"""
Atualiza um procedimento existente.
Verifica permissões de equipe e atualiza o valor financeiro se o tipo for alterado.
"""
# 1. Buscar o procedimento no banco
db_proc = session.get(Procedure, procedure_id)
if not db_proc:
raise HTTPException(status_code=404, detail="Procedimento não encontrado")
# 2. Verificar se o usuário pertence ao time do procedimento (Segurança)
membership = session.exec(
select(Membership).where(
Membership.user_id == current_user.id,
Membership.team_id == db_proc.team_id
)
).first()
if not membership:
raise HTTPException(
status_code=403,
detail="Você não tem permissão para editar procedimentos deste time."
)
# 3. Preparar os dados para atualização
update_data = procedure_in.model_dump(exclude_unset=True)
# 4. Lógica Especial: Se o tipo de procedimento mudou, precisamos atualizar o valor financeiro
if "procedure_type_id" in update_data:
new_type_id = update_data["procedure_type_id"]
# Busca o valor customizado para o time ou o valor base do catálogo
custom_val_stmt = select(TeamProcedureValue.custom_value).where(
TeamProcedureValue.team_id == db_proc.team_id,
TeamProcedureValue.procedure_type_id == new_type_id
)
custom_value = session.exec(custom_val_stmt).first()
if custom_value is not None:
db_proc.value_at_time = custom_value
else:
# Se não houver valor customizado, busca o valor base do catálogo
proc_type = session.get(ProcedureType, new_type_id)
if proc_type:
db_proc.value_at_time = proc_type.base_value
# 5. Aplicar os demais campos dinamicamente
for key, value in update_data.items():
setattr(db_proc, key, value)
# Definir momento da atualização
db_proc.updated_at = datetime.datetime.now(datetime.timezone.utc)
# 6. Salvar e retornar
session.add(db_proc)
session.commit()
session.refresh(db_proc)
return db_proc
@router.delete("/{procedure_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_procedure(
*,
session: Session = Depends(get_session),
procedure_id: uuid.UUID,
current_user: User = Depends(get_current_user)
):
"""
Exclui um procedimento permanentemente.
Apenas Administradores do time ou Staff podem realizar esta ação.
"""
# 1. Buscar o procedimento no banco
db_proc = session.get(Procedure, procedure_id)
if not db_proc:
raise HTTPException(status_code=404, detail="Procedimento não encontrado")
# 2. Verificar se o usuário pertence ao time
membership = session.exec(
select(Membership).where(
Membership.user_id == current_user.id,
Membership.team_id == db_proc.team_id
)
).first()
if not membership:
raise HTTPException(
status_code=403,
detail="Você não tem permissão para acessar este procedimento."
)
# NAO USADA PARA O MVP
# 3. Regra de Negócio (RBAC): Apenas Admin ou Staff exclui
# Nota: membership.role define se ele é 'admin' ou 'member' no time.
# current_user.role define se ele é 'staff' no sistema global.
# if membership.role != "admin" and current_user.role != RoleEnum.staff:
# raise HTTPException(
# status_code=403,
# detail="Apenas o administrador da equipe ou staff podem excluir registros."
# )
# 4. Excluir e salvar (Commit)
session.delete(db_proc)
session.commit()
return {"ok": True}