Compare commits

..

2 Commits

Author SHA1 Message Date
Christian
8ec12819f7 chore: bump version to 1.3.127 2026-01-27 00:34:57 +01:00
Christian
39b49d4d54 feat: Include invoice title keywords in subscription matrix filter
- Match 'periode'/'abonnement' in invoice notes/title fields
- Allows period-only titles to include line items
- Keeps line-level keyword filtering
2026-01-27 00:34:44 +01:00
2 changed files with 49 additions and 33 deletions

View File

@ -1 +1 @@
1.3.126 1.3.127

View File

@ -149,30 +149,9 @@ class SubscriptionMatrixService:
product_matrix = defaultdict(dict) product_matrix = defaultdict(dict)
product_names = {} # Cache product names product_names = {} # Cache product names
# Initialize all products with empty cells for all months # Initialize products only if they have data within the selected month range
try: try:
for invoice in invoices: # Fill in data from invoices, but only for months within range
lines = invoice.get('lines', [])
for line in lines:
product_number = line.get('product', {}).get('productNumber')
product_name = line.get('product', {}).get('name') or line.get('description')
if product_number and product_number not in product_names:
product_names[product_number] = product_name or f"Product {product_number}"
# Initialize all products with all months (empty)
for product_number in product_names.keys():
for year_month in all_months:
product_matrix[product_number][year_month] = {
"amount": 0.0,
"status": "missing",
"invoice_number": None,
"period_from": None,
"period_to": None,
"period_label": None
}
# Now fill in data from invoices
for invoice in invoices: for invoice in invoices:
invoice_number = invoice.get('bookedInvoiceNumber') or invoice.get('draftInvoiceNumber') invoice_number = invoice.get('bookedInvoiceNumber') or invoice.get('draftInvoiceNumber')
# Determine status based on invoice type/endpoint it came from # Determine status based on invoice type/endpoint it came from
@ -185,12 +164,35 @@ class SubscriptionMatrixService:
elif invoice.get('draftInvoiceNumber'): elif invoice.get('draftInvoiceNumber'):
invoice_status = 'draft' invoice_status = 'draft'
invoice_date = invoice.get('date') invoice_date = invoice.get('date')
# Build invoice-level text (title/notes) for keyword matching
invoice_text_parts = []
notes = invoice.get('notes')
if isinstance(notes, dict):
for key in ["heading", "textLine1", "textLine2", "textLine3", "textLine4", "textLine5"]:
val = notes.get(key)
if val:
invoice_text_parts.append(str(val))
elif notes:
invoice_text_parts.append(str(notes))
other_ref = invoice.get('otherReference') or invoice.get('orderNumberDb')
if other_ref:
invoice_text_parts.append(str(other_ref))
invoice_text = " ".join(invoice_text_parts).lower()
# Process invoice lines # Process invoice lines
lines = invoice.get('lines', []) lines = invoice.get('lines', [])
for line in lines: for line in lines:
product_number = line.get('product', {}).get('productNumber') product_number = line.get('product', {}).get('productNumber')
product_name = line.get('product', {}).get('name') or line.get('description') 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
if (
"periode" not in line_description
and "abonnement" not in line_description
and "periode" not in invoice_text
and "abonnement" not in invoice_text
):
continue
if not product_number: if not product_number:
logger.debug(f"Skipping line without product number: {line}") logger.debug(f"Skipping line without product number: {line}")
@ -236,12 +238,26 @@ class SubscriptionMatrixService:
# Determine month key (use first month if multi-month) # Determine month key (use first month if multi-month)
year_month = period_from.strftime('%Y-%m') year_month = period_from.strftime('%Y-%m')
if year_month not in all_months:
# Skip lines outside the displayed month range
continue
# Calculate period label # Calculate period label
period_label = self._format_period_label(period_from, period_to) period_label = self._format_period_label(period_from, period_to)
# Update cell (it should already exist from initialization) # Initialize product if first time within range
if year_month in product_matrix.get(product_number, {}): if product_number not in product_matrix:
for month_key in all_months:
product_matrix[product_number][month_key] = {
"amount": 0.0,
"status": "missing",
"invoice_number": None,
"period_from": None,
"period_to": None,
"period_label": None
}
# Update cell
cell = product_matrix[product_number][year_month] cell = product_matrix[product_number][year_month]
cell["amount"] = amount # Take last amount (or sum if multiple?) cell["amount"] = amount # Take last amount (or sum if multiple?)
cell["status"] = self._determine_status(invoice_status) cell["status"] = self._determine_status(invoice_status)