Files
crm.clientright.ru/include/Webservices/UpsertAccounts.php
Fedor da82100b60 feat: UI/UX improvements + CRM integration methods + documents_meta deduplication
Frontend:
- Changed main title to 'Подать обращение о защите прав потребителя'
- Changed browser title to 'Clientright — защита прав потребителей'
- Enhanced draft cards: show problem_description (250 chars), category tag, document progress bar
- Fixed 'Назад' button to always return to draft selection
- Added SSE connection for OCR status updates
- Renamed steps: Вход, Обращение, Документы, Заявление
- Skip 'Проверка полиса' and 'Тип события' steps for new claim flow

Backend:
- Fixed client IP extraction (X-Forwarded-For, X-Real-IP)
- Added problem_title, category, documents_required_list to draft list API
- Fixed documents_uploaded count to count unique field_labels

CRM Webservices:
- Added UpsertContact.php - create/update contacts with tgid support
- Added UpsertAccounts.php - batch upsert offenders by INN
- Added UpsertProject.php - create/update projects with offender mapping

Database:
- Fixed documents_meta duplicates in existing claims
- SQL query for deduplication by field_name provided
2025-12-01 22:18:21 +03:00

225 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/*********************************************************************************
* API-интерфейс для создания/поиска нескольких Контрагентов (Upsert Batch)
*
* Принимает JSON массив offenders, для каждого:
* - Ищет по ИНН
* - Если найден — возвращает ID (БЕЗ обновления)
* - Если не найден — создаёт новый
*
* Возвращает массив результатов с account_id для каждого offender
*
* Автор: Фёдор, 2025-12-01
********************************************************************************/
include_once 'include/Webservices/Query.php';
include_once 'modules/Users/Users.php';
require_once('include/Webservices/Utils.php');
require_once 'include/Webservices/Create.php';
require_once 'includes/Loader.php';
vimport('includes.runtime.Globals');
vimport('includes.runtime.BaseModel');
vimport('includes.runtime.LanguageHandler');
/**
* Upsert нескольких контрагентов
*
* @param string $offenders_json - JSON массив offenders:
* [
* {
* "accountname": "ООО Рога и Копыта",
* "address": "Москва, ул. Ленина 1",
* "email": "info@example.com",
* "website": "example.com",
* "phone": "+7 999 123-45-67",
* "inn": "7712345678",
* "ogrn": "1234567890123",
* "role": "Турагент" // опционально, для информации
* },
* ...
* ]
* @param mixed $user - пользователь CRM
* @return string JSON с результатами
*/
function vtws_upsertaccounts($offenders_json, $user = false) {
$logFile = 'logs/UpsertAccounts.log';
$logstring = date("Y-m-d H:i:s") . ' REQUEST: ' . substr($offenders_json, 0, 2000);
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
global $adb, $current_user;
// Очистка JSON от мусора (лишние кавычки, BOM, пробелы)
$offenders_json = trim($offenders_json);
$offenders_json = preg_replace('/^\xEF\xBB\xBF/', '', $offenders_json); // Убираем BOM
// Если строка обёрнута в кавычки — убираем
if (preg_match('/^".*"$/s', $offenders_json)) {
$offenders_json = substr($offenders_json, 1, -1);
$offenders_json = stripcslashes($offenders_json); // Убираем экранирование
}
// Убираем лишнюю кавычку в конце (баг n8n)
$offenders_json = preg_replace('/"\s*$/', '', rtrim($offenders_json, '"'));
if (substr($offenders_json, -1) !== ']' && substr($offenders_json, -1) !== '}') {
// Пробуем найти конец массива/объекта
if (($pos = strrpos($offenders_json, ']')) !== false) {
$offenders_json = substr($offenders_json, 0, $pos + 1);
}
}
$logstring = date("Y-m-d H:i:s") . ' CLEANED JSON: ' . substr($offenders_json, 0, 500);
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
// Парсим JSON
$offenders = json_decode($offenders_json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$error = 'Ошибка парсинга JSON: ' . json_last_error_msg();
file_put_contents($logFile, date("Y-m-d H:i:s") . ' ❌ ' . $error . PHP_EOL, FILE_APPEND);
file_put_contents($logFile, date("Y-m-d H:i:s") . ' RAW: ' . $offenders_json . PHP_EOL, FILE_APPEND);
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, $error);
}
if (!is_array($offenders)) {
$offenders = [$offenders]; // Если передан один объект — оборачиваем в массив
}
$logstring = date('Y-m-d H:i:s') . ' Получено offenders: ' . count($offenders);
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
// Результаты
$results = array(
'success' => true,
'total' => count($offenders),
'created' => 0,
'found' => 0,
'errors' => 0,
'accounts' => array()
);
// Обрабатываем каждого offender
foreach ($offenders as $index => $offender) {
$accountResult = array(
'index' => $index,
'success' => false,
'account_id' => null,
'action' => null,
'accountname' => $offender['accountname'] ?? '',
'inn' => $offender['inn'] ?? '',
'role' => $offender['role'] ?? null,
'message' => ''
);
try {
// Извлекаем данные
$accountname = trim($offender['accountname'] ?? '');
$address = trim($offender['address'] ?? '');
$email = trim($offender['email'] ?? '');
$website = trim($offender['website'] ?? '');
$phone = trim($offender['phone'] ?? '');
$inn = preg_replace('/[^0-9]/', '', $offender['inn'] ?? ''); // Только цифры
$ogrn = preg_replace('/[^0-9]/', '', $offender['ogrn'] ?? ''); // Только цифры
$role = trim($offender['role'] ?? '');
// Проверка обязательных полей
if (empty($accountname)) {
throw new Exception('Не указано наименование контрагента (accountname)');
}
if (empty($inn)) {
throw new Exception('Не указан ИНН');
}
// Валидация ИНН (10 или 12 цифр)
if (strlen($inn) != 10 && strlen($inn) != 12) {
$logstring = date('Y-m-d H:i:s') . " ⚠️ Нестандартный ИНН: $inn (длина " . strlen($inn) . ')';
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
// Не падаем, просто логируем
}
// ========================================
// ПОИСК ПО ИНН
// ========================================
$query = "SELECT a.accountid, a.accountname
FROM vtiger_account a
LEFT JOIN vtiger_crmentity e ON e.crmid = a.accountid
WHERE e.deleted = 0 AND a.inn = ?
LIMIT 1";
$res = $adb->pquery($query, array($inn));
if ($adb->num_rows($res) > 0) {
// === НАЙДЕН — просто возвращаем ID ===
$existingId = $adb->query_result($res, 0, 'accountid');
$existingName = $adb->query_result($res, 0, 'accountname');
$accountResult['success'] = true;
$accountResult['account_id'] = $existingId;
$accountResult['action'] = 'found';
$accountResult['message'] = 'Контрагент найден по ИНН';
$accountResult['existing_name'] = $existingName;
$results['found']++;
$logstring = date('Y-m-d H:i:s') . " ✓ [$index] Найден: $existingId ($existingName) по ИНН $inn";
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
} else {
// === НЕ НАЙДЕН — создаём ===
$params = array(
'accountname' => $accountname,
'bill_street' => $address,
'email1' => $email,
'website' => $website,
'phone' => $phone,
'inn' => $inn,
'cf_1951' => $ogrn, // ОГРН в кастомном поле
'assigned_user_id' => vtws_getWebserviceEntityId('Users', $current_user->id)
);
$logstring = date('Y-m-d H:i:s') . " 🆕 [$index] Создаём: " . json_encode($params, JSON_UNESCAPED_UNICODE);
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
$account = vtws_create('Accounts', $params, $current_user);
$newAccountId = substr($account['id'], 3); // Убираем 11x
$accountResult['success'] = true;
$accountResult['account_id'] = $newAccountId;
$accountResult['action'] = 'created';
$accountResult['message'] = 'Контрагент создан';
$results['created']++;
$logstring = date('Y-m-d H:i:s') . " ✅ [$index] Создан: $newAccountId";
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
}
} catch (WebServiceException $ex) {
$accountResult['success'] = false;
$accountResult['message'] = $ex->getMessage();
$results['errors']++;
$logstring = date('Y-m-d H:i:s') . " ❌ [$index] WebService ошибка: " . $ex->getMessage();
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
} catch (Exception $ex) {
$accountResult['success'] = false;
$accountResult['message'] = $ex->getMessage();
$results['errors']++;
$logstring = date('Y-m-d H:i:s') . " ❌ [$index] Ошибка: " . $ex->getMessage();
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
}
$results['accounts'][] = $accountResult;
}
// Итоговый статус
$results['success'] = ($results['errors'] == 0);
$logstring = date('Y-m-d H:i:s') . ' RESULT: total=' . $results['total']
. ', created=' . $results['created']
. ', found=' . $results['found']
. ', errors=' . $results['errors'] . PHP_EOL;
file_put_contents($logFile, $logstring, FILE_APPEND);
return json_encode($results, JSON_UNESCAPED_UNICODE);
}