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 import logging
from datetime import date from datetime import date
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from urllib.parse import quote
import aiohttp import aiohttp
from fastapi import HTTPException from fastapi import HTTPException
@ -195,6 +196,43 @@ class OrdreEconomicExportService:
detail="e-conomic-kunden mangler betalingsbetingelser eller momszone", 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] = { payload: Dict[str, Any] = {
"date": date.today().isoformat(), "date": date.today().isoformat(),
"currency": str(economic_customer.get("currency") or "DKK"), "currency": str(economic_customer.get("currency") or "DKK"),
@ -209,7 +247,7 @@ class OrdreEconomicExportService:
"vatZone": vat_zone, "vatZone": vat_zone,
}, },
"layout": { "layout": {
"layoutNumber": int(layout_number or self.default_layout), "layoutNumber": int(resolved_layout_number),
}, },
"lines": economic_lines, "lines": economic_lines,
} }
@ -229,6 +267,7 @@ class OrdreEconomicExportService:
"read_only": self.read_only, "read_only": self.read_only,
"dry_run": self.dry_run, "dry_run": self.dry_run,
"user_id": user_id, "user_id": user_id,
"skipped_product_numbers": skipped_product_numbers,
"payload": payload, "payload": payload,
}, },
} }
@ -263,6 +302,7 @@ class OrdreEconomicExportService:
"customer_name": customer.get("name"), "customer_name": customer.get("name"),
"selected_line_count": len(selected_lines), "selected_line_count": len(selected_lines),
"user_id": user_id, "user_id": user_id,
"skipped_product_numbers": skipped_product_numbers,
"economic_response": export_result, "economic_response": export_result,
}, },
} }