From ffffa8e004c0bca9b45237924e7b18c3ca6ae7ef Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 27 Jan 2026 07:21:02 +0100 Subject: [PATCH] feat: Fetch from multiple e-conomic endpoints v1.3.143: - Check drafts, booked, paid, unpaid endpoints - Deduplicate invoices by invoice number - Pagination support for each endpoint - Filter by customer + 13 months date after fetching --- VERSION | 2 +- app/services/economic_service.py | 89 +++++++++++++++++++------------- 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/VERSION b/VERSION index 07faf6b..feb928e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.142 \ No newline at end of file +1.3.143 \ No newline at end of file diff --git a/app/services/economic_service.py b/app/services/economic_service.py index 67a81b5..0414340 100644 --- a/app/services/economic_service.py +++ b/app/services/economic_service.py @@ -458,47 +458,64 @@ class EconomicService: logger.info(f"📅 Will filter invoices from {start_date} onwards (13 months for yearly billed items)") async with aiohttp.ClientSession() as session: - # Fetch from /invoices/drafts endpoint (covers active invoices) - # Then filter by customer in code + # Fetch from multiple endpoints to get all invoice types + # Then filter by customer and date in code all_invoices = [] - endpoint = f"{self.api_url}/invoices/drafts" - logger.info(f"📋 Fetching invoices from {endpoint}") + # Check drafts, booked, paid, unpaid endpoints + endpoints_to_try = [ + f"{self.api_url}/invoices/drafts", + f"{self.api_url}/invoices/booked", + f"{self.api_url}/invoices/paid", + f"{self.api_url}/invoices/unpaid", + ] - try: - # Pagination: Keep fetching until no more pages - page = 0 - while True: - async with session.get( - endpoint, - params={"pagesize": 1000, "skippages": page}, - headers=self._get_headers() - ) as response: - logger.info(f"🔍 [API] Response status from {endpoint} (page {page}): {response.status}") - - if response.status == 200: - data = await response.json() - invoices_from_endpoint = data.get('collection', []) - logger.info(f"✅ Fetched {len(invoices_from_endpoint)} invoices from {endpoint} page {page}") + for endpoint in endpoints_to_try: + logger.info(f"📋 Fetching invoices from {endpoint}") + + try: + # Pagination: Keep fetching until no more pages + page = 0 + while True: + async with session.get( + endpoint, + params={"pagesize": 1000, "skippages": page}, + headers=self._get_headers() + ) as response: + logger.info(f"🔍 [API] Response status from {endpoint} (page {page}): {response.status}") - if not invoices_from_endpoint: - # No more invoices on this page + if response.status == 200: + data = await response.json() + invoices_from_endpoint = data.get('collection', []) + logger.info(f"✅ Fetched {len(invoices_from_endpoint)} invoices from {endpoint} page {page}") + + if not invoices_from_endpoint: + # No more invoices on this page + break + + # Add invoices (track unique by invoice number to avoid duplicates) + existing_invoice_numbers = {inv.get('draftInvoiceNumber') or inv.get('bookedInvoiceNumber') for inv in all_invoices if inv.get('draftInvoiceNumber') or inv.get('bookedInvoiceNumber')} + + for inv in invoices_from_endpoint: + inv_num = inv.get('draftInvoiceNumber') or inv.get('bookedInvoiceNumber') + if inv_num and inv_num not in existing_invoice_numbers: + all_invoices.append(inv) + existing_invoice_numbers.add(inv_num) + elif not inv_num: + # Include if no invoice number + all_invoices.append(inv) + + # Check if we got full page (1000) - if so, there might be more pages + if len(invoices_from_endpoint) < 1000: + break # Last page + + page += 1 + else: + error_text = await response.text() + logger.warning(f"⚠️ Endpoint {endpoint} returned {response.status}: {error_text[:200]}") break - - # Add invoices - all_invoices.extend(invoices_from_endpoint) - - # Check if we got full page (1000) - if so, there might be more pages - if len(invoices_from_endpoint) < 1000: - break # Last page - - page += 1 - else: - error_text = await response.text() - logger.warning(f"⚠️ Endpoint {endpoint} returned {response.status}: {error_text[:200]}") - break - except Exception as e: - logger.error(f"❌ Error fetching invoices: {e}") + except Exception as e: + logger.error(f"❌ Error fetching from {endpoint}: {e}") logger.info(f"✅ Found {len(all_invoices)} total invoices")