158 lines
5.8 KiB
Python
158 lines
5.8 KiB
Python
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Depends, Header, Request
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.response import success_response
|
|
from app.db import get_db_session
|
|
from app.services.audit_log_service import AuditLogService
|
|
from app.services.auth_service import AuthService
|
|
from app.services.contract_approval_service import ContractApprovalService
|
|
|
|
router = APIRouter(prefix="/contract-approvals", tags=["contract-approvals"])
|
|
|
|
|
|
class ContractApprovalSubmitRequest(BaseModel):
|
|
contract_id: str = Field(..., min_length=1, max_length=64)
|
|
title: Optional[str] = Field(default=None, max_length=200)
|
|
comment: Optional[str] = Field(default=None, max_length=2000)
|
|
assignee_id: Optional[str] = Field(default=None, max_length=64)
|
|
assignee_name: Optional[str] = Field(default=None, max_length=100)
|
|
step_type: str = Field(default="legal_review", max_length=64)
|
|
|
|
|
|
class ContractApprovalDecisionRequest(BaseModel):
|
|
decision: str = Field(..., min_length=1, max_length=32)
|
|
comment: Optional[str] = Field(default=None, max_length=2000)
|
|
|
|
|
|
def get_contract_approval_service(db: Session = Depends(get_db_session)) -> ContractApprovalService:
|
|
return ContractApprovalService(db)
|
|
|
|
|
|
def get_audit_log_service(db: Session = Depends(get_db_session)) -> AuditLogService:
|
|
return AuditLogService(db)
|
|
|
|
|
|
def require_current_user(
|
|
authorization: Optional[str] = Header(default=None),
|
|
db: Session = Depends(get_db_session),
|
|
):
|
|
return AuthService(db).require_current_user(authorization)
|
|
|
|
|
|
@router.get("")
|
|
def list_contract_approvals(
|
|
page: int = 1,
|
|
page_size: int = 20,
|
|
status_value: Optional[str] = None,
|
|
scope: str = "assigned_to_me",
|
|
contract_approval_service: ContractApprovalService = Depends(get_contract_approval_service),
|
|
current_user: dict = Depends(require_current_user),
|
|
):
|
|
return success_response(
|
|
contract_approval_service.list_approvals(
|
|
current_user=current_user,
|
|
page=page,
|
|
page_size=page_size,
|
|
status_value=status_value,
|
|
scope=scope,
|
|
)
|
|
)
|
|
|
|
|
|
@router.get("/assignees")
|
|
def list_contract_approval_assignees(
|
|
contract_approval_service: ContractApprovalService = Depends(get_contract_approval_service),
|
|
current_user: dict = Depends(require_current_user),
|
|
):
|
|
return success_response(contract_approval_service.list_assignees(current_user))
|
|
|
|
|
|
@router.get("/contracts/{contract_id}")
|
|
def list_contract_approval_history(
|
|
contract_id: str,
|
|
contract_approval_service: ContractApprovalService = Depends(get_contract_approval_service),
|
|
current_user: dict = Depends(require_current_user),
|
|
):
|
|
return success_response(contract_approval_service.list_contract_approvals(current_user, contract_id))
|
|
|
|
|
|
@router.post("")
|
|
def submit_contract_approval(
|
|
payload: ContractApprovalSubmitRequest,
|
|
request: Request,
|
|
contract_approval_service: ContractApprovalService = Depends(get_contract_approval_service),
|
|
audit_log_service: AuditLogService = Depends(get_audit_log_service),
|
|
current_user: dict = Depends(require_current_user),
|
|
):
|
|
result = contract_approval_service.submit_approval(
|
|
current_user=current_user,
|
|
contract_id=payload.contract_id,
|
|
title=payload.title,
|
|
comment=payload.comment,
|
|
assignee_id=payload.assignee_id,
|
|
assignee_name=payload.assignee_name,
|
|
step_type=payload.step_type,
|
|
)
|
|
audit_log_service.record_event(
|
|
tenant_id=current_user.get("tenant_id"),
|
|
user_id=current_user.get("id"),
|
|
action="contract_approval_submit",
|
|
resource_type="contract_approval",
|
|
resource_id=result.get("id"),
|
|
detail_json={
|
|
"contract_id": result.get("contract_id"),
|
|
"approval_id": result.get("id"),
|
|
"title": result.get("title"),
|
|
"step_type": result.get("step_type"),
|
|
"submitter_id": result.get("submitter_id"),
|
|
"assignee_id": result.get("assignee_id"),
|
|
"assignee_name": result.get("assignee_name"),
|
|
"status": result.get("status"),
|
|
"audit_key": result.get("audit_key"),
|
|
},
|
|
ip_address=request.client.host if request.client else None,
|
|
user_agent=request.headers.get("user-agent"),
|
|
)
|
|
return success_response(result)
|
|
|
|
|
|
@router.post("/{approval_id}/decision")
|
|
def decide_contract_approval(
|
|
approval_id: str,
|
|
payload: ContractApprovalDecisionRequest,
|
|
request: Request,
|
|
contract_approval_service: ContractApprovalService = Depends(get_contract_approval_service),
|
|
audit_log_service: AuditLogService = Depends(get_audit_log_service),
|
|
current_user: dict = Depends(require_current_user),
|
|
):
|
|
result = contract_approval_service.decide_approval(
|
|
current_user=current_user,
|
|
approval_id=approval_id,
|
|
decision=payload.decision,
|
|
comment=payload.comment,
|
|
)
|
|
audit_log_service.record_event(
|
|
tenant_id=current_user.get("tenant_id"),
|
|
user_id=current_user.get("id"),
|
|
action=f"contract_approval_{result.get('status')}",
|
|
resource_type="contract_approval",
|
|
resource_id=result.get("id"),
|
|
detail_json={
|
|
"contract_id": result.get("contract_id"),
|
|
"approval_id": result.get("id"),
|
|
"decision": payload.decision,
|
|
"comment": result.get("comment"),
|
|
"decided_by": result.get("decided_by"),
|
|
"decided_by_name": result.get("decided_by_name"),
|
|
"decided_at": result.get("decided_at"),
|
|
"status": result.get("status"),
|
|
"audit_key": result.get("audit_key"),
|
|
},
|
|
ip_address=request.client.host if request.client else None,
|
|
user_agent=request.headers.get("user-agent"),
|
|
)
|
|
return success_response(result)
|