Implement display artifacts, pricing integrity, draft base and publish preview bundle
This commit is contained in:
107
backend/app/services/scheme_validation.py
Normal file
107
backend/app/services/scheme_validation.py
Normal file
@@ -0,0 +1,107 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import Counter
|
||||
|
||||
from app.repositories.pricing import find_effective_price_rule
|
||||
from app.repositories.scheme_groups import list_scheme_version_groups
|
||||
from app.repositories.scheme_seats import list_scheme_version_seats
|
||||
from app.repositories.scheme_sectors import list_scheme_version_sectors
|
||||
|
||||
|
||||
async def build_scheme_validation_report(
|
||||
*,
|
||||
scheme_id: str,
|
||||
scheme_version_id: str,
|
||||
) -> dict:
|
||||
sectors = await list_scheme_version_sectors(scheme_version_id)
|
||||
groups = await list_scheme_version_groups(scheme_version_id)
|
||||
seats = await list_scheme_version_seats(scheme_version_id)
|
||||
|
||||
seat_ids = [row.seat_id for row in seats if row.seat_id]
|
||||
duplicate_seat_ids = sorted([seat_id for seat_id, count in Counter(seat_ids).items() if count > 1])
|
||||
|
||||
seats_without_price: list[str] = []
|
||||
seats_without_sector_or_group: list[str] = []
|
||||
seats_with_missing_contract: list[str] = []
|
||||
priced_seats_count = 0
|
||||
|
||||
for seat in seats:
|
||||
if not seat.seat_id or not seat.element_id:
|
||||
seats_with_missing_contract.append(seat.element_id or seat.seat_id or "unknown")
|
||||
continue
|
||||
|
||||
if not seat.sector_id and not seat.group_id:
|
||||
seats_without_sector_or_group.append(seat.seat_id)
|
||||
|
||||
try:
|
||||
await find_effective_price_rule(
|
||||
scheme_id=scheme_id,
|
||||
seat_id=seat.seat_id,
|
||||
group_id=seat.group_id,
|
||||
sector_id=seat.sector_id,
|
||||
)
|
||||
priced_seats_count += 1
|
||||
except Exception:
|
||||
seats_without_price.append(seat.seat_id)
|
||||
|
||||
errors: list[dict] = []
|
||||
warnings: list[dict] = []
|
||||
|
||||
if duplicate_seat_ids:
|
||||
errors.append(
|
||||
{
|
||||
"code": "duplicate_seat_ids",
|
||||
"message": "Duplicate seat_id values found",
|
||||
"items": duplicate_seat_ids,
|
||||
}
|
||||
)
|
||||
|
||||
if seats_with_missing_contract:
|
||||
errors.append(
|
||||
{
|
||||
"code": "missing_seat_contract",
|
||||
"message": "Some seats have missing contract fields",
|
||||
"items": seats_with_missing_contract,
|
||||
}
|
||||
)
|
||||
|
||||
if seats_without_sector_or_group:
|
||||
warnings.append(
|
||||
{
|
||||
"code": "seats_without_sector_or_group",
|
||||
"message": "Some seats do not belong to sector or group",
|
||||
"items": sorted(seats_without_sector_or_group),
|
||||
}
|
||||
)
|
||||
|
||||
if seats_without_price:
|
||||
warnings.append(
|
||||
{
|
||||
"code": "seats_without_price",
|
||||
"message": "Some seats have no pricing rule",
|
||||
"items": sorted(seats_without_price),
|
||||
}
|
||||
)
|
||||
|
||||
is_publishable = len(errors) == 0
|
||||
|
||||
return {
|
||||
"summary": {
|
||||
"sectors_count": len(sectors),
|
||||
"groups_count": len(groups),
|
||||
"seats_count": len(seats),
|
||||
"priced_seats_count": priced_seats_count,
|
||||
"unpriced_seats_count": len(seats_without_price),
|
||||
"duplicate_seat_ids_count": len(duplicate_seat_ids),
|
||||
"seats_with_missing_contract_count": len(seats_with_missing_contract),
|
||||
"is_publishable": is_publishable,
|
||||
},
|
||||
"errors": errors,
|
||||
"warnings": warnings,
|
||||
"issues": {
|
||||
"duplicate_seat_ids": duplicate_seat_ids,
|
||||
"seats_without_price": sorted(seats_without_price),
|
||||
"seats_without_sector_or_group": sorted(seats_without_sector_or_group),
|
||||
"seats_with_missing_contract": seats_with_missing_contract,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user