Files
sam-api/app/Swagger/v1/ContractApi.php
권혁성 7246ac003f fix(WEB): 수주 페이지 필드 매핑 및 제품-부품 트리 구조 개선
- ApiClient 인터페이스: representative → manager_name, contact_person 변경
- transformApiToFrontend: client.representative → client.manager_name 수정
- ApiOrderItem에 floor_code, symbol_code 필드 추가 (제품-부품 매핑)
- ApiOrder에 options 타입 정의 추가
- ApiQuote에 calculation_inputs 타입 정의 추가
- 수주 상세 페이지 제품-부품 트리 구조 UI 개선
2026-01-20 16:14:46 +09:00

401 lines
23 KiB
PHP

<?php
namespace App\Swagger\v1;
/**
* @OA\Tag(name="Contract", description="시공관리 - 계약관리")
*
* @OA\Schema(
* schema="Contract",
* type="object",
* required={"id","contract_code","project_name"},
*
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="tenant_id", type="integer", example=1),
* @OA\Property(property="contract_code", type="string", maxLength=50, example="CT-2026-001", description="계약번호"),
* @OA\Property(property="project_name", type="string", maxLength=255, example="서울 A타워 시공", description="현장명"),
* @OA\Property(property="partner_id", type="integer", nullable=true, example=1, description="거래처 ID"),
* @OA\Property(property="partner_name", type="string", nullable=true, maxLength=255, example="ABC건설", description="거래처명"),
* @OA\Property(property="contract_manager_id", type="integer", nullable=true, example=1, description="계약담당자 ID"),
* @OA\Property(property="contract_manager_name", type="string", nullable=true, maxLength=100, example="홍길동", description="계약담당자명"),
* @OA\Property(property="construction_pm_id", type="integer", nullable=true, example=2, description="공사PM ID"),
* @OA\Property(property="construction_pm_name", type="string", nullable=true, maxLength=100, example="김철수", description="공사PM명"),
* @OA\Property(property="total_locations", type="integer", example=15, description="총 개소수"),
* @OA\Property(property="contract_amount", type="number", format="float", example=150000000, description="계약금액"),
* @OA\Property(property="contract_start_date", type="string", format="date", nullable=true, example="2026-01-01", description="계약시작일"),
* @OA\Property(property="contract_end_date", type="string", format="date", nullable=true, example="2026-12-31", description="계약종료일"),
* @OA\Property(property="status", type="string", enum={"pending", "completed"}, example="pending", description="상태 (pending: 계약대기, completed: 계약완료)"),
* @OA\Property(property="stage", type="string", enum={"estimate_selected", "estimate_progress", "delivery", "installation", "inspection", "other"}, example="estimate_selected", description="단계 (estimate_selected: 견적선정, estimate_progress: 견적진행, delivery: 납품, installation: 설치중, inspection: 검수, other: 기타)"),
* @OA\Property(property="bidding_id", type="integer", nullable=true, example=1, description="입찰 ID"),
* @OA\Property(property="bidding_code", type="string", nullable=true, maxLength=50, example="BID-2025-001", description="입찰번호"),
* @OA\Property(property="remarks", type="string", nullable=true, example="특이사항 없음", description="비고"),
* @OA\Property(property="is_active", type="boolean", example=true, description="활성 여부"),
* @OA\Property(property="created_by", type="integer", nullable=true, example=1),
* @OA\Property(property="updated_by", type="integer", nullable=true, example=1),
* @OA\Property(property="created_at", type="string", example="2026-01-08 12:00:00"),
* @OA\Property(property="updated_at", type="string", example="2026-01-08 12:00:00")
* )
*
* @OA\Schema(
* schema="ContractPagination",
* type="object",
*
* @OA\Property(property="current_page", type="integer", example=1),
* @OA\Property(
* property="data",
* type="array",
*
* @OA\Items(ref="#/components/schemas/Contract")
* ),
*
* @OA\Property(property="first_page_url", type="string", example="/api/v1/construction/contracts?page=1"),
* @OA\Property(property="from", type="integer", example=1),
* @OA\Property(property="last_page", type="integer", example=3),
* @OA\Property(property="last_page_url", type="string", example="/api/v1/construction/contracts?page=3"),
* @OA\Property(
* property="links",
* type="array",
*
* @OA\Items(type="object",
*
* @OA\Property(property="url", type="string", nullable=true, example=null),
* @OA\Property(property="label", type="string", example="&laquo; Previous"),
* @OA\Property(property="active", type="boolean", example=false)
* )
* ),
* @OA\Property(property="next_page_url", type="string", nullable=true, example="/api/v1/construction/contracts?page=2"),
* @OA\Property(property="path", type="string", example="/api/v1/construction/contracts"),
* @OA\Property(property="per_page", type="integer", example=20),
* @OA\Property(property="prev_page_url", type="string", nullable=true, example=null),
* @OA\Property(property="to", type="integer", example=20),
* @OA\Property(property="total", type="integer", example=50)
* )
*
* @OA\Schema(
* schema="ContractCreateRequest",
* type="object",
* required={"contract_code","project_name"},
*
* @OA\Property(property="contract_code", type="string", maxLength=50, example="CT-2026-001", description="계약번호"),
* @OA\Property(property="project_name", type="string", maxLength=255, example="서울 A타워 시공", description="현장명"),
* @OA\Property(property="partner_id", type="integer", nullable=true, example=1, description="거래처 ID"),
* @OA\Property(property="partner_name", type="string", nullable=true, maxLength=255, example="ABC건설", description="거래처명"),
* @OA\Property(property="contract_manager_id", type="integer", nullable=true, example=1, description="계약담당자 ID"),
* @OA\Property(property="contract_manager_name", type="string", nullable=true, maxLength=100, example="홍길동", description="계약담당자명"),
* @OA\Property(property="construction_pm_id", type="integer", nullable=true, example=2, description="공사PM ID"),
* @OA\Property(property="construction_pm_name", type="string", nullable=true, maxLength=100, example="김철수", description="공사PM명"),
* @OA\Property(property="total_locations", type="integer", nullable=true, example=15, description="총 개소수"),
* @OA\Property(property="contract_amount", type="number", format="float", nullable=true, example=150000000, description="계약금액"),
* @OA\Property(property="contract_start_date", type="string", format="date", nullable=true, example="2026-01-01", description="계약시작일"),
* @OA\Property(property="contract_end_date", type="string", format="date", nullable=true, example="2026-12-31", description="계약종료일"),
* @OA\Property(property="status", type="string", enum={"pending", "completed"}, nullable=true, example="pending", description="상태"),
* @OA\Property(property="stage", type="string", enum={"estimate_selected", "estimate_progress", "delivery", "installation", "inspection", "other"}, nullable=true, example="estimate_selected", description="단계"),
* @OA\Property(property="bidding_id", type="integer", nullable=true, example=1, description="입찰 ID"),
* @OA\Property(property="bidding_code", type="string", nullable=true, maxLength=50, example="BID-2025-001", description="입찰번호"),
* @OA\Property(property="remarks", type="string", nullable=true, example="특이사항 없음", description="비고"),
* @OA\Property(property="is_active", type="boolean", nullable=true, example=true, description="활성 여부")
* )
*
* @OA\Schema(
* schema="ContractUpdateRequest",
* type="object",
*
* @OA\Property(property="contract_code", type="string", maxLength=50, description="계약번호"),
* @OA\Property(property="project_name", type="string", maxLength=255, description="현장명"),
* @OA\Property(property="partner_id", type="integer", nullable=true, description="거래처 ID"),
* @OA\Property(property="partner_name", type="string", nullable=true, maxLength=255, description="거래처명"),
* @OA\Property(property="contract_manager_id", type="integer", nullable=true, description="계약담당자 ID"),
* @OA\Property(property="contract_manager_name", type="string", nullable=true, maxLength=100, description="계약담당자명"),
* @OA\Property(property="construction_pm_id", type="integer", nullable=true, description="공사PM ID"),
* @OA\Property(property="construction_pm_name", type="string", nullable=true, maxLength=100, description="공사PM명"),
* @OA\Property(property="total_locations", type="integer", nullable=true, description="총 개소수"),
* @OA\Property(property="contract_amount", type="number", format="float", nullable=true, description="계약금액"),
* @OA\Property(property="contract_start_date", type="string", format="date", nullable=true, description="계약시작일"),
* @OA\Property(property="contract_end_date", type="string", format="date", nullable=true, description="계약종료일"),
* @OA\Property(property="status", type="string", enum={"pending", "completed"}, nullable=true, description="상태"),
* @OA\Property(property="stage", type="string", enum={"estimate_selected", "estimate_progress", "delivery", "installation", "inspection", "other"}, nullable=true, description="단계"),
* @OA\Property(property="bidding_id", type="integer", nullable=true, description="입찰 ID"),
* @OA\Property(property="bidding_code", type="string", nullable=true, maxLength=50, description="입찰번호"),
* @OA\Property(property="remarks", type="string", nullable=true, description="비고"),
* @OA\Property(property="is_active", type="boolean", nullable=true, description="활성 여부")
* )
*
* @OA\Schema(
* schema="ContractStats",
* type="object",
*
* @OA\Property(property="total_count", type="integer", example=50, description="전체 계약 수"),
* @OA\Property(property="pending_count", type="integer", example=30, description="계약대기 수"),
* @OA\Property(property="completed_count", type="integer", example=20, description="계약완료 수"),
* @OA\Property(property="total_amount", type="number", format="float", example=5000000000, description="총 계약금액"),
* @OA\Property(property="total_locations", type="integer", example=500, description="총 개소수")
* )
*
* @OA\Schema(
* schema="ContractStageCounts",
* type="object",
*
* @OA\Property(property="estimate_selected", type="integer", example=10, description="견적선정 수"),
* @OA\Property(property="estimate_progress", type="integer", example=15, description="견적진행 수"),
* @OA\Property(property="delivery", type="integer", example=8, description="납품 수"),
* @OA\Property(property="installation", type="integer", example=12, description="설치중 수"),
* @OA\Property(property="inspection", type="integer", example=3, description="검수 수"),
* @OA\Property(property="other", type="integer", example=2, description="기타 수")
* )
*
* @OA\Schema(
* schema="ContractFromBiddingRequest",
* type="object",
* description="입찰에서 계약 생성 요청 (모든 필드 선택, 입찰 데이터에서 자동 매핑)",
*
* @OA\Property(property="project_name", type="string", maxLength=255, description="현장명 (미입력시 입찰 데이터 사용)"),
* @OA\Property(property="partner_id", type="integer", nullable=true, description="거래처 ID (미입력시 입찰 거래처 사용)"),
* @OA\Property(property="partner_name", type="string", nullable=true, maxLength=255, description="거래처명 (미입력시 입찰 거래처명 사용)"),
* @OA\Property(property="contract_manager_id", type="integer", nullable=true, description="계약담당자 ID"),
* @OA\Property(property="contract_manager_name", type="string", nullable=true, maxLength=100, description="계약담당자명"),
* @OA\Property(property="construction_pm_id", type="integer", nullable=true, description="공사PM ID"),
* @OA\Property(property="construction_pm_name", type="string", nullable=true, maxLength=100, description="공사PM명"),
* @OA\Property(property="total_locations", type="integer", nullable=true, description="총 개소수 (미입력시 입찰 총수량 사용)"),
* @OA\Property(property="contract_amount", type="number", format="float", nullable=true, description="계약금액 (미입력시 입찰금액 사용)"),
* @OA\Property(property="contract_start_date", type="string", format="date", nullable=true, description="계약시작일 (미입력시 입찰 공사시작일 사용)"),
* @OA\Property(property="contract_end_date", type="string", format="date", nullable=true, description="계약종료일 (미입력시 입찰 공사종료일 사용)"),
* @OA\Property(property="status", type="string", enum={"pending", "completed"}, nullable=true, example="pending", description="상태 (기본: pending)"),
* @OA\Property(property="stage", type="string", enum={"estimate_selected", "estimate_progress", "delivery", "installation", "inspection", "other"}, nullable=true, example="estimate_selected", description="단계 (기본: estimate_selected)"),
* @OA\Property(property="remarks", type="string", nullable=true, description="비고 (미입력시 입찰 비고 사용)"),
* @OA\Property(property="is_active", type="boolean", nullable=true, example=true, description="활성 여부 (기본: true)")
* )
*/
class ContractApi
{
/**
* @OA\Get(
* path="/api/v1/construction/contracts",
* tags={"Contract"},
* summary="계약 목록 조회",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="page", in="query", @OA\Schema(type="integer", example=1)),
* @OA\Parameter(name="per_page", in="query", @OA\Schema(type="integer", example=20)),
* @OA\Parameter(name="search", in="query", description="계약번호/현장명/거래처명 검색", @OA\Schema(type="string")),
* @OA\Parameter(name="status", in="query", description="상태 필터", @OA\Schema(type="string", enum={"pending", "completed"})),
* @OA\Parameter(name="stage", in="query", description="단계 필터", @OA\Schema(type="string", enum={"estimate_selected", "estimate_progress", "delivery", "installation", "inspection", "other"})),
* @OA\Parameter(name="partner_id", in="query", description="거래처 ID 필터", @OA\Schema(type="integer")),
* @OA\Parameter(name="contract_manager_id", in="query", description="계약담당자 ID 필터", @OA\Schema(type="integer")),
* @OA\Parameter(name="construction_pm_id", in="query", description="공사PM ID 필터", @OA\Schema(type="integer")),
* @OA\Parameter(name="start_date", in="query", description="시작일 필터 (계약시작일 기준)", @OA\Schema(type="string", format="date")),
* @OA\Parameter(name="end_date", in="query", description="종료일 필터 (계약종료일 기준)", @OA\Schema(type="string", format="date")),
* @OA\Parameter(name="is_active", in="query", description="활성화 상태 필터", @OA\Schema(type="boolean")),
* @OA\Parameter(name="sort_by", in="query", description="정렬 기준", @OA\Schema(type="string", example="created_at")),
* @OA\Parameter(name="sort_dir", in="query", description="정렬 방향", @OA\Schema(type="string", enum={"asc", "desc"}, example="desc")),
*
* @OA\Response(response=200, description="조회 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/ContractPagination"))
* })
* ),
*
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function index() {}
/**
* @OA\Get(
* path="/api/v1/construction/contracts/{id}",
* tags={"Contract"},
* summary="계약 상세 조회",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
*
* @OA\Response(response=200, description="조회 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/Contract"))
* })
* ),
*
* @OA\Response(response=404, description="데이터 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function show() {}
/**
* @OA\Post(
* path="/api/v1/construction/contracts",
* tags={"Contract"},
* summary="계약 등록",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/ContractCreateRequest")),
*
* @OA\Response(response=200, description="등록 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/Contract"))
* })
* ),
*
* @OA\Response(response=400, description="검증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function store() {}
/**
* @OA\Put(
* path="/api/v1/construction/contracts/{id}",
* tags={"Contract"},
* summary="계약 수정",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
*
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/ContractUpdateRequest")),
*
* @OA\Response(response=200, description="수정 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/Contract"))
* })
* ),
*
* @OA\Response(response=404, description="데이터 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function update() {}
/**
* @OA\Delete(
* path="/api/v1/construction/contracts/{id}",
* tags={"Contract"},
* summary="계약 삭제",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
*
* @OA\Response(response=200, description="삭제 성공", @OA\JsonContent(ref="#/components/schemas/ApiResponse")),
* @OA\Response(response=404, description="데이터 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function destroy() {}
/**
* @OA\Delete(
* path="/api/v1/construction/contracts/bulk",
* tags={"Contract"},
* summary="계약 일괄 삭제",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\RequestBody(required=true,
*
* @OA\JsonContent(
*
* @OA\Property(property="ids", type="array", @OA\Items(type="integer"), example={1, 2, 3}, description="삭제할 계약 ID 목록")
* )
* ),
*
* @OA\Response(response=200, description="삭제 성공", @OA\JsonContent(ref="#/components/schemas/ApiResponse")),
* @OA\Response(response=400, description="검증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function bulkDestroy() {}
/**
* @OA\Get(
* path="/api/v1/construction/contracts/stats",
* tags={"Contract"},
* summary="계약 통계 조회",
* description="전체 계약 수, 상태별 수, 총 계약금액, 총 개소수 등의 통계 정보를 반환합니다.",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="start_date", in="query", description="시작일 필터", @OA\Schema(type="string", format="date")),
* @OA\Parameter(name="end_date", in="query", description="종료일 필터", @OA\Schema(type="string", format="date")),
*
* @OA\Response(response=200, description="조회 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/ContractStats"))
* })
* ),
*
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function stats() {}
/**
* @OA\Get(
* path="/api/v1/construction/contracts/stage-counts",
* tags={"Contract"},
* summary="계약 단계별 카운트 조회",
* description="각 단계별 계약 수를 반환합니다.",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="start_date", in="query", description="시작일 필터", @OA\Schema(type="string", format="date")),
* @OA\Parameter(name="end_date", in="query", description="종료일 필터", @OA\Schema(type="string", format="date")),
*
* @OA\Response(response=200, description="조회 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/ContractStageCounts"))
* })
* ),
*
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function stageCounts() {}
/**
* @OA\Post(
* path="/api/v1/construction/contracts/from-bidding/{biddingId}",
* tags={"Contract"},
* summary="입찰에서 계약 생성 (낙찰 → 계약 전환)",
* description="낙찰(awarded) 상태의 입찰을 계약으로 전환합니다. 계약번호는 CTR-YYYY-NNN 형식으로 자동 생성됩니다. 요청 본문의 필드는 모두 선택이며, 미입력시 입찰 데이터에서 자동으로 매핑됩니다.",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(
* name="biddingId",
* in="path",
* required=true,
* description="입찰 ID",
*
* @OA\Schema(type="integer", example=11)
* ),
*
* @OA\RequestBody(
* required=false,
*
* @OA\JsonContent(ref="#/components/schemas/ContractFromBiddingRequest")
* ),
*
* @OA\Response(response=200, description="계약 생성 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/Contract"))
* })
* ),
*
* @OA\Response(response=404, description="입찰을 찾을 수 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
* @OA\Response(response=409, description="이미 계약이 등록된 입찰", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
* @OA\Response(response=422, description="낙찰 상태가 아닌 입찰", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function storeFromBidding() {}
}