- ReceivingController: CRUD 및 목록 조회 API - ReceivingService: 입고 비즈니스 로직 - Receiving 모델: 다중 테넌트 지원 - FormRequest 검증 클래스 - Swagger 문서화 - receivings 테이블 마이그레이션 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
361 lines
19 KiB
PHP
361 lines
19 KiB
PHP
<?php
|
|
|
|
namespace App\Swagger\v1;
|
|
|
|
/**
|
|
* @OA\Tag(name="Receivings", description="입고 관리")
|
|
*
|
|
* @OA\Schema(
|
|
* schema="Receiving",
|
|
* type="object",
|
|
* description="입고 정보",
|
|
*
|
|
* @OA\Property(property="id", type="integer", example=1, description="입고 ID"),
|
|
* @OA\Property(property="tenant_id", type="integer", example=1, description="테넌트 ID"),
|
|
* @OA\Property(property="receiving_number", type="string", example="RV202512260001", description="입고번호"),
|
|
* @OA\Property(property="order_no", type="string", example="PO-2025-001", nullable=true, description="발주번호"),
|
|
* @OA\Property(property="order_date", type="string", format="date", example="2025-12-20", nullable=true, description="발주일자"),
|
|
* @OA\Property(property="item_id", type="integer", example=1, nullable=true, description="품목 ID"),
|
|
* @OA\Property(property="item_code", type="string", example="ITEM-001", description="품목코드"),
|
|
* @OA\Property(property="item_name", type="string", example="원재료 A", description="품목명"),
|
|
* @OA\Property(property="specification", type="string", example="100x100", nullable=true, description="규격"),
|
|
* @OA\Property(property="supplier", type="string", example="(주)공급사", description="공급업체"),
|
|
* @OA\Property(property="order_qty", type="number", format="float", example=100.00, description="발주수량"),
|
|
* @OA\Property(property="order_unit", type="string", example="EA", description="발주단위"),
|
|
* @OA\Property(property="due_date", type="string", format="date", example="2025-12-26", nullable=true, description="납기일"),
|
|
* @OA\Property(property="receiving_qty", type="number", format="float", example=100.00, nullable=true, description="입고수량"),
|
|
* @OA\Property(property="receiving_date", type="string", format="date", example="2025-12-26", nullable=true, description="입고일자"),
|
|
* @OA\Property(property="lot_no", type="string", example="251226-01", nullable=true, description="입고 LOT번호"),
|
|
* @OA\Property(property="supplier_lot", type="string", example="SUP-LOT-001", nullable=true, description="공급업체 LOT"),
|
|
* @OA\Property(property="receiving_location", type="string", example="A-01-01", nullable=true, description="입고위치"),
|
|
* @OA\Property(property="receiving_manager", type="string", example="홍길동", nullable=true, description="입고담당"),
|
|
* @OA\Property(property="status", type="string", enum={"order_completed","shipping","inspection_pending","receiving_pending","completed"}, example="receiving_pending", description="상태"),
|
|
* @OA\Property(property="status_label", type="string", example="입고대기", description="상태 라벨"),
|
|
* @OA\Property(property="remark", type="string", example="비고 내용", nullable=true, description="비고"),
|
|
* @OA\Property(property="creator", type="object", nullable=true,
|
|
* @OA\Property(property="id", type="integer", example=1),
|
|
* @OA\Property(property="name", type="string", example="관리자"),
|
|
* description="생성자 정보"
|
|
* ),
|
|
* @OA\Property(property="created_at", type="string", format="date-time"),
|
|
* @OA\Property(property="updated_at", type="string", format="date-time")
|
|
* )
|
|
*
|
|
* @OA\Schema(
|
|
* schema="ReceivingStats",
|
|
* type="object",
|
|
* description="입고 통계",
|
|
*
|
|
* @OA\Property(property="receiving_pending_count", type="integer", example=5, description="입고대기 건수"),
|
|
* @OA\Property(property="shipping_count", type="integer", example=3, description="배송중 건수"),
|
|
* @OA\Property(property="inspection_pending_count", type="integer", example=2, description="검사대기 건수"),
|
|
* @OA\Property(property="today_receiving_count", type="integer", example=10, description="금일 입고 건수")
|
|
* )
|
|
*
|
|
* @OA\Schema(
|
|
* schema="ReceivingCreateRequest",
|
|
* type="object",
|
|
* required={"item_code","item_name","supplier","order_qty"},
|
|
* description="입고 등록 요청",
|
|
*
|
|
* @OA\Property(property="order_no", type="string", example="PO-2025-001", maxLength=30, nullable=true, description="발주번호"),
|
|
* @OA\Property(property="order_date", type="string", format="date", example="2025-12-20", nullable=true, description="발주일자"),
|
|
* @OA\Property(property="item_id", type="integer", example=1, nullable=true, description="품목 ID"),
|
|
* @OA\Property(property="item_code", type="string", example="ITEM-001", maxLength=50, description="품목코드"),
|
|
* @OA\Property(property="item_name", type="string", example="원재료 A", maxLength=200, description="품목명"),
|
|
* @OA\Property(property="specification", type="string", example="100x100", maxLength=200, nullable=true, description="규격"),
|
|
* @OA\Property(property="supplier", type="string", example="(주)공급사", maxLength=100, description="공급업체"),
|
|
* @OA\Property(property="order_qty", type="number", format="float", example=100.00, description="발주수량"),
|
|
* @OA\Property(property="order_unit", type="string", example="EA", maxLength=20, description="발주단위"),
|
|
* @OA\Property(property="due_date", type="string", format="date", example="2025-12-26", nullable=true, description="납기일"),
|
|
* @OA\Property(property="status", type="string", enum={"order_completed","shipping","inspection_pending","receiving_pending"}, example="order_completed", description="상태"),
|
|
* @OA\Property(property="remark", type="string", example="비고 내용", maxLength=1000, nullable=true, description="비고")
|
|
* )
|
|
*
|
|
* @OA\Schema(
|
|
* schema="ReceivingUpdateRequest",
|
|
* type="object",
|
|
* description="입고 수정 요청",
|
|
*
|
|
* @OA\Property(property="order_no", type="string", example="PO-2025-001", maxLength=30, nullable=true, description="발주번호"),
|
|
* @OA\Property(property="order_date", type="string", format="date", example="2025-12-20", nullable=true, description="발주일자"),
|
|
* @OA\Property(property="item_code", type="string", example="ITEM-001", maxLength=50, description="품목코드"),
|
|
* @OA\Property(property="item_name", type="string", example="원재료 A", maxLength=200, description="품목명"),
|
|
* @OA\Property(property="specification", type="string", example="100x100", maxLength=200, nullable=true, description="규격"),
|
|
* @OA\Property(property="supplier", type="string", example="(주)공급사", maxLength=100, description="공급업체"),
|
|
* @OA\Property(property="order_qty", type="number", format="float", example=100.00, description="발주수량"),
|
|
* @OA\Property(property="order_unit", type="string", example="EA", maxLength=20, description="발주단위"),
|
|
* @OA\Property(property="due_date", type="string", format="date", example="2025-12-26", nullable=true, description="납기일"),
|
|
* @OA\Property(property="status", type="string", enum={"order_completed","shipping","inspection_pending","receiving_pending"}, example="shipping", description="상태"),
|
|
* @OA\Property(property="remark", type="string", example="비고 내용", maxLength=1000, nullable=true, description="비고")
|
|
* )
|
|
*
|
|
* @OA\Schema(
|
|
* schema="ReceivingProcessRequest",
|
|
* type="object",
|
|
* required={"receiving_qty","receiving_location"},
|
|
* description="입고처리 요청",
|
|
*
|
|
* @OA\Property(property="receiving_qty", type="number", format="float", example=100.00, description="입고수량"),
|
|
* @OA\Property(property="receiving_date", type="string", format="date", example="2025-12-26", nullable=true, description="입고일자 (미입력시 오늘)"),
|
|
* @OA\Property(property="lot_no", type="string", example="251226-01", maxLength=50, nullable=true, description="LOT번호 (미입력시 자동생성)"),
|
|
* @OA\Property(property="supplier_lot", type="string", example="SUP-LOT-001", maxLength=50, nullable=true, description="공급업체 LOT"),
|
|
* @OA\Property(property="receiving_location", type="string", example="A-01-01", maxLength=100, description="입고위치"),
|
|
* @OA\Property(property="receiving_manager", type="string", example="홍길동", maxLength=50, nullable=true, description="입고담당"),
|
|
* @OA\Property(property="remark", type="string", example="비고 내용", maxLength=1000, nullable=true, description="비고")
|
|
* )
|
|
*/
|
|
class ReceivingApi
|
|
{
|
|
/**
|
|
* @OA\Get(
|
|
* path="/api/v1/receivings",
|
|
* tags={"Receivings"},
|
|
* summary="입고 목록 조회",
|
|
* description="입고 목록을 조회합니다.",
|
|
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
|
*
|
|
* @OA\Parameter(name="search", in="query", description="검색어 (발주번호, 품목코드, 품목명, 공급업체)", @OA\Schema(type="string")),
|
|
* @OA\Parameter(name="status", in="query", description="상태 (receiving_pending 선택시 inspection_pending도 포함)", @OA\Schema(type="string", enum={"order_completed","shipping","inspection_pending","receiving_pending","completed"})),
|
|
* @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="sort_by", in="query", description="정렬 기준", @OA\Schema(type="string", enum={"receiving_date","due_date","created_at"}, default="created_at")),
|
|
* @OA\Parameter(name="sort_dir", in="query", description="정렬 방향", @OA\Schema(type="string", enum={"asc","desc"}, default="desc")),
|
|
* @OA\Parameter(ref="#/components/parameters/Page"),
|
|
* @OA\Parameter(ref="#/components/parameters/Size"),
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="조회 성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
* allOf={
|
|
*
|
|
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
|
* @OA\Schema(
|
|
*
|
|
* @OA\Property(
|
|
* property="data",
|
|
* type="object",
|
|
* @OA\Property(property="current_page", type="integer", example=1),
|
|
* @OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/Receiving")),
|
|
* @OA\Property(property="per_page", type="integer", example=20),
|
|
* @OA\Property(property="total", type="integer", example=50)
|
|
* )
|
|
* )
|
|
* }
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=500, description="서버 에러", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
|
* )
|
|
*/
|
|
public function index() {}
|
|
|
|
/**
|
|
* @OA\Get(
|
|
* path="/api/v1/receivings/stats",
|
|
* tags={"Receivings"},
|
|
* summary="입고 통계 조회",
|
|
* description="입고 현황 통계를 조회합니다.",
|
|
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="조회 성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
* allOf={
|
|
*
|
|
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
|
* @OA\Schema(
|
|
*
|
|
* @OA\Property(property="data", ref="#/components/schemas/ReceivingStats")
|
|
* )
|
|
* }
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=500, description="서버 에러", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
|
* )
|
|
*/
|
|
public function stats() {}
|
|
|
|
/**
|
|
* @OA\Post(
|
|
* path="/api/v1/receivings",
|
|
* tags={"Receivings"},
|
|
* summary="입고 등록",
|
|
* description="새로운 입고를 등록합니다. 입고번호는 자동 생성됩니다.",
|
|
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
|
*
|
|
* @OA\RequestBody(
|
|
* required=true,
|
|
*
|
|
* @OA\JsonContent(ref="#/components/schemas/ReceivingCreateRequest")
|
|
* ),
|
|
*
|
|
* @OA\Response(
|
|
* response=201,
|
|
* description="등록 성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
* allOf={
|
|
*
|
|
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
|
* @OA\Schema(
|
|
*
|
|
* @OA\Property(property="data", ref="#/components/schemas/Receiving")
|
|
* )
|
|
* }
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=400, description="잘못된 요청", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=500, description="서버 에러", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
|
* )
|
|
*/
|
|
public function store() {}
|
|
|
|
/**
|
|
* @OA\Get(
|
|
* path="/api/v1/receivings/{id}",
|
|
* tags={"Receivings"},
|
|
* summary="입고 상세 조회",
|
|
* description="입고 상세 정보를 조회합니다.",
|
|
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
|
*
|
|
* @OA\Parameter(name="id", in="path", required=true, description="입고 ID", @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/Receiving")
|
|
* )
|
|
* }
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=404, description="입고 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=500, description="서버 에러", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
|
* )
|
|
*/
|
|
public function show() {}
|
|
|
|
/**
|
|
* @OA\Put(
|
|
* path="/api/v1/receivings/{id}",
|
|
* tags={"Receivings"},
|
|
* summary="입고 수정",
|
|
* description="입고 정보를 수정합니다. 입고완료(completed) 상태에서는 수정 불가합니다.",
|
|
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
|
*
|
|
* @OA\Parameter(name="id", in="path", required=true, description="입고 ID", @OA\Schema(type="integer")),
|
|
*
|
|
* @OA\RequestBody(
|
|
* required=true,
|
|
*
|
|
* @OA\JsonContent(ref="#/components/schemas/ReceivingUpdateRequest")
|
|
* ),
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="수정 성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
* allOf={
|
|
*
|
|
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
|
* @OA\Schema(
|
|
*
|
|
* @OA\Property(property="data", ref="#/components/schemas/Receiving")
|
|
* )
|
|
* }
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=400, description="잘못된 요청 또는 수정 불가", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=404, description="입고 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=500, description="서버 에러", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
|
* )
|
|
*/
|
|
public function update() {}
|
|
|
|
/**
|
|
* @OA\Delete(
|
|
* path="/api/v1/receivings/{id}",
|
|
* tags={"Receivings"},
|
|
* summary="입고 삭제",
|
|
* description="입고를 삭제합니다. 입고완료(completed) 상태에서는 삭제 불가합니다. (Soft Delete)",
|
|
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
|
*
|
|
* @OA\Parameter(name="id", in="path", required=true, description="입고 ID", @OA\Schema(type="integer")),
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="삭제 성공",
|
|
*
|
|
* @OA\JsonContent(ref="#/components/schemas/ApiResponse")
|
|
* ),
|
|
*
|
|
* @OA\Response(response=400, description="삭제 불가", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=404, description="입고 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=500, description="서버 에러", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
|
* )
|
|
*/
|
|
public function destroy() {}
|
|
|
|
/**
|
|
* @OA\Post(
|
|
* path="/api/v1/receivings/{id}/process",
|
|
* tags={"Receivings"},
|
|
* summary="입고처리",
|
|
* description="입고를 처리합니다. 입고수량, 입고위치, LOT번호 등을 입력하고 상태를 '입고완료'로 변경합니다.",
|
|
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
|
|
*
|
|
* @OA\Parameter(name="id", in="path", required=true, description="입고 ID", @OA\Schema(type="integer")),
|
|
*
|
|
* @OA\RequestBody(
|
|
* required=true,
|
|
*
|
|
* @OA\JsonContent(ref="#/components/schemas/ReceivingProcessRequest")
|
|
* ),
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="입고처리 성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
* allOf={
|
|
*
|
|
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
|
|
* @OA\Schema(
|
|
*
|
|
* @OA\Property(property="data", ref="#/components/schemas/Receiving")
|
|
* )
|
|
* }
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=400, description="입고처리 불가", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=401, description="인증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=404, description="입고 없음", @OA\JsonContent(ref="#/components/schemas/ErrorResponse")),
|
|
* @OA\Response(response=500, description="서버 에러", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
|
|
* )
|
|
*/
|
|
public function process() {}
|
|
}
|