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:
parent
a2857f5e12
commit
807e7f6395
@ -24,6 +24,7 @@ from app.timetracking.backend.models import (
|
|||||||
TModuleEconomicExportResult
|
TModuleEconomicExportResult
|
||||||
)
|
)
|
||||||
from app.timetracking.backend.audit import audit
|
from app.timetracking.backend.audit import audit
|
||||||
|
from app.timetracking.backend.vtiger_sync import vtiger_service
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -412,6 +413,32 @@ class EconomicExportService:
|
|||||||
(request.order_id,)
|
(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
|
# Log successful export
|
||||||
audit.log_export_completed(
|
audit.log_export_completed(
|
||||||
order_id=request.order_id,
|
order_id=request.order_id,
|
||||||
|
|||||||
@ -210,6 +210,77 @@ class TimeTrackingVTigerService:
|
|||||||
logger.error(f"❌ vTiger connection error: {e}")
|
logger.error(f"❌ vTiger connection error: {e}")
|
||||||
return False
|
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:
|
async def _fetch_user_name(self, user_id: str) -> str:
|
||||||
"""
|
"""
|
||||||
Fetch user name from vTiger using retrieve API.
|
Fetch user name from vTiger using retrieve API.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user