release: v2.8.6 validate e-conomic order references

This commit is contained in:
Christian 2026-09-11 16:58:18 +02:00
parent 7c1a748012
commit 13f36a36c0
3 changed files with 55 additions and 2 deletions

View File

@ -0,0 +1,13 @@
# BMC Hub v2.8.6
## e-conomic ordreeksport
- Bruger kundens gyldige standardlayout fra e-conomic i stedet for et ugyldigt lokalt standardnummer.
- Kontrollerer produktnumre mod e-conomic inden eksport.
- Ukendte lokale produktnumre eksporteres som fritekstlinjer uden ugyldig produktreference.
- Bevarer produktreferencen, når produktet findes i e-conomic.
## Verifikation
- Fokuserede regressionstests består.
- Python-syntaks og diff-kontrol består.

View File

@ -1 +1 @@
2.8.5
2.8.6

View File

@ -2,6 +2,7 @@ import json
import logging
from datetime import date
from typing import Any, Dict, List, Optional
from urllib.parse import quote
import aiohttp
from fastapi import HTTPException
@ -195,6 +196,43 @@ class OrdreEconomicExportService:
detail="e-conomic-kunden mangler betalingsbetingelser eller momszone",
)
product_numbers = {
str(line.get("product", {}).get("productNumber") or "").strip()
for line in economic_lines
if line.get("product")
}
valid_product_numbers = set()
for product_number in product_numbers:
if not product_number:
continue
async with session.get(
f"{self.api_url}/products/{quote(product_number, safe='')}",
headers=self._headers(),
timeout=aiohttp.ClientTimeout(total=30),
) as product_response:
if product_response.status == 200:
valid_product_numbers.add(product_number)
elif product_response.status == 404:
logger.warning(
"e-conomic product %s does not exist; exporting matching lines as free text",
product_number,
)
else:
product_text = await product_response.text()
raise HTTPException(
status_code=502,
detail=_economic_error_message(product_response.status, product_text),
)
skipped_product_numbers = sorted(product_numbers - valid_product_numbers)
for line in economic_lines:
product_number = str(line.get("product", {}).get("productNumber") or "").strip()
if product_number and product_number not in valid_product_numbers:
line.pop("product", None)
customer_layout = economic_customer.get("layout") or {}
resolved_layout_number = customer_layout.get("layoutNumber") or self.default_layout
payload: Dict[str, Any] = {
"date": date.today().isoformat(),
"currency": str(economic_customer.get("currency") or "DKK"),
@ -209,7 +247,7 @@ class OrdreEconomicExportService:
"vatZone": vat_zone,
},
"layout": {
"layoutNumber": int(layout_number or self.default_layout),
"layoutNumber": int(resolved_layout_number),
},
"lines": economic_lines,
}
@ -229,6 +267,7 @@ class OrdreEconomicExportService:
"read_only": self.read_only,
"dry_run": self.dry_run,
"user_id": user_id,
"skipped_product_numbers": skipped_product_numbers,
"payload": payload,
},
}
@ -263,6 +302,7 @@ class OrdreEconomicExportService:
"customer_name": customer.get("name"),
"selected_line_count": len(selected_lines),
"user_id": user_id,
"skipped_product_numbers": skipped_product_numbers,
"economic_response": export_result,
},
}