setUpAuthenticatedUser(); // 공정 $this->process = Process::create([ 'tenant_id' => $this->tenant->id, 'process_code' => 'PRC-'.uniqid(), 'process_name' => '스크린 조립', 'process_type' => 'assembly', 'is_active' => true, 'created_by' => $this->user->id, 'updated_by' => $this->user->id, ]); // 수주 (작업지시의 부모) $client = Client::create([ 'tenant_id' => $this->tenant->id, 'name' => '테스트 거래처', 'client_code' => 'CLI'.uniqid(), 'is_active' => true, 'created_by' => $this->user->id, ]); $this->order = Order::create([ 'tenant_id' => $this->tenant->id, 'order_no' => 'ORD-'.uniqid(), 'order_type_code' => Order::TYPE_ORDER, 'status_code' => Order::STATUS_CONFIRMED, 'client_id' => $client->id, 'client_name' => $client->name, 'quantity' => 10, 'supply_amount' => 1000000, 'tax_amount' => 100000, 'total_amount' => 1100000, 'created_by' => $this->user->id, 'updated_by' => $this->user->id, ]); } // ==================== 목록/통계 조회 ==================== public function test_작업지시_목록_조회(): void { $this->createWorkOrder(); $response = $this->api('get', '/api/v1/work-orders'); $this->assertApiPaginated($response); } public function test_작업지시_통계_조회(): void { $this->createWorkOrder(); $response = $this->api('get', '/api/v1/work-orders/stats'); $response->assertStatus(200) ->assertJsonStructure(['success', 'data']); } // ==================== CRUD ==================== public function test_작업지시_생성_성공(): void { $response = $this->api('post', '/api/v1/work-orders', [ 'process_id' => $this->process->id, 'sales_order_id' => $this->order->id, 'assignee_ids' => [$this->user->id], 'priority' => 5, 'scheduled_date' => now()->addDays(7)->format('Y-m-d'), 'memo' => '테스트 작업지시', ]); $response->assertStatus(200); $this->assertTrue($response->json('success')); $this->assertDatabaseHas('work_orders', [ 'process_id' => $this->process->id, 'sales_order_id' => $this->order->id, 'tenant_id' => $this->tenant->id, ]); } public function test_작업지시_상세_조회(): void { $wo = $this->createWorkOrder(); $response = $this->api('get', "/api/v1/work-orders/{$wo->id}"); $this->assertApiSuccess($response); $data = $response->json('data'); $this->assertEquals($wo->id, $data['id']); } public function test_작업지시_수정(): void { $wo = $this->createWorkOrder(); $response = $this->api('put', "/api/v1/work-orders/{$wo->id}", [ 'memo' => '수정된 메모', 'priority' => 1, ]); $response->assertStatus(200); $this->assertDatabaseHas('work_orders', [ 'id' => $wo->id, 'memo' => '수정된 메모', ]); } public function test_작업지시_삭제(): void { $wo = $this->createWorkOrder(); $response = $this->api('delete', "/api/v1/work-orders/{$wo->id}"); $response->assertStatus(200); $this->assertSoftDeleted('work_orders', ['id' => $wo->id]); } public function test_존재하지_않는_작업지시_조회시_404(): void { $response = $this->api('get', '/api/v1/work-orders/999999'); $response->assertStatus(404); } // ==================== 상태 전이 ==================== public function test_상태_미배정에서_대기로_변경(): void { $wo = $this->createWorkOrder(WorkOrder::STATUS_UNASSIGNED); $response = $this->api('patch', "/api/v1/work-orders/{$wo->id}/status", [ 'status' => WorkOrder::STATUS_PENDING, ]); $response->assertStatus(200); $this->assertDatabaseHas('work_orders', [ 'id' => $wo->id, 'status' => WorkOrder::STATUS_PENDING, ]); } public function test_상태_대기에서_준비중으로_변경(): void { $wo = $this->createWorkOrder(WorkOrder::STATUS_PENDING); $response = $this->api('patch', "/api/v1/work-orders/{$wo->id}/status", [ 'status' => WorkOrder::STATUS_WAITING, ]); $response->assertStatus(200); $this->assertDatabaseHas('work_orders', [ 'id' => $wo->id, 'status' => WorkOrder::STATUS_WAITING, ]); } public function test_상태_준비중에서_진행중으로_변경(): void { $wo = $this->createWorkOrder(WorkOrder::STATUS_WAITING); $response = $this->api('patch', "/api/v1/work-orders/{$wo->id}/status", [ 'status' => WorkOrder::STATUS_IN_PROGRESS, ]); $response->assertStatus(200); $this->assertDatabaseHas('work_orders', [ 'id' => $wo->id, 'status' => WorkOrder::STATUS_IN_PROGRESS, ]); } public function test_상태_진행중에서_완료로_변경(): void { $wo = $this->createWorkOrder(WorkOrder::STATUS_IN_PROGRESS); $response = $this->api('patch', "/api/v1/work-orders/{$wo->id}/status", [ 'status' => WorkOrder::STATUS_COMPLETED, ]); $response->assertStatus(200); $this->assertDatabaseHas('work_orders', [ 'id' => $wo->id, 'status' => WorkOrder::STATUS_COMPLETED, ]); } // ==================== 담당자 배정 ==================== public function test_담당자_배정(): void { $wo = $this->createWorkOrder(WorkOrder::STATUS_UNASSIGNED); $response = $this->api('patch', "/api/v1/work-orders/{$wo->id}/assign", [ 'assignee_id' => $this->user->id, ]); $response->assertStatus(200); $this->assertDatabaseHas('work_orders', [ 'id' => $wo->id, 'assignee_id' => $this->user->id, ]); } // ==================== 공정 단계 ==================== public function test_공정_단계_진행_조회(): void { $wo = $this->createWorkOrder(); $response = $this->api('get', "/api/v1/work-orders/{$wo->id}/step-progress"); $response->assertStatus(200) ->assertJsonStructure(['success', 'data']); } // ==================== 자재 ==================== public function test_자재_목록_조회(): void { $wo = $this->createWorkOrder(); $response = $this->api('get', "/api/v1/work-orders/{$wo->id}/materials"); $response->assertStatus(200) ->assertJsonStructure(['success', 'data']); } public function test_자재_투입_이력_조회(): void { $wo = $this->createWorkOrder(); $response = $this->api('get', "/api/v1/work-orders/{$wo->id}/material-input-history"); $response->assertStatus(200) ->assertJsonStructure(['success', 'data']); } // ==================== 인증 ==================== public function test_미인증_요청시_401(): void { $response = $this->withHeaders([ 'X-API-KEY' => $this->apiKey, 'Accept' => 'application/json', ])->getJson('/api/v1/work-orders'); $response->assertStatus(401); } // ==================== 헬퍼 ==================== private function createWorkOrder(string $status = WorkOrder::STATUS_UNASSIGNED): WorkOrder { return WorkOrder::create([ 'tenant_id' => $this->tenant->id, 'work_order_no' => 'WO-'.uniqid(), 'sales_order_id' => $this->order->id, 'process_id' => $this->process->id, 'project_name' => '테스트 프로젝트', 'status' => $status, 'priority' => 5, 'scheduled_date' => now()->addDays(7), 'memo' => '테스트 작업지시', 'is_active' => true, 'created_by' => $this->user->id, 'updated_by' => $this->user->id, ]); } }