Feature: Opdater vTiger Timelog med Hub ordre ID efter eksport

- Tilføjet update_timelog_billed() metode til vtiger_sync.py
- Opdaterer billed_via_thehub_id felt i vTiger Timelog records
- Kaldes automatisk efter succesfuld e-conomic eksport
- Respekterer READ_ONLY og DRY_RUN safety flags
- Fejler ikke eksporten hvis vTiger update fejler (bare logger warning)
This commit is contained in:
Christian 2025-12-23 01:44:14 +01:00
parent a2857f5e12
commit 807e7f6395
2 changed files with 98 additions and 0 deletions

View File

@ -24,6 +24,7 @@ from app.timetracking.backend.models import (
TModuleEconomicExportResult
)
from app.timetracking.backend.audit import audit
from app.timetracking.backend.vtiger_sync import vtiger_service
logger = logging.getLogger(__name__)
@ -412,6 +413,32 @@ class EconomicExportService:
(request.order_id,)
)
# Hent vTiger IDs for tidsregistreringerne
vtiger_ids_query = """
SELECT t.vtiger_id
FROM tmodule_times t
WHERE t.id IN (
SELECT UNNEST(time_entry_ids)
FROM tmodule_order_lines
WHERE order_id = %s
)
"""
vtiger_time_records = execute_query(vtiger_ids_query, (request.order_id,))
vtiger_ids = [r['vtiger_id'] for r in vtiger_time_records]
# Opdater Timelog records i vTiger med Hub ordre ID
if vtiger_ids:
logger.info(f"📝 Updating {len(vtiger_ids)} timelogs in vTiger with Hub order {request.order_id}...")
try:
vtiger_update_result = await vtiger_service.update_timelog_billed(
vtiger_ids=vtiger_ids,
hub_order_id=request.order_id
)
logger.info(f"✅ vTiger update: {vtiger_update_result}")
except Exception as vtiger_error:
# Don't fail export if vTiger update fails - just log it
logger.error(f"⚠️ Could not update vTiger timelogs: {vtiger_error}")
# Log successful export
audit.log_export_completed(
order_id=request.order_id,

View File

@ -210,6 +210,77 @@ class TimeTrackingVTigerService:
logger.error(f"❌ vTiger connection error: {e}")
return False
async def update_timelog_billed(self, vtiger_ids: List[str], hub_order_id: int) -> Dict[str, int]:
"""
Update Timelog records in vTiger with Hub ordre ID (markér som faktureret).
🚨 WRITE operation - respekterer safety flags
Args:
vtiger_ids: Liste af vTiger Timelog IDs (format: "43x1234")
hub_order_id: Hub ordre ID til at sætte i billed_via_thehub_id felt
Returns:
Dict with success/error counts
"""
if self.read_only:
logger.warning(f"🔒 READ-ONLY mode: Would update {len(vtiger_ids)} timelogs with Hub order {hub_order_id}")
return {"updated": 0, "errors": 0, "message": "READ-ONLY mode"}
if self.dry_run:
logger.warning(f"🏃 DRY-RUN mode: Would update {len(vtiger_ids)} timelogs with Hub order {hub_order_id}")
return {"updated": 0, "errors": 0, "message": "DRY-RUN mode"}
stats = {"updated": 0, "errors": 0}
logger.info(f"📝 Updating {len(vtiger_ids)} timelogs in vTiger with Hub order {hub_order_id}...")
try:
async with aiohttp.ClientSession() as session:
for vtiger_id in vtiger_ids:
try:
# Build update payload
update_url = f"{self.rest_endpoint}/update"
payload = {
"elementType": "Timelog",
"element": {
"id": vtiger_id,
"billed_via_thehub_id": str(hub_order_id)
}
}
async with session.post(
update_url,
json=payload,
auth=self._get_auth(),
timeout=aiohttp.ClientTimeout(total=30)
) as response:
text = await response.text()
if response.status == 200:
data = json.loads(text)
if data.get('success'):
logger.debug(f"✅ Updated timelog {vtiger_id}")
stats["updated"] += 1
else:
error_msg = data.get('error', {}).get('message', 'Unknown error')
logger.error(f"❌ vTiger error for {vtiger_id}: {error_msg}")
stats["errors"] += 1
else:
logger.error(f"❌ HTTP {response.status} for {vtiger_id}: {text[:200]}")
stats["errors"] += 1
except Exception as e:
logger.error(f"❌ Error updating timelog {vtiger_id}: {e}")
stats["errors"] += 1
except Exception as e:
logger.error(f"❌ Batch update error: {e}")
stats["errors"] += len(vtiger_ids)
logger.info(f"✅ vTiger update complete: {stats['updated']} updated, {stats['errors']} errors")
return stats
async def _fetch_user_name(self, user_id: str) -> str:
"""
Fetch user name from vTiger using retrieve API.