feat(T04): Rentman integration – equipment sync, API endpoints, frontend updates
- Update equipment model with Rentman-compatible fields - Add/Update equipment router endpoints for sync operations - Enhance rentman_service with full API client logic - Improve sync_service for bidirectional equipment sync - Update docker-compose with Rentman env vars - Update frontend useApi composable and nuxt config - Update mietkatalog pages with Rentman-integrated data display - Add images/ to .gitignore (binary assets, 36MB)
This commit is contained in:
@@ -50,6 +50,65 @@ class RentmanService:
|
||||
offset += limit
|
||||
return all_items
|
||||
|
||||
async def get_file_url(self, file_id: str | int) -> str | None:
|
||||
"""Fetch the S3 URL for a file from Rentman.
|
||||
|
||||
GET /files/{file_id} → response contains 'url' field with S3 link.
|
||||
"""
|
||||
url = f"{self._base_url}/files/{file_id}"
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
resp = await client.get(url, headers=self._headers())
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
file_data = data.get("data", data)
|
||||
file_url = file_data.get("url")
|
||||
if file_url:
|
||||
return file_url
|
||||
logger.warning("No url field in file response for file_id=%s", file_id)
|
||||
return None
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to fetch file URL for file_id=%s: %s", file_id, exc)
|
||||
return None
|
||||
|
||||
async def transform_equipment(self, raw: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Map a raw Rentman equipment object to equipment_cache schema.
|
||||
|
||||
Uses 'image' (singular) field which contains a relative path like '/files/3173'.
|
||||
Fetches the actual S3 URL via GET /files/{file_id}.
|
||||
"""
|
||||
image_path = raw.get("image")
|
||||
image_urls: list[str] = []
|
||||
|
||||
if image_path and isinstance(image_path, str):
|
||||
# Extract file_id from path like /files/3173
|
||||
match = re.search(r"/files/(\d+)", image_path)
|
||||
if match:
|
||||
file_id = match.group(1)
|
||||
s3_url = await self.get_file_url(file_id)
|
||||
if s3_url:
|
||||
image_urls = [s3_url]
|
||||
else:
|
||||
logger.debug("Could not extract file_id from image path: %s", image_path)
|
||||
|
||||
group = raw.get("equipment_group") or {}
|
||||
category = group.get("name", "") if isinstance(group, dict) else str(group or "")
|
||||
|
||||
return {
|
||||
"rentman_id": str(raw.get("id", "")),
|
||||
"name": raw.get("name", ""),
|
||||
"number": raw.get("number") or raw.get("code", ""),
|
||||
"category": category,
|
||||
"subcategory": raw.get("subcategory", ""),
|
||||
"description": raw.get("description", ""),
|
||||
"specifications": raw.get("specifications", {}),
|
||||
"images": image_urls,
|
||||
"rental_price": raw.get("rental_price"),
|
||||
"brand": raw.get("brand", ""),
|
||||
"available": raw.get("available", True),
|
||||
"update_hash": raw.get("updateHash", ""),
|
||||
}
|
||||
|
||||
async def create_project_request(self, payload: dict[str, Any]) -> dict[str, Any]:
|
||||
"""POST /projectrequests to create a new project request in Rentman."""
|
||||
url = f"{self._base_url}/projectrequests"
|
||||
@@ -68,35 +127,6 @@ class RentmanService:
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
@staticmethod
|
||||
def transform_equipment(raw: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Map a raw Rentman equipment object to equipment_cache schema."""
|
||||
images = raw.get("images") or raw.get("files") or []
|
||||
if isinstance(images, list):
|
||||
image_urls = [
|
||||
img.get("url", img.get("filename", "")) if isinstance(img, dict) else str(img)
|
||||
for img in images
|
||||
]
|
||||
else:
|
||||
image_urls = []
|
||||
|
||||
group = raw.get("equipment_group") or {}
|
||||
category = group.get("name", "") if isinstance(group, dict) else str(group or "")
|
||||
|
||||
return {
|
||||
"rentman_id": str(raw.get("id", "")),
|
||||
"name": raw.get("name", ""),
|
||||
"number": raw.get("number") or raw.get("code", ""),
|
||||
"category": category,
|
||||
"subcategory": raw.get("subcategory", ""),
|
||||
"description": raw.get("description", ""),
|
||||
"specifications": raw.get("specifications", {}),
|
||||
"images": image_urls,
|
||||
"rental_price": raw.get("rental_price"),
|
||||
"brand": raw.get("brand", ""),
|
||||
"available": raw.get("available", True),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def build_project_request_payload(data: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Map frontend rental request data to Rentman POST /projectrequests payload."""
|
||||
|
||||
Reference in New Issue
Block a user