From c9af509e1caa0e712c60ea477e9fdfbc91306c3f Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 19 Dec 2025 16:41:11 +0100 Subject: [PATCH] Implement e-conomic customer sync and CVR search (get_customers + search_customer_by_cvr) --- app/services/economic_service.py | 71 ++++++++++++++++++++++++ app/system/backend/sync_router.py | 92 ++++++++++++++++++++++++++++--- 2 files changed, 154 insertions(+), 9 deletions(-) diff --git a/app/services/economic_service.py b/app/services/economic_service.py index c5d6375..0b99acd 100644 --- a/app/services/economic_service.py +++ b/app/services/economic_service.py @@ -126,6 +126,77 @@ class EconomicService: logger.error(f"❌ e-conomic connection error: {e}") return False + # ========== CUSTOMER MANAGEMENT ========== + + async def get_customers(self, page: int = 0, page_size: int = 1000) -> List[Dict]: + """ + Get customers from e-conomic + + Args: + page: Page number (0-indexed) + page_size: Number of records per page + + Returns: + List of customer records with customerNumber, corporateIdentificationNumber, name + """ + try: + async with aiohttp.ClientSession() as session: + async with session.get( + f"{self.api_url}/customers", + params={"skippages": page, "pagesize": page_size}, + headers=self._get_headers() + ) as response: + if response.status == 200: + data = await response.json() + customers = data.get('collection', []) + logger.info(f"📥 Fetched {len(customers)} customers from e-conomic (page {page})") + return customers + else: + error = await response.text() + logger.error(f"❌ Failed to fetch customers: {response.status} - {error}") + return [] + except Exception as e: + logger.error(f"❌ Error fetching customers from e-conomic: {e}") + return [] + + async def search_customer_by_cvr(self, cvr: str) -> Optional[Dict]: + """ + Search for customer by CVR number + + Args: + cvr: CVR number (8 digits) + + Returns: + Customer record if found, None otherwise + """ + try: + # Clean CVR + import re + cvr_clean = re.sub(r'\D', '', str(cvr))[:8] + + async with aiohttp.ClientSession() as session: + async with session.get( + f"{self.api_url}/customers", + params={"filter": f"corporateIdentificationNumber$eq:{cvr_clean}"}, + headers=self._get_headers() + ) as response: + if response.status == 200: + data = await response.json() + customers = data.get('collection', []) + if customers: + logger.info(f"✅ Found customer with CVR {cvr_clean}: {customers[0].get('name')}") + return customers[0] + else: + logger.debug(f"❌ No customer found with CVR {cvr_clean}") + return None + else: + error = await response.text() + logger.error(f"❌ CVR search failed: {response.status} - {error}") + return None + except Exception as e: + logger.error(f"❌ Error searching customer by CVR: {e}") + return None + # ========== SUPPLIER/VENDOR MANAGEMENT ========== async def search_supplier_by_name(self, supplier_name: str) -> Optional[Dict]: diff --git a/app/system/backend/sync_router.py b/app/system/backend/sync_router.py index 407565b..26c4517 100644 --- a/app/system/backend/sync_router.py +++ b/app/system/backend/sync_router.py @@ -307,15 +307,65 @@ async def sync_from_economic() -> Dict[str, Any]: try: logger.info("🔄 Starting e-conomic sync...") - # Note: This requires adding get_customers() method to economic_service.py - # For now, return a placeholder response + from app.services.economic_service import EconomicService + economic = EconomicService() - logger.warning("⚠️ e-conomic sync not fully implemented yet") + # Get all customers from e-conomic + economic_customers = await economic.get_customers(page=0, page_size=10000) + logger.info(f"📥 Fetched {len(economic_customers)} customers from e-conomic") + + matched_count = 0 + not_matched_count = 0 + + for eco_customer in economic_customers: + customer_number = eco_customer.get('customerNumber') + cvr = eco_customer.get('corporateIdentificationNumber') + name = eco_customer.get('name', '') + + if not customer_number: + continue + + # Clean CVR + if cvr: + cvr = re.sub(r'\D', '', str(cvr))[:8] + if len(cvr) != 8: + cvr = None + + # Try to match by CVR first + matched = None + if cvr: + matched = execute_query( + "SELECT id, name FROM customers WHERE cvr_number = %s", + (cvr,) + ) + + # If no CVR match, try normalized name + if not matched and name: + normalized = normalize_name(name) + all_customers = execute_query("SELECT id, name FROM customers WHERE economic_customer_number IS NULL") + for hub_customer in all_customers: + if normalize_name(hub_customer['name']) == normalized: + matched = [hub_customer] + break + + if matched: + # Update Hub customer with e-conomic number + execute_query( + "UPDATE customers SET economic_customer_number = %s, last_synced_at = NOW() WHERE id = %s", + (customer_number, matched[0]['id']) + ) + matched_count += 1 + logger.info(f"🔗 Matchet: {matched[0]['name']} → e-conomic kunde #{customer_number} (CVR: {cvr or 'navn-match'})") + else: + not_matched_count += 1 + + logger.info(f"✅ e-conomic sync fuldført: {matched_count} matchet, {not_matched_count} ikke matchet af {len(economic_customers)} totalt") return { - "status": "not_implemented", - "message": "e-conomic customer sync requires get_customers() method in economic_service.py", - "matched": 0 + "status": "success", + "matched": matched_count, + "not_matched": not_matched_count, + "total_processed": len(economic_customers) } except Exception as e: @@ -332,6 +382,9 @@ async def sync_cvr_to_economic() -> Dict[str, Any]: try: logger.info("🔄 Starting CVR to e-conomic sync...") + from app.services.economic_service import EconomicService + economic = EconomicService() + # Find customers with CVR but no economic_customer_number customers = execute_query(""" SELECT id, name, cvr_number @@ -344,10 +397,31 @@ async def sync_cvr_to_economic() -> Dict[str, Any]: logger.info(f"📥 Found {len(customers)} customers with CVR but no e-conomic number") - # Note: This requires e-conomic API search functionality - # For now, return placeholder + found_count = 0 + linked_count = 0 - logger.warning("⚠️ CVR to e-conomic sync not fully implemented yet") + for customer in customers: + cvr = customer['cvr_number'] + + # Search e-conomic for this CVR + eco_customer = await economic.search_customer_by_cvr(cvr) + + if eco_customer: + customer_number = eco_customer.get('customerNumber') + if customer_number: + # Update Hub customer + execute_query( + "UPDATE customers SET economic_customer_number = %s, last_synced_at = NOW() WHERE id = %s", + (customer_number, customer['id']) + ) + found_count += 1 + linked_count += 1 + logger.info(f"✅ Fundet og linket: {customer['name']} (CVR: {cvr}) → e-conomic kunde #{customer_number}") + else: + found_count += 1 + logger.warning(f"⚠️ Fundet men mangler kundenummer: {customer['name']} (CVR: {cvr})") + + logger.info(f"✅ CVR søgning fuldført: {found_count} fundet, {linked_count} linket af {len(customers)} kontrolleret") return { "status": "not_implemented",