diff --git a/app/Http/Controllers/Api/V1/EmployeeController.php b/app/Http/Controllers/Api/V1/EmployeeController.php index 5f1a4f7..9783dc9 100644 --- a/app/Http/Controllers/Api/V1/EmployeeController.php +++ b/app/Http/Controllers/Api/V1/EmployeeController.php @@ -109,6 +109,17 @@ public function createAccount(int $id, Request $request): JsonResponse return ApiResponse::handle(function () use ($id, $request) { return $this->service->createAccount($id, $request->input('password')); - }, __('message.updated')); + }, __('employee.account_created')); + } + + /** + * 시스템 계정 해제 (로그인 불가, 사원 정보 유지) + * POST /v1/employees/{id}/revoke-account + */ + public function revokeAccount(int $id): JsonResponse + { + return ApiResponse::handle(function () use ($id) { + return $this->service->revokeAccount($id); + }, __('employee.account_revoked')); } } diff --git a/app/Services/EmployeeService.php b/app/Services/EmployeeService.php index 903d1cf..7069c1f 100644 --- a/app/Services/EmployeeService.php +++ b/app/Services/EmployeeService.php @@ -89,6 +89,10 @@ public function show(int $id): TenantUserProfile /** * 사원 등록 (users 테이블에 사용자 생성 + tenant_user_profiles 생성) + * + * @param array $data 사원 데이터 + * - create_account: bool (true=시스템 계정 생성, false=사원 전용) + * - password: string (create_account=true일 때 필수) */ public function store(array $data): TenantUserProfile { @@ -96,25 +100,32 @@ public function store(array $data): TenantUserProfile $userId = $this->apiUserId(); return DB::transaction(function () use ($data, $tenantId, $userId) { - // 1. users 테이블에 사용자 생성 + // 1. 비밀번호 결정: create_account=false면 NULL (사원 전용, 로그인 불가) + $password = null; + $createAccount = $data['create_account'] ?? false; + if ($createAccount && ! empty($data['password'])) { + $password = Hash::make($data['password']); + } + + // 2. users 테이블에 사용자 생성 $user = User::create([ 'user_id' => $data['user_id'] ?? $this->generateUserId($data['email']), 'name' => $data['name'], 'email' => $data['email'], 'phone' => $data['phone'] ?? null, - 'password' => Hash::make($data['password'] ?? Str::random(16)), + 'password' => $password, 'is_active' => $data['is_active'] ?? true, 'created_by' => $userId, ]); - // 2. user_tenants pivot에 관계 추가 + // 3. user_tenants pivot에 관계 추가 $user->tenantsMembership()->attach($tenantId, [ 'is_active' => true, 'is_default' => true, 'joined_at' => now(), ]); - // 3. tenant_user_profiles 생성 + // 4. tenant_user_profiles 생성 $profile = TenantUserProfile::create([ 'tenant_id' => $tenantId, 'user_id' => $user->id, @@ -129,7 +140,7 @@ public function store(array $data): TenantUserProfile 'display_name' => $data['display_name'] ?? null, ]); - // 4. json_extra 사원 정보 설정 + // 5. json_extra 사원 정보 설정 $profile->updateEmployeeInfo([ 'employee_code' => $data['employee_code'] ?? null, 'resident_number' => $data['resident_number'] ?? null, @@ -331,6 +342,43 @@ public function createAccount(int $id, string $password): TenantUserProfile return $profile->fresh(['user', 'department', 'manager']); } + /** + * 시스템 계정 해제 (비밀번호 제거 → 로그인 불가, 사원 정보 유지) + */ + public function revokeAccount(int $id): TenantUserProfile + { + $tenantId = $this->tenantId(); + $userId = $this->apiUserId(); + + $profile = TenantUserProfile::query() + ->where('tenant_id', $tenantId) + ->with('user') + ->find($id); + + if (! $profile) { + throw new NotFoundHttpException(__('error.not_found')); + } + + $user = $profile->user; + + // 이미 계정이 없는 경우 + if (empty($user->password)) { + throw new \InvalidArgumentException(__('employee.no_account')); + } + + // 1. 비밀번호 제거 (로그인 불가) + $user->update([ + 'password' => null, + 'must_change_password' => false, + 'updated_by' => $userId, + ]); + + // 2. 기존 토큰 무효화 (로그아웃 처리) + $user->tokens()->delete(); + + return $profile->fresh(['user', 'department', 'manager']); + } + /** * 사용자 ID 자동 생성 */ diff --git a/lang/en/employee.php b/lang/en/employee.php new file mode 100644 index 0000000..afeebd3 --- /dev/null +++ b/lang/en/employee.php @@ -0,0 +1,22 @@ + 'System account has been created.', + 'account_revoked' => 'System account has been revoked.', + 'no_account' => 'This employee does not have a system account.', + 'already_has_account' => 'This employee already has a system account.', + + // Employee status + 'status_changed' => 'Employee status has been changed.', + 'resigned' => 'Employee has been marked as resigned.', + + // Employee registration + 'created_employee_only' => 'Employee registered. (No login account)', + 'created_with_account' => 'Employee registered. (System account created)', +]; diff --git a/lang/ko/employee.php b/lang/ko/employee.php new file mode 100644 index 0000000..7fea9f6 --- /dev/null +++ b/lang/ko/employee.php @@ -0,0 +1,22 @@ + '시스템 계정이 생성되었습니다.', + 'account_revoked' => '시스템 계정이 해제되었습니다.', + 'no_account' => '시스템 계정이 없는 사원입니다.', + 'already_has_account' => '이미 시스템 계정을 보유하고 있습니다.', + + // 사원 상태 + 'status_changed' => '사원 상태가 변경되었습니다.', + 'resigned' => '퇴직 처리되었습니다.', + + // 사원 등록 + 'created_employee_only' => '사원이 등록되었습니다. (로그인 계정 없음)', + 'created_with_account' => '사원이 등록되었습니다. (시스템 계정 생성됨)', +]; diff --git a/routes/api.php b/routes/api.php index a536b44..23befaf 100644 --- a/routes/api.php +++ b/routes/api.php @@ -286,6 +286,7 @@ Route::delete('/{id}', [EmployeeController::class, 'destroy'])->name('v1.employees.destroy'); Route::post('/bulk-delete', [EmployeeController::class, 'bulkDelete'])->name('v1.employees.bulkDelete'); Route::post('/{id}/create-account', [EmployeeController::class, 'createAccount'])->name('v1.employees.createAccount'); + Route::post('/{id}/revoke-account', [EmployeeController::class, 'revokeAccount'])->name('v1.employees.revokeAccount'); }); // Attendance API (근태 관리)