false, 'error' => '로그인이 필요합니다.']); exit; } $currentUser = $_SESSION['sales_user']; $pdo = db_connect(); /** * 테넌트에 대한 접근 권한 확인 */ function checkTenantPermission($pdo, $tenant_id, $currentUser) { if (!$tenant_id) return false; if ($currentUser['role'] === 'operator') return true; $stmt = $pdo->prepare("SELECT manager_id, sales_manager_id FROM sales_tenants WHERE id = ?"); $stmt->execute([$tenant_id]); $tenant = $stmt->fetch(PDO::FETCH_ASSOC); if (!$tenant) return false; // 매니저는 본인이 '담당 매니저'로 배정된 경우만 가능 if ($currentUser['role'] === 'manager') { return $tenant['sales_manager_id'] == $currentUser['id']; } // 영업관리자는 본인이 등록했거나, 본인이 담당 매니저인 경우 가능 return ($tenant['manager_id'] == $currentUser['id'] || $tenant['sales_manager_id'] == $currentUser['id']); } // 테이블 자동 생성 (없을 경우) $pdo->exec(" CREATE TABLE IF NOT EXISTS `sales_tenants` ( `id` int(11) NOT NULL AUTO_INCREMENT, `manager_id` int(11) NOT NULL COMMENT '영업한 영업관리자 ID', `sales_manager_id` int(11) DEFAULT NULL COMMENT '매칭된 매니저 ID (영업관리자 본인 또는 별도 매니저)', `tenant_name` varchar(200) NOT NULL, `representative` varchar(100) DEFAULT NULL, `business_no` varchar(20) DEFAULT NULL, `contact_phone` varchar(20) DEFAULT NULL, `email` varchar(100) DEFAULT NULL, `address` varchar(500) DEFAULT NULL, `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS `sales_tenant_products` ( `id` int(11) NOT NULL AUTO_INCREMENT, `tenant_id` int(11) NOT NULL, `product_name` varchar(200) NOT NULL, `contract_amount` decimal(15,2) NOT NULL DEFAULT 0.00, `commission_rate` decimal(5,2) NOT NULL DEFAULT 0.00, `commission_amount` decimal(15,2) DEFAULT 0.00, `contract_date` date DEFAULT NULL, `operator_confirmed` tinyint(1) DEFAULT 0, `sub_models` text DEFAULT NULL COMMENT '선택모델인 경우 모델 ID 목록 (JSON)', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS `sales_tenant_scenarios` ( `id` int(11) NOT NULL AUTO_INCREMENT, `tenant_id` int(11) NOT NULL, `scenario_type` varchar(20) NOT NULL DEFAULT 'manager' COMMENT 'sales or manager', `step_id` int(11) NOT NULL, `checkpoint_index` int(11) NOT NULL, `is_checked` tinyint(1) DEFAULT 0, `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `idx_unique_step` (`tenant_id`, `scenario_type`, `step_id`, `checkpoint_index`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS `sales_tenant_consultations` ( `id` int(11) NOT NULL AUTO_INCREMENT, `tenant_id` int(11) NOT NULL, `manager_id` int(11) NOT NULL, `scenario_type` varchar(20) NOT NULL DEFAULT 'manager' COMMENT 'sales or manager', `step_id` int(11) DEFAULT NULL, `log_text` text NOT NULL, `audio_file_path` varchar(500) DEFAULT NULL, `attachment_paths` text DEFAULT NULL, `consultation_type` varchar(20) DEFAULT 'text' COMMENT 'text, audio, file', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; "); // --- DB Migration (기존 테이블 업데이트) --- try { // 1. sales_tenant_scenarios 테이블에 scenario_type 컬럼 추가 $check = $pdo->query("SHOW COLUMNS FROM `sales_tenant_scenarios` LIKE 'scenario_type'")->fetch(); if (!$check) { $pdo->exec("ALTER TABLE `sales_tenant_scenarios` ADD COLUMN `scenario_type` varchar(20) NOT NULL DEFAULT 'manager' AFTER `tenant_id` "); } // 2. sales_tenant_consultations 테이블에 scenario_type 컬럼 추가 $check2 = $pdo->query("SHOW COLUMNS FROM `sales_tenant_consultations` LIKE 'scenario_type'")->fetch(); if (!$check2) { $pdo->exec("ALTER TABLE `sales_tenant_consultations` ADD COLUMN `scenario_type` varchar(20) NOT NULL DEFAULT 'manager' AFTER `manager_id` "); } // 3. sales_tenant_scenarios 테이블의 유니크 키 업데이트 (scenario_type 포함) // 인덱스 구성을 확인하여 scenario_type이 포함되어 있지 않으면 재구성 $indices = $pdo->query("SHOW INDEX FROM `sales_tenant_scenarios` WHERE Key_name = 'idx_unique_step'")->fetchAll(); $hasScenarioTypeInIndex = false; foreach ($indices as $index) { if ($index['Column_name'] === 'scenario_type') { $hasScenarioTypeInIndex = true; break; } } if (!$hasScenarioTypeInIndex) { try { $pdo->exec("ALTER TABLE `sales_tenant_scenarios` DROP INDEX `idx_unique_step` "); } catch (Exception $e) {} $pdo->exec("ALTER TABLE `sales_tenant_scenarios` ADD UNIQUE KEY `idx_unique_step` (`tenant_id`, `scenario_type`, `step_id`, `checkpoint_index`) "); } // 4. sales_tenant_consultations 테이블에 오디오/첨부파일 컬럼 추가 $cols = $pdo->query("SHOW COLUMNS FROM `sales_tenant_consultations`")->fetchAll(PDO::FETCH_COLUMN); if (!in_array('audio_file_path', $cols)) { $pdo->exec("ALTER TABLE `sales_tenant_consultations` ADD COLUMN `audio_file_path` varchar(500) DEFAULT NULL AFTER `log_text` "); } if (!in_array('attachment_paths', $cols)) { $pdo->exec("ALTER TABLE `sales_tenant_consultations` ADD COLUMN `attachment_paths` text DEFAULT NULL AFTER `audio_file_path` "); } if (!in_array('consultation_type', $cols)) { $pdo->exec("ALTER TABLE `sales_tenant_consultations` ADD COLUMN `consultation_type` varchar(20) DEFAULT 'text' AFTER `attachment_paths` "); } } catch (Exception $e) { error_log("Migration error: " . $e->getMessage()); } try { switch ($method) { case 'GET': if ($action === 'list_tenants') { // 운영자는 모든 테넌트, 영업관리/매니저는 본인 소속 테넌트만 if ($currentUser['role'] === 'operator') { $stmt = $pdo->prepare(" SELECT t.*, m.name as register_name, m2.name as manager_name, m2.role as manager_role FROM sales_tenants t JOIN sales_member m ON t.manager_id = m.id LEFT JOIN sales_member m2 ON t.sales_manager_id = m2.id ORDER BY t.created_at DESC "); $stmt->execute(); } else { if ($currentUser['role'] === 'manager') { // 매니저는 본인이 담당 매니저로 배정된 테넌트만 조회 $stmt = $pdo->prepare(" SELECT t.*, m.name as register_name, m2.name as manager_name, m2.role as manager_role FROM sales_tenants t JOIN sales_member m ON t.manager_id = m.id LEFT JOIN sales_member m2 ON t.sales_manager_id = m2.id WHERE t.sales_manager_id = ? ORDER BY t.created_at DESC "); $stmt->execute([$currentUser['id']]); } else { // 영업관리자는 본인이 영업했거나, 본인이 매니저로 배정된 테넌트 조회 $stmt = $pdo->prepare(" SELECT t.*, m.name as register_name, m2.name as manager_name, m2.role as manager_role FROM sales_tenants t JOIN sales_member m ON t.manager_id = m.id LEFT JOIN sales_member m2 ON t.sales_manager_id = m2.id WHERE t.manager_id = ? OR t.sales_manager_id = ? ORDER BY t.created_at DESC "); $stmt->execute([$currentUser['id'], $currentUser['id']]); } } $tenants = $stmt->fetchAll(PDO::FETCH_ASSOC); echo json_encode(['success' => true, 'data' => $tenants]); } elseif ($action === 'tenant_products') { $tenant_id = $_GET['tenant_id'] ?? null; if (!checkTenantPermission($pdo, $tenant_id, $currentUser)) throw new Exception("권한이 없습니다."); $stmt = $pdo->prepare("SELECT * FROM sales_tenant_products WHERE tenant_id = ? ORDER BY created_at DESC"); $stmt->execute([$tenant_id]); $products = $stmt->fetchAll(PDO::FETCH_ASSOC); echo json_encode(['success' => true, 'data' => $products]); } elseif ($action === 'my_stats') { // 현재 로그인한 사용자의 요약 통계 $sql = " SELECT COUNT(DISTINCT t.id) as tenant_count, SUM(p.contract_amount) as total_revenue, SUM(p.commission_amount) as total_commission, SUM(CASE WHEN p.operator_confirmed = 1 THEN p.commission_amount ELSE 0 END) as confirmed_commission FROM sales_tenants t LEFT JOIN sales_tenant_products p ON t.id = p.tenant_id "; if ($currentUser['role'] === 'manager') { $sql .= " WHERE t.sales_manager_id = ?"; } else { $sql .= " WHERE t.manager_id = ? OR t.sales_manager_id = ?"; } $stmt = $pdo->prepare($sql); if ($currentUser['role'] === 'manager') { $stmt->execute([$currentUser['id']]); } else { $stmt->execute([$currentUser['id'], $currentUser['id']]); } $stats = $stmt->fetch(PDO::FETCH_ASSOC); echo json_encode(['success' => true, 'data' => $stats]); } elseif ($action === 'get_scenario') { $tenant_id = $_GET['tenant_id'] ?? null; $scenario_type = $_GET['scenario_type'] ?? 'manager'; if (!checkTenantPermission($pdo, $tenant_id, $currentUser)) throw new Exception("권한이 없습니다."); $stmt = $pdo->prepare("SELECT * FROM sales_tenant_scenarios WHERE tenant_id = ? AND scenario_type = ?"); $stmt->execute([$tenant_id, $scenario_type]); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); echo json_encode(['success' => true, 'data' => $results]); } elseif ($action === 'get_consultations') { $tenant_id = $_GET['tenant_id'] ?? null; $scenario_type = $_GET['scenario_type'] ?? 'manager'; $step_id = $_GET['step_id'] ?? null; if (!checkTenantPermission($pdo, $tenant_id, $currentUser)) throw new Exception("권한이 없습니다."); $sql = "SELECT * FROM sales_tenant_consultations WHERE tenant_id = ? AND scenario_type = ?"; $params = [$tenant_id, $scenario_type]; if ($step_id) { $sql .= " AND step_id = ?"; $params[] = $step_id; } $sql .= " ORDER BY created_at DESC"; $stmt = $pdo->prepare($sql); $stmt->execute($params); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); echo json_encode(['success' => true, 'data' => $results]); } elseif ($action === 'list_managers') { // 매니저로 매칭 가능한 사람 목록 (영업관리 또는 매니저 직급) $stmt = $pdo->prepare("SELECT id, name, role, member_id, parent_id FROM sales_member WHERE role IN ('sales_admin', 'manager') AND is_active = 1 ORDER BY name ASC"); $stmt->execute(); $managers = $stmt->fetchAll(PDO::FETCH_ASSOC); echo json_encode(['success' => true, 'data' => $managers]); } break; case 'POST': // multipart/form-data인 경우 $_POST 사용, 아니면 JSON 입력 사용 if (empty($_POST)) { $data = json_decode(file_get_contents('php://input'), true); } else { $data = $_POST; } if ($action === 'create_tenant') { if ($currentUser['role'] === 'manager') { throw new Exception("매니저는 테넌트를 등록할 권한이 없습니다."); } $tenant_name = $data['tenant_name'] ?? ''; $representative = $data['representative'] ?? ''; $business_no = $data['business_no'] ?? ''; $contact_phone = $data['contact_phone'] ?? ''; $email = $data['email'] ?? ''; $address = $data['address'] ?? ''; $sales_manager_id = $data['sales_manager_id'] ?? $currentUser['id']; // 지정 안하면 본인 if (!$tenant_name) throw new Exception("업체명은 필수입니다."); $stmt = $pdo->prepare("INSERT INTO sales_tenants (manager_id, sales_manager_id, tenant_name, representative, business_no, contact_phone, email, address) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([$currentUser['id'], $sales_manager_id, $tenant_name, $representative, $business_no, $contact_phone, $email, $address]); echo json_encode(['success' => true, 'id' => $pdo->lastInsertId(), 'message' => '테넌트가 등록되었습니다.']); } elseif ($action === 'add_product') { $tenant_id = $data['tenant_id'] ?? null; if (!checkTenantPermission($pdo, $tenant_id, $currentUser)) throw new Exception("권한이 없습니다."); $product_name = $data['product_name'] ?? ''; $contract_amount = $data['contract_amount'] ?? 0; $commission_rate = $data['commission_rate'] ?? 0; $contract_date = $data['contract_date'] ?? date('Y-m-d'); $sub_models = isset($data['sub_models']) ? json_encode($data['sub_models']) : null; if (!$tenant_id || !$product_name) throw new Exception("필수 정보가 누락되었습니다."); $commission_amount = ($contract_amount * $commission_rate) / 100; $stmt = $pdo->prepare("INSERT INTO sales_tenant_products (tenant_id, product_name, contract_amount, commission_rate, commission_amount, contract_date, sub_models) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([$tenant_id, $product_name, $contract_amount, $commission_rate, $commission_amount, $contract_date, $sub_models]); echo json_encode(['success' => true, 'message' => '상품 계약 정보가 등록되었습니다.']); } elseif ($action === 'confirm_product') { if ($currentUser['role'] !== 'operator') throw new Exception("권한이 없습니다."); $product_id = $data['id'] ?? null; $confirmed = $data['confirmed'] ? 1 : 0; if (!$product_id) throw new Exception("ID가 누락되었습니다."); $stmt = $pdo->prepare("UPDATE sales_tenant_products SET operator_confirmed = ? WHERE id = ?"); $stmt->execute([$confirmed, $product_id]); echo json_encode(['success' => true, 'message' => $confirmed ? '승인되었습니다.' : '승인이 취소되었습니다.']); } elseif ($action === 'update_checklist') { $tenant_id = isset($data['tenant_id']) ? intval($data['tenant_id']) : null; $step_id = isset($data['step_id']) ? intval($data['step_id']) : null; $checkpoint_index = isset($data['checkpoint_index']) ? intval($data['checkpoint_index']) : null; $is_checked = (isset($data['is_checked']) && ($data['is_checked'] === true || $data['is_checked'] == 1)) ? 1 : 0; $scenario_type = $data['scenario_type'] ?? 'manager'; if ($tenant_id === null || $step_id === null || $checkpoint_index === null) { throw new Exception("필수 파라미터가 누락되었습니다. (T: $tenant_id, S: $step_id, C: $checkpoint_index)"); } if (!checkTenantPermission($pdo, $tenant_id, $currentUser)) throw new Exception("권한이 없습니다."); $stmt = $pdo->prepare(" INSERT INTO sales_tenant_scenarios (tenant_id, scenario_type, step_id, checkpoint_index, is_checked) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE is_checked = VALUES(is_checked) "); $stmt->execute([$tenant_id, $scenario_type, $step_id, $checkpoint_index, $is_checked]); echo json_encode(['success' => true]); } elseif ($action === 'save_consultation') { $tenant_id = $data['tenant_id'] ?? null; if (!checkTenantPermission($pdo, $tenant_id, $currentUser)) throw new Exception("권한이 없습니다."); $scenario_type = $data['scenario_type'] ?? 'manager'; $step_id = $data['step_id'] ?? null; $log_text = $data['log_text'] ?? ''; $consultation_type = $data['consultation_type'] ?? 'text'; $audio_file_path = null; if (!$tenant_id || !$step_id) throw new Exception("필수 정보가 누락되었습니다."); // 오디오 파일 업로드 처리 if ($consultation_type === 'audio' && isset($_FILES['audio_file'])) { $upload_dir = __DIR__ . "/../uploads/consultations/" . $tenant_id . "/"; if (!file_exists($upload_dir)) mkdir($upload_dir, 0777, true); $file_ext = pathinfo($_FILES['audio_file']['name'], PATHINFO_EXTENSION) ?: 'webm'; $file_name = "audio_" . date('Ymd_His') . "_" . uniqid() . "." . $file_ext; $audio_file_path = "uploads/consultations/" . $tenant_id . "/" . $file_name; if (!move_uploaded_file($_FILES['audio_file']['tmp_name'], __DIR__ . "/../" . $audio_file_path)) { throw new Exception("오디오 파일 저장 실패"); } } $stmt = $pdo->prepare("INSERT INTO sales_tenant_consultations (tenant_id, manager_id, scenario_type, step_id, log_text, audio_file_path, consultation_type) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([$tenant_id, $currentUser['id'], $scenario_type, $step_id, $log_text, $audio_file_path, $consultation_type]); echo json_encode(['success' => true, 'message' => '기록이 저장되었습니다.']); } elseif ($action === 'upload_attachments') { $tenant_id = $data['tenant_id'] ?? null; if (!checkTenantPermission($pdo, $tenant_id, $currentUser)) throw new Exception("권한이 없습니다."); $scenario_type = $data['scenario_type'] ?? 'manager'; $step_id = $data['step_id'] ?? null; if (!$tenant_id || !$step_id || !isset($_FILES['files'])) throw new Exception("필수 정보가 누락되었습니다."); $upload_dir = __DIR__ . "/../uploads/attachments/" . $tenant_id . "/"; if (!file_exists($upload_dir)) mkdir($upload_dir, 0777, true); $saved_paths = []; $files = $_FILES['files']; $count = is_array($files['name']) ? count($files['name']) : 1; for ($i = 0; $i < $count; $i++) { $tmp_name = is_array($files['tmp_name']) ? $files['tmp_name'][$i] : $files['tmp_name']; $name = is_array($files['name']) ? $files['name'][$i] : $files['name']; if (is_uploaded_file($tmp_name)) { $file_name = date('Ymd_His') . "_" . uniqid() . "_" . $name; $save_path = "uploads/attachments/" . $tenant_id . "/" . $file_name; if (move_uploaded_file($tmp_name, __DIR__ . "/../" . $save_path)) { $saved_paths[] = [ 'name' => $name, 'path' => $save_path, 'size' => is_array($files['size']) ? $files['size'][$i] : $files['size'] ]; } } } if (empty($saved_paths)) throw new Exception("저장된 파일이 없습니다."); $stmt = $pdo->prepare("INSERT INTO sales_tenant_consultations (tenant_id, manager_id, scenario_type, step_id, log_text, attachment_paths, consultation_type) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([$tenant_id, $currentUser['id'], $scenario_type, $step_id, '첨부파일 업로드', json_encode($saved_paths, JSON_UNESCAPED_UNICODE), 'file']); echo json_encode(['success' => true, 'message' => '파일이 업로드되었습니다.']); } elseif ($action === 'delete_consultation') { $id = $data['id'] ?? null; if (!$id) throw new Exception("ID가 누락되었습니다."); // 권한 확인을 위해 정보 조회 $stmt = $pdo->prepare("SELECT tenant_id, audio_file_path, attachment_paths FROM sales_tenant_consultations WHERE id = ?"); $stmt->execute([$id]); $c = $stmt->fetch(PDO::FETCH_ASSOC); if (!$c) throw new Exception("해당 기록을 찾을 수 없습니다."); if (!checkTenantPermission($pdo, $c['tenant_id'], $currentUser)) throw new Exception("권한이 없습니다."); // 파일 삭제 처리 if ($c['audio_file_path'] && file_exists(__DIR__ . "/../" . $c['audio_file_path'])) { @unlink(__DIR__ . "/../" . $c['audio_file_path']); } if ($c['attachment_paths']) { $paths = json_decode($c['attachment_paths'], true); if (is_array($paths)) { foreach ($paths as $p) { if (isset($p['path']) && file_exists(__DIR__ . "/../" . $p['path'])) { @unlink(__DIR__ . "/../" . $p['path']); } } } } $stmt = $pdo->prepare("DELETE FROM sales_tenant_consultations WHERE id = ?"); $stmt->execute([$id]); echo json_encode(['success' => true, 'message' => '성공적으로 삭제되었습니다.']); } elseif ($action === 'delete_product') { $product_id = $data['id'] ?? null; if (!$product_id) throw new Exception("ID가 누락되었습니다."); // 정보 및 권한 조회 $stmt = $pdo->prepare("SELECT tenant_id, operator_confirmed FROM sales_tenant_products WHERE id = ?"); $stmt->execute([$product_id]); $p = $stmt->fetch(PDO::FETCH_ASSOC); if (!$p) throw new Exception("해당 정보를 찾을 수 없습니다."); if (!checkTenantPermission($pdo, $p['tenant_id'], $currentUser)) throw new Exception("권한이 없습니다."); if ($p['operator_confirmed'] == 1) throw new Exception("이미 승인된 계약은 삭제할 수 없습니다."); $stmt = $pdo->prepare("DELETE FROM sales_tenant_products WHERE id = ?"); $stmt->execute([$product_id]); echo json_encode(['success' => true, 'message' => '성공적으로 삭제되었습니다.']); } elseif ($action === 'update_tenant_manager') { $tenant_id = isset($data['tenant_id']) ? intval($data['tenant_id']) : null; $sales_manager_id = isset($data['sales_manager_id']) ? $data['sales_manager_id'] : null; if (!$tenant_id) throw new Exception("테넌트 ID가 누락되었습니다."); // 권한 확인: 배지 지정은 운영자 또는 해당 테넌트를 등록한 영업관리자만 가능 $stmt = $pdo->prepare("SELECT manager_id FROM sales_tenants WHERE id = ?"); $stmt->execute([$tenant_id]); $t = $stmt->fetch(PDO::FETCH_ASSOC); if (!$t) throw new Exception("테넌트를 찾을 수 없습니다."); if ($currentUser['role'] !== 'operator' && $t['manager_id'] != $currentUser['id']) { throw new Exception("배정 권한이 없습니다."); } // manager_id가 0이거나 empty면 null로 처리 (지정 취소) $manager_val = (!empty($sales_manager_id)) ? intval($sales_manager_id) : null; $stmt = $pdo->prepare("UPDATE sales_tenants SET sales_manager_id = ? WHERE id = ?"); $stmt->execute([$manager_val, $tenant_id]); echo json_encode(['success' => true, 'message' => $manager_val ? '담당 매니저가 지정되었습니다.' : '담당 매니저 지정이 취소되었습니다.']); } elseif ($action === 'update_product') { $product_id = $data['id'] ?? null; if (!$product_id) throw new Exception("ID가 누락되었습니다."); // 정보 및 권한 조회 $stmt = $pdo->prepare("SELECT tenant_id, operator_confirmed FROM sales_tenant_products WHERE id = ?"); $stmt->execute([$product_id]); $p = $stmt->fetch(PDO::FETCH_ASSOC); if (!$p) throw new Exception("해당 정보를 찾을 수 없습니다."); if (!checkTenantPermission($pdo, $p['tenant_id'], $currentUser)) throw new Exception("권한이 없습니다."); if ($p['operator_confirmed'] == 1) throw new Exception("이미 승인된 계약은 수정할 수 없습니다."); $product_name = $data['product_name'] ?? ''; $commission_amount = ($contract_amount * $commission_rate) / 100; $stmt = $pdo->prepare("UPDATE sales_tenant_products SET product_name = ?, contract_amount = ?, commission_rate = ?, commission_amount = ?, contract_date = ?, sub_models = ? WHERE id = ?"); $stmt->execute([$product_name, $contract_amount, $commission_rate, $commission_amount, $contract_date, $sub_models, $product_id]); echo json_encode(['success' => true, 'message' => '계약 정보가 수정되었습니다.']); } break; } } catch (Exception $e) { echo json_encode(['success' => false, 'error' => $e->getMessage()]); } ?>