add backend readiness contract for publish prechecks add pricing diagnostics to explain publish-blocking conditions make publish decisions more explicit and easier to debug for clients
171 lines
5.9 KiB
Python
171 lines
5.9 KiB
Python
from fastapi import APIRouter, Depends, Query
|
|
|
|
from app.core.config import settings
|
|
from app.repositories.audit import create_audit_event
|
|
from app.repositories.scheme_version_pricing import replace_scheme_version_pricing_snapshot
|
|
from app.schemas.publish_preview import (
|
|
PublishPreviewResponse,
|
|
RemapApplyRequest,
|
|
RemapApplyResponse,
|
|
RemapPreviewRequest,
|
|
RemapPreviewResponse,
|
|
RemapPreviewSeatItem,
|
|
)
|
|
from app.schemas.publish_readiness import PublishReadinessResponse
|
|
from app.security.auth import require_api_key
|
|
from app.services.draft_guard import get_current_draft_context
|
|
from app.services.publish_preview import get_or_build_publish_preview_bundle
|
|
from app.services.publish_readiness import build_publish_readiness
|
|
from app.services.remap_service import apply_remap, preview_remap
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.post(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/pricing/snapshot")
|
|
async def create_draft_pricing_snapshot(
|
|
scheme_id: str,
|
|
expected_scheme_version_id: str | None = Query(default=None),
|
|
role: str = Depends(require_api_key),
|
|
):
|
|
scheme, version = await get_current_draft_context(
|
|
scheme_id,
|
|
expected_scheme_version_id=expected_scheme_version_id,
|
|
)
|
|
result = await replace_scheme_version_pricing_snapshot(
|
|
scheme_id=scheme.scheme_id,
|
|
scheme_version_id=version.scheme_version_id,
|
|
)
|
|
|
|
await create_audit_event(
|
|
scheme_id=scheme.scheme_id,
|
|
event_type="draft.pricing.snapshot.created",
|
|
object_type="pricing_snapshot",
|
|
object_ref=version.scheme_version_id,
|
|
details=result,
|
|
)
|
|
|
|
return {
|
|
"scheme_id": scheme.scheme_id,
|
|
"scheme_version_id": version.scheme_version_id,
|
|
**result,
|
|
}
|
|
|
|
|
|
@router.get(
|
|
f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/publish-readiness",
|
|
response_model=PublishReadinessResponse,
|
|
)
|
|
async def get_draft_publish_readiness(
|
|
scheme_id: str,
|
|
expected_scheme_version_id: str | None = Query(default=None),
|
|
role: str = Depends(require_api_key),
|
|
):
|
|
scheme, version = await get_current_draft_context(
|
|
scheme_id,
|
|
expected_scheme_version_id=expected_scheme_version_id,
|
|
)
|
|
readiness = await build_publish_readiness(
|
|
scheme_id=scheme.scheme_id,
|
|
scheme_version_id=version.scheme_version_id,
|
|
)
|
|
return PublishReadinessResponse(**readiness)
|
|
|
|
|
|
@router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/publish-preview", response_model=PublishPreviewResponse)
|
|
async def get_publish_preview(
|
|
scheme_id: str,
|
|
baseline_scheme_version_id: str | None = Query(default=None),
|
|
expected_scheme_version_id: str | None = Query(default=None),
|
|
refresh: bool = Query(default=False),
|
|
role: str = Depends(require_api_key),
|
|
):
|
|
scheme, version = await get_current_draft_context(
|
|
scheme_id,
|
|
expected_scheme_version_id=expected_scheme_version_id,
|
|
)
|
|
bundle = await get_or_build_publish_preview_bundle(
|
|
scheme_id=scheme.scheme_id,
|
|
scheme_version_id=version.scheme_version_id,
|
|
baseline_override_scheme_version_id=baseline_scheme_version_id,
|
|
refresh=refresh,
|
|
)
|
|
return PublishPreviewResponse(
|
|
scheme_id=scheme.scheme_id,
|
|
scheme_version_id=version.scheme_version_id,
|
|
artifacts=bundle["artifacts"],
|
|
validation=bundle["validation"],
|
|
structure_diff=bundle["structure_diff"],
|
|
pricing_coverage=bundle["pricing_coverage"],
|
|
summary=bundle["summary"],
|
|
)
|
|
|
|
|
|
@router.post(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/remap/preview", response_model=RemapPreviewResponse)
|
|
async def preview_draft_remap(
|
|
scheme_id: str,
|
|
payload: RemapPreviewRequest,
|
|
expected_scheme_version_id: str | None = Query(default=None),
|
|
role: str = Depends(require_api_key),
|
|
):
|
|
scheme, version = await get_current_draft_context(
|
|
scheme_id,
|
|
expected_scheme_version_id=expected_scheme_version_id,
|
|
)
|
|
items = await preview_remap(
|
|
scheme_version_id=version.scheme_version_id,
|
|
seat_record_ids=payload.seat_record_ids,
|
|
from_sector_id=payload.from_sector_id,
|
|
to_sector_id=payload.to_sector_id,
|
|
from_group_id=payload.from_group_id,
|
|
to_group_id=payload.to_group_id,
|
|
)
|
|
|
|
return RemapPreviewResponse(
|
|
scheme_id=scheme.scheme_id,
|
|
scheme_version_id=version.scheme_version_id,
|
|
matched_count=len(items),
|
|
items=[RemapPreviewSeatItem(**item) for item in items],
|
|
)
|
|
|
|
|
|
@router.post(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/remap/apply", response_model=RemapApplyResponse)
|
|
async def apply_draft_remap(
|
|
scheme_id: str,
|
|
payload: RemapApplyRequest,
|
|
expected_scheme_version_id: str | None = Query(default=None),
|
|
role: str = Depends(require_api_key),
|
|
):
|
|
scheme, version = await get_current_draft_context(
|
|
scheme_id,
|
|
expected_scheme_version_id=expected_scheme_version_id,
|
|
)
|
|
items = await apply_remap(
|
|
scheme_version_id=version.scheme_version_id,
|
|
seat_record_ids=payload.seat_record_ids,
|
|
from_sector_id=payload.from_sector_id,
|
|
to_sector_id=payload.to_sector_id,
|
|
from_group_id=payload.from_group_id,
|
|
to_group_id=payload.to_group_id,
|
|
)
|
|
|
|
await create_audit_event(
|
|
scheme_id=scheme.scheme_id,
|
|
event_type="draft.remap.applied",
|
|
object_type="draft_structure",
|
|
object_ref=version.scheme_version_id,
|
|
details={
|
|
"matched_count": len(items),
|
|
"from_sector_id": payload.from_sector_id,
|
|
"to_sector_id": payload.to_sector_id,
|
|
"from_group_id": payload.from_group_id,
|
|
"to_group_id": payload.to_group_id,
|
|
},
|
|
)
|
|
|
|
return RemapApplyResponse(
|
|
scheme_id=scheme.scheme_id,
|
|
scheme_version_id=version.scheme_version_id,
|
|
updated_count=len(items),
|
|
items=[RemapPreviewSeatItem(**item) for item in items],
|
|
)
|