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>
132 lines
4.7 KiB
Python
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
|