false, 'error' => 'Invalid JSON input']); exit; } $secret = $input['secret'] ?? ''; $action = $input['action'] ?? ''; $params = $input['params'] ?? []; if ($secret !== 'Podcluchenie_sec3retno') { writeLog("ERROR: Wrong secret"); http_response_code(403); echo json_encode(['success' => false, 'error' => 'Forbidden']); exit; } if (empty($action)) { writeLog("ERROR: No action"); http_response_code(400); echo json_encode(['success' => false, 'error' => 'No action specified']); exit; } writeLog("Action: $action"); try { $pdo = new PDO( "sqlsrv:Server=localhost,1433;Database=base;TrustServerCertificate=yes", "mobile", "Baza78653434ZXC@", [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8 ] ); writeLog("DB: Connected"); } catch (Exception $e) { writeLog("ERROR DB: " . $e->getMessage()); http_response_code(500); echo json_encode(['success' => false, 'error' => 'DB error', 'details' => $e->getMessage()]); exit; } switch ($action) { case 'products': handleProducts($pdo, $params); break; case 'product': handleProduct($pdo, $params); break; case 'categories': handleCategories($pdo, $params); break; // ====== НОВОЕ: авторизация по телефону ====== case 'login_phone': handleLoginPhone($pdo, $params); break; case 'register_phone': handleRegisterPhone($pdo, $params); break; // ============================================ default: http_response_code(400); echo json_encode(['success' => false, 'error' => 'Unknown action']); break; } /** * ТОВАРЫ */ function handleProducts(PDO $pdo, array $params): void { try { $page = max(1, (int)($params['page'] ?? 1)); $limit = max(1, min(10000, (int)($params['limit'] ?? 1000))); $offset = ($page - 1) * $limit; $search = $params['search'] ?? null; $folderId = $params['folder_id'] ?? null; // SQL: получаем только Розничную и Оптовую цены (исключаем Закупочную) $sql = " SELECT CONVERT(NVARCHAR(32), pr._Fld7115RRef, 2) AS id, ISNULL(nom._Description, 'NO NAME') AS name, ISNULL(nom._Code, '') AS article, CONVERT(NVARCHAR(32), nom._ParentIDRRef, 2) AS category_id, ISNULL(cat._Description, '') AS category_name, pr._Fld7118 AS price, ISNULL(type_price._Description, '') AS price_type FROM _InfoRg7113 pr LEFT JOIN _Reference70 nom ON pr._Fld7115RRef = nom._IDRRef LEFT JOIN _Reference70 cat ON nom._ParentIDRRef = cat._IDRRef LEFT JOIN _Reference95 type_price ON pr._Fld7114RRef = type_price._IDRRef WHERE pr._Fld7118 > 0 AND pr._Active = 0x01 AND nom._Fld24310 = 0x01 AND type_price._Description IN ('Розничный', 'Оптовый') "; $args = []; if ($folderId) { $cleanId = preg_replace('/[^0-9a-f]/i', '', $folderId); if (strlen($cleanId) === 32) { $sql .= " AND nom._ParentIDRRef = 0x" . $cleanId; } } if ($search) { $sql .= " AND nom._Description LIKE ?"; $args[] = "%$search%"; } $sql .= " ORDER BY pr._Fld7115RRef, type_price._Description"; $stmt = $pdo->prepare($sql); $stmt->execute($args); $rows = $stmt->fetchAll(); // Группируем товары, показываем каждый один раз с вариантами цен $products = []; $productMap = []; foreach ($rows as $row) { $productId = $row['id']; if (!isset($productMap[$productId])) { $productMap[$productId] = [ 'id' => $row['id'], 'name' => $row['name'], 'article' => $row['article'], 'category_id' => $row['category_id'], 'category_name' => $row['category_name'], 'prices' => [] ]; $products[] = &$productMap[$productId]; } $productMap[$productId]['prices'][$row['price_type']] = (float)$row['price']; } // Подсчитываем всего уникальных товаров $countSql = " SELECT COUNT(DISTINCT pr._Fld7115RRef) FROM _InfoRg7113 pr LEFT JOIN _Reference70 nom ON pr._Fld7115RRef = nom._IDRRef LEFT JOIN _Reference95 type_price ON pr._Fld7114RRef = type_price._IDRRef WHERE pr._Fld7118 > 0 AND pr._Active = 0x01 AND nom._Fld24310 = 0x01 AND type_price._Description IN ('Розничный', 'Оптовый') "; $countArgs = []; if ($folderId) { $cleanId = preg_replace('/[^0-9a-f]/i', '', $folderId); if (strlen($cleanId) === 32) { $countSql .= " AND nom._ParentIDRRef = 0x" . $cleanId; } } if ($search) { $countSql .= " AND nom._Description LIKE ?"; $countArgs[] = "%$search%"; } $countStmt = $pdo->prepare($countSql); $countStmt->execute($countArgs); $total = (int)$countStmt->fetchColumn(); echo json_encode([ 'success' => true, 'page' => $page, 'limit' => $limit, 'total' => $total, 'products' => array_slice($products, $offset, $limit) ], JSON_UNESCAPED_UNICODE); } catch (Exception $e) { http_response_code(500); echo json_encode(['success' => false, 'error' => 'DB error', 'details' => $e->getMessage()]); } } /** * ОДИН ТОВАР */ function handleProduct(PDO $pdo, array $params): void { try { $id = $params['id'] ?? ''; if (!preg_match('/^[a-f0-9]{32}$/i', $id)) { http_response_code(400); echo json_encode(['success' => false, 'error' => 'Invalid ID']); exit; } // Получаем только Розничную и Оптовую цены $sql = " SELECT CONVERT(NVARCHAR(32), pr._Fld7115RRef, 2) AS id, ISNULL(nom._Description, 'NO NAME') AS name, ISNULL(nom._Code, '') AS article, CONVERT(NVARCHAR(32), nom._ParentIDRRef, 2) AS category_id, ISNULL(cat._Description, '') AS category_name, pr._Fld7118 AS price, ISNULL(type_price._Description, '') AS price_type FROM _InfoRg7113 pr LEFT JOIN _Reference70 nom ON pr._Fld7115RRef = nom._IDRRef LEFT JOIN _Reference70 cat ON nom._ParentIDRRef = cat._IDRRef LEFT JOIN _Reference95 type_price ON pr._Fld7114RRef = type_price._IDRRef WHERE pr._Fld7115RRef = 0x" . $id . " AND pr._Active = 0x01 AND pr._Fld7118 > 0 AND nom._Fld24310 = 0x01 AND type_price._Description IN ('Розничный', 'Оптовый') ORDER BY type_price._Description "; $stmt = $pdo->prepare($sql); $stmt->execute(); $prices = $stmt->fetchAll(); if (empty($prices)) { http_response_code(404); echo json_encode(['success' => false, 'error' => 'Not found']); exit; } $product = [ 'id' => $prices[0]['id'], 'name' => $prices[0]['name'], 'article' => $prices[0]['article'], 'category_id' => $prices[0]['category_id'], 'category_name' => $prices[0]['category_name'], 'prices' => [] ]; foreach ($prices as $p) { if (!empty($p['price_type'])) { $product['prices'][$p['price_type']] = (float)$p['price']; } } echo json_encode(['success' => true, 'product' => $product], JSON_UNESCAPED_UNICODE); } catch (Exception $e) { http_response_code(500); echo json_encode(['success' => false, 'error' => 'DB error', 'details' => $e->getMessage()]); } } /** * КАТЕГОРИИ * Показывает ВСЕ категории из базы */ function handleCategories(PDO $pdo, array $params): void { try { $parentId = $params['parent_id'] ?? null; $folderId = null; if ($parentId) { $cleanId = preg_replace('/[^0-9a-f]/i', '', $parentId); if (strlen($cleanId) === 32) { $folderId = $cleanId; } } // SQL: получаем ВСЕ категории $sql = " SELECT CONVERT(NVARCHAR(32), _IDRRef, 2) AS id, _Description AS name, _Code AS article FROM _Reference70 WHERE _Folder = 0x00 AND _Marked = 0x00 "; if ($folderId) { $sql .= " AND _ParentIDRRef = 0x" . $folderId; } $sql .= " ORDER BY _Description"; writeLog("Categories SQL: " . $sql); $stmt = $pdo->prepare($sql); $stmt->execute(); $categories = $stmt->fetchAll(); writeLog("Categories found: " . count($categories)); // Преобразуем результат в нужный формат $result = array_map(function($cat) { return [ 'id' => $cat['id'], 'name' => $cat['name'], 'article' => $cat['article'] ]; }, $categories); echo json_encode([ 'success' => true, 'categories' => $result ], JSON_UNESCAPED_UNICODE); } catch (Exception $e) { writeLog("ERROR in categories: " . $e->getMessage()); http_response_code(500); echo json_encode([ 'success' => false, 'error' => 'DB error', 'details' => $e->getMessage() ]); } } /** * ========================== * АВТОРИЗАЦИЯ ПОЛЬЗОВАТЕЛЕЙ * ========================== * * Таблица 1С: _Reference51 * - _IDRRef binary(16) — ID клиента * - _Code nchar — код клиента (строковый номер) * - _Description nvarchar — ФИО * - _Fld695 nvarchar — номер карты * - _Fld13488 nchar — телефон * * Регистр бонусов: _AccumRgTn7733 * - _Fld7729RRef binary(16) — ссылка на клиента * - _Fld7731 numeric — ресурс (баллы) */ // Получить клиента по телефону + баланс бонусов function fetchUserByPhone(PDO $pdo, string $phone): ?array { $sql = " SELECT CONVERT(NVARCHAR(32), c._IDRRef, 2) AS id, c._Code AS code, c._Description AS name, LTRIM(RTRIM(c._Fld13488)) AS phone, c._Fld695 AS card_number, ISNULL(SUM(t._Fld7731), 0) AS bonus_balance FROM _Reference51 AS c LEFT JOIN _AccumRgTn7733 AS t ON t._Fld7729RRef = c._IDRRef WHERE c._Marked = 0x00 AND c._Folder = 0x01 AND LTRIM(RTRIM(c._Fld13488)) = ? GROUP BY c._IDRRef, c._Code, c._Description, c._Fld13488, c._Fld695 "; $stmt = $pdo->prepare($sql); $stmt->execute([$phone]); $row = $stmt->fetch(PDO::FETCH_ASSOC); if (!$row) { return null; } return [ 'id' => $row['id'], 'code' => $row['code'], 'name' => $row['name'], 'phone' => $row['phone'], 'card_number' => $row['card_number'], 'bonus_balance' => (float)$row['bonus_balance'], ]; } /** * ВХОД ПО ТЕЛЕФОНУ * params: { "phone": "8999..." } * - если клиент найден → success=true + user * - если нет → success=false, error="not_found" */ function handleLoginPhone(PDO $pdo, array $params): void { $phone = trim($params['phone'] ?? ''); if ($phone === '') { http_response_code(400); echo json_encode([ 'success' => false, 'error' => 'phone_required', 'message' => 'Не передан номер телефона', ], JSON_UNESCAPED_UNICODE); return; } writeLog("login_phone: phone=$phone"); $user = fetchUserByPhone($pdo, $phone); if ($user === null) { echo json_encode([ 'success' => false, 'error' => 'not_found', 'message' => 'Пользователь с таким телефоном не найден', ], JSON_UNESCAPED_UNICODE); return; } echo json_encode([ 'success' => true, 'usedExistingUser' => true, 'user' => $user, ], JSON_UNESCAPED_UNICODE); } function handleRegisterPhone(PDO $pdo, array $params): void { $phone = trim($params['phone'] ?? ''); $name = trim($params['name'] ?? ''); if ($phone === '') { http_response_code(400); echo json_encode([ 'success' => false, 'error' => 'phone_required', 'message' => 'Не передан номер телефона', ], JSON_UNESCAPED_UNICODE); return; } if ($name === '') { $name = $phone; } writeLog("register_phone: phone=$phone, name=$name"); // 1. Проверяем, есть ли такой телефон уже в 1С $existing = fetchUserByPhone($pdo, $phone); if ($existing !== null) { echo json_encode([ 'success' => true, 'usedExistingUser'=> true, 'user' => $existing, ], JSON_UNESCAPED_UNICODE); return; } // 2. Пользователя нет → создаём нового клиента в _Reference51 try { $pdo->beginTransaction(); // Получаем следующий числовой код клиента $codeSql = " SELECT ISNULL(MAX(CAST(_Code AS INT)), 0) + 1 FROM _Reference51 WHERE ISNUMERIC(_Code) = 1 "; $codeStmt = $pdo->query($codeSql); $nextCodeInt = (int)$codeStmt->fetchColumn(); $nextCode = str_pad((string)$nextCodeInt, 9, '0', STR_PAD_LEFT); // Номер карты на этапе регистрации НЕ задаём — оставляем пустым. $cardNumber = null; // или '' если так удобнее в 1С $insertSql = " INSERT INTO _Reference51 ( _IDRRef, _Marked, _PredefinedID, _ParentIDRRef, _Folder, _Code, _Description, _Fld695, _Fld13488 ) VALUES ( CAST(NEWID() AS BINARY(16)), -- _IDRRef 0x00, -- _Marked 0x00000000000000000000000000000000, -- _PredefinedID 0x00000000000000000000000000000000, -- _ParentIDRRef 0x01, -- _Folder = элемент ?, -- _Code ?, -- _Description ?, -- _Fld695 (номер карты, пока пусто) ? -- _Fld13488 (телефон) ) "; $stmt = $pdo->prepare($insertSql); $stmt->execute([ $nextCode, $name, $cardNumber, // <-- тут теперь null/пусто, без автоматической карты $phone, ]); $pdo->commit(); // 3. Читаем пользователя по телефону, чтобы вернуть полные данные + бонусы $user = fetchUserByPhone($pdo, $phone); if ($user === null) { echo json_encode([ 'success' => false, 'error' => 'created_but_not_found', 'message' => 'Клиент создан, но не найден при чтении', ], JSON_UNESCAPED_UNICODE); return; } echo json_encode([ 'success' => true, 'usedExistingUser'=> false, 'user' => $user, ], JSON_UNESCAPED_UNICODE); } catch (Exception $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } writeLog("ERROR register_phone: " . $e->getMessage()); http_response_code(500); echo json_encode([ 'success' => false, 'error' => 'db_error', 'message' => 'Ошибка при регистрации клиента', 'details' => $e->getMessage(), ], JSON_UNESCAPED_UNICODE); } } ?>