work-order-hardening

This commit is contained in:
VPN SaaS Dev
2026-05-16 19:35:01 +09:00
parent 4ee83690f6
commit b03b63a5cc
2 changed files with 123 additions and 0 deletions

View File

@@ -88,6 +88,13 @@ async def get_work_order_with_items(session: AsyncSession, work_order_id: int) -
return visit
async def get_work_order_correction(session: AsyncSession, correction_id: int) -> WorkOrderCorrection:
correction = await session.get(WorkOrderCorrection, correction_id)
if correction is None:
raise HTTPException(status_code=404, detail="Work order correction not found")
return correction
async def ensure_work_order_sto_access(
session: AsyncSession, visit: ServiceVisit, user: User, allowed_roles: set[str] | None = None
) -> None:
@@ -539,6 +546,26 @@ async def request_vehicle_profile_details(
return visit
@router.get("/{work_order_id}/corrections", response_model=list[WorkOrderCorrectionRead])
async def list_work_order_corrections(
work_order_id: int,
session: AsyncSession = Depends(get_session),
current_user: User = Depends(get_current_telegram_user),
) -> list[WorkOrderCorrection]:
visit = await get_work_order(session, work_order_id)
vehicle = await session.get(Car, visit.vehicle_id)
if vehicle is None:
raise HTTPException(status_code=404, detail="Vehicle not found")
if vehicle.owner_id != current_user.id:
await ensure_work_order_sto_access(session, visit, current_user)
result = await session.execute(
select(WorkOrderCorrection)
.where(WorkOrderCorrection.service_visit_id == visit.id)
.order_by(WorkOrderCorrection.created_at.desc(), WorkOrderCorrection.id.desc())
)
return list(result.scalars())
@router.post("/{work_order_id}/corrections", response_model=WorkOrderCorrectionRead, status_code=status.HTTP_201_CREATED)
async def create_work_order_correction(
work_order_id: int,
@@ -559,6 +586,19 @@ async def create_work_order_correction(
created_version=visit.version or 1,
)
session.add(correction)
vehicle = await session.get(Car, visit.vehicle_id)
if payload.owner_approval_required and vehicle is not None:
await create_service_notification(
session,
recipient_user_id=vehicle.owner_id,
service_center_id=visit.service_center_id,
notification_type="work_order.correction_waiting_owner_approval",
title="СТО просит согласовать правку заказ-наряда",
body=payload.reason,
idempotency_key=f"work_order:{visit.id}:correction:{visit.version or 1}:{payload.reason[:80]}",
web_app_url=work_order_webapp_url(visit.id),
button_text="Открыть заказ-наряд",
)
await log_audit(
session,
actor=current_user,
@@ -572,6 +612,60 @@ async def create_work_order_correction(
return correction
@router.post("/corrections/{correction_id}/approve", response_model=WorkOrderCorrectionRead)
async def approve_work_order_correction(
correction_id: int,
payload: WorkOrderDecision,
session: AsyncSession = Depends(get_session),
current_user: User = Depends(get_current_telegram_user),
) -> WorkOrderCorrection:
correction = await get_work_order_correction(session, correction_id)
visit = await get_work_order(session, correction.service_visit_id)
await ensure_work_order_owner_access(session, visit, current_user)
if correction.status != "pending":
raise HTTPException(status_code=409, detail="Correction is already resolved")
correction.status = "approved"
correction.resolved_at = datetime.now(UTC)
await log_audit(
session,
actor=current_user,
action="work_order.correction.approve",
target_type="work_order_correction",
target_id=correction.id,
metadata={"comment": payload.comment},
)
await session.commit()
await session.refresh(correction)
return correction
@router.post("/corrections/{correction_id}/reject", response_model=WorkOrderCorrectionRead)
async def reject_work_order_correction(
correction_id: int,
payload: WorkOrderDecision,
session: AsyncSession = Depends(get_session),
current_user: User = Depends(get_current_telegram_user),
) -> WorkOrderCorrection:
correction = await get_work_order_correction(session, correction_id)
visit = await get_work_order(session, correction.service_visit_id)
await ensure_work_order_owner_access(session, visit, current_user)
if correction.status != "pending":
raise HTTPException(status_code=409, detail="Correction is already resolved")
correction.status = "rejected"
correction.resolved_at = datetime.now(UTC)
await log_audit(
session,
actor=current_user,
action="work_order.correction.reject",
target_type="work_order_correction",
target_id=correction.id,
metadata={"comment": payload.comment},
)
await session.commit()
await session.refresh(correction)
return correction
@router.get("/{work_order_id}/status-history", response_model=list[WorkOrderStatusHistoryRead])
async def work_order_status_history(
work_order_id: int,