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
This commit is contained in:
parent
8ec457bba1
commit
39b49d4d54
@ -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,19 +238,33 @@ 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:
|
||||||
cell = product_matrix[product_number][year_month]
|
for month_key in all_months:
|
||||||
cell["amount"] = amount # Take last amount (or sum if multiple?)
|
product_matrix[product_number][month_key] = {
|
||||||
cell["status"] = self._determine_status(invoice_status)
|
"amount": 0.0,
|
||||||
cell["invoice_number"] = invoice_number
|
"status": "missing",
|
||||||
cell["period_from"] = period_from.isoformat().split('T')[0]
|
"invoice_number": None,
|
||||||
cell["period_to"] = period_to.isoformat().split('T')[0]
|
"period_from": None,
|
||||||
cell["period_label"] = period_label
|
"period_to": None,
|
||||||
|
"period_label": None
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update cell
|
||||||
|
cell = product_matrix[product_number][year_month]
|
||||||
|
cell["amount"] = amount # Take last amount (or sum if multiple?)
|
||||||
|
cell["status"] = self._determine_status(invoice_status)
|
||||||
|
cell["invoice_number"] = invoice_number
|
||||||
|
cell["period_from"] = period_from.isoformat().split('T')[0]
|
||||||
|
cell["period_to"] = period_to.isoformat().split('T')[0]
|
||||||
|
cell["period_label"] = period_label
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ Error aggregating data: {e}")
|
logger.error(f"❌ Error aggregating data: {e}")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user