Files
space-booking/backend/app/api/settings.py
Claude Agent df4031d99c feat: Space Booking System - MVP complet
Sistem web pentru rezervarea de birouri și săli de ședință
cu flux de aprobare administrativă.

Stack: FastAPI + Vue.js 3 + SQLite + TypeScript

Features implementate:
- Autentificare JWT + Self-registration cu email verification
- CRUD Spații, Utilizatori, Settings (Admin)
- Calendar interactiv (FullCalendar) cu drag-and-drop
- Creare rezervări cu validare (durată, program, overlap, max/zi)
- Rezervări recurente (săptămânal)
- Admin: aprobare/respingere/anulare cereri
- Admin: creare directă rezervări (bypass approval)
- Admin: editare orice rezervare
- User: editare/anulare rezervări proprii
- Notificări in-app (bell icon + dropdown)
- Notificări email (async SMTP cu BackgroundTasks)
- Jurnal acțiuni administrative (audit log)
- Rapoarte avansate (utilizare, top users, approval rate)
- Șabloane rezervări (booking templates)
- Atașamente fișiere (upload/download)
- Conflict warnings (verificare disponibilitate real-time)
- Integrare Google Calendar (OAuth2)
- Suport timezone (UTC storage + user preference)
- 225+ teste backend

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:51:29 +00:00

132 lines
4.7 KiB
Python

"""Settings management endpoints."""
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.deps import get_current_admin, get_db
from app.models.settings import Settings
from app.models.user import User
from app.schemas.settings import SettingsResponse, SettingsUpdate
from app.services.audit_service import log_action
router = APIRouter(prefix="/admin/settings", tags=["admin"])
@router.get("", response_model=SettingsResponse)
def get_settings(
db: Annotated[Session, Depends(get_db)],
_: Annotated[User, Depends(get_current_admin)],
) -> Settings:
"""
Get global settings (admin only).
Returns the current global booking rules.
"""
settings = db.query(Settings).filter(Settings.id == 1).first()
if not settings:
# Create default settings if not exist
settings = Settings(
id=1,
min_duration_minutes=30,
max_duration_minutes=480,
working_hours_start=8,
working_hours_end=20,
max_bookings_per_day_per_user=3,
min_hours_before_cancel=2,
)
db.add(settings)
db.commit()
db.refresh(settings)
return settings
@router.put("", response_model=SettingsResponse)
def update_settings(
settings_data: SettingsUpdate,
db: Annotated[Session, Depends(get_db)],
current_admin: Annotated[User, Depends(get_current_admin)],
) -> Settings:
"""
Update global settings (admin only).
All booking rules are validated on the client side and applied
to all new booking requests.
"""
settings = db.query(Settings).filter(Settings.id == 1).first()
if not settings:
# Create if not exist
settings = Settings(id=1)
db.add(settings)
# Validate: min_duration <= max_duration
if settings_data.min_duration_minutes > settings_data.max_duration_minutes:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Minimum duration cannot be greater than maximum duration",
)
# Validate: working_hours_start < working_hours_end
if settings_data.working_hours_start >= settings_data.working_hours_end:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Working hours start must be before working hours end",
)
# Track which fields changed
changed_fields = {}
if settings.min_duration_minutes != settings_data.min_duration_minutes:
changed_fields["min_duration_minutes"] = {
"old": settings.min_duration_minutes,
"new": settings_data.min_duration_minutes
}
if settings.max_duration_minutes != settings_data.max_duration_minutes:
changed_fields["max_duration_minutes"] = {
"old": settings.max_duration_minutes,
"new": settings_data.max_duration_minutes
}
if settings.working_hours_start != settings_data.working_hours_start:
changed_fields["working_hours_start"] = {
"old": settings.working_hours_start,
"new": settings_data.working_hours_start
}
if settings.working_hours_end != settings_data.working_hours_end:
changed_fields["working_hours_end"] = {
"old": settings.working_hours_end,
"new": settings_data.working_hours_end
}
if settings.max_bookings_per_day_per_user != settings_data.max_bookings_per_day_per_user:
changed_fields["max_bookings_per_day_per_user"] = {
"old": settings.max_bookings_per_day_per_user,
"new": settings_data.max_bookings_per_day_per_user
}
if settings.min_hours_before_cancel != settings_data.min_hours_before_cancel:
changed_fields["min_hours_before_cancel"] = {
"old": settings.min_hours_before_cancel,
"new": settings_data.min_hours_before_cancel
}
# Update all fields
setattr(settings, "min_duration_minutes", settings_data.min_duration_minutes)
setattr(settings, "max_duration_minutes", settings_data.max_duration_minutes)
setattr(settings, "working_hours_start", settings_data.working_hours_start)
setattr(settings, "working_hours_end", settings_data.working_hours_end)
setattr(settings, "max_bookings_per_day_per_user", settings_data.max_bookings_per_day_per_user)
setattr(settings, "min_hours_before_cancel", settings_data.min_hours_before_cancel)
db.commit()
db.refresh(settings)
# Log the action
log_action(
db=db,
action="settings_updated",
user_id=current_admin.id,
target_type="settings",
target_id=1, # Settings ID is always 1 (singleton)
details={"changed_fields": changed_fields}
)
return settings