szolgáltatók beálltásai, szerkesztése , létrehozása
This commit is contained in:
@@ -1,11 +1,18 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/api/v1/endpoints/expenses.py
|
||||
import logging
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
from app.api.deps import get_db, get_current_user
|
||||
from app.models import Asset, AssetCost, OrganizationMember, SystemParameter
|
||||
from app.models import Asset, AssetCost, AssetEvent, OrganizationMember, SystemParameter
|
||||
from app.schemas.asset_cost import AssetCostCreate
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Cost category IDs that are service/maintenance/repair related
|
||||
# These trigger automatic AssetEvent creation
|
||||
SERVICE_RELATED_CATEGORY_IDS = {2, 16, 17, 18} # MAINTENANCE, MAINT_SERVICE, MAINT_OIL, MAINT_BRAKES
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -18,6 +25,10 @@ async def create_expense(
|
||||
"""
|
||||
Create a new expense (fuel, service, tax, insurance) for an asset.
|
||||
Uses AssetCostCreate schema which includes mileage_at_cost, cost_type, etc.
|
||||
|
||||
**Bidirectional Sync:**
|
||||
- If the cost category is service-related (MAINTENANCE, MAINT_SERVICE, MAINT_OIL, MAINT_BRAKES),
|
||||
an AssetEvent is automatically created and linked to the cost via cost_id.
|
||||
"""
|
||||
# Validate asset exists
|
||||
stmt = select(Asset).where(Asset.id == expense.asset_id)
|
||||
@@ -85,32 +96,87 @@ async def create_expense(
|
||||
if expense.description:
|
||||
data["description"] = expense.description
|
||||
|
||||
# Create AssetCost instance
|
||||
new_cost = AssetCost(
|
||||
asset_id=expense.asset_id,
|
||||
organization_id=organization_id,
|
||||
category_id=expense.category_id,
|
||||
amount_net=expense.amount_net,
|
||||
currency=expense.currency,
|
||||
date=expense.date,
|
||||
invoice_number=data.get("invoice_number"),
|
||||
data=data
|
||||
)
|
||||
try:
|
||||
# Create AssetCost instance
|
||||
new_cost = AssetCost(
|
||||
asset_id=expense.asset_id,
|
||||
organization_id=organization_id,
|
||||
category_id=expense.category_id,
|
||||
amount_net=expense.amount_net,
|
||||
currency=expense.currency,
|
||||
date=expense.date,
|
||||
invoice_number=data.get("invoice_number"),
|
||||
data=data
|
||||
)
|
||||
|
||||
db.add(new_cost)
|
||||
await db.flush() # Flush to get new_cost.id
|
||||
|
||||
# ── BIDIRECTIONAL SYNC: Auto-create AssetEvent for service-related costs ──
|
||||
event_id = None
|
||||
if expense.category_id in SERVICE_RELATED_CATEGORY_IDS:
|
||||
# Map cost category to event type
|
||||
event_type = _map_category_to_event_type(expense.category_id)
|
||||
|
||||
description = expense.description or data.get("description", f"Service cost: {expense.category_id}")
|
||||
mileage = expense.mileage_at_cost
|
||||
|
||||
new_event = AssetEvent(
|
||||
asset_id=expense.asset_id,
|
||||
user_id=getattr(current_user, 'id', None),
|
||||
organization_id=organization_id,
|
||||
event_type=event_type,
|
||||
odometer_reading=mileage,
|
||||
description=description,
|
||||
cost_id=new_cost.id,
|
||||
event_date=expense.date or datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(new_event)
|
||||
await db.flush() # Flush to get new_event.id
|
||||
event_id = new_event.id
|
||||
|
||||
logger.info(
|
||||
f"Bidirectional sync: Auto-created AssetEvent {event_id} "
|
||||
f"for AssetCost {new_cost.id} (category_id={expense.category_id})"
|
||||
)
|
||||
|
||||
# Update Asset.current_mileage if mileage_at_cost is higher
|
||||
if expense.mileage_at_cost is not None and expense.mileage_at_cost > (asset.current_mileage or 0):
|
||||
asset.current_mileage = expense.mileage_at_cost
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(new_cost)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"id": new_cost.id,
|
||||
"asset_id": new_cost.asset_id,
|
||||
"category_id": new_cost.category_id,
|
||||
"amount_net": new_cost.amount_net,
|
||||
"date": new_cost.date,
|
||||
"event_id": str(event_id) if event_id else None,
|
||||
}
|
||||
|
||||
db.add(new_cost)
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Expense creation error for asset {expense.asset_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Hiba a költség rögzítésekor"
|
||||
)
|
||||
|
||||
|
||||
def _map_category_to_event_type(category_id: int) -> str:
|
||||
"""
|
||||
Map a cost category ID to the appropriate AssetEvent event_type.
|
||||
|
||||
# Update Asset.current_mileage if mileage_at_cost is higher
|
||||
if expense.mileage_at_cost is not None and expense.mileage_at_cost > (asset.current_mileage or 0):
|
||||
asset.current_mileage = expense.mileage_at_cost
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(new_cost)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"id": new_cost.id,
|
||||
"asset_id": new_cost.asset_id,
|
||||
"category_id": new_cost.category_id,
|
||||
"amount_net": new_cost.amount_net,
|
||||
"date": new_cost.date
|
||||
}
|
||||
Returns:
|
||||
str: The event type string (SERVICE, REPAIR, etc.)
|
||||
"""
|
||||
category_event_map = {
|
||||
2: "MAINTENANCE", # MAINTENANCE
|
||||
16: "SERVICE", # MAINT_SERVICE
|
||||
17: "SERVICE", # MAINT_OIL
|
||||
18: "REPAIR", # MAINT_BRAKES
|
||||
}
|
||||
return category_event_map.get(category_id, "MAINTENANCE")
|
||||
Reference in New Issue
Block a user