feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
"""
|
|
|
|
|
e-conomic Export Service for Time Tracking Module
|
|
|
|
|
==================================================
|
|
|
|
|
|
|
|
|
|
🚨 KRITISK: Denne service skal respektere safety flags.
|
|
|
|
|
Eksporterer ordrer til e-conomic som draft orders.
|
|
|
|
|
|
|
|
|
|
Safety Flags:
|
|
|
|
|
- TIMETRACKING_ECONOMIC_READ_ONLY = True (default)
|
|
|
|
|
- TIMETRACKING_ECONOMIC_DRY_RUN = True (default)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import logging
|
2025-12-10 18:29:13 +01:00
|
|
|
import json
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
from typing import Optional, Dict, Any
|
|
|
|
|
|
|
|
|
|
import aiohttp
|
|
|
|
|
from fastapi import HTTPException
|
|
|
|
|
|
|
|
|
|
from app.core.config import settings
|
2025-12-16 22:07:20 +01:00
|
|
|
from app.core.database import execute_query, execute_update, execute_query_single
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
from app.timetracking.backend.models import (
|
|
|
|
|
TModuleEconomicExportRequest,
|
|
|
|
|
TModuleEconomicExportResult
|
2025-12-16 22:07:20 +01:00
|
|
|
)
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
from app.timetracking.backend.audit import audit
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EconomicExportService:
|
|
|
|
|
"""
|
|
|
|
|
e-conomic integration for Time Tracking Module.
|
|
|
|
|
|
|
|
|
|
🔒 SAFETY-FIRST service - all writes controlled by flags.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.api_url = settings.ECONOMIC_API_URL
|
|
|
|
|
self.app_secret_token = settings.ECONOMIC_APP_SECRET_TOKEN
|
|
|
|
|
self.agreement_grant_token = settings.ECONOMIC_AGREEMENT_GRANT_TOKEN
|
|
|
|
|
|
|
|
|
|
# Safety flags
|
|
|
|
|
self.read_only = settings.TIMETRACKING_ECONOMIC_READ_ONLY
|
|
|
|
|
self.dry_run = settings.TIMETRACKING_ECONOMIC_DRY_RUN
|
|
|
|
|
self.export_type = settings.TIMETRACKING_EXPORT_TYPE
|
|
|
|
|
|
|
|
|
|
# Log safety status
|
|
|
|
|
if self.read_only:
|
|
|
|
|
logger.warning("🔒 TIMETRACKING e-conomic READ-ONLY mode: Enabled")
|
|
|
|
|
if self.dry_run:
|
|
|
|
|
logger.warning("🏃 TIMETRACKING e-conomic DRY-RUN mode: Enabled")
|
|
|
|
|
|
|
|
|
|
if not self.read_only:
|
|
|
|
|
logger.error("⚠️ WARNING: TIMETRACKING e-conomic READ-ONLY disabled!")
|
|
|
|
|
|
|
|
|
|
def _get_headers(self) -> Dict[str, str]:
|
|
|
|
|
"""Get e-conomic API headers"""
|
|
|
|
|
return {
|
|
|
|
|
'X-AppSecretToken': self.app_secret_token,
|
|
|
|
|
'X-AgreementGrantToken': self.agreement_grant_token,
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def _check_write_permission(self, operation: str) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
Check om write operation er tilladt.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
True hvis operationen må udføres, False hvis blokeret
|
|
|
|
|
"""
|
|
|
|
|
if self.read_only:
|
|
|
|
|
logger.error(f"🚫 BLOCKED: {operation} - READ_ONLY mode enabled")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
if self.dry_run:
|
|
|
|
|
logger.warning(f"🏃 DRY-RUN: {operation} - Would execute but not sending")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
logger.warning(f"⚠️ EXECUTING WRITE: {operation}")
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
async def test_connection(self) -> bool:
|
|
|
|
|
"""Test e-conomic connection"""
|
|
|
|
|
try:
|
|
|
|
|
logger.info("🔍 Testing e-conomic connection...")
|
|
|
|
|
|
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
|
|
async with session.get(
|
|
|
|
|
f"{self.api_url}/self",
|
|
|
|
|
headers=self._get_headers(),
|
|
|
|
|
timeout=aiohttp.ClientTimeout(total=10)
|
|
|
|
|
) as response:
|
|
|
|
|
if response.status == 200:
|
|
|
|
|
logger.info("✅ e-conomic connection successful")
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
logger.error(f"❌ e-conomic connection failed: {response.status}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"❌ e-conomic connection error: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
2025-12-15 12:28:12 +01:00
|
|
|
async def check_draft_exists(self, draft_id: int) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
Tjek om en draft order stadig eksisterer i e-conomic.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
draft_id: e-conomic draft order nummer
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
True hvis draft findes, False hvis slettet eller ikke fundet
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
logger.info(f"🔍 Checking if draft {draft_id} exists in e-conomic...")
|
|
|
|
|
|
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
|
|
async with session.get(
|
|
|
|
|
f"{self.api_url}/orders/drafts/{draft_id}",
|
|
|
|
|
headers=self._get_headers(),
|
|
|
|
|
timeout=aiohttp.ClientTimeout(total=10)
|
|
|
|
|
) as response:
|
|
|
|
|
if response.status == 200:
|
|
|
|
|
logger.info(f"✅ Draft {draft_id} exists in e-conomic")
|
|
|
|
|
return True
|
|
|
|
|
elif response.status == 404:
|
|
|
|
|
logger.info(f"✅ Draft {draft_id} NOT found in e-conomic (deleted)")
|
|
|
|
|
return False
|
|
|
|
|
else:
|
|
|
|
|
error_text = await response.text()
|
|
|
|
|
logger.error(f"❌ e-conomic check failed ({response.status}): {error_text}")
|
|
|
|
|
raise Exception(f"e-conomic API error: {response.status}")
|
|
|
|
|
|
|
|
|
|
except aiohttp.ClientError as e:
|
|
|
|
|
logger.error(f"❌ e-conomic connection error: {e}")
|
|
|
|
|
raise Exception(f"Kunne ikke forbinde til e-conomic: {str(e)}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"❌ Draft check error: {e}")
|
|
|
|
|
raise
|
|
|
|
|
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
async def export_order(
|
|
|
|
|
self,
|
|
|
|
|
request: TModuleEconomicExportRequest,
|
|
|
|
|
user_id: Optional[int] = None
|
|
|
|
|
) -> TModuleEconomicExportResult:
|
|
|
|
|
"""
|
|
|
|
|
Eksporter ordre til e-conomic som draft order.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
request: Export request med order_id
|
|
|
|
|
user_id: ID på brugeren der eksporterer
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Export result med success status
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# Hent ordre med linjer
|
|
|
|
|
order_query = """
|
|
|
|
|
SELECT o.*, c.name as customer_name, c.vtiger_id as customer_vtiger_id
|
|
|
|
|
FROM tmodule_orders o
|
|
|
|
|
JOIN tmodule_customers c ON o.customer_id = c.id
|
|
|
|
|
WHERE o.id = %s
|
|
|
|
|
"""
|
2025-12-16 15:36:11 +01:00
|
|
|
order = execute_query_single(order_query, (request.order_id,))
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
|
|
|
|
|
if not order:
|
|
|
|
|
raise HTTPException(status_code=404, detail="Order not found")
|
|
|
|
|
|
feat: Implement email processing system with scheduler, fetching, classification, and rule matching
- Added EmailProcessorService to orchestrate email workflow: fetching, saving, classifying, and matching rules.
- Introduced EmailScheduler for background processing of emails every 5 minutes.
- Developed EmailService to handle email fetching from IMAP and Microsoft Graph API.
- Created database migration for email system, including tables for email messages, rules, attachments, and analysis.
- Implemented AI classification and extraction for invoices and time confirmations.
- Added logging for better traceability and error handling throughout the email processing pipeline.
2025-12-11 02:31:29 +01:00
|
|
|
# Check if order is posted (locked)
|
|
|
|
|
if order['status'] == 'posted':
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=403,
|
|
|
|
|
detail=f"Ordre er bogført til e-conomic og kan ikke ændres. e-conomic ordre nr.: {order.get('economic_order_number')}"
|
|
|
|
|
)
|
|
|
|
|
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
# Check if already exported
|
|
|
|
|
if order['economic_draft_id'] and not request.force:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=400,
|
|
|
|
|
detail=f"Order already exported (draft ID: {order['economic_draft_id']}). Use force=true to re-export."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Hent ordre-linjer
|
|
|
|
|
lines_query = """
|
|
|
|
|
SELECT * FROM tmodule_order_lines
|
|
|
|
|
WHERE order_id = %s
|
|
|
|
|
ORDER BY line_number
|
|
|
|
|
"""
|
2025-12-17 07:56:33 +01:00
|
|
|
lines = execute_query(lines_query, (request.order_id,))
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
|
|
|
|
|
if not lines:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=400,
|
|
|
|
|
detail="Order has no lines"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Log export start
|
|
|
|
|
audit.log_export_started(
|
|
|
|
|
order_id=request.order_id,
|
|
|
|
|
user_id=user_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Check safety flags
|
|
|
|
|
operation = f"Export order {request.order_id} to e-conomic"
|
|
|
|
|
|
|
|
|
|
if not self._check_write_permission(operation):
|
|
|
|
|
# Dry-run or read-only mode
|
|
|
|
|
result = TModuleEconomicExportResult(
|
|
|
|
|
success=True,
|
|
|
|
|
dry_run=True,
|
|
|
|
|
order_id=request.order_id,
|
|
|
|
|
economic_draft_id=None,
|
|
|
|
|
economic_order_number=None,
|
|
|
|
|
message=f"DRY-RUN: Would export order {order['order_number']} to e-conomic",
|
|
|
|
|
details={
|
|
|
|
|
"order_number": order['order_number'],
|
|
|
|
|
"customer_name": order['customer_name'],
|
|
|
|
|
"total_amount": float(order['total_amount']),
|
|
|
|
|
"line_count": len(lines),
|
|
|
|
|
"read_only": self.read_only,
|
|
|
|
|
"dry_run": self.dry_run
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Log dry-run completion
|
|
|
|
|
audit.log_export_completed(
|
|
|
|
|
order_id=request.order_id,
|
|
|
|
|
economic_draft_id=None,
|
|
|
|
|
economic_order_number=None,
|
|
|
|
|
dry_run=True,
|
|
|
|
|
user_id=user_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# REAL EXPORT (kun hvis safety flags er disabled)
|
|
|
|
|
logger.warning(f"⚠️ REAL EXPORT STARTING for order {request.order_id}")
|
|
|
|
|
|
2025-12-13 12:06:28 +01:00
|
|
|
# Hent e-conomic customer number fra Hub customers via hub_customer_id
|
2025-12-10 18:29:13 +01:00
|
|
|
customer_number_query = """
|
2025-12-13 12:06:28 +01:00
|
|
|
SELECT c.economic_customer_number
|
|
|
|
|
FROM tmodule_customers tc
|
|
|
|
|
LEFT JOIN customers c ON tc.hub_customer_id = c.id
|
|
|
|
|
WHERE tc.id = %s
|
2025-12-10 18:29:13 +01:00
|
|
|
"""
|
2025-12-17 07:56:33 +01:00
|
|
|
customer_data = execute_query_single(customer_number_query, (order['customer_id'],))
|
2025-12-10 18:29:13 +01:00
|
|
|
|
|
|
|
|
if not customer_data or not customer_data.get('economic_customer_number'):
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=400,
|
2025-12-13 12:06:28 +01:00
|
|
|
detail=f"Customer {order['customer_name']} has no e-conomic customer number. Link customer to Hub customer first."
|
2025-12-10 18:29:13 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
customer_number = customer_data['economic_customer_number']
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
|
|
|
|
|
# Build e-conomic draft order payload
|
|
|
|
|
economic_payload = {
|
2025-12-10 18:29:13 +01:00
|
|
|
"date": order['order_date'].isoformat() if hasattr(order['order_date'], 'isoformat') else str(order['order_date']),
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
"currency": "DKK",
|
|
|
|
|
"exchangeRate": 100,
|
2025-12-10 18:29:13 +01:00
|
|
|
"customer": {
|
|
|
|
|
"customerNumber": customer_number
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
},
|
2025-12-10 18:29:13 +01:00
|
|
|
"recipient": {
|
|
|
|
|
"name": order['customer_name'],
|
|
|
|
|
"vatZone": {
|
|
|
|
|
"vatZoneNumber": 1 # Domestic Denmark
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
}
|
2025-12-10 18:29:13 +01:00
|
|
|
},
|
|
|
|
|
"paymentTerms": {
|
|
|
|
|
"paymentTermsNumber": 1 # Default payment terms
|
|
|
|
|
},
|
|
|
|
|
"layout": {
|
2025-12-23 01:04:44 +01:00
|
|
|
"layoutNumber": settings.TIMETRACKING_ECONOMIC_LAYOUT
|
2025-12-10 18:29:13 +01:00
|
|
|
},
|
|
|
|
|
"notes": {
|
|
|
|
|
"heading": f"Tidsregistrering - {order['order_number']}"
|
|
|
|
|
},
|
|
|
|
|
"lines": []
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-10 18:29:13 +01:00
|
|
|
# Add notes if present
|
|
|
|
|
if order.get('notes'):
|
|
|
|
|
economic_payload['notes']['textLine1'] = order['notes']
|
|
|
|
|
|
|
|
|
|
# Build order lines
|
|
|
|
|
for idx, line in enumerate(lines, start=1):
|
feat: Implement email processing system with scheduler, fetching, classification, and rule matching
- Added EmailProcessorService to orchestrate email workflow: fetching, saving, classifying, and matching rules.
- Introduced EmailScheduler for background processing of emails every 5 minutes.
- Developed EmailService to handle email fetching from IMAP and Microsoft Graph API.
- Created database migration for email system, including tables for email messages, rules, attachments, and analysis.
- Implemented AI classification and extraction for invoices and time confirmations.
- Added logging for better traceability and error handling throughout the email processing pipeline.
2025-12-11 02:31:29 +01:00
|
|
|
# Format: "CC0042. 3 timer 1200,- 3600 / Fejlsøgning / 27.05.2025 - Kontaktnavn"
|
|
|
|
|
# Extract case number and title from existing description
|
|
|
|
|
desc_parts = line['description'].split(' - ', 1)
|
|
|
|
|
case_number = desc_parts[0] if desc_parts else ""
|
|
|
|
|
case_title = desc_parts[1] if len(desc_parts) > 1 else line['description']
|
|
|
|
|
|
|
|
|
|
# Build formatted description
|
|
|
|
|
hours = float(line['quantity'])
|
|
|
|
|
price = float(line['unit_price'])
|
|
|
|
|
total = hours * price
|
|
|
|
|
|
|
|
|
|
# Format date (Danish format DD.MM.YYYY)
|
|
|
|
|
date_str = ""
|
|
|
|
|
if line.get('time_date'):
|
|
|
|
|
time_date = line['time_date']
|
|
|
|
|
if isinstance(time_date, str):
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
time_date = datetime.fromisoformat(time_date).date()
|
|
|
|
|
date_str = time_date.strftime("%d.%m.%Y")
|
|
|
|
|
|
|
|
|
|
# Build description
|
|
|
|
|
contact_part = f" - {line['case_contact']}" if line.get('case_contact') else ""
|
|
|
|
|
travel_marker = " - (Udkørsel)" if line.get('is_travel') else ""
|
|
|
|
|
formatted_desc = f"{case_number}. {hours} timer {price:,.0f},- {total:,.0f} / {case_title} / {date_str}{contact_part}{travel_marker}"
|
|
|
|
|
|
2025-12-10 18:29:13 +01:00
|
|
|
economic_line = {
|
|
|
|
|
"lineNumber": idx,
|
|
|
|
|
"sortKey": idx,
|
feat: Implement email processing system with scheduler, fetching, classification, and rule matching
- Added EmailProcessorService to orchestrate email workflow: fetching, saving, classifying, and matching rules.
- Introduced EmailScheduler for background processing of emails every 5 minutes.
- Developed EmailService to handle email fetching from IMAP and Microsoft Graph API.
- Created database migration for email system, including tables for email messages, rules, attachments, and analysis.
- Implemented AI classification and extraction for invoices and time confirmations.
- Added logging for better traceability and error handling throughout the email processing pipeline.
2025-12-11 02:31:29 +01:00
|
|
|
"description": formatted_desc,
|
|
|
|
|
"quantity": hours,
|
|
|
|
|
"unitNetPrice": price,
|
|
|
|
|
"product": {
|
2025-12-23 01:11:58 +01:00
|
|
|
"productNumber": line.get('product_number') or settings.TIMETRACKING_ECONOMIC_PRODUCT
|
feat: Implement email processing system with scheduler, fetching, classification, and rule matching
- Added EmailProcessorService to orchestrate email workflow: fetching, saving, classifying, and matching rules.
- Introduced EmailScheduler for background processing of emails every 5 minutes.
- Developed EmailService to handle email fetching from IMAP and Microsoft Graph API.
- Created database migration for email system, including tables for email messages, rules, attachments, and analysis.
- Implemented AI classification and extraction for invoices and time confirmations.
- Added logging for better traceability and error handling throughout the email processing pipeline.
2025-12-11 02:31:29 +01:00
|
|
|
},
|
2025-12-10 18:29:13 +01:00
|
|
|
"unit": {
|
feat: Implement email processing system with scheduler, fetching, classification, and rule matching
- Added EmailProcessorService to orchestrate email workflow: fetching, saving, classifying, and matching rules.
- Introduced EmailScheduler for background processing of emails every 5 minutes.
- Developed EmailService to handle email fetching from IMAP and Microsoft Graph API.
- Created database migration for email system, including tables for email messages, rules, attachments, and analysis.
- Implemented AI classification and extraction for invoices and time confirmations.
- Added logging for better traceability and error handling throughout the email processing pipeline.
2025-12-11 02:31:29 +01:00
|
|
|
"unitNumber": 2 # timer (unit 2 in e-conomic)
|
2025-12-10 18:29:13 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Add discount if present
|
|
|
|
|
if line.get('discount_percentage'):
|
|
|
|
|
economic_line['discountPercentage'] = float(line['discount_percentage'])
|
|
|
|
|
|
|
|
|
|
economic_payload['lines'].append(economic_line)
|
|
|
|
|
|
|
|
|
|
logger.info(f"📤 Sending to e-conomic: {json.dumps(economic_payload, indent=2, default=str)}")
|
|
|
|
|
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
# Call e-conomic API
|
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
|
|
async with session.post(
|
|
|
|
|
f"{self.api_url}/orders/drafts",
|
|
|
|
|
headers=self._get_headers(),
|
|
|
|
|
json=economic_payload,
|
|
|
|
|
timeout=aiohttp.ClientTimeout(total=30)
|
|
|
|
|
) as response:
|
2025-12-10 18:29:13 +01:00
|
|
|
response_text = await response.text()
|
|
|
|
|
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
if response.status not in [200, 201]:
|
2025-12-10 18:29:13 +01:00
|
|
|
logger.error(f"❌ e-conomic export failed: {response.status}")
|
|
|
|
|
logger.error(f"Response: {response_text}")
|
|
|
|
|
logger.error(f"Payload: {json.dumps(economic_payload, indent=2, default=str)}")
|
|
|
|
|
|
|
|
|
|
# Try to parse error message
|
|
|
|
|
try:
|
|
|
|
|
error_data = json.loads(response_text)
|
|
|
|
|
error_msg = error_data.get('message', response_text)
|
|
|
|
|
|
|
|
|
|
# Parse detailed validation errors if present
|
|
|
|
|
if 'errors' in error_data:
|
|
|
|
|
error_details = []
|
|
|
|
|
for entity, entity_errors in error_data['errors'].items():
|
|
|
|
|
if isinstance(entity_errors, dict) and 'errors' in entity_errors:
|
|
|
|
|
for err in entity_errors['errors']:
|
|
|
|
|
field = err.get('propertyName', entity)
|
|
|
|
|
msg = err.get('errorMessage', err.get('message', 'Unknown'))
|
|
|
|
|
error_details.append(f"{field}: {msg}")
|
|
|
|
|
if error_details:
|
|
|
|
|
error_msg = '; '.join(error_details)
|
|
|
|
|
except:
|
|
|
|
|
error_msg = response_text
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
|
|
|
|
|
# Log failed export
|
|
|
|
|
audit.log_export_failed(
|
|
|
|
|
order_id=request.order_id,
|
2025-12-10 18:29:13 +01:00
|
|
|
error=error_msg,
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
user_id=user_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=response.status,
|
2025-12-10 18:29:13 +01:00
|
|
|
detail=f"e-conomic API error: {error_msg}"
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result_data = await response.json()
|
2025-12-10 18:29:13 +01:00
|
|
|
logger.info(f"✅ e-conomic response: {json.dumps(result_data, indent=2, default=str)}")
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
|
feat: Implement email processing system with scheduler, fetching, classification, and rule matching
- Added EmailProcessorService to orchestrate email workflow: fetching, saving, classifying, and matching rules.
- Introduced EmailScheduler for background processing of emails every 5 minutes.
- Developed EmailService to handle email fetching from IMAP and Microsoft Graph API.
- Created database migration for email system, including tables for email messages, rules, attachments, and analysis.
- Implemented AI classification and extraction for invoices and time confirmations.
- Added logging for better traceability and error handling throughout the email processing pipeline.
2025-12-11 02:31:29 +01:00
|
|
|
# e-conomic returnerer orderNumber direkte for draft orders
|
|
|
|
|
order_number = result_data.get('orderNumber') or result_data.get('draftOrderNumber')
|
|
|
|
|
economic_draft_id = int(order_number) if order_number else None
|
|
|
|
|
economic_order_number = str(order_number) if order_number else None
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
|
feat: Implement email processing system with scheduler, fetching, classification, and rule matching
- Added EmailProcessorService to orchestrate email workflow: fetching, saving, classifying, and matching rules.
- Introduced EmailScheduler for background processing of emails every 5 minutes.
- Developed EmailService to handle email fetching from IMAP and Microsoft Graph API.
- Created database migration for email system, including tables for email messages, rules, attachments, and analysis.
- Implemented AI classification and extraction for invoices and time confirmations.
- Added logging for better traceability and error handling throughout the email processing pipeline.
2025-12-11 02:31:29 +01:00
|
|
|
# Update order med e-conomic IDs og status = posted (bogført)
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
execute_update(
|
|
|
|
|
"""UPDATE tmodule_orders
|
|
|
|
|
SET economic_draft_id = %s,
|
|
|
|
|
economic_order_number = %s,
|
|
|
|
|
exported_at = CURRENT_TIMESTAMP,
|
|
|
|
|
exported_by = %s,
|
feat: Implement email processing system with scheduler, fetching, classification, and rule matching
- Added EmailProcessorService to orchestrate email workflow: fetching, saving, classifying, and matching rules.
- Introduced EmailScheduler for background processing of emails every 5 minutes.
- Developed EmailService to handle email fetching from IMAP and Microsoft Graph API.
- Created database migration for email system, including tables for email messages, rules, attachments, and analysis.
- Implemented AI classification and extraction for invoices and time confirmations.
- Added logging for better traceability and error handling throughout the email processing pipeline.
2025-12-11 02:31:29 +01:00
|
|
|
status = 'posted'
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
WHERE id = %s""",
|
|
|
|
|
(economic_draft_id, economic_order_number, user_id, request.order_id)
|
|
|
|
|
)
|
|
|
|
|
|
feat: Implement email processing system with scheduler, fetching, classification, and rule matching
- Added EmailProcessorService to orchestrate email workflow: fetching, saving, classifying, and matching rules.
- Introduced EmailScheduler for background processing of emails every 5 minutes.
- Developed EmailService to handle email fetching from IMAP and Microsoft Graph API.
- Created database migration for email system, including tables for email messages, rules, attachments, and analysis.
- Implemented AI classification and extraction for invoices and time confirmations.
- Added logging for better traceability and error handling throughout the email processing pipeline.
2025-12-11 02:31:29 +01:00
|
|
|
# Marker time entries som billed
|
|
|
|
|
execute_update(
|
|
|
|
|
"""UPDATE tmodule_times
|
|
|
|
|
SET status = 'billed'
|
|
|
|
|
WHERE id IN (
|
|
|
|
|
SELECT UNNEST(time_entry_ids)
|
|
|
|
|
FROM tmodule_order_lines
|
|
|
|
|
WHERE order_id = %s
|
|
|
|
|
)""",
|
|
|
|
|
(request.order_id,)
|
|
|
|
|
)
|
|
|
|
|
|
feat(timetracking): Implement time tracking module with frontend views, HTML templates, and database migrations
- Added FastAPI router for time tracking views including dashboard, wizard, and orders.
- Created HTML templates for the time tracking wizard with responsive design and Bootstrap integration.
- Developed SQL migration script for the time tracking module, including tables for customers, cases, time entries, orders, and audit logs.
- Introduced a script to list all registered routes, focusing on time tracking routes.
- Added test script to verify route registration and specifically check for time tracking routes.
2025-12-09 22:46:30 +01:00
|
|
|
# Log successful export
|
|
|
|
|
audit.log_export_completed(
|
|
|
|
|
order_id=request.order_id,
|
|
|
|
|
economic_draft_id=economic_draft_id,
|
|
|
|
|
economic_order_number=economic_order_number,
|
|
|
|
|
dry_run=False,
|
|
|
|
|
user_id=user_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
logger.info(f"✅ Exported order {request.order_id} → e-conomic draft {economic_draft_id}")
|
|
|
|
|
|
|
|
|
|
return TModuleEconomicExportResult(
|
|
|
|
|
success=True,
|
|
|
|
|
dry_run=False,
|
|
|
|
|
order_id=request.order_id,
|
|
|
|
|
economic_draft_id=economic_draft_id,
|
|
|
|
|
economic_order_number=economic_order_number,
|
|
|
|
|
message=f"Successfully exported to e-conomic draft {economic_draft_id}",
|
|
|
|
|
details=result_data
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"❌ Error exporting order: {e}")
|
|
|
|
|
|
|
|
|
|
# Log failed export
|
|
|
|
|
audit.log_export_failed(
|
|
|
|
|
order_id=request.order_id,
|
|
|
|
|
error=str(e),
|
|
|
|
|
user_id=user_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Singleton instance
|
|
|
|
|
economic_service = EconomicExportService()
|