5 Commits

Author SHA1 Message Date
dddennnisss 9a9b90b3a1 photo_bot: remove AI validator, restore prepare_ad_photo workflow 2026-08-14 00:54:27 +07:00
dddennnisss 95803b2c3c Update photo_bot/handlers.py: preview after AI approval + dynamic log path + system_error_text 2026-08-13 14:52:30 +07:00
dddennnisss 809f5f4980 Update photo_bot/handlers.py: AI photo verification instead of preview 2026-08-13 14:15:38 +07:00
dddennnisss fbe9c042ce revert 4db994f6de
revert fix: import config module for email alerts
2026-08-11 15:34:05 +07:00
dddennnisss 508bfd36df revert 941cd569d8
revert fix: lk_diagnostic.py - check q_firm for ALL periods
2026-08-11 12:59:19 +07:00
3 changed files with 172 additions and 196 deletions
+172 -192
View File
@@ -1,265 +1,245 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Диагностика данных сотрудника для модуля ЛК (расчетnye listki).""" """Диагностика данных сотрудника для модуля ЛК (расчетные листки)."""
import calendar
import os import os
import sys import sys
# ============================================================
# 1. Загрузка .env и путей
# ============================================================
BASE_DIR = "/opt/trueconf_bot" BASE_DIR = "/opt/trueconf_bot"
os.chdir(BASE_DIR) if os.path.exists(BASE_DIR):
os.chdir(BASE_DIR)
else:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# Загрузка .env
def load_env(filepath): def load_env(filepath):
with open(filepath, "r", encoding="utf-8") as f: try:
for line in f: with open(filepath, "r", encoding="utf-8") as f:
line = line.strip() for line in f:
if line and not line.startswith("#") and "=" in line: line = line.strip()
key, value = line.split("=", 1) if line and not line.startswith("#") and "=" in line:
value = value.strip(' "') key, value = line.split("=", 1)
value = value.strip("'") os.environ[key.strip()] = value.strip(' "\'\r\n')
value = value.strip() except FileNotFoundError:
os.environ[key.strip()] = value print(f"⚠️ Файл {filepath} не найден.")
load_env(os.path.join(BASE_DIR, "config", ".env")) load_env(os.path.join(BASE_DIR, "config", ".env"))
# Загрузка passwork # ============================================================
for pw_path in ["/opt/passwork", os.path.join(BASE_DIR, "passwork")]: # 2. Загрузка паролей из Passwork (AD и SQL)
# ============================================================
CREDENTIALS_MAP = {
"AD_PASSWORD": os.getenv("PW_ID_AD", "").strip(' "\'\r\n'),
"DB_PASSWORD": os.getenv("PW_ID_SQL", "").strip(' "\'\r\n'),
}
passwork_paths = [
"/opt/passwork",
os.path.join(BASE_DIR, "passwork"),
os.path.join(BASE_DIR, "config", "passwork"),
]
for pw_path in passwork_paths:
if os.path.isdir(pw_path) and pw_path not in sys.path: if os.path.isdir(pw_path) and pw_path not in sys.path:
sys.path.insert(0, pw_path) sys.path.insert(0, pw_path)
from passwork import get_passwork_secrets if BASE_DIR not in sys.path:
card_name = os.getenv("PW_ID_SQL", "").strip() sys.path.insert(0, BASE_DIR)
passwork_pool = get_passwork_secrets(required_cards=[card_name])
card_data = passwork_pool[card_name] AD_USER, AD_PASSWORD, DB_USER, DB_PASSWORD = "", "", "", ""
try:
from passwork import get_passwork_secrets
required_cards = [name for name in CREDENTIALS_MAP.values() if name]
passwork_pool = get_passwork_secrets(required_cards=required_cards)
ad_card_name = CREDENTIALS_MAP.get("AD_PASSWORD")
if ad_card_name and ad_card_name in passwork_pool:
card_ad = passwork_pool[ad_card_name]
AD_USER = card_ad.get("login", "").strip(' "\'\r\n')
AD_PASSWORD = card_ad.get("password", "").strip(' "\'\r\n')
sql_card_name = CREDENTIALS_MAP.get("DB_PASSWORD")
if sql_card_name and sql_card_name in passwork_pool:
card_sql = passwork_pool[sql_card_name]
DB_USER = card_sql.get("login", "").strip(' "\'\r\n')
DB_PASSWORD = card_sql.get("password", "").strip(' "\'\r\n')
except Exception as e:
print(f"⚠️ Ошибка загрузки из Passwork: {e}")
if not AD_USER: AD_USER = os.getenv("AD_USER", "")
if not AD_PASSWORD: AD_PASSWORD = os.getenv("AD_PASSWORD", "")
if not DB_USER: DB_USER = os.getenv("DB_USER", "")
if not DB_PASSWORD: DB_PASSWORD = os.getenv("DB_PASSWORD", "")
DB_USER = card_data["login"]
DB_PASSWORD = card_data["password"]
SQL_SERVER = os.getenv("SQL_SERVER", "SRVKEM-MOBILEIN.sibcem.ru") SQL_SERVER = os.getenv("SQL_SERVER", "SRVKEM-MOBILEIN.sibcem.ru")
SQL_DB_NAME = os.getenv("SQL_DB_NAME", "BossCopy") SQL_DB_NAME = os.getenv("SQL_DB_NAME", "BossCopy")
AD_SERVER = os.getenv("AD_SERVER", "172.16.20.20")
AD_BASE = os.getenv("AD_BASE", "DC=sibcem,DC=ru")
import pymssql
import ldap3 import ldap3
import calendar import pymssql
# --- Подключение к AD ---
print("=" * 60)
print("Диагностика сотрудника: la.guseynova@sibcem.ru")
print("=" * 60)
# ============================================================
# 3. Поиск сотрудника в Active Directory
# ============================================================
email = "la.guseynova@sibcem.ru" email = "la.guseynova@sibcem.ru"
search_id = email.replace("@tcs.sibcem.ru", "@sibcem.ru") search_id = email.replace("@tcs.sibcem.ru", "@sibcem.ru")
short_name = search_id.split("@")[0] short_name = search_id.split("@")[0]
print(f"\n--- 0. Поиск в AD ---") print("=" * 60)
print(f" Email: {email}") print(f"Диагностика сотрудника: {email}")
print(f" Search ID: {search_id}") print("=" * 60)
print(f" Short (без домена): {short_name}")
AD_SERVER = "172.16.20.20" print("\n--- 0. Поиск в AD ---")
AD_BASE = "DC=sibcem,DC=ru" card_id_ad = None
try: try:
server = ldap3.Server(AD_SERVER, connect_timeout=5)
bind_user = AD_USER
if bind_user and "@" not in bind_user and "\\" not in bind_user:
bind_user = f"{bind_user}@sibcem.ru"
conn = ldap3.Connection( conn = ldap3.Connection(
ldap3.Server(AD_SERVER, connect_timeout=5), server,
user=bind_user,
password=AD_PASSWORD,
auto_bind=True, auto_bind=True,
read_only=True auto_referrals=False
) )
search_filter = f"(|(mail={search_id}*)(sAMAccountName={short_name})(userPrincipalName={search_id}*)(anr={short_name}))" attributes_list = ["cn", "sAMAccountName", "mail", "userPrincipalName", "proxyAddresses", "extensionAttribute2"]
print(f" LDAP-фильтр: {search_filter}") search_filter = f"(|(sAMAccountName={short_name})(mail={search_id})(userPrincipalName={search_id})(proxyAddresses=*:{search_id}*)(proxyAddresses=*{short_name}*)(anr={short_name}))"
conn.search( conn.search(
search_base=AD_BASE, search_base=AD_BASE,
search_filter=search_filter, search_filter=search_filter,
attributes=["cn", "sAMAccountName", "mail", "userPrincipalName", "extensionAttribute2", "mobile", "personalPager"] attributes=attributes_list
) )
if conn.entries: if conn.entries:
entry = conn.entries[0] entry = conn.entries[0]
cn = str(entry.cn) cn = str(entry.cn)
sam = str(entry.sAMAccountName) sam = str(entry.sAMAccountName) if 'sAMAccountName' in entry else ""
mail = str(entry.mail) if 'mail' in entry else "" mail_val = str(entry.mail) if 'mail' in entry else ""
upn = str(entry.userPrincipalName) if 'userPrincipalName' in entry else "" upn = str(entry.userPrincipalName) if 'userPrincipalName' in entry else ""
ext2 = str(entry.extensionAttribute2) if 'extensionAttribute2' in entry else "" card_id_ad = str(entry.extensionAttribute2) if 'extensionAttribute2' in entry else None
mobile = str(entry.mobile) if 'mobile' in entry else ""
pager = str(entry.personalPager) if 'personalPager' in entry else ""
print(f"\n НАЙДЕН:") print(" НАЙДЕН В AD!")
print(f" CN: {cn}") print(f" CN: {cn}")
print(f" sAMAccountName: {sam}") print(f" sAMAccountName: {sam}")
print(f" mail: {mail}") print(f" mail: {mail_val}")
print(f" UPN: {upn}") print(f" UPN: {upn}")
print(f" extensionAttribute2 (CARD_ID): {ext2}") print(f" extensionAttribute2 (CARD_ID из AD): {card_id_ad}")
print(f" mobile: {mobile}")
print(f" pager: {pager}")
else: else:
print(" Не найден!") print(" ❌ НЕ НАЙДЕН в AD!")
conn.unbind()
sys.exit(0)
conn.unbind() conn.unbind()
except Exception as e: except Exception as e:
print(f" Ошибка подключения к AD: {e}") print(f" Ошибка обращения к AD: {e}")
sys.exit(0)
# --- Подключение к SQL --- # ============================================================
print(f"\n--- 1. Подключение к SQL ---") # 4. Подключение к SQL Server
# ============================================================
print("\n--- 1. Подключение к SQL Server ---")
try: try:
conn_sql = pymssql.connect( conn_sql = pymssql.connect(
server=SQL_SERVER, server=SQL_SERVER,
user=DB_USER, user=DB_USER,
password=DB_PASSWORD, password=DB_PASSWORD,
database=SQL_DB_NAME, database=SQL_DB_NAME,
charset="cp1251", charset='cp1251',
login_timeout=10 login_timeout=10
) )
cursor = conn_sql.cursor() cursor = conn_sql.cursor()
print(f" Подключено к {SQL_SERVER}/{SQL_DB_NAME}") print(" Подключено к SQL Server!")
except Exception as e: except Exception as e:
print(f" Ошибка подключения к SQL: {e}") print(f" Ошибка подключения к SQL: {e}")
sys.exit(0) sys.exit(1)
# --- Получаем CARD_ID --- # ============================================================
card_id = None # 5. Определение CARD_ID
print(f"\n--- 2. Поиск CARD_ID в PR_CARD ---") # ============================================================
cursor.execute( card_id = card_id_ad
"SELECT c.ID FROM UOV_SELFSERVICE_PR_CARD c "
"JOIN UOV_SELFSERVICE_PR_EMP e ON e.CARD_ID = c.ID "
"WHERE e.EMAIL_ID LIKE %s OR e.USERNAME LIKE %s",
(f"%{short_name}%", f"%{short_name}%")
)
rows = cursor.fetchall()
if rows:
for r in rows:
print(f" CARD_ID (по username): {r[0]}")
card_id = rows[0][0]
else:
print(f" Не найден по username")
if not card_id: if not card_id:
cursor.execute( print("\n--- 2. Поиск CARD_ID по ФИО в PR_CARD ---")
"SELECT c.ID FROM UOV_SELFSERVICE_PR_CARD c " cursor.execute("SELECT c.ID, c.NAME FROM UOV_SELFSERVICE_PR_CARD c WHERE c.NAME LIKE %s", ("%Гусейнова%",))
"JOIN UOV_SELFSERVICE_PR_EMP e ON e.CARD_ID = c.ID "
"WHERE e.EMAIL_ID LIKE %s",
(f"%{search_id.split('@')[0]}%",)
)
rows = cursor.fetchall() rows = cursor.fetchall()
if rows: if rows:
for r in rows: card_id = str(rows[0][0])
print(f" CARD_ID (по email): {r[0]}") print(f" ✅ НАЙДЕН по ФИО: CARD_ID={card_id} ({rows[0][1]})")
card_id = rows[0][0]
else: if not card_id:
print(f" CARD_ID не найден!") print(" ❌ Не удалось определить CARD_ID!")
conn_sql.close() conn_sql.close()
sys.exit(0) sys.exit(1)
print(f"\n Используем CARD_ID: {card_id}") print(f"\n Используем CARD_ID: {card_id}")
# --- PR_EMP --- # ============================================================
print(f"\n--- 3. PR_EMP (профиль сотрудника) ---") # 6. Проверка доступных периодов (get_latest_payslip_months)
cursor.execute( # ============================================================
"SELECT ID, FIRM_ID, EMAIL_ID, USERNAME FROM UOV_SELFSERVICE_PR_EMP WHERE CARD_ID = %s", print("\n--- 3. Расчетные периоды (SL_YTAX) ---")
(card_id,) q_months = """
) SELECT DISTINCT TOP 6 (t.TYEAR * 12 + t.TMONTH) as CMONTH, t.TYEAR, t.TMONTH
emp_rows = cursor.fetchall() FROM UOV_SELFSERVICE_PR_EMP e
if emp_rows: JOIN UOV_SELFSERVICE_PR_CARD c on c.ID = e.CARD_ID
for r in emp_rows: JOIN UOV_SELFSERVICE_SL_YTAX t on t.CARD_ID = e.CARD_ID
print(f" EMP_ID: {r[0]}, FIRM_ID: {r[1]}, EMAIL: {r[2]}, USERNAME: {r[3]}") JOIN UOV_SELFSERVICE_PR_TRANS tr on tr.EMP_ID = e.ID
JOIN UOV_SELFSERVICE_ST_APPOINT a on a.ID = tr.APPOINT_ID
JOIN UOV_SELFSERVICE_HR_FIRM f on f.ID = e.FIRM_ID
WHERE c.ID = %s
ORDER BY CMONTH DESC
"""
cursor.execute(q_months, (card_id,))
month_rows = cursor.fetchall()
if not month_rows:
print(" ❌ Отсутствуют расчетные периоды для данного CARD_ID!")
conn_sql.close()
sys.exit(1)
print(f" Найдено периодов: {len(month_rows)}")
for r in month_rows:
print(f" • CMONTH={r[0]} ({r[1]}-{r[2]:02d})")
target_cmonth = month_rows[0][0]
# ============================================================
# 7. Тест определения организации (q_firm) для периода
# ============================================================
print(f"\n--- 4. Определение FIRM_ID для CMONTH={target_cmonth} ---")
tyear = (target_cmonth - 1) // 12
tmonth = (target_cmonth - 1) % 12 + 1
_, last_day = calendar.monthrange(tyear, tmonth)
date_start = f"{tyear}-{tmonth:02d}-01"
date_end = f"{tyear}-{tmonth:02d}-{last_day:02d}"
date_params = (date_start, date_start, date_end, date_end, date_end)
q_firm = """
select e.FIRM_ID, f.SNAME from (select e.id, e.FIRM_ID from UOV_SELFSERVICE_PR_EMP e
join UOV_SELFSERVICE_PR_CARD c on c.ID=e.CARD_ID where c.ID = %s
group by e.ID, e.FIRM_ID ) as e
join UOV_SELFSERVICE_PR_TRANS t on t.EMP_ID = e.ID
join UOV_SELFSERVICE_HR_FIRM f on f.ID = e.FIRM_ID
where ((t.D_FROM <= %s and t.D_TO >= %s) or (t.D_FROM <= %s and t.D_TO >= %s) or (t.D_FROM <= %s and t.D_TO = '1999-12-31'))
group by e.FIRM_ID, f.SNAME
"""
cursor.execute(q_firm, (card_id, *date_params))
firm_row = cursor.fetchone()
if firm_row:
print(f" ✅ q_firm УСПЕШНО определил FIRM_ID={firm_row[0]} ({firm_row[1]}) за {tyear}-{tmonth:02d}")
else: else:
print(" ПУСТО - нет записей в PR_EMP") print(f" ❌ q_firm НЕ СМОГ определить FIRM_ID за {tyear}-{tmonth:02d}!")
# --- PR_TRANS ---
print(f"\n--- 4. PR_TRANS (кадровые назначения) ---")
if emp_rows:
emp_ids = [r[0] for r in emp_rows]
placeholders = ",".join(["%s"] * len(emp_ids))
cursor.execute(
f"SELECT EMP_ID, D_FROM, D_TO, TP_ABBR, SYSPAY_ID "
f"FROM UOV_SELFSERVICE_PR_TRANS "
f"WHERE EMP_ID IN ({placeholders}) "
f"ORDER BY D_FROM DESC",
emp_ids
)
trans_rows = cursor.fetchall()
if trans_rows:
print(f" Найдено записей: {len(trans_rows)}")
for r in trans_rows[:10]:
print(f" EMP_ID: {r[0]}, D_FROM: {r[1]}, D_TO: {r[2]}, TP: {r[3]}, SYSPAY: {r[4]}")
else:
print(" ПУСТО - нет записей в PR_TRANS")
else:
print(" Пропуск (нет PR_EMP)")
# --- SL_YTAX ---
print(f"\n--- 5. SL_YTAX (периоды с данными) ---")
cursor.execute(
"SELECT DISTINCT TYEAR, TMONTH, CMONTH FROM UOV_SELFSERVICE_SL_YTAX "
"WHERE CARD_ID = %s ORDER BY CMONTH DESC",
(card_id,)
)
tax_rows = cursor.fetchall()
if tax_rows:
print(f" Найдено периодов: {len(tax_rows)}")
for r in tax_rows:
print(f" {r[0]}-{r[1]:02d} (CMONTH={r[2]})")
else:
print(" ПУСТО - нет периодов в SL_YTAX")
# --- 6. Проверка q_firm для ВСЕХ периодов ---
print(f"\n--- 6. Проверка q_firm для КАЖДОГО периода ---")
if tax_rows:
for tax_row in tax_rows:
tyear = tax_row[0]
tmonth = tax_row[1]
cmonth = tax_row[2]
_, last_day = calendar.monthrange(tyear, tmonth)
date_start = f"{tyear}-{tmonth:02d}-01"
date_end = f"{tyear}-{tmonth:02d}-{last_day:02d}"
date_params = (date_start, date_start, date_end, date_end, date_end)
q_firm = """
select e.FIRM_ID, f.SNAME from (select e.id, e.FIRM_ID from UOV_SELFSERVICE_PR_EMP e
join UOV_SELFSERVICE_PR_CARD c on c.ID=e.CARD_ID where c.ID = %s
group by e.ID, e.FIRM_ID ) as e
join UOV_SELFSERVICE_PR_TRANS t on t.EMP_ID = e.ID
join UOV_SELFSERVICE_HR_FIRM f on f.ID = e.FIRM_ID
where ((t.D_FROM <= %s and t.D_TO >= %s) or (t.D_FROM <= %s and t.D_TO >= %s) or (t.D_FROM <= %s and t.D_TO = '1999-12-31'))
group by e.FIRM_ID, f.SNAME
"""
cursor.execute(q_firm, (card_id, *date_params))
firm_row = cursor.fetchone()
if firm_row:
print(f" {tyear}-{tmonth:02d} (CMONTH={cmonth}): ✅ FIRM_ID={firm_row[0]}, SNAME={firm_row[1]}")
else:
print(f" {tyear}-{tmonth:02d} (CMONTH={cmonth}): ❌ q_firm НЕ НАШЁЛ FIRM_ID!")
# Фоллбэк
q_firm_fb = """
select e.FIRM_ID from UOV_SELFSERVICE_PR_EMP e
join UOV_SELFSERVICE_PR_CARD c on c.ID=e.CARD_ID
where c.ID = %s
group by e.ID, e.FIRM_ID
"""
cursor.execute(q_firm_fb, (card_id,))
fb_row = cursor.fetchone()
if fb_row:
print(f" Фоллбэк: FIRM_ID={fb_row[0]}")
else:
print(f" Фоллбэк НЕ НАШЁЛ - критическая ошибка!")
else:
print(" Нет периодов для проверки")
# --- 7. Сводка ---
print(f"\n--- 7. Сводка ---")
cursor.execute(
"SELECT COUNT(DISTINCT CMONTH), MIN(CMONTH), MAX(CMONTH) FROM UOV_SELFSERVICE_SL_YTAX WHERE CARD_ID = %s",
(card_id,)
)
summary = cursor.fetchone()
if summary:
print(f" Периодов в SL_YTAX: {summary[0]}")
print(f" Диапазон: {summary[1]} - {summary[2]}")
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
print("Диагностика завершена") print("Диагностика завершена")
print("=" * 60) print("=" * 60)
conn_sql.close() conn_sql.close()
-1
View File
@@ -240,4 +240,3 @@ async def photo_module_handler(msg: Message):
except Exception as cleanup_err: except Exception as cleanup_err:
await asyncio.to_thread(log_and_notify_photo_error, user_id, "Очистка временных файлов", str(cleanup_err)) await asyncio.to_thread(log_and_notify_photo_error, user_id, "Очистка временных файлов", str(cleanup_err))
return return
-3
View File
@@ -7,9 +7,7 @@ import httpx
from ldap3 import Server, Connection, ALL from ldap3 import Server, Connection, ALL
from trueconf import Router, Message from trueconf import Router, Message
import config.config as config
from config.config import * from config.config import *
from config.config import DEFAULT_EMAIL_FROM, SMTP_SERVER, SMTP_PORT, ALERTS_SUPPORT_EMAILS
from utils import states from utils import states
from utils.menu import MENU_TEXT from utils.menu import MENU_TEXT
from utils.texts import ( from utils.texts import (
@@ -186,4 +184,3 @@ async def transcription_handler(msg: Message):