|
|
|
|
@ -10,7 +10,7 @@ from sqlalchemy import func
|
|
|
|
|
from app.db.mock_database import SessionMockLocal
|
|
|
|
|
from app.db.mock_models import Customer, Order, ReviewSchedule, Vehicle
|
|
|
|
|
|
|
|
|
|
# Nesse arquivo eu faço a limpeza dos dados para persisti-los no DB
|
|
|
|
|
# Nesse arquivo eu faço a normalização dos dados para persisti-los no DB
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def normalize_cpf(value: str) -> str:
|
|
|
|
|
@ -385,6 +385,176 @@ async def agendar_revisao(
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def listar_agendamentos_revisao(
|
|
|
|
|
user_id: Optional[int] = None,
|
|
|
|
|
placa: Optional[str] = None,
|
|
|
|
|
status: Optional[str] = None,
|
|
|
|
|
limite: Optional[int] = 20,
|
|
|
|
|
) -> List[Dict[str, Any]]:
|
|
|
|
|
"""Lista agendamentos de revisao do usuario autenticado com filtros opcionais."""
|
|
|
|
|
if user_id is None:
|
|
|
|
|
raise HTTPException(status_code=400, detail="Informe user_id para listar seus agendamentos de revisao.")
|
|
|
|
|
|
|
|
|
|
placa_normalizada = placa.upper().strip() if placa else None
|
|
|
|
|
status_normalizado = status.lower().strip() if status else None
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
limite_int = int(limite) if limite is not None else 20
|
|
|
|
|
except (TypeError, ValueError):
|
|
|
|
|
limite_int = 20
|
|
|
|
|
limite_int = max(1, min(limite_int, 100))
|
|
|
|
|
|
|
|
|
|
db = SessionMockLocal()
|
|
|
|
|
try:
|
|
|
|
|
query = db.query(ReviewSchedule).filter(ReviewSchedule.user_id == user_id)
|
|
|
|
|
if placa_normalizada:
|
|
|
|
|
query = query.filter(ReviewSchedule.placa == placa_normalizada)
|
|
|
|
|
if status_normalizado:
|
|
|
|
|
query = query.filter(func.lower(ReviewSchedule.status) == status_normalizado)
|
|
|
|
|
|
|
|
|
|
agendamentos = (
|
|
|
|
|
query.order_by(ReviewSchedule.data_hora.asc())
|
|
|
|
|
.limit(limite_int)
|
|
|
|
|
.all()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
"protocolo": row.protocolo,
|
|
|
|
|
"user_id": row.user_id,
|
|
|
|
|
"placa": row.placa,
|
|
|
|
|
"data_hora": row.data_hora.isoformat(),
|
|
|
|
|
"status": row.status,
|
|
|
|
|
"created_at": row.created_at.isoformat() if row.created_at else None,
|
|
|
|
|
}
|
|
|
|
|
for row in agendamentos
|
|
|
|
|
]
|
|
|
|
|
finally:
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def cancelar_agendamento_revisao(
|
|
|
|
|
protocolo: str,
|
|
|
|
|
motivo: Optional[str] = None,
|
|
|
|
|
user_id: Optional[int] = None,
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""Cancela um agendamento de revisao existente pelo protocolo."""
|
|
|
|
|
if user_id is None:
|
|
|
|
|
raise HTTPException(status_code=400, detail="Informe user_id para cancelar seu agendamento de revisao.")
|
|
|
|
|
|
|
|
|
|
db = SessionMockLocal()
|
|
|
|
|
try:
|
|
|
|
|
agendamento = (
|
|
|
|
|
db.query(ReviewSchedule)
|
|
|
|
|
.filter(ReviewSchedule.protocolo == protocolo)
|
|
|
|
|
.filter(ReviewSchedule.user_id == user_id)
|
|
|
|
|
.first()
|
|
|
|
|
)
|
|
|
|
|
if not agendamento:
|
|
|
|
|
raise HTTPException(status_code=404, detail="Agendamento de revisao nao encontrado para este usuario.")
|
|
|
|
|
|
|
|
|
|
if agendamento.status.lower() == "cancelado":
|
|
|
|
|
return {
|
|
|
|
|
"protocolo": agendamento.protocolo,
|
|
|
|
|
"user_id": agendamento.user_id,
|
|
|
|
|
"placa": agendamento.placa,
|
|
|
|
|
"data_hora": agendamento.data_hora.isoformat(),
|
|
|
|
|
"status": agendamento.status,
|
|
|
|
|
"motivo": motivo,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
agendamento.status = "cancelado"
|
|
|
|
|
db.commit()
|
|
|
|
|
db.refresh(agendamento)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"protocolo": agendamento.protocolo,
|
|
|
|
|
"user_id": agendamento.user_id,
|
|
|
|
|
"placa": agendamento.placa,
|
|
|
|
|
"data_hora": agendamento.data_hora.isoformat(),
|
|
|
|
|
"status": agendamento.status,
|
|
|
|
|
"motivo": motivo,
|
|
|
|
|
}
|
|
|
|
|
finally:
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def editar_data_revisao(
|
|
|
|
|
protocolo: str,
|
|
|
|
|
nova_data_hora: str,
|
|
|
|
|
user_id: Optional[int] = None,
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""Edita a data/hora de um agendamento de revisao existente."""
|
|
|
|
|
if user_id is None:
|
|
|
|
|
raise HTTPException(status_code=400, detail="Informe user_id para editar seu agendamento de revisao.")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
nova_data = _normalize_review_slot(_parse_data_hora_revisao(nova_data_hora))
|
|
|
|
|
except ValueError:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=400,
|
|
|
|
|
detail=(
|
|
|
|
|
"nova_data_hora invalida. Exemplos aceitos: "
|
|
|
|
|
"2026-03-10T09:00:00-03:00, 2026-03-10 09:00, 10/03/2026 09:00, "
|
|
|
|
|
"10/03/2026 as 09:00."
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
db = SessionMockLocal()
|
|
|
|
|
try:
|
|
|
|
|
agendamento = (
|
|
|
|
|
db.query(ReviewSchedule)
|
|
|
|
|
.filter(ReviewSchedule.protocolo == protocolo)
|
|
|
|
|
.filter(ReviewSchedule.user_id == user_id)
|
|
|
|
|
.first()
|
|
|
|
|
)
|
|
|
|
|
if not agendamento:
|
|
|
|
|
raise HTTPException(status_code=404, detail="Agendamento de revisao nao encontrado para este usuario.")
|
|
|
|
|
|
|
|
|
|
if agendamento.status.lower() == "cancelado":
|
|
|
|
|
raise HTTPException(status_code=400, detail="Nao e possivel editar um agendamento ja cancelado.")
|
|
|
|
|
|
|
|
|
|
conflito = (
|
|
|
|
|
db.query(ReviewSchedule)
|
|
|
|
|
.filter(ReviewSchedule.id != agendamento.id)
|
|
|
|
|
.filter(ReviewSchedule.data_hora == nova_data)
|
|
|
|
|
.filter(func.lower(ReviewSchedule.status) != "cancelado")
|
|
|
|
|
.first()
|
|
|
|
|
)
|
|
|
|
|
if conflito:
|
|
|
|
|
proximo_horario = _find_next_available_review_slot(db=db, requested_dt=nova_data)
|
|
|
|
|
if proximo_horario:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=409,
|
|
|
|
|
detail=(
|
|
|
|
|
f"O horario {_format_datetime_pt_br(nova_data)} ja esta ocupado. "
|
|
|
|
|
f"Sugestao: {_format_datetime_pt_br(proximo_horario)} "
|
|
|
|
|
f"(ISO: {proximo_horario.isoformat()})."
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=409,
|
|
|
|
|
detail=(
|
|
|
|
|
f"O horario {_format_datetime_pt_br(nova_data)} ja esta ocupado e nao encontrei "
|
|
|
|
|
"disponibilidade nas proximas 8 horas."
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
agendamento.data_hora = nova_data
|
|
|
|
|
db.commit()
|
|
|
|
|
db.refresh(agendamento)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"protocolo": agendamento.protocolo,
|
|
|
|
|
"user_id": agendamento.user_id,
|
|
|
|
|
"placa": agendamento.placa,
|
|
|
|
|
"data_hora": agendamento.data_hora.isoformat(),
|
|
|
|
|
"status": agendamento.status,
|
|
|
|
|
}
|
|
|
|
|
finally:
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def cancelar_pedido(numero_pedido: str, motivo: str, user_id: Optional[int] = None) -> Dict[str, Any]:
|
|
|
|
|
"""Cancela pedido existente e registra motivo e data de cancelamento."""
|
|
|
|
|
db = SessionMockLocal()
|
|
|
|
|
|