Actual prod
This commit is contained in:
parent
f593915c05
commit
a6ab73246e
@ -6,6 +6,25 @@ use Complex\Exception;
|
||||
|
||||
class AdvertsController extends ApiController
|
||||
{
|
||||
// Найденный листинг парсера read-only: рекламные действия недоступны
|
||||
// (паритет с десктопом, где switchAdvert*/addPromotion и т.п. отбивают листинг). Вернёт true + 403, если листинг.
|
||||
// Offset-free: после снятия смещения id листинга (= реальный external_listings.id) коллизит с objects.id,
|
||||
// поэтому различить листинг по id нельзя. Признак приходит явным флагом is_external из запроса
|
||||
// (фронт знает, что действие над найденным листингом). Сервер по id отличить листинг уже не может.
|
||||
private function _rejectIfExternalListing($app, $is_external)
|
||||
{
|
||||
if (!empty($is_external)) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setStatus(403);
|
||||
$app->response->setBody(json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['Действие недоступно для найденного листинга. Скопируйте объект в свои, чтобы управлять им.'],
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPublishedPackages($app, $get, $post) {
|
||||
|
||||
$error = false;
|
||||
@ -90,6 +109,9 @@ class AdvertsController extends ApiController
|
||||
if (isset($data['object_id']))
|
||||
$object_id = $data['object_id'];
|
||||
|
||||
// Признак найденного листинга — явный флаг из запроса (offset-free: id листинга коллизит с objects.id)
|
||||
if ($this->_rejectIfExternalListing($app, isset($data['is_external']) ? $data['is_external'] : null)) return;
|
||||
|
||||
$service = null;
|
||||
if (isset($data['service']))
|
||||
$service = $data['service'];
|
||||
@ -1814,6 +1836,9 @@ class AdvertsController extends ApiController
|
||||
if (isset($get['object_id']))
|
||||
$object_id = $get['object_id'];
|
||||
|
||||
// Признак найденного листинга — явный флаг из запроса (offset-free: id листинга коллизит с objects.id)
|
||||
if ($this->_rejectIfExternalListing($app, isset($get['is_external']) ? $get['is_external'] : null)) return;
|
||||
|
||||
if (($user_id = $_SESSION['id']) && !is_null($object_id)) {
|
||||
|
||||
// Открываем соединение с БД
|
||||
@ -1867,6 +1892,9 @@ class AdvertsController extends ApiController
|
||||
if (isset($data['object_id']))
|
||||
$object_id = $data['object_id'];
|
||||
|
||||
// Признак найденного листинга — явный флаг из запроса (offset-free: id листинга коллизит с objects.id)
|
||||
if ($this->_rejectIfExternalListing($app, isset($data['is_external']) ? $data['is_external'] : null)) return;
|
||||
|
||||
$packages = null;
|
||||
if (isset($data['packages']))
|
||||
$packages = $data['packages'];
|
||||
@ -2036,6 +2064,9 @@ class AdvertsController extends ApiController
|
||||
if (isset($data['object_id']))
|
||||
$object_id = $data['object_id'];
|
||||
|
||||
// Признак найденного листинга — явный флаг из запроса (offset-free: id листинга коллизит с objects.id)
|
||||
if ($this->_rejectIfExternalListing($app, isset($data['is_external']) ? $data['is_external'] : null)) return;
|
||||
|
||||
$price = null;
|
||||
if (isset($data['price']))
|
||||
$price = $data['price'];
|
||||
@ -2131,6 +2162,9 @@ class AdvertsController extends ApiController
|
||||
if (isset($data['object_id']))
|
||||
$object_id = $data['object_id'];
|
||||
|
||||
// Признак найденного листинга — явный флаг из запроса (offset-free: id листинга коллизит с objects.id)
|
||||
if ($this->_rejectIfExternalListing($app, isset($data['is_external']) ? $data['is_external'] : null)) return;
|
||||
|
||||
$price = null;
|
||||
if (isset($data['price']))
|
||||
$price = $data['price'];
|
||||
@ -2597,6 +2631,9 @@ class AdvertsController extends ApiController
|
||||
if (isset($data['object_id']))
|
||||
$object_id = $data['object_id'];
|
||||
|
||||
// Признак найденного листинга — явный флаг из запроса (offset-free: id листинга коллизит с objects.id)
|
||||
if ($this->_rejectIfExternalListing($app, isset($data['is_external']) ? $data['is_external'] : null)) return;
|
||||
|
||||
$services = null;
|
||||
if (isset($data['services']))
|
||||
$services = $data['services'];
|
||||
@ -2647,6 +2684,9 @@ class AdvertsController extends ApiController
|
||||
if (isset($data['object_id']))
|
||||
$object_id = $data['object_id'];
|
||||
|
||||
// Признак найденного листинга — явный флаг из запроса (offset-free: id листинга коллизит с objects.id)
|
||||
if ($this->_rejectIfExternalListing($app, isset($data['is_external']) ? $data['is_external'] : null)) return;
|
||||
|
||||
$employee_ids = null;
|
||||
if (isset($data['employee_ids']))
|
||||
$employee_ids = $data['employee_ids'];
|
||||
|
||||
@ -25,6 +25,16 @@ class ClientsController extends ApiController
|
||||
$menuPermissions = $user->checkMenuPermissions();
|
||||
$agency_id = $_SESSION['agency_id'];
|
||||
|
||||
// Наполняем new_transfers для is_new здесь, не полагаясь на getFunnels
|
||||
$nt_agency_id = \User::getUserAgencyID($user_id);
|
||||
$nt_ids = [];
|
||||
$nt_sql = "SELECT DISTINCT `clients`.`id` FROM `events_clients` AS `events` LEFT JOIN `clients` AS `clients` ON `clients`.`id` = `events`.`client_id` LEFT JOIN `funnel` AS `funnel` ON `funnel`.`id` = `clients`.`funnel_id` WHERE (`events`.`user_id` = '$user_id' AND `events`.`from_id` = '$user_id') AND (`events`.`event` = 'accepted' OR `events`.`event` = 'accepted_doer') AND `events`.`viewed` = 0 AND `events`.`read` = 1 AND (`clients`.`confirm` = 1 OR `clients`.`doers_confirm` = 1) AND `events`.`req_id` = 0 AND `clients`.`deleted` <> '1' AND (`clients`.`who_work` = '$user_id' OR `clients`.`doers` LIKE '%"{$user_id}":1%') AND (`clients`.`funnel_id` = 0 OR (`funnel`.`agency_id` = '$nt_agency_id' AND `funnel`.`deleted` = 0))";
|
||||
if ($nt_q = mysql_query($nt_sql)) {
|
||||
while ($nt_row = mysql_fetch_assoc($nt_q))
|
||||
$nt_ids[] = $nt_row['id'];
|
||||
}
|
||||
$_SESSION['new_transfers']['clients'] = $nt_ids;
|
||||
|
||||
$clients = [];
|
||||
$pagination = null;
|
||||
|
||||
@ -1092,7 +1102,7 @@ class ClientsController extends ApiController
|
||||
if (isset($users[$client['id_agent']])) {
|
||||
if ($user_who = $users[$client['id_agent']]) {
|
||||
if ($user_who['agency']) {
|
||||
$who_added['name'] = "Агентство {$user_who['agency_name']}";
|
||||
$who_added['name'] = trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']) ?: "Агентство {$user_who['agency_name']}";
|
||||
} else if ($user_who['manager']) {
|
||||
$who_added['name'] = "Менеджер " . trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']);
|
||||
if ($user_who['role_id'] > 0)
|
||||
@ -1117,7 +1127,7 @@ class ClientsController extends ApiController
|
||||
if ($user_who = $users[$client['who_delete']]) {
|
||||
|
||||
if ($user_who['agency']) {
|
||||
$who_delete['name'] = "Агентство {$user_who['agency_name']}";
|
||||
$who_delete['name'] = trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']) ?: "Агентство {$user_who['agency_name']}";
|
||||
} else if ($user_who['manager']) {
|
||||
$who_delete['name'] = "Менеджер " . trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']);
|
||||
} else {
|
||||
@ -1151,7 +1161,7 @@ class ClientsController extends ApiController
|
||||
if ($user_who = $users[$client['who_work']]) {
|
||||
|
||||
if ($user_who['agency']) {
|
||||
$who_work['name'] = "Агентство {$user_who['agency_name']}";
|
||||
$who_work['name'] = trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']) ?: "Агентство {$user_who['agency_name']}";
|
||||
} else if ($user_who['manager']) {
|
||||
if ($user_who['role_id'] > 0) {
|
||||
$who_work['name'] = $departments[$user_who['role_id']]['role_name'] . ' ' . trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']);
|
||||
@ -1197,7 +1207,7 @@ class ClientsController extends ApiController
|
||||
$user_who = $users[$id];
|
||||
if ($status == 0) {
|
||||
if ($user_who['agency']) {
|
||||
$doers_request['name'] = "Агентство {$user_who['agency_name']}";
|
||||
$doers_request['name'] = trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']) ?: "Агентство {$user_who['agency_name']}";
|
||||
} else if ($user_who['manager']) {
|
||||
$doers_request['name'] = "Менеджер " . trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']);
|
||||
} else {
|
||||
@ -1208,7 +1218,7 @@ class ClientsController extends ApiController
|
||||
}
|
||||
if ($status == 1) {
|
||||
if ($user_who['agency']) {
|
||||
$doers_accept['name'] = "Агентство {$user_who['agency_name']}";
|
||||
$doers_accept['name'] = trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']) ?: "Агентство {$user_who['agency_name']}";
|
||||
} else if ($user_who['manager']) {
|
||||
$doers_accept['name'] = "Менеджер " . trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']);
|
||||
} else {
|
||||
@ -1886,7 +1896,7 @@ class ClientsController extends ApiController
|
||||
if (isset($users[$data['id_agent']])) {
|
||||
if ($user_who = $users[$data['id_agent']]) {
|
||||
if ($user_who['agency']) {
|
||||
$who_added['name'] = "Агентство {$user_who['agency_name']}";
|
||||
$who_added['name'] = trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']) ?: "Агентство {$user_who['agency_name']}";
|
||||
} else if ($user_who['manager']) {
|
||||
$who_added['name'] = "Менеджер " . trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']);
|
||||
if ($user_who['role_id'] > 0)
|
||||
@ -1911,7 +1921,7 @@ class ClientsController extends ApiController
|
||||
if ($user_who = $users[$data['who_delete']]) {
|
||||
|
||||
if ($user_who['agency']) {
|
||||
$who_delete['name'] = "Агентство {$user_who['agency_name']}";
|
||||
$who_delete['name'] = trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']) ?: "Агентство {$user_who['agency_name']}";
|
||||
} else if ($user_who['manager']) {
|
||||
$who_delete['name'] = "Менеджер " . trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']);
|
||||
} else {
|
||||
@ -1945,7 +1955,7 @@ class ClientsController extends ApiController
|
||||
if ($user_who = $users[$data['who_work']]) {
|
||||
|
||||
if ($user_who['agency']) {
|
||||
$who_work['name'] = "Агентство {$user_who['agency_name']}";
|
||||
$who_work['name'] = trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']) ?: "Агентство {$user_who['agency_name']}";
|
||||
} else if ($user_who['manager']) {
|
||||
if ($user_who['role_id'] > 0) {
|
||||
$who_work['name'] = $departments[$user_who['role_id']]['role_name'] . ' ' . trim($user_who['last_name'] . ' ' . $user_who['first_name'] . ' ' . $user_who['middle_name']);
|
||||
@ -2446,7 +2456,20 @@ class ClientsController extends ApiController
|
||||
|
||||
$variants = [];
|
||||
|
||||
$sql = "SELECT `sended_pdf`.*,
|
||||
// Распознавание найденных листингов парсера и витринный EXT-id.
|
||||
// Offset-free: id_object для листинга = реальный external_listings.id,
|
||||
// он коллизит с objects.id. Листинг различаем по флагу sended_pdf.is_external;
|
||||
// витринный id собираем как 'EXT-{el.id}' напрямую (listing_guard.php тут больше не нужен).
|
||||
$ext_variant_rows = []; // позиция в $variants => реальный el.id листинга
|
||||
$ext_title_fallback = []; // позиция в $variants => запасной заголовок из снапшота отправки
|
||||
|
||||
// Три ветки UNION:
|
||||
// 1) свои объекты (is_external=0) — INNER JOIN archive по id_object (даёт телефон/собственника/адрес снапшота);
|
||||
// 2) найденные листинги (is_external=1) — БЕЗ join к archive: id_object = реальный el.id коллизит с
|
||||
// objects.id, join к archive подцепил бы чужой одноимённый снапшот; адрес/заголовок добиваются батчем
|
||||
// из external_listings после цикла;
|
||||
// 3) новостройки (sended_pdf_newbuildings) — листингами никогда не бывают.
|
||||
$sql = "SELECT `sended_pdf`.`id`, `sended_pdf`.`id_agent`, `sended_pdf`.`id_object`, `sended_pdf`.`date_send`, `sended_pdf`.`id_client`, `sended_pdf`.`nazv`, `sended_pdf`.`path`, `sended_pdf`.`send_type`, `sended_pdf`.`close_contact`, `sended_pdf`.`hide_address`, `sended_pdf`.`new_price`, `sended_pdf`.`description`, `sended_pdf`.`token`, `sended_pdf`.`id_request`, `sended_pdf`.`sent_catalog_id`, `sended_pdf`.`is_favorite`,
|
||||
0 AS `close_contact`,
|
||||
0 AS `new_price`,
|
||||
DATE_FORMAT(`sended_pdf`.`date_send`, '%d.%m.%Y') AS `date_f`,
|
||||
@ -2456,11 +2479,28 @@ class ClientsController extends ApiController
|
||||
`archive`.`id` as `object_id`,
|
||||
`archive`.`nazv` as `object_title`,
|
||||
`archive`.`adres` as `object_address`,
|
||||
0 as `newbild`
|
||||
0 as `newbild`,
|
||||
0 as `is_external`
|
||||
FROM `sended_pdf`, `archive`
|
||||
WHERE `sended_pdf`.`id_client` = '$client_id' AND
|
||||
`sended_pdf`.`is_external` = 0 AND
|
||||
`archive`.`id_obj`=`sended_pdf`.`id_object`
|
||||
UNION ALL SELECT `sended_pdf_newbuildings`.*,
|
||||
UNION ALL SELECT `sended_pdf`.`id`, `sended_pdf`.`id_agent`, `sended_pdf`.`id_object`, `sended_pdf`.`date_send`, `sended_pdf`.`id_client`, `sended_pdf`.`nazv`, `sended_pdf`.`path`, `sended_pdf`.`send_type`, `sended_pdf`.`close_contact`, `sended_pdf`.`hide_address`, `sended_pdf`.`new_price`, `sended_pdf`.`description`, `sended_pdf`.`token`, `sended_pdf`.`id_request`, `sended_pdf`.`sent_catalog_id`, `sended_pdf`.`is_favorite`,
|
||||
0 AS `close_contact`,
|
||||
0 AS `new_price`,
|
||||
DATE_FORMAT(`sended_pdf`.`date_send`, '%d.%m.%Y') AS `date_f`,
|
||||
DATE_FORMAT(`sended_pdf`.`date_send`, '%H:%i') AS `time_f`,
|
||||
null as `phone`,
|
||||
null as `sobstv`,
|
||||
null as `object_id`,
|
||||
null as `object_title`,
|
||||
null as `object_address`,
|
||||
0 as `newbild`,
|
||||
1 as `is_external`
|
||||
FROM `sended_pdf`
|
||||
WHERE `sended_pdf`.`id_client` = '$client_id' AND
|
||||
`sended_pdf`.`is_external` = 1
|
||||
UNION ALL SELECT `sended_pdf_newbuildings`.`id`, `sended_pdf_newbuildings`.`id_agent`, `sended_pdf_newbuildings`.`id_object`, `sended_pdf_newbuildings`.`date_send`, `sended_pdf_newbuildings`.`id_client`, `sended_pdf_newbuildings`.`nazv`, `sended_pdf_newbuildings`.`path`, `sended_pdf_newbuildings`.`send_type`, `sended_pdf_newbuildings`.`close_contact`, `sended_pdf_newbuildings`.`hide_address`, `sended_pdf_newbuildings`.`new_price`, null as `description`, `sended_pdf_newbuildings`.`token`, `sended_pdf_newbuildings`.`id_request`, `sended_pdf_newbuildings`.`sent_catalog_id`, `sended_pdf_newbuildings`.`is_favorite`,
|
||||
0 AS `close_contact`,
|
||||
0 AS `new_price`,
|
||||
DATE_FORMAT(`sended_pdf_newbuildings`.`date_send`, '%d.%m.%Y') AS `date_f`,
|
||||
@ -2470,7 +2510,8 @@ class ClientsController extends ApiController
|
||||
null as `object_id`,
|
||||
null as `object_title`,
|
||||
null as `object_address`,
|
||||
1 as `newbild`
|
||||
1 as `newbild`,
|
||||
0 as `is_external`
|
||||
FROM `sended_pdf_newbuildings`
|
||||
WHERE `sended_pdf_newbuildings`.`id_client` = '$client_id'
|
||||
ORDER BY `date_send` DESC";
|
||||
@ -2513,6 +2554,58 @@ class ClientsController extends ApiController
|
||||
'is_sended' => in_array($pdf['id_object'], $toSend),
|
||||
'is_new' => $new_date,
|
||||
];
|
||||
|
||||
// Найденный листинг парсера: строки в objects нет, помечаем элемент и
|
||||
// запоминаем позицию — дообогащение из external_listings батчем после цикла.
|
||||
// Offset-free: листинг различаем по флагу is_external (Sphinx/БД-атрибут), id_object —
|
||||
// уже реальный external_listings.id, его и кладём как ключ дообогащения.
|
||||
if ((int)$pdf['is_external'] === 1) {
|
||||
$ext_idx = count($variants) - 1;
|
||||
$ext_real_id = (int)$pdf['id_object'];
|
||||
$variants[$ext_idx]['is_external'] = true;
|
||||
$variants[$ext_idx]['display_id'] = 'EXT-' . $ext_real_id;
|
||||
$variants[$ext_idx]['is_removed'] = false;
|
||||
$ext_variant_rows[$ext_idx] = $ext_real_id;
|
||||
$ext_title_fallback[$ext_idx] = trim($pdf['nazv']);
|
||||
}
|
||||
}
|
||||
|
||||
// Дообогащение строк-листингов: заголовок/адрес берём из external_listings одним батчем.
|
||||
// Offset-free: ключ — реальный external_listings.id (без смещения), запрашиваем и индексируем по нему.
|
||||
if (!empty($ext_variant_rows)) {
|
||||
// мягкая деградация: проблема с external_listings не должна валить историю отправок
|
||||
try {
|
||||
$pdo = $app->container->get('pdo');
|
||||
$ext_real_ids = array_map('intval', array_values($ext_variant_rows));
|
||||
|
||||
$ext_sth = $pdo->prepare("SELECT id, address_text, price FROM external_listings WHERE id IN (" . implode(',', array_unique($ext_real_ids)) . ")");
|
||||
if ($ext_sth->execute()) {
|
||||
|
||||
// Мапа листингов ключуется на реальный el.id и держится ОТДЕЛЬНО от objects —
|
||||
// одноимённый objects.id сюда не попадает (запрос только по external_listings).
|
||||
$ext_by_id = [];
|
||||
while ($ext_row = $ext_sth->fetch(\PDO::FETCH_ASSOC))
|
||||
$ext_by_id[(int)$ext_row['id']] = $ext_row;
|
||||
|
||||
foreach ($ext_variant_rows as $ext_idx => $ext_real_id) {
|
||||
if (isset($ext_by_id[$ext_real_id])) {
|
||||
// живой листинг: пустые заголовок/адрес добиваем адресом первоисточника
|
||||
$ext_address = trim($ext_by_id[$ext_real_id]['address_text']);
|
||||
if ($variants[$ext_idx]['object_title'] === '')
|
||||
$variants[$ext_idx]['object_title'] = $ext_address;
|
||||
if ($variants[$ext_idx]['object_address'] === '')
|
||||
$variants[$ext_idx]['object_address'] = $ext_address;
|
||||
} else {
|
||||
// объявление снято с публикации: остаётся nazv-снапшот из истории отправки
|
||||
$variants[$ext_idx]['is_removed'] = true;
|
||||
if ($variants[$ext_idx]['object_title'] === '')
|
||||
$variants[$ext_idx]['object_title'] = $ext_title_fallback[$ext_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// данных по листингам нет — строки остаются с пометкой is_external без дообогащения
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$error = true;
|
||||
@ -2579,6 +2672,7 @@ class ClientsController extends ApiController
|
||||
|
||||
$history = [];
|
||||
|
||||
// copy_created / copy_from — лейбл пустой: текст идёт в dop_text, иначе в истории двоится.
|
||||
$array_ev = [
|
||||
'add'=>'Добавлен',
|
||||
'transmitted' => 'Передан',
|
||||
@ -2590,7 +2684,10 @@ class ClientsController extends ApiController
|
||||
'notadopted_doer' => 'Приглашение не принято',
|
||||
'stageedit' => 'Изменен этап',
|
||||
'delete' => 'Закрыт',
|
||||
'double_client' => 'Повторное обращение'
|
||||
'double_client' => 'Повторное обращение',
|
||||
// copy_* — плейсхолдер {N} подменяется ниже на кликабельный ID из dop_text.
|
||||
'copy_created' => 'Создана копия → ID {N}',
|
||||
'copy_from' => 'Создана как копия заявки ID {N}'
|
||||
];
|
||||
|
||||
$sql = "(SELECT id, client_id, user_id, from_id, `event`, `date`, `dop_text`, 0 as `of_objects` FROM events_clients WHERE client_id = $client_id)
|
||||
@ -2614,7 +2711,7 @@ class ClientsController extends ApiController
|
||||
$mstr = mysql_fetch_assoc($q_m);
|
||||
|
||||
if ($mstr['agency'])
|
||||
$master = "Агентство $mstr[agency_name]";
|
||||
$master = trim($mstr['last_name'] . ' ' . $mstr['first_name'] . ' ' . $mstr['middle_name']) ?: "Агентство {$mstr['agency_name']}";
|
||||
else if ($mstr['manager'])
|
||||
$master = "Менеджер " . trim($mstr['last_name'] . ' ' . $mstr['first_name'] . ' ' . $mstr['middle_name']);
|
||||
else
|
||||
@ -2634,7 +2731,7 @@ class ClientsController extends ApiController
|
||||
$mstr = mysql_fetch_assoc($q_m);
|
||||
|
||||
if ($mstr['agency'])
|
||||
$from_master = "агентство $mstr[agency_name]";
|
||||
$from_master = trim($mstr['last_name'] . ' ' . $mstr['first_name'] . ' ' . $mstr['middle_name']) ?: "агентство {$mstr['agency_name']}";
|
||||
else if($mstr['manager'])
|
||||
$from_master = "менеджер " . trim($mstr['last_name'] . ' ' . $mstr['first_name'] . ' ' . $mstr['middle_name']);
|
||||
else
|
||||
@ -2676,13 +2773,23 @@ class ClientsController extends ApiController
|
||||
$dop_text .= ' по звонку по объекту '.$name;
|
||||
}
|
||||
|
||||
// copy_* — рендерим label-шаблон, подставляя int-id из dop_text.
|
||||
$event_label = $array_ev[$r_ev['event']];
|
||||
if (in_array($r_ev['event'], ['copy_from', 'copy_created'], true)) {
|
||||
$linkId = (int)$r_ev['dop_text'];
|
||||
if ($linkId > 0) {
|
||||
$event_label = str_replace('{N}', (string)$linkId, $event_label);
|
||||
$r_ev['dop_text'] = ''; // весь текст уже в label
|
||||
}
|
||||
}
|
||||
|
||||
$history[] = [
|
||||
'datetime' => date('Y-m-d H:i:s', $udate),
|
||||
'date_string' => $date_row,
|
||||
'time_string' => date('H:i:s', $udate),
|
||||
'to_user' => $master,
|
||||
'from_user' => $from_master,
|
||||
'event' => $array_ev[$r_ev['event']] ." ". (function_exists('mask_text_for_hide_user') ? mask_text_for_hide_user($dop_text . $r_ev['dop_text']) : ($dop_text . $r_ev['dop_text'])) ." ". $from_master,
|
||||
'event' => $event_label ." ". (function_exists('mask_text_for_hide_user') ? mask_text_for_hide_user($dop_text . $r_ev['dop_text']) : ($dop_text . $r_ev['dop_text'])) ." ". $from_master,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
|
||||
60
api/v2/app/src/controllers/CommonController.php
Normal file → Executable file
60
api/v2/app/src/controllers/CommonController.php
Normal file → Executable file
@ -3,7 +3,7 @@
|
||||
namespace App\v2\Controller;
|
||||
|
||||
class CommonController extends ApiController
|
||||
{
|
||||
{
|
||||
|
||||
// Offset-free: листинг парсера (external_listings) различается по ЯВНОМУ флагу из запроса.
|
||||
// Реальный el.id листинга коллизит с objects.id, поэтому различить его по id нельзя.
|
||||
@ -3156,13 +3156,13 @@ class CommonController extends ApiController
|
||||
$r['type'] = 'id';
|
||||
$r['link'] = $host . '/download_file.php?id=' . $r['id'];
|
||||
|
||||
$unix_time = filemtime($file);
|
||||
$unix_time = !empty($r['created_at']) ? strtotime($r['created_at']) : @filemtime($file);
|
||||
$r['date'] = date('d.m.Y в H:i', $unix_time);
|
||||
|
||||
if (!isset($list['names'][$r['name_id']]))
|
||||
$list['names'][$r['name_id']] = array('type' => 'id', 'from' => $section, 'from_id' => $source_id, 'id' => (int)$r['name_id']);
|
||||
|
||||
if (file_exists($file))
|
||||
if (!empty($r['guid']))
|
||||
$row[$r['name_id']][] = $r;
|
||||
|
||||
}
|
||||
@ -3193,13 +3193,13 @@ class CommonController extends ApiController
|
||||
$r['type'] = 'guid';
|
||||
$r['link'] = $host . '/download_file.php?guid=' . $r['guid'];
|
||||
|
||||
$unix_time = filemtime($file);
|
||||
$unix_time = !empty($r['created_at']) ? strtotime($r['created_at']) : @filemtime($file);
|
||||
$r['date'] = date('d.m.Y в H:i', $unix_time);
|
||||
|
||||
if (!isset($list['names'][$r['etap_id']]))
|
||||
$list['names'][$r['etap_id']] = array('type' => 'guid', 'from' => $section, 'from_id' => $source_id, 'id' => (int)$r['etap_id']);
|
||||
|
||||
if (file_exists($file))
|
||||
if (!empty($r['guid']))
|
||||
$row[$r['etap_id']][] = $r;
|
||||
|
||||
}
|
||||
@ -3235,12 +3235,12 @@ class CommonController extends ApiController
|
||||
$r['type'] = 'id';
|
||||
$r['link'] = $host . '/download_file.php?id=' . $r['id'];
|
||||
|
||||
$unix_time = filemtime($file);
|
||||
$unix_time = !empty($r['created_at']) ? strtotime($r['created_at']) : @filemtime($file);
|
||||
$r['date'] = date('d.m.Y в H:i', $unix_time);
|
||||
if (!isset($list['names'][$r['name_id']]))
|
||||
$list['names'][$r['name_id']] = array('type' => 'id', 'from' => 'object', 'from_id' => $r['object_id'], 'id' => (int)$r['name_id']);
|
||||
|
||||
if (file_exists($file))
|
||||
if (!empty($r['guid']))
|
||||
$row[$r['name_id']][] = $r;
|
||||
|
||||
}
|
||||
@ -3281,13 +3281,13 @@ class CommonController extends ApiController
|
||||
$r['type'] = 'id';
|
||||
$r['link'] = $host . '/download_file.php?id=' . $r['id'];
|
||||
|
||||
$unix_time = filemtime($file);
|
||||
$unix_time = !empty($r['created_at']) ? strtotime($r['created_at']) : @filemtime($file);
|
||||
$r['date'] = date('d.m.Y в H:i', $unix_time);
|
||||
|
||||
if (!isset($list['names'][$r['name_id']]))
|
||||
$list['names'][$r['name_id']] = array('type' => 'id', 'from' => 'req', 'from_id' => $req['id'], 'id' => (int)$r['name_id']);
|
||||
|
||||
if (file_exists($file))
|
||||
if (!empty($r['guid']))
|
||||
$row[$r['name_id']][] = $r;
|
||||
|
||||
}
|
||||
@ -3311,13 +3311,13 @@ class CommonController extends ApiController
|
||||
$file .= $r['guid'] . '.' . $r['type'];
|
||||
$r['type'] = 'guid';
|
||||
$r['link'] = $host . '/download_file.php?guid=' . $r['guid'];
|
||||
$unix_time = filemtime($file);
|
||||
$unix_time = !empty($r['created_at']) ? strtotime($r['created_at']) : @filemtime($file);
|
||||
$r['date'] = date('d.m.Y в H:i', $unix_time);
|
||||
if (!isset($list['names'][$r['etap_id']])) {
|
||||
|
||||
$list['names'][$r['etap_id']] = array('type' => 'guid', 'from' => 'req', 'from_id' => $req['id'], 'id' => (int)$r['etap_id']);
|
||||
}
|
||||
if (file_exists($file)) {
|
||||
if (!empty($r['guid'])) {
|
||||
$row[$r['etap_id']][] = $r;
|
||||
}
|
||||
}
|
||||
@ -7471,10 +7471,10 @@ class CommonController extends ApiController
|
||||
if ($files[$section]['error'][$i] == 0) {
|
||||
|
||||
if (!is_dir($_SERVER['DOCUMENT_ROOT'] . $temp_dir))
|
||||
mkdir($_SERVER['DOCUMENT_ROOT'] . $temp_dir);
|
||||
mkdir($_SERVER['DOCUMENT_ROOT'] . $temp_dir, 0777, true);
|
||||
|
||||
if (!is_dir($_SERVER['DOCUMENT_ROOT'] . $temp_dir))
|
||||
mkdir($_SERVER['DOCUMENT_ROOT'] . $temp_dir);
|
||||
mkdir($_SERVER['DOCUMENT_ROOT'] . $temp_dir, 0777, true);
|
||||
|
||||
$guid = md5($filename);
|
||||
$extension = pathinfo($filename, PATHINFO_EXTENSION);
|
||||
@ -7503,7 +7503,7 @@ class CommonController extends ApiController
|
||||
VALUES ('{$guid}', '{$client_id}', '{$extension}', '{$filename}', '{$event_id}')";
|
||||
|
||||
//отпрвляем файл в Селектел
|
||||
$token = \SelectelApi::getNewToken();
|
||||
// токен Selectel в запросе не нужен: заливку делает фоновый крон offloadFilesToSelectel.php
|
||||
|
||||
$contType = "application/octet-stream";
|
||||
|
||||
@ -7547,13 +7547,13 @@ class CommonController extends ApiController
|
||||
|
||||
$new_path = "/upload/clients/". $client_id . "/" . $guid . '.' . $extension;
|
||||
|
||||
\SelectelApi::uploadUserFile($token, $_SERVER['DOCUMENT_ROOT'] . $path, $new_path, $contType);
|
||||
// синхронная заливка в Selectel убрана; файл подхватит фоновый крон (reconcile → offloadFilesToSelectel)
|
||||
} else if ($requisition_id > 0) {
|
||||
$sql = "INSERT INTO clients_files (guid, req_id, type, name, event_id)
|
||||
VALUES ('{$guid}', '{$requisition_id}', '{$extension}', '{$filename}', '{$event_id}')";
|
||||
|
||||
//отпрвляем файл в Селектел
|
||||
$token = \SelectelApi::getNewToken();
|
||||
// токен Selectel в запросе не нужен: заливку делает фоновый крон offloadFilesToSelectel.php
|
||||
|
||||
$contType = "application/octet-stream";
|
||||
|
||||
@ -7597,14 +7597,14 @@ class CommonController extends ApiController
|
||||
|
||||
$new_path = "/upload/reqs/". $requisition_id . "/" . $guid . '.' . $extension;
|
||||
|
||||
\SelectelApi::uploadUserFile($token, $_SERVER['DOCUMENT_ROOT'] . $path, $new_path, $contType);
|
||||
// синхронная заливка в Selectel убрана; файл подхватит фоновый крон (reconcile → offloadFilesToSelectel)
|
||||
|
||||
} else if ($object_id > 0) {
|
||||
$sql = "INSERT INTO clients_files (guid, object_id, type, name, event_id)
|
||||
VALUES ('{$guid}', '{$object_id}', '{$extension}', '{$filename}', '{$event_id}')";
|
||||
|
||||
//отпарвляем файл в Селектел
|
||||
$token = \SelectelApi::getNewToken();
|
||||
// токен Selectel в запросе не нужен: заливку делает фоновый крон offloadFilesToSelectel.php
|
||||
|
||||
$contType = "application/octet-stream";
|
||||
|
||||
@ -7648,7 +7648,7 @@ class CommonController extends ApiController
|
||||
|
||||
$new_path = "/upload/objects/". $object_id . "/" . $guid . '.' . $extension;
|
||||
|
||||
\SelectelApi::uploadUserFile($token, $_SERVER['DOCUMENT_ROOT'] . $path, $new_path, $contType);
|
||||
// синхронная заливка в Selectel убрана; файл подхватит фоновый крон (reconcile → offloadFilesToSelectel)
|
||||
}
|
||||
|
||||
if (!empty($sql)) {
|
||||
@ -7709,7 +7709,7 @@ class CommonController extends ApiController
|
||||
)";
|
||||
|
||||
//отпрвляем файл в Селектел
|
||||
$token = \SelectelApi::getNewToken();
|
||||
// токен Selectel в запросе не нужен: заливку делает фоновый крон offloadFilesToSelectel.php
|
||||
|
||||
$contType = "application/octet-stream";
|
||||
|
||||
@ -7753,7 +7753,7 @@ class CommonController extends ApiController
|
||||
|
||||
$new_path = "/upload/funnels/clients/". $client_id . "/" . $guid . '.' . $extension;
|
||||
|
||||
\SelectelApi::uploadUserFile($token, $_SERVER['DOCUMENT_ROOT'] . $path, $new_path, $contType);
|
||||
// синхронная заливка в Selectel убрана; файл подхватит фоновый крон (reconcile → offloadFilesToSelectel)
|
||||
|
||||
} else if ($requisition_id > 0) {
|
||||
$sql = "INSERT INTO funnel_files (
|
||||
@ -7773,7 +7773,7 @@ class CommonController extends ApiController
|
||||
)";
|
||||
|
||||
//отпрвляем файл в Селектел
|
||||
$token = \SelectelApi::getNewToken();
|
||||
// токен Selectel в запросе не нужен: заливку делает фоновый крон offloadFilesToSelectel.php
|
||||
|
||||
$contType = "application/octet-stream";
|
||||
|
||||
@ -7817,10 +7817,10 @@ class CommonController extends ApiController
|
||||
|
||||
$new_path = "/upload/funnels/req/". $requisition_id . "/" . $guid . '.' . $extension;
|
||||
|
||||
\SelectelApi::uploadUserFile($token, $_SERVER['DOCUMENT_ROOT'] . $path, $new_path, $contType);
|
||||
// синхронная заливка в Selectel убрана; файл подхватит фоновый крон (reconcile → offloadFilesToSelectel)
|
||||
} else {
|
||||
//отпрвляем файл в Селектел
|
||||
$token = \SelectelApi::getNewToken();
|
||||
// токен Selectel в запросе не нужен: заливку делает фоновый крон offloadFilesToSelectel.php
|
||||
|
||||
$contType = "application/octet-stream";
|
||||
|
||||
@ -7864,7 +7864,7 @@ class CommonController extends ApiController
|
||||
|
||||
$new_path = "/upload/funnels/". $funnel_id . "/" . $guid . '.' . $extension;
|
||||
|
||||
\SelectelApi::uploadUserFile($token, $_SERVER['DOCUMENT_ROOT'] . $path, $new_path, $contType);
|
||||
// синхронная заливка в Selectel убрана; файл подхватит фоновый крон (reconcile → offloadFilesToSelectel)
|
||||
}
|
||||
|
||||
if (!mysql_query($sql)) {
|
||||
@ -8456,6 +8456,8 @@ class CommonController extends ApiController
|
||||
$rez_cl = mysql_query($sql_cl);
|
||||
if ($doc_cl = mysql_fetch_assoc($rez_cl)) {
|
||||
|
||||
// Маскируем email/phone клиента, если у текущего пользователя
|
||||
// включено `hide_client_contacts`. Helper сам проверяет право.
|
||||
$cl_contacts = [];
|
||||
if ($doc_cl['email'])
|
||||
$cl_contacts[] = mask_email_value_if_needed($doc_cl['email']);
|
||||
@ -8469,6 +8471,7 @@ class CommonController extends ApiController
|
||||
'title' => $doc_cl['fio'],
|
||||
'contacts' => ((!empty($cl_contacts)) ? ' (' . implode(', ', $cl_contacts) . ')' : ''),
|
||||
];
|
||||
// Маскируем title (fio), если он вида "Новый клиент +7...".
|
||||
$doc_party['title'] = mask_fio_if_contains_phone($doc_party['title']);
|
||||
$doc_parties[] = $doc_party;
|
||||
}
|
||||
@ -9159,6 +9162,8 @@ class CommonController extends ApiController
|
||||
$rez_cl = mysql_query($sql_cl);
|
||||
if ($doc_cl = mysql_fetch_assoc($rez_cl)) {
|
||||
|
||||
// Маскируем email/phone клиента, если у текущего пользователя
|
||||
// включено `hide_client_contacts`. Helper сам проверяет право.
|
||||
$cl_contacts = [];
|
||||
if ($doc_cl['email'])
|
||||
$cl_contacts[] = mask_email_value_if_needed($doc_cl['email']);
|
||||
@ -9172,6 +9177,7 @@ class CommonController extends ApiController
|
||||
'title' => $doc_cl['fio'],
|
||||
'contacts' => ((!empty($cl_contacts)) ? ' (' . implode(', ', $cl_contacts) . ')' : ''),
|
||||
];
|
||||
// Маскируем title (fio), если он вида "Новый клиент +7...".
|
||||
$doc_party['title'] = mask_fio_if_contains_phone($doc_party['title']);
|
||||
$doc_parties[] = $doc_party;
|
||||
}
|
||||
@ -13501,7 +13507,7 @@ class CommonController extends ApiController
|
||||
$name_doc = 'https://uf.joywork.ru/upload/funnels/req/' . $requisition_id . '/' . $doc['guid'] . '.' . $doc['type'];
|
||||
}
|
||||
|
||||
$text .= '<p><b>' . $event['label'] . '</b>: <a title="Скачать" download href="/' . $name_doc . '</a></p>';
|
||||
$text .= '<p><b>' . $event['label'] . '</b>: <a title="Скачать" download href="' . $name_doc . '">' . $doc['name'] . '</a></p>';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -13536,7 +13542,7 @@ class CommonController extends ApiController
|
||||
$name_doc = 'https://uf.joywork.ru/upload/funnels/req/' . $requisition_id . '/' . $doc['guid'] . '.' . $doc['type'];
|
||||
}
|
||||
|
||||
$text .= '<p><b>' . $event['label'] . '</b>: <a title="Скачать" download href="' . $name_doc . '</a></p>';
|
||||
$text .= '<p><b>' . $event['label'] . '</b>: <a title="Скачать" download href="' . $name_doc . '">' . $doc['name'] . '</a></p>';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -153,6 +153,23 @@ class ContractsController extends ApiController
|
||||
else if ($section == "objects")
|
||||
$parent_type = 'object';
|
||||
|
||||
// Договор на найденном листинге парсера запрещён — листинг read-only,
|
||||
// сначала копирование объекта в свои.
|
||||
// Offset-free: после снятия смещения id листинга (= реальный external_listings.id) коллизит
|
||||
// с objects.id, поэтому различить листинг по id нельзя. Признак — явный флаг is_external
|
||||
// из запроса (фронт знает, что договор оформляют на найденном листинге).
|
||||
if ($parent_type == 'object' && !empty($data['is_external'])) {
|
||||
if ($need_return)
|
||||
return false;
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setStatus(403);
|
||||
$app->response->setBody(json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['Действие недоступно для найденного листинга. Скопируйте объект в свои, чтобы управлять им.'],
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
if (($user_id = $_SESSION['id']) && $parent_type) {
|
||||
|
||||
// Открываем соединение с БД
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,27 @@ namespace App\v2\Controller;
|
||||
|
||||
class RequisitionsController extends ApiController
|
||||
{
|
||||
// Гейтинг найденных листингов парсера: заявку нельзя привязать к чужому объявлению —
|
||||
// оно read-only, сначала копирование объекта в свои.
|
||||
// Offset-free: после снятия смещения id листинга (= реальный external_listings.id) коллизит
|
||||
// с objects.id, поэтому различить листинг по id нельзя. Признак — явный флаг is_external
|
||||
// из запроса (фронт знает, что заявку привязывают к найденному листингу).
|
||||
// Возвращает true, если запрос отклонён (вызывающий метод делает return).
|
||||
private function _rejectIfExternalListing($app, $is_external)
|
||||
{
|
||||
if (!empty($is_external)) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setStatus(403);
|
||||
$app->response->setBody(json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['Действие недоступно для найденного листинга. Скопируйте объект в свои, чтобы управлять им.'],
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getRequisitions($app, $get = null, $post = null, $need_return = false) {
|
||||
$start_time = microtime(true);
|
||||
$error = false;
|
||||
@ -385,6 +406,17 @@ class RequisitionsController extends ApiController
|
||||
}
|
||||
|
||||
$req_filter['stages'] = (object)$req_filter['stages'];
|
||||
|
||||
// Наполняем new_transfers для is_new здесь, не полагаясь на getFunnels
|
||||
$nt_agency_id = \User::getUserAgencyID($user_id);
|
||||
$nt_ids = [];
|
||||
$nt_sql = "SELECT DISTINCT `req`.`id` FROM `events_clients` AS `events` LEFT JOIN `requisitions` AS `req` ON `req`.`id` = `events`.`req_id` LEFT JOIN `funnel` AS `funnel` ON `funnel`.`id` = `req`.`funnel_id` WHERE (`events`.`user_id` = '$user_id' AND `events`.`from_id` = '$user_id') AND (`events`.`event` = 'accepted' OR `events`.`event` = 'accepted_doer') AND `events`.`viewed` = 0 AND `events`.`read` = 1 AND (`req`.`confirm` = 1 OR `req`.`doers_confirm` = 1) AND `events`.`client_id` = 0 AND `req`.`deleted` = 0 AND (`req`.`who_work` = '$user_id' OR `req`.`doers` LIKE '%"{$user_id}":1%') AND (`req`.`funnel_id` = 0 OR (`funnel`.`agency_id` = '$nt_agency_id' AND `funnel`.`deleted` = 0))";
|
||||
if ($nt_q = mysql_query($nt_sql)) {
|
||||
while ($nt_row = mysql_fetch_assoc($nt_q))
|
||||
$nt_ids[] = $nt_row['id'];
|
||||
}
|
||||
$_SESSION['new_transfers']['requisitions'] = $nt_ids;
|
||||
|
||||
$results = $req_inst->get_all($search_user_id, (object)$req_filter, (string)$funnel_id, $client_id, $object_id, $need_autosearch);
|
||||
|
||||
$pagination = [
|
||||
@ -1193,6 +1225,11 @@ class RequisitionsController extends ApiController
|
||||
|
||||
$fields_result = null;
|
||||
|
||||
// Заявку нельзя привязать к найденному листингу парсера — листинг read-only.
|
||||
// Признак найденного листинга — явный флаг из запроса (offset-free: id листинга коллизит с objects.id).
|
||||
if ($this->_rejectIfExternalListing($app, isset($data['is_external']) ? $data['is_external'] : null))
|
||||
return;
|
||||
|
||||
$req = new \Requisitions($pdo);
|
||||
$result = $req->add_edit($params);
|
||||
if (isset($result['errors'])) {
|
||||
@ -2588,4 +2625,288 @@ ini_set('display_startup_errors', 1);*/
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
// POST /api/v2/requisitions/copy-preview — дефолты модалки + embed funnels/employees (1 round-trip для mJW).
|
||||
public function copyPreview($app, $get = null, $post = null, $need_return = false) {
|
||||
|
||||
$body = $app->request->getBody();
|
||||
$data = json_decode($body, true);
|
||||
|
||||
$user_id = isset($_SESSION['id']) ? (int)$_SESSION['id'] : 0;
|
||||
if ($user_id <= 0) {
|
||||
$app->response->setStatus(401);
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(['success' => false, 'error' => 'unauthorized'], JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
$srcId = isset($data['req_id']) ? (int)$data['req_id'] : 0;
|
||||
if ($srcId <= 0) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(['success' => false, 'error' => 'Заявка обязательна'], JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
// mysql-сервис нужен: User::getAll* используют mysql_*.
|
||||
$mdb = $app->container->get('mysql');
|
||||
$pdo = $app->container->get('mypdo');
|
||||
|
||||
$src = mysql_fetch_assoc(mysql_query("SELECT * FROM requisitions WHERE id = {$srcId} LIMIT 1"));
|
||||
if (!$src) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(['success' => false, 'error' => 'Заявка не найдена'], JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
if ((int)$src['deleted'] === 1) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(['success' => false, 'error' => 'Заявка удалена'], JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
// set_user — для checkMenuPermissions внутри user_can_edit_req.
|
||||
$req = new \Requisitions($pdo);
|
||||
$userForCheck = new \User();
|
||||
$userForCheck->checkPermissions();
|
||||
$req->set_user($userForCheck);
|
||||
|
||||
if (!$req->user_can_edit_req($src, $user_id)) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(['success' => false, 'error' => 'Нет прав на копирование'], JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
$srcTypeId = (int)$src['type_id'];
|
||||
$typeRow = mysql_fetch_assoc(mysql_query("SELECT id, heir FROM requisitions_type WHERE id = {$srcTypeId} LIMIT 1"));
|
||||
$baseTypeId = $typeRow ? requisition_base_type_id($typeRow) : 0;
|
||||
|
||||
// warning contract_without_object — object_id не копируется, но у исходника есть договор продажи.
|
||||
$warnings = array();
|
||||
if (!requisition_should_copy_object($baseTypeId) && (int)$src['object_id'] > 0) {
|
||||
$hasSaleContract = mysql_fetch_assoc(
|
||||
mysql_query("SELECT id FROM contracts_req WHERE req_id = {$srcId} AND type = 2 LIMIT 1")
|
||||
);
|
||||
if ($hasSaleContract) {
|
||||
$warnings[] = 'contract_without_object';
|
||||
}
|
||||
}
|
||||
|
||||
// Канонический источник воронок: Funnel::get_funnels (включает дефолт id=0 и visibility).
|
||||
$userAgency = (int)$_SESSION['agency_id'];
|
||||
$funnelClass = new \Funnel($pdo);
|
||||
$funnelClass->set_agency_id($userAgency);
|
||||
$funnelClass->set_user_id($user_id);
|
||||
$funnelsRaw = $funnelClass->get_funnels('requisitions');
|
||||
$funnels = array();
|
||||
if (is_array($funnelsRaw)) {
|
||||
foreach ($funnelsRaw as $rF) {
|
||||
$funnels[] = array(
|
||||
'id' => (int)$rF['id'],
|
||||
'name' => (isset($rF['name']) && $rF['name'] !== null && $rF['name'] !== '')
|
||||
? (string)$rF['name']
|
||||
: 'Обычные',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Сотрудники: Я + «Всё агентство» (если разрешено) + менеджеры + агенты.
|
||||
$employees = array(array('id' => $user_id, 'name' => 'Я'));
|
||||
$r_perm = mysql_fetch_assoc(
|
||||
mysql_query("SELECT menu_can_transfer_to_common FROM user_permissions WHERE user_id = {$user_id} LIMIT 1")
|
||||
);
|
||||
if (
|
||||
!empty($_SESSION['agency'])
|
||||
|| !empty($_SESSION['users_admin'])
|
||||
|| ($r_perm && (int)$r_perm['menu_can_transfer_to_common'] === 1)
|
||||
) {
|
||||
$employees[] = array('id' => 0, 'name' => 'Всё агентство');
|
||||
}
|
||||
$managers = \User::getAllManagers($userAgency);
|
||||
if ($managers) {
|
||||
foreach ($managers as $m) {
|
||||
$employees[] = array(
|
||||
'id' => (int)$m['id'],
|
||||
'name' => trim($m['last_name'] . ' ' . $m['first_name'] . ' ' . $m['middle_name']),
|
||||
'group' => 'Менеджеры',
|
||||
);
|
||||
}
|
||||
}
|
||||
$agents = \User::getAllAgents($userAgency);
|
||||
if ($agents) {
|
||||
foreach ($agents as $a) {
|
||||
$employees[] = array(
|
||||
'id' => (int)$a['id'],
|
||||
'name' => trim($a['last_name'] . ' ' . $a['first_name'] . ' ' . $a['middle_name']),
|
||||
'group' => 'Агенты',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setStatus(200);
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => true,
|
||||
'default_funnel_id' => (int)$src['funnel_id'],
|
||||
'default_who_work_id' => (int)$src['who_work'],
|
||||
'base_type_id' => $baseTypeId,
|
||||
'warnings' => $warnings,
|
||||
'funnels' => $funnels,
|
||||
'employees' => $employees,
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
// POST /api/v2/requisitions/copy — создание копии заявки.
|
||||
public function copy($app, $get = null, $post = null, $need_return = false) {
|
||||
|
||||
$body = $app->request->getBody();
|
||||
$data = json_decode($body, true);
|
||||
|
||||
$currentId = isset($_SESSION['id']) ? (int)$_SESSION['id'] : 0;
|
||||
if ($currentId <= 0) {
|
||||
$app->response->setStatus(401);
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(['success' => false, 'error' => 'unauthorized'], JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
$srcId = isset($data['req_id']) ? (int)$data['req_id'] : 0;
|
||||
$funnelId = isset($data['funnel_id']) ? (int)$data['funnel_id'] : 0;
|
||||
$whoWorkId = isset($data['who_work_id']) ? (int)$data['who_work_id'] : 0;
|
||||
$userAgency = (int)$_SESSION['agency_id'];
|
||||
|
||||
// funnel_id = 0 — дефолтная воронка (funnel_default).
|
||||
if ($srcId <= 0 || $funnelId < 0 || $whoWorkId <= 0) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => false,
|
||||
'error' => 'req_id, funnel_id, who_work_id обязательны',
|
||||
'code' => 'bad_payload',
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
// mysql-сервис нужен: legacy-SQL ниже + User::* внутри валидаций.
|
||||
$mdb = $app->container->get('mysql');
|
||||
$pdo = $app->container->get('mypdo');
|
||||
|
||||
$src = mysql_fetch_assoc(mysql_query("SELECT * FROM requisitions WHERE id = {$srcId} LIMIT 1"));
|
||||
if (!$src) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => false,
|
||||
'error' => 'Заявка не найдена',
|
||||
'code' => 'src_not_found',
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
if ((int)$src['deleted'] === 1) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => false,
|
||||
'error' => 'Заявка удалена',
|
||||
'code' => 'src_not_found',
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
// set_user — для checkMenuPermissions внутри user_can_edit_req.
|
||||
$req = new \Requisitions($pdo);
|
||||
$userForCheck = new \User();
|
||||
$userForCheck->checkPermissions();
|
||||
$req->set_user($userForCheck);
|
||||
|
||||
// Право на копирование == право на редактирование исходника.
|
||||
if (!$req->user_can_edit_req($src, $currentId)) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => false,
|
||||
'error' => 'Нет прав на копирование',
|
||||
'code' => 'no_edit_right',
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
|
||||
// Доступ к воронке. funnel_id=0 — проверяем funnel_default.is_req.
|
||||
if ($funnelId === 0) {
|
||||
$funnelDef = mysql_fetch_assoc(mysql_query(
|
||||
"SELECT `is_req` FROM `funnel_default`
|
||||
WHERE `agency_id` = {$userAgency}
|
||||
LIMIT 1"
|
||||
));
|
||||
if ($funnelDef && (int)$funnelDef['is_req'] === 0) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => false,
|
||||
'error' => 'Воронка недоступна',
|
||||
'code' => 'funnel_forbidden',
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
} else {
|
||||
// Проверка доступности — через канонический Funnel::get_funnels() (учитывает is_no_see + раскрытие 'all_X').
|
||||
$funnelCheckClass = new \Funnel($pdo);
|
||||
$funnelCheckClass->set_agency_id($userAgency);
|
||||
$funnelCheckClass->set_user_id($currentId);
|
||||
$availableFunnels = $funnelCheckClass->get_funnels('requisitions');
|
||||
$isFunnelAvailable = false;
|
||||
if (is_array($availableFunnels)) {
|
||||
foreach ($availableFunnels as $af) {
|
||||
if ((int)$af['id'] === $funnelId) { $isFunnelAvailable = true; break; }
|
||||
}
|
||||
}
|
||||
if (!$isFunnelAvailable) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => false,
|
||||
'error' => 'Воронка недоступна',
|
||||
'code' => 'funnel_forbidden',
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
}
|
||||
|
||||
// Ответственный. Себя всегда можно назначить.
|
||||
if ($whoWorkId !== $currentId) {
|
||||
if ($funnelId === 0) {
|
||||
// user_can_be_responsible требует funnel_id > 0 — для дефолтной проверяем вручную (agency + blocked).
|
||||
$candidateAgency = (int)\User::getUserAgencyID($whoWorkId);
|
||||
if ($candidateAgency !== $userAgency) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => false,
|
||||
'error' => 'Выбранный ответственный недоступен',
|
||||
'code' => 'who_work_forbidden',
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
$blockedRow = mysql_fetch_assoc(mysql_query(
|
||||
"SELECT `blocked` FROM `users` WHERE `id` = {$whoWorkId} LIMIT 1"
|
||||
));
|
||||
if (!$blockedRow || (int)$blockedRow['blocked'] === 1) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => false,
|
||||
'error' => 'Выбранный ответственный недоступен',
|
||||
'code' => 'who_work_forbidden',
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
} else if (!$req->user_can_be_responsible($whoWorkId, $userAgency, $funnelId)) {
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setBody(json_encode(array(
|
||||
'success' => false,
|
||||
'error' => 'Выбранный ответственный недоступен',
|
||||
'code' => 'who_work_forbidden',
|
||||
), JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
}
|
||||
|
||||
$result = $req->copy($srcId, $funnelId, $whoWorkId, $currentId);
|
||||
|
||||
$app->response->header('Content-Type', 'application/json');
|
||||
$app->response->setStatus(200);
|
||||
$app->response->setBody(json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
$app->stop();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user