Compare commits
15 Commits
main
...
dispatcher
| Author | SHA1 | Date | |
|---|---|---|---|
| b7c25c4ff1 | |||
| 23960cbbb6 | |||
| c3503975a0 | |||
| 1949a461b2 | |||
| 2d9c50d0b9 | |||
| fa5dae47ed | |||
| de1e06cb2c | |||
| 7caea05c7e | |||
| b3ff03bfe6 | |||
| f8258173fb | |||
| 6add9d813e | |||
| 7bd7700314 | |||
| ecc9443400 | |||
| 304444b079 | |||
| e9861bb5ea |
@@ -0,0 +1,336 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Тестовый скрипт — поиск пользователя в Active Directory.
|
||||||
|
Аналог find_user_in_ad() из sd_dispatcher.py.
|
||||||
|
|
||||||
|
Использует Passwork для загрузки учётных данных AD (как в основном коде).
|
||||||
|
Если Passwork недоступен — просит ввести учётные данные вручную.
|
||||||
|
|
||||||
|
Единственная зависимость: ldap3
|
||||||
|
pip install ldap3
|
||||||
|
|
||||||
|
Запуск:
|
||||||
|
python3 test_ad_lookup.py --login ivanov
|
||||||
|
python3 test_ad_lookup.py --email ivanov@sibcem.ru
|
||||||
|
python3 test_ad_lookup.py --phone 79131234567
|
||||||
|
python3 test_ad_lookup.py --test-all
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# 1. Загрузка учётных данных AD
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
def load_ad_credentials():
|
||||||
|
"""
|
||||||
|
Загружает AD_USER и AD_PASSWORD из Passwork (как в config.py).
|
||||||
|
fallback: ручной ввод.
|
||||||
|
"""
|
||||||
|
# --- Путь 1: через модуль Passwork ---
|
||||||
|
passwork_dir = "/opt/passwork"
|
||||||
|
card_name = os.getenv("PW_ID_AD", "AD").strip()
|
||||||
|
|
||||||
|
if passwork_dir not in sys.path:
|
||||||
|
sys.path.insert(0, passwork_dir)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from passwork import get_passwork_secrets
|
||||||
|
print(f"[Passwork] Загрузка карточки '{card_name}'...")
|
||||||
|
pool = get_passwork_secrets(required_cards=[card_name])
|
||||||
|
card = pool.get(card_name)
|
||||||
|
if card:
|
||||||
|
ad_user = card.get("login", "").strip()
|
||||||
|
ad_pass = card.get("password", "").strip()
|
||||||
|
if ad_user and ad_pass:
|
||||||
|
print(f"[Passwork] ✅ AD_USER: {ad_user}")
|
||||||
|
return ad_user, ad_pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# --- Путь 2: env-переменные ---
|
||||||
|
ad_user = os.getenv("AD_USER", "").strip()
|
||||||
|
ad_pass = os.getenv("AD_PASS", "").strip()
|
||||||
|
if ad_user and ad_pass:
|
||||||
|
print("[Env] ✅ Учётные данные загружены из окружения")
|
||||||
|
return ad_user, ad_pass
|
||||||
|
|
||||||
|
# --- Путь 3: ручной ввод ---
|
||||||
|
print("\n❌ Passwork недоступен.")
|
||||||
|
print("Введите учётные данные AD вручную:\n")
|
||||||
|
ad_user = input(" AD user (DOMAIN\\account): ").strip()
|
||||||
|
ad_pass = input(" AD password: ").strip()
|
||||||
|
if not ad_user or not ad_pass:
|
||||||
|
print("❌ Отмена.")
|
||||||
|
sys.exit(1)
|
||||||
|
print(f"[Manual] ✅ AD_USER: {ad_user}")
|
||||||
|
return ad_user, ad_pass
|
||||||
|
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# 2. Подключение к LDAP
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
def connect_to_ad(ad_user, ad_pass):
|
||||||
|
from ldap3 import Server, Connection
|
||||||
|
|
||||||
|
server = Server(
|
||||||
|
"ldap://172.16.20.20",
|
||||||
|
get_info=None,
|
||||||
|
connect_timeout=5,
|
||||||
|
)
|
||||||
|
conn = Connection(
|
||||||
|
server,
|
||||||
|
user=ad_user,
|
||||||
|
password=ad_pass,
|
||||||
|
auto_bind=True,
|
||||||
|
receive_timeout=5,
|
||||||
|
)
|
||||||
|
return conn
|
||||||
|
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# 3. Поиск пользователя
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
def find_user(conn, login=None, email=None, phone=None):
|
||||||
|
"""
|
||||||
|
Приоритет: телефон > логин > email.
|
||||||
|
Возвращает dict атрибутов или None.
|
||||||
|
"""
|
||||||
|
if phone:
|
||||||
|
filt = (
|
||||||
|
f"(|(telephoneNumber=*{phone}*)(mobile=*{phone}*) "
|
||||||
|
f"(homePhone=*{phone}*)(pager=*{phone}*) "
|
||||||
|
f"(facsimileTelephoneNumber=*{phone}*)(ipPhone=*{phone}*))"
|
||||||
|
)
|
||||||
|
by = f"телефону {phone}"
|
||||||
|
elif login:
|
||||||
|
clean = login.split("\\")[-1].split("/")[-1]
|
||||||
|
filt = f"(sAMAccountName={clean})"
|
||||||
|
by = f"логину {clean}"
|
||||||
|
elif email:
|
||||||
|
filt = f"(mail={email})"
|
||||||
|
by = f"почте {email}"
|
||||||
|
else:
|
||||||
|
print("❌ Укажите --login, --email или --phone")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"\n[LDAP] Фильтр: {filt}")
|
||||||
|
print(f"[LDAP] Base: OU=-Пользователи,DC=sibcem,DC=ru")
|
||||||
|
|
||||||
|
conn.search(
|
||||||
|
search_base="OU=-Пользователи,DC=sibcem,DC=ru",
|
||||||
|
search_filter=filt,
|
||||||
|
attributes=[
|
||||||
|
"sAMAccountName", "displayName", "givenName", "sn",
|
||||||
|
"mail", "title", "department", "l", "company",
|
||||||
|
"manager", "telephoneNumber", "mobile",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not conn.entries:
|
||||||
|
print(f"\n❌ Не найден по {by}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
user = conn.entries[0]
|
||||||
|
print(f"\n✅ Найдено: {user.entry_dn}")
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"sam": user.sAMAccountName.value if "sAMAccountName" in user else None,
|
||||||
|
"given_name": user.givenName.value if "givenName" in user else None,
|
||||||
|
"surname": user.sn.value if "sn" in user else None,
|
||||||
|
"display_name": user.displayName.value if "displayName" in user else None,
|
||||||
|
"mail": user.mail.value if "mail" in user else None,
|
||||||
|
"title": user.title.value if "title" in user else None,
|
||||||
|
"department": user.department.value if "department" in user else None,
|
||||||
|
"city": user.l.value if "l" in user and user.l.value else None,
|
||||||
|
"company": user.company.value if "company" in user and user.company.value else None,
|
||||||
|
"dn": user.entry_dn,
|
||||||
|
"phone": user.telephoneNumber.value if "telephoneNumber" in user else None,
|
||||||
|
"mobile": user.mobile.value if "mobile" in user else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Город из OU если атрибут l пустой
|
||||||
|
if not result["city"]:
|
||||||
|
match = re.search(r"OU=([^,]+),OU=-Пользователи", result["dn"], re.IGNORECASE)
|
||||||
|
if match:
|
||||||
|
result["city"] = match.group(1).strip()
|
||||||
|
if result["city"].lower() == "красноярск" and "OU=ООО Комбинат Волна" in result["dn"]:
|
||||||
|
result["company"] = "ООО Комбинат Волна"
|
||||||
|
print(f" Город из OU: {result['city']}")
|
||||||
|
|
||||||
|
# Руководитель — второй запрос
|
||||||
|
manager_dn = user.manager.value if "manager" in user else None
|
||||||
|
if manager_dn:
|
||||||
|
conn.search(
|
||||||
|
search_base=manager_dn,
|
||||||
|
search_filter="(objectClass=user)",
|
||||||
|
attributes=["mail", "displayName"],
|
||||||
|
search_scope="BASE",
|
||||||
|
)
|
||||||
|
if conn.entries and "mail" in conn.entries[0]:
|
||||||
|
result["manager_email"] = conn.entries[0].mail.value
|
||||||
|
result["manager_name"] = (
|
||||||
|
conn.entries[0].displayName.value if "displayName" in conn.entries[0] else None
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result["manager_email"] = None
|
||||||
|
else:
|
||||||
|
result["manager_email"] = None
|
||||||
|
|
||||||
|
# Проверка department на исключение из автосогласования
|
||||||
|
excluded_depts = [
|
||||||
|
# "IT-Администраторы",
|
||||||
|
# "Системные аналитики",
|
||||||
|
# Добавьте нужные отделы из config.py: APPROVAL_EXCLUDED_DEPARTMENTS
|
||||||
|
]
|
||||||
|
|
||||||
|
if result["department"] and any(
|
||||||
|
dept.lower() in result["department"].lower() or
|
||||||
|
result["department"].lower() in dept.lower()
|
||||||
|
for dept in excluded_depts
|
||||||
|
):
|
||||||
|
result["approval_excluded"] = True
|
||||||
|
result["excluded_department"] = result["department"]
|
||||||
|
else:
|
||||||
|
result["approval_excluded"] = False
|
||||||
|
result["excluded_department"] = None
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# 4. Вывод
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
def print_report(data):
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print(" РЕЗУЛЬТАТ ПОИСКА В ACTIVE DIRECTORY")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f" sAMAccountName: {data['sam']}")
|
||||||
|
print(f" Имя: {data['given_name']}")
|
||||||
|
print(f" Фамилия: {data['surname']}")
|
||||||
|
print(f" ФИО: {data['display_name']}")
|
||||||
|
print(f" Email: {data['mail']}")
|
||||||
|
print(f" Должность: {data['title']}")
|
||||||
|
print(f" Отдел: {data['department'] or '—'}")
|
||||||
|
print(f" Город: {data['city'] or '—'}")
|
||||||
|
print(f" Компания: {data['company'] or '—'}")
|
||||||
|
print(f" Телефон: {data['phone'] or '—'}")
|
||||||
|
print(f" Мобильный: {data['mobile'] or '—'}")
|
||||||
|
print(f" Руководитель: {data['manager_name'] or '—'}")
|
||||||
|
if data.get("manager_email"):
|
||||||
|
print(f" Email руково.: {data['manager_email']}")
|
||||||
|
|
||||||
|
# Проверка на исключение из автосогласования
|
||||||
|
if data.get("approval_excluded"):
|
||||||
|
print("\n" + "!" * 60)
|
||||||
|
print(f" ⚠️ ОТДЕЛ: {data['excluded_department']}")
|
||||||
|
print(f" ⚠️ АВТОСОГЛАСОВАНИЕ ОТМЕНЕНО")
|
||||||
|
print(f" Пользователь находится в исключённом отделе.")
|
||||||
|
print("!" * 60)
|
||||||
|
|
||||||
|
print("-" * 60)
|
||||||
|
print(f" DN: {data['dn']}")
|
||||||
|
|
||||||
|
# OU-дерево
|
||||||
|
ou_parts = [p.strip() for p in data["dn"].split(",") if p.strip().startswith("OU=")]
|
||||||
|
if ou_parts:
|
||||||
|
print(f"\n OU-структура:")
|
||||||
|
for i, ou in enumerate(reversed(ou_parts)):
|
||||||
|
print(f" {' ' * i}└── {ou}")
|
||||||
|
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# 5. Тест всех методов
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
def test_all_methods(conn):
|
||||||
|
print("\n" + "─" * 60)
|
||||||
|
print(" ТЕСТИРОВАНИЕ: все методы поиска")
|
||||||
|
print("─" * 60)
|
||||||
|
|
||||||
|
conn.search(
|
||||||
|
search_base="OU=-Пользователи,DC=sibcem,DC=ru",
|
||||||
|
search_filter="(objectClass=user)",
|
||||||
|
attributes=["sAMAccountName", "mail", "telephoneNumber"],
|
||||||
|
size_limit=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not conn.entries:
|
||||||
|
print("❌ Нет пользователей в AD.")
|
||||||
|
return
|
||||||
|
|
||||||
|
sample = conn.entries[0]
|
||||||
|
sam = sample.sAMAccountName.value
|
||||||
|
mail = sample.mail.value if "mail" in sample else None
|
||||||
|
phone = sample.telephoneNumber.value if "telephoneNumber" in sample else None
|
||||||
|
|
||||||
|
print(f"\n📌 Тестовый: {sam} ({mail})")
|
||||||
|
|
||||||
|
for label, kwargs in [("логину", {"login": sam}),
|
||||||
|
("email", {"email": mail}) if mail else None,
|
||||||
|
("телефону", {"phone": phone}) if phone else None]:
|
||||||
|
if kwargs is None:
|
||||||
|
continue
|
||||||
|
print(f"\n[Тест] По {label}...")
|
||||||
|
r = find_user(conn, **kwargs)
|
||||||
|
if r:
|
||||||
|
print_report(r)
|
||||||
|
|
||||||
|
print("\n" + "─" * 60)
|
||||||
|
print(" ✅ Готово")
|
||||||
|
print("─" * 60)
|
||||||
|
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# MAIN
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Поиск пользователя в Active Directory",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog="""
|
||||||
|
Примеры:
|
||||||
|
python3 test_ad_lookup.py --login ivanov
|
||||||
|
python3 test_ad_lookup.py --email ivanov@sibcem.ru
|
||||||
|
python3 test_ad_lookup.py --phone 79131234567
|
||||||
|
python3 test_ad_lookup.py --test-all
|
||||||
|
|
||||||
|
Учётные данные AD загружаются из Passwork (как в основном коде).
|
||||||
|
Если Passwork недоступен — запрашиваются вручную.
|
||||||
|
|
||||||
|
Проверка department: если department пользователя совпадает с
|
||||||
|
APPROVAL_EXCLUDED_DEPARTMENTS из config.py — автосогласование отменяется.
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
group = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
group.add_argument("--login", help="sAMAccountName пользователя")
|
||||||
|
group.add_argument("--email", help="Email пользователя")
|
||||||
|
group.add_argument("--phone", help="Номер телефона")
|
||||||
|
parser.add_argument("--test-all", action="store_true",
|
||||||
|
help="Найти первого пользователя и проверить все методы")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Загрузка учётных данных (Passwork → env → ручной ввод)
|
||||||
|
ad_user, ad_pass = load_ad_credentials()
|
||||||
|
|
||||||
|
conn = connect_to_ad(ad_user, ad_pass)
|
||||||
|
print(f"[LDAP] Подключено к ldap://172.16.20.20 как {ad_user}")
|
||||||
|
|
||||||
|
if args.test_all:
|
||||||
|
test_all_methods(conn)
|
||||||
|
else:
|
||||||
|
data = find_user(conn, login=args.login, email=args.email, phone=args.phone)
|
||||||
|
if data:
|
||||||
|
print_report(data)
|
||||||
|
|
||||||
|
conn.unbind()
|
||||||
@@ -287,6 +287,15 @@ APPROVAL_CATEGORIES = {
|
|||||||
"1с": ["*"]
|
"1с": ["*"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Отделы, исключающие пользователя из автосогласования
|
||||||
|
# Если department пользователя совпадает с любым из этих значений —
|
||||||
|
# автосогласование НЕ запускается, даже если все остальные условия выполнены.
|
||||||
|
APPROVAL_EXCLUDED_DEPARTMENTS = [
|
||||||
|
# "IT-Администраторы",
|
||||||
|
# "Системные аналитики",
|
||||||
|
# Добавьте нужные отделы:
|
||||||
|
]
|
||||||
|
|
||||||
# План «Б» маршрутизации: жесткое определение локации по телефонному префиксу внутренней АТС
|
# План «Б» маршрутизации: жесткое определение локации по телефонному префиксу внутренней АТС
|
||||||
PREFIX_ROUTING = {
|
PREFIX_ROUTING = {
|
||||||
"700": {"city": "Красноярск", "company": "ООО Комбинат Волна"},
|
"700": {"city": "Красноярск", "company": "ООО Комбинат Волна"},
|
||||||
|
|||||||
+28
-19
@@ -2,6 +2,7 @@
|
|||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
import urllib3
|
import urllib3
|
||||||
|
import os
|
||||||
import math
|
import math
|
||||||
import warnings
|
import warnings
|
||||||
import datetime
|
import datetime
|
||||||
@@ -170,7 +171,7 @@ def find_user_in_ad(requester, caller_phone=None, ticket_id=None):
|
|||||||
try:
|
try:
|
||||||
server = Server(AD_SERVER, get_info=ALL, connect_timeout=AD_CONNECT_TIMEOUT)
|
server = Server(AD_SERVER, get_info=ALL, connect_timeout=AD_CONNECT_TIMEOUT)
|
||||||
conn = Connection(server, user=AD_USER, password=AD_PASSWORD, auto_bind=True, receive_timeout=AD_RECEIVE_TIMEOUT)
|
conn = Connection(server, user=AD_USER, password=AD_PASSWORD, auto_bind=True, receive_timeout=AD_RECEIVE_TIMEOUT)
|
||||||
conn.search(search_base=AD_BASE, search_filter=ldap_filter, attributes=["l", "company", "manager"])
|
conn.search(search_base=AD_BASE, search_filter=ldap_filter, attributes=["l", "company", "manager", "department"])
|
||||||
|
|
||||||
if not conn.entries:
|
if not conn.entries:
|
||||||
log_to_file(f" [AD] ❌ Объект ({search_by}) не найден in AD!", ticket_id)
|
log_to_file(f" [AD] ❌ Объект ({search_by}) не найден in AD!", ticket_id)
|
||||||
@@ -195,7 +196,8 @@ def find_user_in_ad(requester, caller_phone=None, ticket_id=None):
|
|||||||
if conn.entries and 'mail' in conn.entries[0]:
|
if conn.entries and 'mail' in conn.entries[0]:
|
||||||
manager_email = conn.entries[0].mail.value
|
manager_email = conn.entries[0].mail.value
|
||||||
|
|
||||||
return {"city": city, "company": company, "ad_path": ad_path, "manager_email": manager_email, "source": f"AD ({search_by})"}
|
department = user.department.value if "department" in user and user.department.value else None
|
||||||
|
return {"city": city, "company": company, "department": department, "ad_path": ad_path, "manager_email": manager_email, "source": f"AD ({search_by})"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_to_file(f"❌ Ошибка LDAP или таймаут сети AD: {e}", ticket_id)
|
log_to_file(f"❌ Ошибка LDAP или таймаут сети AD: {e}", ticket_id)
|
||||||
return None
|
return None
|
||||||
@@ -554,9 +556,7 @@ def classify_ticket(text, ticket_id=None):
|
|||||||
if not result:
|
if not result:
|
||||||
return "Не определено", 0, [], True, [], {"pure_qdrant_choice": "Не определено", "q_variance": 0, "q_unique_groups": 0, "q_max_score": 0, "q_dominance": 0}
|
return "Не определено", 0, [], True, [], {"pure_qdrant_choice": "Не определено", "q_variance": 0, "q_unique_groups": 0, "q_max_score": 0, "q_dominance": 0}
|
||||||
|
|
||||||
# =================================================================
|
|
||||||
# ЭТАП 1: РАСЧЕТ ВЫБОРА ЧИСТОГО QDRANT (ДО СЛУЖБЫ РЕРАНКИНГА)
|
# ЭТАП 1: РАСЧЕТ ВЫБОРА ЧИСТОГО QDRANT (ДО СЛУЖБЫ РЕРАНКИНГА)
|
||||||
# =================================================================
|
|
||||||
pure_scores = defaultdict(float)
|
pure_scores = defaultdict(float)
|
||||||
pure_counts = defaultdict(int)
|
pure_counts = defaultdict(int)
|
||||||
for p in result:
|
for p in result:
|
||||||
@@ -572,9 +572,7 @@ def classify_ticket(text, ticket_id=None):
|
|||||||
|
|
||||||
qdrant_scores = [p.score for p in result]
|
qdrant_scores = [p.score for p in result]
|
||||||
|
|
||||||
# =================================================================
|
|
||||||
# ЭТАП 2: СЛУЖБА ПЕРЕРАНЖИРОВАНИЯ CROSS-ENCODER + БЛЕНДИНГ СКОРОВ
|
# ЭТАП 2: СЛУЖБА ПЕРЕРАНЖИРОВАНИЯ CROSS-ENCODER + БЛЕНДИНГ СКОРОВ
|
||||||
# =================================================================
|
|
||||||
documents = [p.payload.get("text", "Текст отсутствует").strip()[:RERANKER_MAX_DOC_CHARS] for p in result]
|
documents = [p.payload.get("text", "Текст отсутствует").strip()[:RERANKER_MAX_DOC_CHARS] for p in result]
|
||||||
|
|
||||||
rerank_payload = {
|
rerank_payload = {
|
||||||
@@ -764,6 +762,7 @@ def process_ticket_logic(req_id):
|
|||||||
ad_path = user_info["ad_path"] if user_info else None
|
ad_path = user_info["ad_path"] if user_info else None
|
||||||
manager_email = user_info["manager_email"] if user_info else None
|
manager_email = user_info["manager_email"] if user_info else None
|
||||||
info_source = user_info["source"] if user_info else "Не определен"
|
info_source = user_info["source"] if user_info else "Не определен"
|
||||||
|
user_department = user_info["department"] if user_info else None
|
||||||
|
|
||||||
prefix_used = False
|
prefix_used = False
|
||||||
if caller_phone and not user_info:
|
if caller_phone and not user_info:
|
||||||
@@ -891,22 +890,32 @@ def process_ticket_logic(req_id):
|
|||||||
allowed_subcats = APPROVAL_CATEGORIES[cat_name]
|
allowed_subcats = APPROVAL_CATEGORIES[cat_name]
|
||||||
if "*" in allowed_subcats or subcat_name in allowed_subcats:
|
if "*" in allowed_subcats or subcat_name in allowed_subcats:
|
||||||
is_cat_ok = True
|
is_cat_ok = True
|
||||||
|
# Проверка: отдел пользователя в списке исключений
|
||||||
|
is_excluded = False
|
||||||
|
if user_department and APPROVAL_EXCLUDED_DEPARTMENTS:
|
||||||
|
for dept in APPROVAL_EXCLUDED_DEPARTMENTS:
|
||||||
|
if dept.lower() in user_department.lower() or user_department.lower() in dept.lower():
|
||||||
|
is_excluded = True
|
||||||
|
t_log(f" ⚠️ Отдел '{user_department}' в списке исключений — автосогласование отменено")
|
||||||
|
break
|
||||||
|
if is_excluded:
|
||||||
|
t_log(" ⏭ Пропуск: пользователь в исключённом отделе.")
|
||||||
|
else:
|
||||||
|
if is_city_ok and (is_group_ok or is_cat_ok):
|
||||||
|
if manager_email:
|
||||||
|
t_log(f"✔ Условия совпали. Запуск процесса согласования для {manager_email}...")
|
||||||
|
ai_was_used = True
|
||||||
|
|
||||||
if is_city_ok and (is_group_ok or is_cat_ok):
|
share_success, share_log = share_ticket_for_approval(req_id, manager_email)
|
||||||
if manager_email:
|
t_log(share_log)
|
||||||
t_log(f"✔ Условия совпали. Запуск процесса согласования для {manager_email}...")
|
|
||||||
ai_was_used = True
|
|
||||||
|
|
||||||
share_success, share_log = share_ticket_for_approval(req_id, manager_email)
|
cc_success, cc_log = add_email_to_notify(req_id, manager_email)
|
||||||
t_log(share_log)
|
t_log(cc_log)
|
||||||
|
|
||||||
cc_success, cc_log = add_email_to_notify(req_id, manager_email)
|
success, step_detail = trigger_manager_approval(req_id, manager_email)
|
||||||
t_log(cc_log)
|
t_log(step_detail)
|
||||||
|
else:
|
||||||
success, step_detail = trigger_manager_approval(req_id, manager_email)
|
t_log("⚠️ Согласование невозможно: у пользователя не указан руководитель в AD.")
|
||||||
t_log(step_detail)
|
|
||||||
else:
|
|
||||||
t_log("⚠️ Согласование невозможно: у пользователя не указан руководитель в AD.")
|
|
||||||
else:
|
else:
|
||||||
if ai_was_used:
|
if ai_was_used:
|
||||||
t_log("\n=== ШАГ 5. АВТОСОГЛАСОВАНИЕ ===")
|
t_log("\n=== ШАГ 5. АВТОСОГЛАСОВАНИЕ ===")
|
||||||
|
|||||||
Reference in New Issue
Block a user