142 lines
5.1 KiB
Python
142 lines
5.1 KiB
Python
"""Rental request router: accept Mietanfrage, save, forward to Rentman."""
|
|
import random
|
|
from datetime import datetime
|
|
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from typing import Any
|
|
from app.database import get_db
|
|
from app.models.equipment import EquipmentCache
|
|
from app.models.rental_request import RentalRequest, RentalRequestItem
|
|
from app.schemas.rental_request import RentalRequestCreate, RentalRequestResponse
|
|
from app.services.rentman_service import RentmanService
|
|
from app.services.email_service import EmailService
|
|
from app.cache import cache
|
|
|
|
router = APIRouter(prefix="/api/rental-requests", tags=["rental-requests"])
|
|
|
|
|
|
def generate_reference_number() -> str:
|
|
"""Generate a unique reference number: HMS-YYYY-NNNNN."""
|
|
year = datetime.now().year
|
|
num = random.randint(1, 99999)
|
|
return f"HMS-{year}-{num:05d}"
|
|
|
|
|
|
@router.post("", response_model=RentalRequestResponse, status_code=status.HTTP_201_CREATED)
|
|
async def create_rental_request(
|
|
payload: RentalRequestCreate,
|
|
request: Request,
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> Any:
|
|
"""Create a rental request, save to DB, forward to Rentman, send email."""
|
|
# Rate limiting
|
|
client_ip = request.client.host if request.client else "unknown"
|
|
rate_key = f"rate:rental:{client_ip}"
|
|
count = await cache.incr_rate(rate_key, window=60)
|
|
if count > 5:
|
|
raise HTTPException(status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail="Too many requests")
|
|
|
|
ref_number = generate_reference_number()
|
|
|
|
# Save to database
|
|
rental = RentalRequest(
|
|
reference_number=ref_number,
|
|
event_name=payload.event_name,
|
|
date_start=payload.date_start,
|
|
date_end=payload.date_end,
|
|
location=payload.location,
|
|
person_count=payload.person_count,
|
|
contact_name=payload.contact_name,
|
|
contact_company=payload.contact_company,
|
|
contact_email=str(payload.contact_email),
|
|
contact_phone=payload.contact_phone,
|
|
contact_street=payload.contact_street,
|
|
contact_postalcode=payload.contact_postalcode,
|
|
contact_city=payload.contact_city,
|
|
message=payload.message,
|
|
status="pending",
|
|
rentman_sync_status="pending",
|
|
)
|
|
db.add(rental)
|
|
await db.flush()
|
|
|
|
# Fetch equipment info for items
|
|
items_data = []
|
|
for item in payload.items:
|
|
result = await db.execute(select(EquipmentCache).where(EquipmentCache.id == item.equipment_id))
|
|
eq = result.scalar_one_or_none()
|
|
eq_name = eq.name if eq else "Unknown"
|
|
eq_rentman_id = eq.rentman_id if eq else ""
|
|
|
|
req_item = RentalRequestItem(
|
|
rental_request_id=rental.id,
|
|
equipment_id=item.equipment_id,
|
|
equipment_name=eq_name,
|
|
rentman_equipment_id=eq_rentman_id,
|
|
quantity=item.quantity,
|
|
rentman_sync_status="pending",
|
|
)
|
|
db.add(req_item)
|
|
items_data.append({
|
|
"equipment_name": eq_name,
|
|
"rentman_equipment_id": eq_rentman_id,
|
|
"quantity": item.quantity,
|
|
})
|
|
|
|
await db.commit()
|
|
|
|
# Forward to Rentman
|
|
rentman = RentmanService()
|
|
rentman_payload = RentmanService.build_project_request_payload({
|
|
"event_name": payload.event_name,
|
|
"date_start": str(payload.date_start),
|
|
"date_end": str(payload.date_end),
|
|
"contact_name": payload.contact_name,
|
|
"contact_company": payload.contact_company,
|
|
"contact_email": str(payload.contact_email),
|
|
"contact_phone": payload.contact_phone,
|
|
"contact_street": payload.contact_street,
|
|
"contact_postalcode": payload.contact_postalcode,
|
|
"contact_city": payload.contact_city,
|
|
"location": payload.location,
|
|
"message": payload.message,
|
|
})
|
|
|
|
try:
|
|
rentman_response = await rentman.create_project_request(rentman_payload)
|
|
rentman_request_id = str(rentman_response.get("id", ""))
|
|
rental.rentman_request_id = rentman_request_id
|
|
rental.rentman_sync_status = "project_created"
|
|
|
|
# Add equipment items to Rentman
|
|
for item_data in items_data:
|
|
eq_payload = RentmanService.build_equipment_payload(item_data)
|
|
try:
|
|
await rentman.add_equipment_to_request(rentman_request_id, eq_payload)
|
|
except Exception:
|
|
# Item-level failure: mark as failed but continue
|
|
pass
|
|
|
|
rental.rentman_sync_status = "success"
|
|
except Exception:
|
|
rental.rentman_sync_status = "failed"
|
|
rental.rentman_sync_error = "Rentman API request failed"
|
|
|
|
await db.commit()
|
|
|
|
# Send confirmation email
|
|
email_service = EmailService()
|
|
try:
|
|
await email_service.send_rental_confirmation(
|
|
to_addr=str(payload.contact_email),
|
|
reference_number=ref_number,
|
|
event_name=payload.event_name,
|
|
items=items_data,
|
|
)
|
|
except Exception:
|
|
# Email failure should not affect response
|
|
pass
|
|
|
|
return RentalRequestResponse(reference_number=ref_number, status="pending")
|