jasan/api.php
2026-03-14 21:51:23 +09:00

1148 lines
No EOL
50 KiB
PHP

<?php
require_once 'config.php';
header('Content-Type: application/json; charset=utf-8');
session_start();
$action = $_GET['action'] ?? '';
// 로그인 세션 체크 (login 액션 제외)
$public_actions = ['login'];
if (!isset($_SESSION['admin_id']) && !in_array($action, $public_actions)) {
http_response_code(401);
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
exit;
}
try {
switch ($action) {
case 'login':
$data = json_decode(file_get_contents('php://input'), true);
$login_id = $data['login_id'] ?? '';
$password = $data['password'] ?? '';
$stmt = $db->prepare("SELECT * FROM administrators WHERE login_id = ?");
$stmt->execute([$login_id]);
$admin = $stmt->fetch();
if ($admin && password_verify($password, $admin['password_hash'])) {
$_SESSION['admin_id'] = $admin['id'];
$_SESSION['admin_login_id'] = $admin['login_id'];
$_SESSION['admin_name'] = $admin['admin_name'];
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'error' => '아이디 또는 비밀번호가 일치하지 않습니다.']);
}
break;
case 'logout':
session_destroy();
echo json_encode(['success' => true]);
break;
case 'get_admins':
$stmt = $db->query("SELECT id, login_id, admin_name, created_at FROM administrators ORDER BY id ASC");
echo json_encode($stmt->fetchAll());
break;
case 'add_admin':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("INSERT INTO administrators (login_id, password_hash, admin_name) VALUES (?, ?, ?)");
$stmt->execute([
$data['login_id'],
password_hash($data['password'], PASSWORD_DEFAULT),
$data['admin_name']
]);
echo json_encode(['success' => true]);
break;
case 'update_admin_password':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("UPDATE administrators SET password_hash = ? WHERE id = ?");
$stmt->execute([
password_hash($data['password'], PASSWORD_DEFAULT),
$data['id']
]);
echo json_encode(['success' => true]);
break;
case 'delete_admin':
$data = json_decode(file_get_contents('php://input'), true);
if ($data['id'] == $_SESSION['admin_id']) {
echo json_encode(['success' => false, 'error' => '자기 자신은 삭제할 수 없습니다.']);
break;
}
$stmt = $db->prepare("DELETE FROM administrators WHERE id = ?");
$stmt->execute([$data['id']]);
echo json_encode(['success' => true]);
break;
case 'get_dashboard_stats':
// Laptops
$laptop_total = $db->query("SELECT COUNT(*) FROM laptop_assets WHERE status != 'disposed'")->fetchColumn();
$laptop_assigned = $db->query("SELECT COUNT(*) FROM laptop_assets WHERE status = 'assigned'")->fetchColumn();
$laptop_disposed = $db->query("SELECT COUNT(*) FROM laptop_assets WHERE status = 'disposed'")->fetchColumn();
// Cards
$card_total = $db->query("SELECT COUNT(*) FROM access_cards")->fetchColumn();
$card_assigned = $db->query("SELECT COUNT(*) FROM access_cards WHERE status = 'assigned'")->fetchColumn();
// MFP
$mfp_total = $db->query("SELECT COUNT(*) FROM mfp_accounts")->fetchColumn();
$mfp_active = $db->query("SELECT COUNT(*) FROM mfp_accounts WHERE status = 'active'")->fetchColumn();
// Users
$users_general = $db->query("SELECT COUNT(*) FROM users WHERE accounting_type = '일반회계'")->fetchColumn();
$users_special = $db->query("SELECT COUNT(*) FROM users WHERE accounting_type = '특별회계'")->fetchColumn();
// Asset Replacement Analysis (4 years)
$current_year = (int)date('Y');
// 4년 이상 경과 (매각 대상)
$replacement_targets = $db->query("SELECT a.*, m.model_name, m.manufacturer
FROM laptop_assets a
LEFT JOIN laptop_models m ON a.model_id = m.id
WHERE a.status != 'disposed'
AND a.purchase_date IS NOT NULL
AND CAST(SUBSTR(a.purchase_date, 1, 4) AS INTEGER) <= " . ($current_year - 4))
->fetchAll();
// 4년차 도래 (올해 매각 예정 대상)
// 예: 2022년 구매 -> 2026년 매각대상 (만 4년이 되는 해)
$upcoming_replacements = $db->query("SELECT a.*, m.model_name, m.manufacturer
FROM laptop_assets a
LEFT JOIN laptop_models m ON a.model_id = m.id
WHERE a.status != 'disposed'
AND a.purchase_date IS NOT NULL
AND CAST(SUBSTR(a.purchase_date, 1, 4) AS INTEGER) = " . ($current_year - 3))
->fetchAll();
echo json_encode([
'laptops' => ['total' => (int) $laptop_total, 'assigned' => (int) $laptop_assigned, 'disposed' => (int) $laptop_disposed],
'cards' => ['total' => (int) $card_total, 'assigned' => (int) $card_assigned],
'mfp' => ['total' => (int) $mfp_total, 'active' => (int) $mfp_active],
'users' => ['general' => (int) $users_general, 'special' => (int) $users_special],
'replacements' => [
'targets' => $replacement_targets,
'upcoming' => $upcoming_replacements,
'current_year' => $current_year
]
]);
break;
break;
case 'get_users':
$query = "WITH RECURSIVE dept_path(id, path) AS (
SELECT id, name FROM departments WHERE parent_id IS NULL
UNION ALL
SELECT d.id, dp.path || ' > ' || d.name
FROM departments d JOIN dept_path dp ON d.parent_id = dp.id
)
SELECT u.*, dp.path as dept_name,
(SELECT asset_tag FROM laptop_assets WHERE current_user_id = u.id ORDER BY id DESC LIMIT 1) as laptop_tag,
(SELECT purchase_date FROM laptop_assets WHERE current_user_id = u.id ORDER BY id DESC LIMIT 1) as laptop_purchase_date,
(SELECT m.model_name FROM laptop_assets a JOIN laptop_models m ON a.model_id = m.id WHERE a.current_user_id = u.id ORDER
BY a.id DESC LIMIT 1) as laptop_model_name,
(SELECT COUNT(*) FROM laptop_assets WHERE rental_user_id = u.id) as rental_count
FROM users u
LEFT JOIN dept_path dp ON u.department_id = dp.id";
$search = $_GET['search'] ?? '';
if ($search) {
$query .= " WHERE u.name LIKE :search OR u.emp_id LIKE :search OR u.email LIKE :search";
$stmt = $db->prepare($query);
$stmt->execute(['search' => "%$search%"]);
} else {
$stmt = $db->query($query);
}
echo json_encode($stmt->fetchAll());
break;
case 'add_user':
$data = json_decode(file_get_contents('php://input'), true);
$status = $data['status'] ?? 'active';
$department_id = $data['department_id'] ?: null;
$emp_id = $data['emp_id'];
// Duplicate emp_id check
$check = $db->prepare("SELECT id FROM users WHERE emp_id = ?");
$check->execute([$emp_id]);
if ($check->fetch()) {
echo json_encode(['success' => false, 'error' => "이미 존재하는 사번({$emp_id})입니다."]);
break;
}
// Auto-relocate based on status
if ($status === 'on_leave' || $status === 'retired') {
$dept_name = ($status === 'on_leave') ? '휴직' : '퇴사';
$target_dept = $db->query("SELECT id FROM departments WHERE name = '$dept_name'")->fetchColumn();
if ($target_dept)
$department_id = $target_dept;
}
try {
$stmt = $db->prepare("INSERT INTO users (emp_id, name, department_id, position, email, phone, mobile, accounting_type,
status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([
$emp_id,
$data['name'],
$department_id,
$data['position'],
$data['email'],
$data['phone'] ?? '',
$data['mobile'],
$data['accounting_type'],
$status
]);
echo json_encode(['success' => true]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
break;
case 'update_user':
$data = json_decode(file_get_contents('php://input'), true);
$status = $data['status'];
$department_id = $data['department_id'] ?: null;
$emp_id = $data['emp_id'];
$user_id = $data['id'];
// Duplicate emp_id check (excluding current user)
$check = $db->prepare("SELECT id FROM users WHERE emp_id = ? AND id != ?");
$check->execute([$emp_id, $user_id]);
if ($check->fetch()) {
echo json_encode(['success' => false, 'error' => "이미 존재하는 사번({$emp_id})입니다."]);
break;
}
// Auto-relocate based on status
if ($status === 'on_leave' || $status === 'retired') {
$dept_name = ($status === 'on_leave') ? '휴직' : '퇴사';
$target_dept = $db->query("SELECT id FROM departments WHERE name = '$dept_name'")->fetchColumn();
if ($target_dept)
$department_id = $target_dept;
}
try {
$stmt = $db->prepare("UPDATE users SET emp_id = ?, name = ?, department_id = ?, position = ?, email = ?, phone = ?,
mobile = ?, accounting_type = ?, status = ? WHERE id = ?");
$stmt->execute([
$emp_id,
$data['name'],
$department_id,
$data['position'],
$data['email'],
$data['phone'] ?? '',
$data['mobile'],
$data['accounting_type'],
$status,
$user_id
]);
echo json_encode(['success' => true]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
break;
case 'get_laptops':
$query = "SELECT a.*, m.model_name, m.manufacturer, m.specs, m.processor, m.ram, m.storage, u.name as user_name
FROM laptop_assets a
LEFT JOIN laptop_models m ON a.model_id = m.id
LEFT JOIN users u ON a.current_user_id = u.id";
echo json_encode($db->query($query)->fetchAll());
break;
case 'get_rental_laptops':
$query = "SELECT a.*, m.model_name, m.manufacturer, m.product_name
FROM laptop_assets a
LEFT JOIN laptop_models m ON a.model_id = m.id
WHERE (a.assigned_user_name LIKE '%업무용%' OR a.current_user_id IS NULL) AND a.status != 'disposed'";
echo json_encode($db->query($query)->fetchAll());
break;
case 'update_rental':
$data = json_decode(file_get_contents('php://input'), true);
$startDate = $data['rental_start_date'] ?: date('Y-m-d');
$stmt = $db->prepare("UPDATE laptop_assets SET
rental_user_id = ?,
rental_user_name = ?,
rental_start_date = ?,
rental_end_scheduled = ?,
rental_reason = ?,
rental_peripherals = ?,
remarks = ?
WHERE id = ?");
$stmt->execute([
$data['rental_user_id'] ?? null,
$data['rental_user_name'],
$startDate,
$data['rental_end_scheduled'] ?? null,
$data['rental_reason'] ?? '',
$data['rental_peripherals'] ?? '',
$data['remarks'] ?? '',
$data['id']
]);
// Log the rental action
$peripherals = $data['rental_peripherals'] ? "\n부속품: " . $data['rental_peripherals'] : "";
$note = "사유: " . ($data['rental_reason'] ?? '없음') . $peripherals;
$log_stmt = $db->prepare("INSERT INTO asset_history (asset_id, log_type, user_name, action_date, note) VALUES (?,
'rental', ?, ?, ?)");
$log_stmt->execute([$data['id'], $data['rental_user_name'], $startDate, $note]);
echo json_encode(['success' => true]);
break;
case 'return_rental':
$data = json_decode(file_get_contents('php://input'), true);
$asset = $db->query("SELECT * FROM laptop_assets WHERE id = " . (int) $data['id'])->fetch();
if ($asset) {
// 반납 처리: 임대 정보는 초기화하되 비고(remarks)는 유지
$stmt = $db->prepare("UPDATE laptop_assets SET
rental_user_id = NULL,
rental_user_name = NULL,
rental_start_date = NULL,
rental_end_scheduled = NULL,
rental_reason = NULL,
rental_peripherals = NULL
WHERE id = ?");
$stmt->execute([$data['id']]);
// Log return
$log_stmt = $db->prepare("INSERT INTO asset_history (asset_id, log_type, user_name, action_date, note) VALUES (?,
'return', ?, ?, ?)");
$log_stmt->execute([$data['id'], $asset['rental_user_name'], date('Y-m-d'), '반납 완료']);
}
echo json_encode(['success' => true]);
break;
case 'get_user_rentals':
$user_id = (int) $_GET['user_id'];
$query = "SELECT a.*, m.model_name, m.manufacturer
FROM laptop_assets a
JOIN laptop_models m ON a.model_id = m.id
WHERE a.rental_user_id = ?";
$stmt = $db->prepare($query);
$stmt->execute([$user_id]);
echo json_encode($stmt->fetchAll());
break;
case 'update_asset_remarks':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("UPDATE laptop_assets SET remarks = ? WHERE id = ?");
$stmt->execute([$data['remarks'] ?? '', $data['id']]);
echo json_encode(['success' => true]);
break;
case 'get_asset_history':
$asset_id = $_GET['asset_id'];
$rental_logs = $db->query("SELECT * FROM asset_history WHERE asset_id = $asset_id AND log_type = 'rental' ORDER BY id
DESC LIMIT 20")->fetchAll();
$assign_logs = $db->query("SELECT * FROM asset_history WHERE asset_id = $asset_id AND log_type = 'assignment' ORDER BY
id DESC LIMIT 20")->fetchAll();
echo json_encode([
'rental' => $rental_logs,
'assignment' => $assign_logs
]);
break;
case 'get_cards':
echo json_encode($db->query("SELECT * FROM access_cards")->fetchAll());
break;
case 'add_card':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("INSERT INTO access_cards (card_number, card_type, nfc_id, status) VALUES (?, ?, ?, ?)");
$stmt->execute([$data['card_number'], $data['card_type'], $data['nfc_id'], 'available']);
echo json_encode(['success' => true]);
break;
case 'update_card':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("UPDATE access_cards SET card_number = ?, card_type = ?, nfc_id = ? WHERE id = ?");
$stmt->execute([$data['card_number'], $data['card_type'], $data['nfc_id'], $data['id']]);
echo json_encode(['success' => true]);
break;
case 'get_mfp':
echo json_encode($db->query("SELECT * FROM mfp_accounts")->fetchAll());
break;
case 'add_mfp':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("INSERT INTO mfp_accounts (account_id, account_pw, purpose, status) VALUES (?, ?, ?, ?)");
$stmt->execute([$data['account_id'], $data['account_pw'], $data['purpose'], 'active']);
echo json_encode(['success' => true]);
break;
case 'update_mfp':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("UPDATE mfp_accounts SET account_id = ?, account_pw = ?, purpose = ? WHERE id = ?");
$stmt->execute([$data['account_id'], $data['account_pw'], $data['purpose'], $data['id']]);
echo json_encode(['success' => true]);
break;
case 'get_models':
echo json_encode($db->query("SELECT * FROM laptop_models ORDER BY manufacturer, model_name")->fetchAll());
break;
case 'add_model':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("INSERT INTO laptop_models (
model_name, manufacturer, specs, cpu, npu, hdd0_model, hdd0_capacity,
hdd1_model, hdd1_capacity, ram, asset_status, assigned_user, fixed_ip,
remarks, options, power_rating, purchase_date, vendor, product_name
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([
$data['model_name'],
$data['manufacturer'],
$data['specs'] ?? '',
$data['cpu'] ?? '',
$data['npu'] ?? '',
$data['hdd0_model'] ?? '',
$data['hdd0_capacity'] ?? '',
$data['hdd1_model'] ?? '',
$data['hdd1_capacity'] ?? '',
$data['ram'] ?? '',
$data['asset_status'] ?? '',
$data['assigned_user'] ?? '',
$data['fixed_ip'] ?? '',
$data['remarks'] ?? '',
$data['options'] ?? '',
$data['power_rating'] ?? '',
$data['purchase_date'] ?? '',
$data['vendor'] ?? '',
$data['product_name'] ?? ''
]);
echo json_encode(['success' => true]);
break;
case 'update_model':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("UPDATE laptop_models SET
model_name = ?, manufacturer = ?, specs = ?, cpu = ?, npu = ?,
hdd0_model = ?, hdd0_capacity = ?, hdd1_model = ?, hdd1_capacity = ?,
ram = ?, asset_status = ?, assigned_user = ?, fixed_ip = ?,
remarks = ?, options = ?, power_rating = ?, purchase_date = ?,
vendor = ?, product_name = ?
WHERE id = ?");
$stmt->execute([
$data['model_name'],
$data['manufacturer'],
$data['specs'] ?? '',
$data['cpu'] ?? '',
$data['npu'] ?? '',
$data['hdd0_model'] ?? '',
$data['hdd0_capacity'] ?? '',
$data['hdd1_model'] ?? '',
$data['hdd1_capacity'] ?? '',
$data['ram'] ?? '',
$data['asset_status'] ?? '',
$data['assigned_user'] ?? '',
$data['fixed_ip'] ?? '',
$data['remarks'] ?? '',
$data['options'] ?? '',
$data['power_rating'] ?? '',
$data['purchase_date'] ?? '',
$data['vendor'] ?? '',
$data['product_name'] ?? '',
$data['id']
]);
echo json_encode(['success' => true]);
break;
case 'get_departments':
$sql = "WITH RECURSIVE dept_path(id, name, path, level, parent_id) AS (
SELECT id, name, name, level, parent_id FROM departments WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.name, dp.path || ' > ' || d.name, d.level, d.parent_id
FROM departments d
JOIN dept_path dp ON d.parent_id = dp.id
)
SELECT dp.*,
(SELECT COUNT(*) FROM users WHERE department_id = dp.id) as member_count
FROM dept_path dp
ORDER BY path";
echo json_encode($db->query($sql)->fetchAll());
break;
case 'add_laptop_asset':
$data = json_decode(file_get_contents('php://input'), true);
$asset_tag = trim($data['asset_tag'] ?? '');
// Duplicate check
$check = $db->prepare("SELECT id FROM laptop_assets WHERE asset_tag = ?");
$check->execute([$asset_tag]);
if ($check->fetch()) {
echo json_encode(['success' => false, 'error' => "이미 존재하는 자산번호({$asset_tag})입니다."]);
break;
}
$stmt = $db->prepare("INSERT INTO laptop_assets (
asset_tag, model_id, current_user_id, status, serial_number, purchase_date, ip_address,
cpu, npu, hdd0_model, hdd0_capacity, hdd1_model, hdd1_capacity, ram,
asset_status, assigned_user_name, fixed_ip, options, power_rating, manufacturer, vendor, product_name, remarks,
last_confirmed_date, disposal_recipient, disposal_date, real_user, disposal_user_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([
$data['asset_tag'],
$data['model_id'],
$data['current_user_id'] ?: null,
$data['status'] ?: ($data['current_user_id'] ? 'assigned' : 'stock'),
$data['serial_number'],
$data['purchase_date'],
$data['ip_address'] ?? null,
$data['cpu'] ?? '',
$data['npu'] ?? '',
$data['hdd0_model'] ?? '',
$data['hdd0_capacity'] ?? '',
$data['hdd1_model'] ?? '',
$data['hdd1_capacity'] ?? '',
$data['ram'] ?? '',
$data['asset_status'] ?? '',
$data['assigned_user_name'] ?? '',
$data['fixed_ip'] ?? '',
$data['options'] ?? '',
$data['power_rating'] ?? '',
$data['manufacturer'] ?? '',
$data['vendor'] ?? '',
$data['product_name'] ?? '',
$data['remarks'] ?? '',
$data['last_confirmed_date'] ?? null,
$data['disposal_recipient'] ?? null,
$data['disposal_date'] ?? null,
$data['real_user'] ?? null,
$data['disposal_user_id'] ?? null
]);
$new_asset_id = $db->lastInsertId();
if ($data['assigned_user_name']) {
$log_stmt = $db->prepare("INSERT INTO asset_history (asset_id, log_type, user_name, action_date) VALUES (?,
'assignment', ?, ?)");
$log_stmt->execute([$new_asset_id, $data['assigned_user_name'], date('Y-m-d')]);
}
echo json_encode(['success' => true]);
break;
case 'update_laptop_asset':
$data = json_decode(file_get_contents('php://input'), true);
// Get old data for logging comparison
$old_stmt = $db->prepare("SELECT assigned_user_name FROM laptop_assets WHERE id = ?");
$old_stmt->execute([$data['id']]);
$old_asset = $old_stmt->fetch();
// Auto-relocate based on status if needed (optional)
// If assigned_user_name is missing but current_user_id exists, try to fill it for logging
if (empty($data['assigned_user_name']) && !empty($data['current_user_id'])) {
$u_stmt = $db->prepare("SELECT name FROM users WHERE id = ?");
$u_stmt->execute([$data['current_user_id']]);
$data['assigned_user_name'] = $u_stmt->fetchColumn() ?: '';
}
$asset_tag = trim($data['asset_tag'] ?? '');
$asset_id = $data['id'];
// Duplicate check (excluding current asset)
$check = $db->prepare("SELECT id FROM laptop_assets WHERE asset_tag = ? AND id != ?");
$check->execute([$asset_tag, $asset_id]);
if ($check->fetch()) {
echo json_encode(['success' => false, 'error' => "이미 존재하는 자산번호({$asset_tag})입니다."]);
break;
}
$stmt = $db->prepare("UPDATE laptop_assets SET
asset_tag = ?, model_id = ?, current_user_id = ?, status = ?, serial_number = ?, purchase_date = ?, ip_address = ?,
cpu = ?, npu = ?, hdd0_model = ?, hdd0_capacity = ?, hdd1_model = ?, hdd1_capacity = ?, ram = ?,
asset_status = ?, assigned_user_name = ?, fixed_ip = ?, options = ?, power_rating = ?, manufacturer = ?, vendor = ?,
product_name = ?, remarks = ?, last_confirmed_date = ?, disposal_recipient = ?, disposal_date = ?, real_user = ?, disposal_user_id = ?
WHERE id = ?");
$stmt->execute([
$data['asset_tag'],
$data['model_id'],
$data['current_user_id'] ?: null,
$data['status'] ?: ($data['current_user_id'] ? 'assigned' : 'stock'),
$data['serial_number'],
$data['purchase_date'],
$data['ip_address'] ?? null,
$data['cpu'] ?? '',
$data['npu'] ?? '',
$data['hdd0_model'] ?? '',
$data['hdd0_capacity'] ?? '',
$data['hdd1_model'] ?? '',
$data['hdd1_capacity'] ?? '',
$data['ram'] ?? '',
$data['asset_status'] ?? '',
$data['assigned_user_name'] ?? '',
$data['fixed_ip'] ?? '',
$data['options'] ?? '',
$data['power_rating'] ?? '',
$data['manufacturer'] ?? '',
$data['vendor'] ?? '',
$data['product_name'] ?? '',
$data['remarks'] ?? '',
$data['last_confirmed_date'] ?? null,
$data['disposal_recipient'] ?? null,
$data['disposal_date'] ?? null,
$data['real_user'] ?? null,
$data['disposal_user_id'] ?? null,
$data['id']
]);
// Auto-log assignment change
$new_name = $data['assigned_user_name'] ?: '';
$old_name = $old_asset['assigned_user_name'] ?? '';
if ($new_name !== $old_name) {
$log_stmt = $db->prepare("INSERT INTO asset_history (asset_id, log_type, user_name, action_date) VALUES (?, 'assignment', ?, ?)");
$log_stmt->execute([$data['id'], $new_name ?: '재고(반납)', date('Y-m-d')]);
}
echo json_encode(['success' => true]);
break;
case 'add_dept':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("INSERT INTO departments (name, parent_id, level) VALUES (?, ?, ?)");
$stmt->execute([$data['name'], $data['parent_id'] ?: null, $data['level'] ?: 1]);
echo json_encode(['success' => true]);
break;
case 'update_dept':
$data = json_decode(file_get_contents('php://input'), true);
$new_name = $data['name'];
$parent_id = $data['parent_id'] ?: null;
$dept_id = $data['id'];
$merge_target_id = $data['merge_target_id'] ?? null;
if ($merge_target_id) {
// Scenario: Merge members to an existing department with the same name
$db->prepare("UPDATE users SET department_id = ? WHERE department_id = ?")->execute([$merge_target_id, $dept_id]);
// Recursively move children? For now, we delete the source dept after merging members
$db->prepare("DELETE FROM departments WHERE id = ?")->execute([$dept_id]);
echo json_encode(['success' => true, 'merged' => true]);
} else {
// Check if a department with the same name exists under the same parent (excluding itself)
$check_stmt = $db->prepare("SELECT id FROM departments WHERE name = ? AND parent_id IS ? AND id != ?");
$check_stmt->execute([$new_name, $parent_id, $dept_id]);
$existing = $check_stmt->fetch();
if ($existing) {
echo json_encode(['success' => false, 'conflict' => true, 'existing_id' => $existing['id']]);
} else {
$stmt = $db->prepare("UPDATE departments SET name = ?, parent_id = ? WHERE id = ?");
$stmt->execute([$new_name, $parent_id, $dept_id]);
echo json_encode(['success' => true]);
}
}
break;
case 'delete_dept':
$dept_id = $_GET['id'];
// Find FKI Root
$fki_root = $db->query("SELECT id FROM departments WHERE name = '한국경제인협회' AND parent_id IS NULL LIMIT 1")->fetch();
$fki_root_id = $fki_root ? $fki_root['id'] : null;
// 1. Find the '미소속' department under FKI Root
$miso_id = $db->query("SELECT id FROM departments WHERE name = '미소속' AND parent_id IS " . ($fki_root_id ?: 'NULL') . "
LIMIT 1")->fetchColumn();
if (!$miso_id) {
$db->prepare("INSERT INTO departments (name, parent_id, level) VALUES ('미소속', ?, 1)")->execute([$fki_root_id]);
$miso_id = $db->lastInsertId();
}
// 2. Get current dept info for re-parenting children
$current_dept = $db->prepare("SELECT parent_id FROM departments WHERE id = ?");
$current_dept->execute([$dept_id]);
$parent_info = $current_dept->fetch();
$target_new_parent = $parent_info ? $parent_info['parent_id'] : null;
// 3. Move members to '미소속'
$db->prepare("UPDATE users SET department_id = ? WHERE department_id = ?")->execute([$miso_id, $dept_id]);
// 4. Move child departments to its grandparent (re-parenting) to avoid FK constraint violation
$db->prepare("UPDATE departments SET parent_id = ? WHERE parent_id = ?")->execute([$target_new_parent, $dept_id]);
// 5. Delete the department
$db->prepare("DELETE FROM departments WHERE id = ?")->execute([$dept_id]);
echo json_encode(['success' => true]);
break;
case 'get_dept_users':
$dept_id = $_GET['dept_id'];
$stmt = $db->prepare("SELECT id, name, emp_id, position FROM users WHERE department_id = ?");
$stmt->execute([$dept_id]);
echo json_encode($stmt->fetchAll());
break;
case 'reassign_user':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("UPDATE users SET department_id = ? WHERE id = ?");
foreach ($data['user_ids'] as $u_id) {
$stmt->execute([$data['new_dept_id'], $u_id]);
}
echo json_encode(['success' => true]);
break;
case 'bulk_update_laptops':
$data = json_decode(file_get_contents('php://input'), true);
$ids = $data['ids'];
$newStatus = $data['status'] ?? null;
$newModelId = $data['model_id'] ?? null;
if (empty($ids)) {
echo json_encode(['success' => false, 'error' => 'No items selected']);
break;
}
$updates = [];
$params = [];
$log_msg = "";
if ($newStatus) {
$updates[] = "status = ?";
$params[] = $newStatus;
if ($newStatus === 'stock') {
$updates[] = "current_user_id = NULL";
$updates[] = "assigned_user_name = '업무용(TEMP_44666b)'";
$log_msg = "일괄 상태 변경: 재고 (업무용 지정)";
} elseif ($newStatus === 'disposed') {
$updates[] = "current_user_id = NULL";
$updates[] = "assigned_user_name = NULL";
$updates[] = "disposal_date = '" . date('Y-m-d') . "'";
$log_msg = "일괄 상태 변경: 매각됨";
} elseif ($newStatus === 'assigned') {
$log_msg = "일괄 상태 변경: 직원배정";
}
}
if ($newModelId) {
$updates[] = "model_id = ?";
$params[] = $newModelId;
$log_msg .= ($log_msg ? ", " : "") . "일괄 모델 변경(ID: $newModelId)";
}
if (empty($updates)) {
echo json_encode(['success' => false, 'error' => 'No updates specified']);
break;
}
$placeholders = implode(",", array_fill(0, count($ids), "?"));
$sql = "UPDATE laptop_assets SET " . implode(", ", $updates) . " WHERE id IN ($placeholders)";
$stmt = $db->prepare($sql);
$stmt->execute(array_merge($params, $ids));
// 히스토리 일괄 기록
if ($log_msg) {
$history_sql = "INSERT INTO asset_history (asset_id, log_type, user_name, action_date, note) VALUES (?, 'bulk_update', '시스템(일괄)', ?, ?)";
$h_stmt = $db->prepare($history_sql);
foreach ($ids as $id) {
$h_stmt->execute([$id, date('Y-m-d'), $log_msg]);
}
}
echo json_encode(['success' => true]);
break;
case 'bulk_delete_laptops':
$data = json_decode(file_get_contents('php://input'), true);
$ids = $data['ids'];
if (empty($ids)) {
echo json_encode(['success' => false, 'error' => 'No items selected']);
break;
}
$placeholders = implode(",", array_fill(0, count($ids), "?"));
$db->prepare("DELETE FROM laptop_assets WHERE id IN ($placeholders)")->execute($ids);
// 관련 히스토리도 삭제? 보통 자산 삭제시에는 히스토리도 함께 날리거나 남김. 여기선 DB 정합성을 위해 연쇄 삭제는 안하더라도 자산은 사라짐.
echo json_encode(['success' => true]);
break;
case 'bulk_update_cards':
$data = json_decode(file_get_contents('php://input'), true);
$ids = $data['ids'];
$newStatus = $data['status'] ?? null;
$newCardType = $data['card_type'] ?? null;
if (empty($ids)) {
echo json_encode(['success' => false, 'error' => 'No items selected']);
break;
}
$updates = [];
$params = [];
if ($newStatus) {
$updates[] = "status = ?";
$params[] = $newStatus;
}
if ($newCardType) {
$updates[] = "card_type = ?";
$params[] = $newCardType;
}
if (empty($updates)) {
echo json_encode(['success' => false, 'error' => 'No updates specified']);
break;
}
$sql = "UPDATE access_cards SET " . implode(", ", $updates) . " WHERE id IN (" . implode(",", array_fill(
0,
count($ids),
"?"
)) . ")";
$stmt = $db->prepare($sql);
$stmt->execute(array_merge($params, $ids));
echo json_encode(['success' => true]);
break;
case 'bulk_update_mfp':
$data = json_decode(file_get_contents('php://input'), true);
$ids = $data['ids'];
$newStatus = $data['status'] ?? null;
$newPurpose = $data['purpose'] ?? null;
if (empty($ids)) {
echo json_encode(['success' => false, 'error' => 'No items selected']);
break;
}
$updates = [];
$params = [];
if ($newStatus) {
$updates[] = "status = ?";
$params[] = $newStatus;
}
if ($newPurpose) {
$updates[] = "purpose = ?";
$params[] = $newPurpose;
}
if (empty($updates)) {
echo json_encode(['success' => false, 'error' => 'No updates specified']);
break;
}
$sql = "UPDATE mfp_accounts SET " . implode(", ", $updates) . " WHERE id IN (" . implode(",", array_fill(
0,
count($ids),
"?"
)) . ")";
$stmt = $db->prepare($sql);
$stmt->execute(array_merge($params, $ids));
echo json_encode(['success' => true]);
break;
case 'bulk_update_models':
$data = json_decode(file_get_contents('php://input'), true);
$ids = $data['ids'];
$newManufacturer = $data['manufacturer'] ?? null;
$newProductName = $data['product_name'] ?? null;
if (empty($ids)) {
echo json_encode(['success' => false, 'error' => 'No items selected']);
break;
}
$updates = [];
$params = [];
if ($newManufacturer) {
$updates[] = "manufacturer = ?";
$params[] = $newManufacturer;
}
if ($newProductName) {
$updates[] = "product_name = ?";
$params[] = $newProductName;
}
if (empty($updates)) {
echo json_encode(['success' => false, 'error' => 'No updates specified']);
break;
}
$sql = "UPDATE laptop_models SET " . implode(", ", $updates) . " WHERE id IN (" . implode(",", array_fill(
0,
count($ids),
"?"
)) . ")";
$stmt = $db->prepare($sql);
$stmt->execute(array_merge($params, $ids));
echo json_encode(['success' => true]);
break;
case 'bulk_delete_models':
$data = json_decode(file_get_contents('php://input'), true);
$ids = $data['ids'];
if (empty($ids)) {
echo json_encode(['success' => false, 'error' => 'No items selected']);
break;
}
try {
$db->beginTransaction();
$placeholders = implode(",", array_fill(0, count($ids), "?"));
// 1. 해당 모델을 참조 중인 자산들의 연결 고리 해제 (ID 참조를 NULL로 변경)
$stmt1 = $db->prepare("UPDATE laptop_assets SET model_id = NULL WHERE model_id IN ($placeholders)");
$stmt1->execute($ids);
// 2. 노트북 모델 마스터 정보 삭제
$stmt2 = $db->prepare("DELETE FROM laptop_models WHERE id IN ($placeholders)");
$stmt2->execute($ids);
$db->commit();
echo json_encode(['success' => true]);
} catch (Exception $e) {
$db->rollBack();
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
break;
case 'bulk_update_users':
$data = json_decode(file_get_contents('php://input'), true);
$userIds = $data['user_ids'];
$newStatus = $data['status'] ?? null;
$newDeptId = $data['department_id'] ?? null;
if (empty($userIds)) {
echo json_encode(['success' => false, 'error' => 'No users selected']);
break;
}
// Enhanced logic: if status is changed to leave/retired, override dept
if ($newStatus === 'on_leave' || $newStatus === 'retired') {
$dept_name = ($newStatus === 'on_leave') ? '휴직' : '퇴사';
$target_dept = $db->query("SELECT id FROM departments WHERE name = '$dept_name'")->fetchColumn();
if ($target_dept)
$newDeptId = $target_dept;
}
$sql = "UPDATE users SET ";
$params = [];
$updates = [];
if ($newStatus) {
$updates[] = "status = ?";
$params[] = $newStatus;
}
if ($newDeptId) {
$updates[] = "department_id = ?";
$params[] = $newDeptId;
}
$newPosition = $data['position'] ?? null;
if ($newPosition) {
$updates[] = "position = ?";
$params[] = $newPosition;
}
if (empty($updates)) {
echo json_encode(['success' => false, 'error' => 'No updates specified']);
break;
}
$sql .= implode(", ", $updates) . " WHERE id IN (" . implode(",", array_fill(0, count($userIds), "?")) . ")";
$params = array_merge($params, $userIds);
$stmt = $db->prepare($sql);
$stmt->execute($params);
echo json_encode(['success' => true]);
break;
case 'archive_users':
$data = json_decode(file_get_contents('php://input'), true);
$userIds = $data['user_ids'];
if (empty($userIds)) {
echo json_encode(['success' => false, 'error' => 'No users selected']);
break;
}
$db->beginTransaction();
try {
$placeholders = implode(',', array_fill(0, count($userIds), '?'));
// Copy to archive
$db->prepare("INSERT INTO users_archive (id, emp_id, name, department_id, position, email, phone, mobile, accounting_type, status)
SELECT id, emp_id, name, department_id, position, email, phone, mobile, accounting_type, status FROM users WHERE id IN ($placeholders)")
->execute($userIds);
// Delete from original
$db->prepare("DELETE FROM users WHERE id IN ($placeholders)")->execute($userIds);
$db->commit();
echo json_encode(['success' => true]);
} catch (Exception $e) {
$db->rollBack();
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
break;
case 'get_archived_users':
$query = "SELECT u.*, dp.path as dept_name
FROM users_archive u
LEFT JOIN (
WITH RECURSIVE dept_path(id, path) AS (
SELECT id, name FROM departments WHERE parent_id IS NULL
UNION ALL
SELECT d.id, dp.path || ' > ' || d.name
FROM departments d JOIN dept_path dp ON d.parent_id = dp.id
) SELECT * FROM dept_path
) dp ON u.department_id = dp.id
ORDER BY u.archived_at DESC";
echo json_encode($db->query($query)->fetchAll());
break;
case 'restore_users':
$data = json_decode(file_get_contents('php://input'), true);
$userIds = $data['user_ids'];
if (empty($userIds)) {
echo json_encode(['success' => false, 'error' => 'No users selected']);
break;
}
$db->beginTransaction();
try {
$placeholders = implode(',', array_fill(0, count($userIds), '?'));
// Restore to users
$db->prepare("INSERT INTO users (id, emp_id, name, department_id, position, email, phone, mobile, accounting_type, status)
SELECT id, emp_id, name, department_id, position, email, phone, mobile, accounting_type, status FROM users_archive WHERE id IN ($placeholders)")
->execute($userIds);
// Delete from archive
$db->prepare("DELETE FROM users_archive WHERE id IN ($placeholders)")->execute($userIds);
$db->commit();
echo json_encode(['success' => true]);
} catch (Exception $e) {
$db->rollBack();
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
break;
case 'bulk_delete_users':
$data = json_decode(file_get_contents('php://input'), true);
$userIds = $data['user_ids'];
if (empty($userIds)) {
echo json_encode(['success' => false, 'error' => 'No users selected']);
break;
}
$placeholders = implode(',', array_fill(0, count($userIds), '?'));
$db->prepare("DELETE FROM users WHERE id IN ($placeholders)")->execute($userIds);
echo json_encode(['success' => true]);
break;
case 'export_users_csv':
$data = json_decode(file_get_contents('php://input'), true);
$userIds = $data['user_ids'] ?? [];
$columns = $data['columns'] ?? []; // ['name' => '이름', 'emp_id' => '사번', ...]
if (empty($columns)) {
echo json_encode(['success' => false, 'error' => '내보낼 컬럼을 선택해주세요.']);
break;
}
// 전체 유저 정보 조회 (기본 쿼리 재사용)
$query = "WITH RECURSIVE dept_path(id, path) AS (
SELECT id, name FROM departments WHERE parent_id IS NULL
UNION ALL
SELECT d.id, dp.path || ' > ' || d.name
FROM departments d JOIN dept_path dp ON d.parent_id = dp.id
)
SELECT u.*, dp.path as dept_name,
(SELECT asset_tag FROM laptop_assets WHERE current_user_id = u.id ORDER BY id DESC LIMIT 1) as laptop_tag
FROM users u
LEFT JOIN dept_path dp ON u.department_id = dp.id";
if (!empty($userIds)) {
$placeholders = implode(',', array_fill(0, count($userIds), '?'));
$query .= " WHERE u.id IN ($placeholders)";
$stmt = $db->prepare($query);
$stmt->execute($userIds);
} else {
$stmt = $db->query($query);
}
$users = $stmt->fetchAll();
// CSV 스트림 생성
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=직원_마스터_리스트_' . date('Ymd_His') . '.csv');
$output = fopen('php://output', 'w');
// Zero-Mojibake: BOM (Byte Order Mark) 삽입
fwrite($output, "\xEF\xBB\xBF");
// 헤더 작성
fputcsv($output, array_values($columns));
// 데이터 작성
foreach ($users as $user) {
$row = [];
foreach (array_keys($columns) as $key) {
$val = $user[$key] ?? '';
// 상태 값 한글 변환
if ($key === 'status') {
$val = match ($val) {
'active' => '재직',
'retired' => '퇴사',
'on_leave' => '휴직',
'classification' => '분류',
default => $val
};
}
$row[] = $val;
}
fputcsv($output, $row);
}
fclose($output);
exit;
case 'get_general_rentals':
$query = "SELECT r.*, GROUP_CONCAT(i.item_name, ', ') as items
FROM general_rentals r
LEFT JOIN general_rental_items i ON r.id = i.rental_id
GROUP BY r.id
ORDER BY r.id DESC";
echo json_encode($db->query($query)->fetchAll());
break;
case 'add_general_rental':
$data = json_decode(file_get_contents('php://input'), true);
$startDate = $data['rental_start_date'] ?: date('Y-m-d');
$db->beginTransaction();
try {
$stmt = $db->prepare("INSERT INTO general_rentals (user_id, user_name, rental_start_date, rental_reason, status) VALUES
(?, ?, ?, ?, 'rented')");
$stmt->execute([
$data['user_id'] ?? null,
$data['user_name'],
$startDate,
$data['rental_reason'] ?? ''
]);
$rentalId = $db->lastInsertId();
if (!empty($data['items'])) {
$itemStmt = $db->prepare("INSERT INTO general_rental_items (rental_id, item_name) VALUES (?, ?)");
foreach ($data['items'] as $itemName) {
if (trim($itemName)) {
$itemStmt->execute([$rentalId, trim($itemName)]);
}
}
}
$db->commit();
echo json_encode(['success' => true]);
} catch (Exception $e) {
$db->rollBack();
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
break;
case 'get_settings':
$settings = $db->query("SELECT key_name, value FROM system_settings")->fetchAll(PDO::FETCH_KEY_PAIR);
echo json_encode(['success' => true, 'settings' => $settings]);
break;
case 'update_settings':
$data = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare("INSERT OR REPLACE INTO system_settings (key_name, value) VALUES (?, ?)");
foreach ($data['settings'] as $key => $value) {
$stmt->execute([$key, (string) $value]);
}
echo json_encode(['success' => true]);
break;
case 'return_general_rental':
$data = json_decode(file_get_contents('php://input'), true);
$returnDate = date('Y-m-d');
$stmt = $db->prepare("UPDATE general_rentals SET status = 'returned', rental_return_date = ? WHERE id = ?");
$stmt->execute([$returnDate, $data['id']]);
echo json_encode(['success' => true]);
break;
default:
echo json_encode(['error' => 'Invalid action']);
break;
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}