From 501032efcd780696c685c553dbdd954afcc59a6a Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 27 Jan 2026 01:19:08 +0100 Subject: [PATCH] fix: Include period-based lines and normalize product grouping - Include lines with period fields even without keywords - Normalize product names to group similar lines - Improves monthly Hosting - AR2 visibility --- app/services/subscription_matrix.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/app/services/subscription_matrix.py b/app/services/subscription_matrix.py index 58fd289..e3444d8 100644 --- a/app/services/subscription_matrix.py +++ b/app/services/subscription_matrix.py @@ -191,20 +191,24 @@ class SubscriptionMatrixService: product_number = line.get('product', {}).get('productNumber') product_name = line.get('product', {}).get('name') or line.get('description') line_description = (line.get('description') or product_name or "").lower() - # Only include lines that indicate a period or subscription + + line_period = self._extract_period_from_text(line_description) or invoice_period + has_period = bool(line.get('period', {}).get('from') if line.get('period') else None) or bool(line_period) + + # Only include lines that indicate a period or subscription or explicit period fields if ( "periode" not in line_description and "abonnement" not in line_description and "periode" not in invoice_text and "abonnement" not in invoice_text + and not has_period ): continue - line_period = self._extract_period_from_text(line_description) or invoice_period if not product_number and not product_name: logger.debug(f"Skipping line without product number: {line}") continue - product_key = product_number or (product_name or "").strip().lower() + product_key = product_number or self._normalize_product_key(product_name or "") # Cache product name if product_key not in product_names: @@ -355,6 +359,19 @@ class SubscriptionMatrixService: next_month = dt.replace(day=28) + timedelta(days=4) return next_month - timedelta(days=next_month.day) + @staticmethod + def _normalize_product_key(name: str) -> str: + """Normalize product names to keep similar lines on the same row.""" + base = (name or "").lower().strip() + # Remove period phrases like "periode" and trailing date ranges + for token in ["periode", "abonnement"]: + if token in base: + base = base.split(token)[0].strip() + # Collapse multiple spaces + while " " in base: + base = base.replace(" ", " ") + return base or name.lower().strip() + @staticmethod def _extract_period_from_text(text: str) -> Optional[datetime]: """Extract month/year from invoice title/notes text (e.g., 'Periode May 2025')."""