From 165512e121ad6161f5562eb1d269ae070388ad82 Mon Sep 17 00:00:00 2001 From: hskwon Date: Thu, 4 Dec 2025 15:40:24 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[client]=20Client=20API=20=EC=82=AC?= =?UTF-8?q?=EC=97=85=EC=9E=90=20=EC=A0=95=EB=B3=B4=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EB=B0=8F=20toggle=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - business_no, business_type, business_item 필드 추가 - toggle 로직 수정: boolean 캐스팅 호환 (is_active === 'Y' → !is_active) --- .../Requests/Client/ClientStoreRequest.php | 3 + .../Requests/Client/ClientUpdateRequest.php | 4 +- app/Models/Orders/Client.php | 3 + app/Services/ClientService.php | 9 +- app/Swagger/v1/ClientApi.php | 9 + claudedocs/flow-tester-client.json | 223 ++++++++++++++++++ ...2_add_business_fields_to_clients_table.php | 30 +++ 7 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 claudedocs/flow-tester-client.json create mode 100644 database/migrations/2025_12_04_145912_add_business_fields_to_clients_table.php diff --git a/app/Http/Requests/Client/ClientStoreRequest.php b/app/Http/Requests/Client/ClientStoreRequest.php index 04c7770..9430654 100644 --- a/app/Http/Requests/Client/ClientStoreRequest.php +++ b/app/Http/Requests/Client/ClientStoreRequest.php @@ -21,6 +21,9 @@ public function rules(): array 'phone' => 'nullable|string|max:20', 'email' => 'nullable|email|max:100', 'address' => 'nullable|string|max:255', + 'business_no' => 'nullable|string|max:20', + 'business_type' => 'nullable|string|max:50', + 'business_item' => 'nullable|string|max:100', 'is_active' => 'nullable|in:Y,N', ]; } diff --git a/app/Http/Requests/Client/ClientUpdateRequest.php b/app/Http/Requests/Client/ClientUpdateRequest.php index 3b48db7..c99b46a 100644 --- a/app/Http/Requests/Client/ClientUpdateRequest.php +++ b/app/Http/Requests/Client/ClientUpdateRequest.php @@ -20,7 +20,9 @@ public function rules(): array 'contact_person' => 'nullable|string|max:100', 'phone' => 'nullable|string|max:20', 'email' => 'nullable|email|max:100', - 'address' => 'nullable|string|max:255', + 'address' => 'nullable|string|max:255', 'business_no' => 'nullable|string|max:20', + 'business_type' => 'nullable|string|max:50', + 'business_item' => 'nullable|string|max:100', 'is_active' => 'nullable|in:Y,N', ]; } diff --git a/app/Models/Orders/Client.php b/app/Models/Orders/Client.php index 1ee3489..d343c14 100644 --- a/app/Models/Orders/Client.php +++ b/app/Models/Orders/Client.php @@ -19,6 +19,9 @@ class Client extends Model 'phone', 'email', 'address', + 'business_no', + 'business_type', + 'business_item', 'is_active', ]; diff --git a/app/Services/ClientService.php b/app/Services/ClientService.php index 56aed5e..978e7c5 100644 --- a/app/Services/ClientService.php +++ b/app/Services/ClientService.php @@ -63,6 +63,9 @@ public function store(array $params) 'phone' => 'nullable|string|max:30', 'email' => 'nullable|email|max:80', 'address' => 'nullable|string|max:255', + 'business_no' => 'nullable|string|max:20', + 'business_type' => 'nullable|string|max:50', + 'business_item' => 'nullable|string|max:100', 'is_active' => 'nullable|in:Y,N', ]); @@ -104,6 +107,9 @@ public function update(int $id, array $params) 'phone' => 'nullable|string|max:30', 'email' => 'nullable|email|max:80', 'address' => 'nullable|string|max:255', + 'business_no' => 'nullable|string|max:20', + 'business_type' => 'nullable|string|max:50', + 'business_item' => 'nullable|string|max:100', 'is_active' => 'nullable|in:Y,N', ]); @@ -157,7 +163,8 @@ public function toggle(int $id) throw new NotFoundHttpException(__('error.not_found')); } - $client->is_active = $client->is_active === 'Y' ? 'N' : 'Y'; + // Model에서 is_active가 boolean으로 캐스팅되므로 boolean으로 토글 + $client->is_active = ! $client->is_active; $client->save(); return $client->refresh(); diff --git a/app/Swagger/v1/ClientApi.php b/app/Swagger/v1/ClientApi.php index 74a765c..1e85991 100644 --- a/app/Swagger/v1/ClientApi.php +++ b/app/Swagger/v1/ClientApi.php @@ -19,6 +19,9 @@ * @OA\Property(property="phone", type="string", nullable=true, example="010-1234-5678"), * @OA\Property(property="email", type="string", nullable=true, example="client@example.com"), * @OA\Property(property="address", type="string", nullable=true, example="서울시 강남구"), + * @OA\Property(property="business_no", type="string", nullable=true, maxLength=20, example="123-45-67890", description="사업자등록번호"), + * @OA\Property(property="business_type", type="string", nullable=true, maxLength=50, example="제조업", description="업태"), + * @OA\Property(property="business_item", type="string", nullable=true, maxLength=100, example="전자부품", description="업종"), * @OA\Property(property="is_active", type="string", enum={"Y", "N"}, example="Y"), * @OA\Property(property="created_at", type="string", example="2025-10-01 12:00:00"), * @OA\Property(property="updated_at", type="string", example="2025-10-01 12:00:00") @@ -71,6 +74,9 @@ * @OA\Property(property="phone", type="string", nullable=true, maxLength=20, example="010-1234-5678"), * @OA\Property(property="email", type="string", nullable=true, maxLength=100, example="client@example.com"), * @OA\Property(property="address", type="string", nullable=true, maxLength=255, example="서울시 강남구"), + * @OA\Property(property="business_no", type="string", nullable=true, maxLength=20, example="123-45-67890", description="사업자등록번호"), + * @OA\Property(property="business_type", type="string", nullable=true, maxLength=50, example="제조업", description="업태"), + * @OA\Property(property="business_item", type="string", nullable=true, maxLength=100, example="전자부품", description="업종"), * @OA\Property(property="is_active", type="string", enum={"Y", "N"}, example="Y") * ) * @@ -85,6 +91,9 @@ * @OA\Property(property="phone", type="string", nullable=true, maxLength=20), * @OA\Property(property="email", type="string", nullable=true, maxLength=100), * @OA\Property(property="address", type="string", nullable=true, maxLength=255), + * @OA\Property(property="business_no", type="string", nullable=true, maxLength=20, description="사업자등록번호"), + * @OA\Property(property="business_type", type="string", nullable=true, maxLength=50, description="업태"), + * @OA\Property(property="business_item", type="string", nullable=true, maxLength=100, description="업종"), * @OA\Property(property="is_active", type="string", enum={"Y", "N"}) * ) */ diff --git a/claudedocs/flow-tester-client.json b/claudedocs/flow-tester-client.json new file mode 100644 index 0000000..72d39e2 --- /dev/null +++ b/claudedocs/flow-tester-client.json @@ -0,0 +1,223 @@ +{ + "name": "Client API CRUD 테스트", + "description": "거래처(Client) API 전체 CRUD 테스트 - 생성, 조회, 수정, 토글, 삭제 포함. business_no, business_type, business_item 신규 필드 검증 포함.", + "version": "1.0", + "config": { + "baseUrl": "https://api.sam.kr/api/v1", + "apiKey": "{{$env.FLOW_TESTER_API_KEY}}", + "timeout": 30000, + "stopOnFailure": true + }, + "variables": { + "user_id": "{{$env.FLOW_TESTER_USER_ID}}", + "user_pwd": "{{$env.FLOW_TESTER_USER_PWD}}", + "test_client_code": "TEST_CLIENT_{{$timestamp}}" + }, + "steps": [ + { + "id": "login", + "name": "1. 로그인 - 토큰 획득", + "method": "POST", + "endpoint": "/login", + "body": { + "user_id": "{{variables.user_id}}", + "user_pwd": "{{variables.user_pwd}}" + }, + "expect": { + "status": [200], + "jsonPath": { + "$.access_token": "@isString" + } + }, + "extract": { + "token": "$.access_token" + } + }, + { + "id": "create_client", + "name": "2. 거래처 생성 (신규 필드 포함)", + "method": "POST", + "endpoint": "/clients", + "headers": { + "Authorization": "Bearer {{login.token}}" + }, + "body": { + "client_code": "{{test_client_code}}", + "name": "테스트 거래처", + "contact_person": "홍길동", + "phone": "02-1234-5678", + "email": "test@example.com", + "address": "서울시 강남구 테헤란로 123", + "business_no": "123-45-67890", + "business_type": "제조업", + "business_item": "전자부품", + "is_active": "Y" + }, + "expect": { + "status": [200], + "jsonPath": { + "$.success": true, + "$.data.id": "@isNumber", + "$.data.client_code": "{{test_client_code}}", + "$.data.name": "테스트 거래처", + "$.data.business_no": "123-45-67890", + "$.data.business_type": "제조업", + "$.data.business_item": "전자부품" + } + }, + "extract": { + "client_id": "$.data.id" + } + }, + { + "id": "list_clients", + "name": "3. 거래처 목록 조회", + "method": "GET", + "endpoint": "/clients", + "headers": { + "Authorization": "Bearer {{login.token}}" + }, + "params": { + "page": 1, + "size": 20, + "q": "테스트" + }, + "expect": { + "status": [200], + "jsonPath": { + "$.success": true, + "$.data.data": "@isArray", + "$.data.current_page": 1 + } + } + }, + { + "id": "show_client", + "name": "4. 거래처 단건 조회", + "method": "GET", + "endpoint": "/clients/{{create_client.client_id}}", + "headers": { + "Authorization": "Bearer {{login.token}}" + }, + "expect": { + "status": [200], + "jsonPath": { + "$.success": true, + "$.data.id": "{{create_client.client_id}}", + "$.data.client_code": "{{test_client_code}}", + "$.data.business_no": "123-45-67890", + "$.data.business_type": "제조업", + "$.data.business_item": "전자부품" + } + } + }, + { + "id": "update_client", + "name": "5. 거래처 수정 (신규 필드 변경)", + "method": "PUT", + "endpoint": "/clients/{{create_client.client_id}}", + "headers": { + "Authorization": "Bearer {{login.token}}" + }, + "body": { + "name": "테스트 거래처 (수정됨)", + "contact_person": "김철수", + "business_no": "987-65-43210", + "business_type": "도소매업", + "business_item": "IT솔루션" + }, + "expect": { + "status": [200], + "jsonPath": { + "$.success": true, + "$.data.name": "테스트 거래처 (수정됨)", + "$.data.contact_person": "김철수", + "$.data.business_no": "987-65-43210", + "$.data.business_type": "도소매업", + "$.data.business_item": "IT솔루션" + } + } + }, + { + "id": "toggle_client", + "name": "6. 거래처 활성/비활성 토글", + "method": "PATCH", + "endpoint": "/clients/{{create_client.client_id}}/toggle", + "headers": { + "Authorization": "Bearer {{login.token}}" + }, + "expect": { + "status": [200], + "jsonPath": { + "$.success": true, + "$.data.is_active": "N" + } + } + }, + { + "id": "toggle_client_back", + "name": "7. 거래처 토글 복원 (Y로)", + "method": "PATCH", + "endpoint": "/clients/{{create_client.client_id}}/toggle", + "headers": { + "Authorization": "Bearer {{login.token}}" + }, + "expect": { + "status": [200], + "jsonPath": { + "$.success": true, + "$.data.is_active": "Y" + } + } + }, + { + "id": "list_active_only", + "name": "8. 활성 거래처만 조회", + "method": "GET", + "endpoint": "/clients", + "headers": { + "Authorization": "Bearer {{login.token}}" + }, + "params": { + "only_active": true + }, + "expect": { + "status": [200], + "jsonPath": { + "$.success": true, + "$.data.data": "@isArray" + } + } + }, + { + "id": "delete_client", + "name": "9. 거래처 삭제", + "method": "DELETE", + "endpoint": "/clients/{{create_client.client_id}}", + "headers": { + "Authorization": "Bearer {{login.token}}" + }, + "expect": { + "status": [200], + "jsonPath": { + "$.success": true + } + } + }, + { + "id": "verify_deleted", + "name": "10. 삭제 확인 (404 예상)", + "method": "GET", + "endpoint": "/clients/{{create_client.client_id}}", + "headers": { + "Authorization": "Bearer {{login.token}}" + }, + "expect": { + "status": [404], + "jsonPath": { + "$.success": false + } + } + } + ] +} diff --git a/database/migrations/2025_12_04_145912_add_business_fields_to_clients_table.php b/database/migrations/2025_12_04_145912_add_business_fields_to_clients_table.php new file mode 100644 index 0000000..ead269c --- /dev/null +++ b/database/migrations/2025_12_04_145912_add_business_fields_to_clients_table.php @@ -0,0 +1,30 @@ +string('business_no', 20)->nullable()->after('address')->comment('사업자등록번호'); + $table->string('business_type', 50)->nullable()->after('business_no')->comment('업태'); + $table->string('business_item', 100)->nullable()->after('business_type')->comment('업종'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('clients', function (Blueprint $table) { + $table->dropColumn(['business_no', 'business_type', 'business_item']); + }); + } +};