перед внедрением API и веб интерфейса.
This commit is contained in:
+50
-52
@@ -1,40 +1,39 @@
|
||||
import os
|
||||
import logging
|
||||
from datetime import date
|
||||
from datetime import date, datetime
|
||||
import pyodbc
|
||||
import pandas as pd
|
||||
from config import DATA_DIR, normalize_fio
|
||||
from config import DATA_DIR, normalize_fio, ZUP_SQL_CONFIG
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Конфигурация MS SQL Server 1С:ЗУП 3.1
|
||||
ZUP_SQL_CONFIG = {
|
||||
"driver": "{ODBC Driver 18 for SQL Server}",
|
||||
"server": os.getenv("ZUP_MS_SQL_SERVER", "192.168.1.10"), # Укажите IP MS SQL 1С
|
||||
"database": os.getenv("ZUP_MS_SQL_DB", "zup_db"),
|
||||
"user": os.getenv("ZUP_MS_SQL_USER", "sa"),
|
||||
"password": os.getenv("ZUP_MS_SQL_PASS", "your_password"),
|
||||
}
|
||||
|
||||
|
||||
def get_zup_connection_string() -> str:
|
||||
"""Формирует строку подключения pyodbc к MS SQL Server."""
|
||||
"""Формирует строку подключения pyodbc к MS SQL Server из config.py."""
|
||||
return (
|
||||
f"DRIVER={ZUP_SQL_CONFIG['driver']};"
|
||||
f"SERVER={ZUP_SQL_CONFIG['server']};"
|
||||
f"DATABASE={ZUP_SQL_CONFIG['database']};"
|
||||
f"UID={ZUP_SQL_CONFIG['user']};"
|
||||
f"PWD={ZUP_SQL_CONFIG['password']};"
|
||||
f"TrustServerCertificate=yes;"
|
||||
f"Encrypt=no;"
|
||||
f"TrustServerCertificate={ZUP_SQL_CONFIG.get('trust_server_certificate', 'yes')};"
|
||||
f"Encrypt={ZUP_SQL_CONFIG.get('encrypt', 'no')};"
|
||||
)
|
||||
|
||||
|
||||
def fetch_zup_absences_from_sql(target_date: date) -> pd.DataFrame:
|
||||
def fetch_zup_absences_from_sql(target_date) -> pd.DataFrame:
|
||||
"""
|
||||
Извлекает оперативные отсутствия и действующие декреты из MS SQL 1С:ЗУП 3.1
|
||||
на указанную дату.
|
||||
на указанную дату (принимает как datetime.date, так и строку 'DD.MM.YYYY' / 'DD_MM_YYYY').
|
||||
"""
|
||||
if isinstance(target_date, str):
|
||||
clean_date_str = target_date.replace('_', '.')
|
||||
try:
|
||||
target_date = datetime.strptime(clean_date_str, "%d.%m.%Y").date()
|
||||
except ValueError:
|
||||
logger.error(f"[❌] Неверный формат даты для SQL-запроса: {target_date}. Ожидался DD.MM.YYYY")
|
||||
return pd.DataFrame()
|
||||
|
||||
query = """
|
||||
DECLARE @TargetDate DATE = ?;
|
||||
|
||||
@@ -86,17 +85,21 @@ def fetch_zup_absences_from_sql(target_date: date) -> pd.DataFrame:
|
||||
ORDER BY [ФИО] ASC;
|
||||
"""
|
||||
|
||||
conn_str = get_zup_connection_string()
|
||||
with pyodbc.connect(conn_str) as conn:
|
||||
df = pd.read_sql(query, conn, params=[target_date])
|
||||
|
||||
if not df.empty:
|
||||
df['fio_clean'] = df['ФИО'].apply(normalize_fio)
|
||||
return df
|
||||
try:
|
||||
conn_str = get_zup_connection_string()
|
||||
with pyodbc.connect(conn_str, timeout=5) as conn:
|
||||
df = pd.read_sql(query, conn, params=[target_date])
|
||||
|
||||
if not df.empty:
|
||||
df['fio_clean'] = df['ФИО'].apply(normalize_fio)
|
||||
return df
|
||||
except Exception as e:
|
||||
logger.error(f"[❌] Ошибка SQL-выгрузки отсутствий за {target_date}: {e}")
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
def fetch_zup_staff_from_sql() -> pd.DataFrame:
|
||||
"""Извлекает весь актуальный штат из MS SQL 1С:ЗУП."""
|
||||
"""Резервная выгрузка штата из MS SQL."""
|
||||
query = """
|
||||
SELECT DISTINCT
|
||||
LTRIM(RTRIM(ref_emp._Description)) AS [ФИО],
|
||||
@@ -107,53 +110,48 @@ def fetch_zup_staff_from_sql() -> pd.DataFrame:
|
||||
AND ref_emp._Marked = 0x00
|
||||
ORDER BY [ФИО] ASC;
|
||||
"""
|
||||
conn_str = get_zup_connection_string()
|
||||
with pyodbc.connect(conn_str) as conn:
|
||||
df = pd.read_sql(query, conn)
|
||||
|
||||
if not df.empty:
|
||||
df['fio_clean'] = df['ФИО'].apply(normalize_fio)
|
||||
return df
|
||||
|
||||
|
||||
def sync_zup_to_excel(target_date: date) -> bool:
|
||||
"""
|
||||
Извлекает данные из MS SQL и автоматически создает локальные Excel-файлы 1С:
|
||||
data/1c/Штат_DD_MM_YYYY.xlsx
|
||||
data/1c/Отсутствия_DD_MM_YYYY.xlsx
|
||||
"""
|
||||
try:
|
||||
date_str_file = target_date.strftime("%d_%m_%Y")
|
||||
conn_str = get_zup_connection_string()
|
||||
with pyodbc.connect(conn_str, timeout=5) as conn:
|
||||
df = pd.read_sql(query, conn)
|
||||
|
||||
if not df.empty:
|
||||
df['fio_clean'] = df['ФИО'].apply(normalize_fio)
|
||||
return df
|
||||
except Exception as e:
|
||||
logger.error(f"[❌] Ошибка выгрузки штата из MS SQL: {e}")
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
def sync_zup_to_excel(target_date) -> bool:
|
||||
"""Создает дамп в Excel при необходимости."""
|
||||
try:
|
||||
if isinstance(target_date, str):
|
||||
clean_date_str = target_date.replace('_', '.')
|
||||
target_date_obj = datetime.strptime(clean_date_str, "%d.%m.%Y").date()
|
||||
else:
|
||||
target_date_obj = target_date
|
||||
|
||||
date_str_file = target_date_obj.strftime("%d_%m_%Y")
|
||||
c_1c_dir = os.path.join(DATA_DIR, "1c")
|
||||
os.makedirs(c_1c_dir, exist_ok=True)
|
||||
|
||||
df_staff = fetch_zup_staff_from_sql()
|
||||
df_absences = fetch_zup_absences_from_sql(target_date)
|
||||
df_absences = fetch_zup_absences_from_sql(target_date_obj)
|
||||
|
||||
logger.info(f"[1С Direct SQL] Выгрузка за {target_date.strftime('%d.%m.%Y')}: Штат ({len(df_staff)} чел.), Отсутствия ({len(df_absences)} чел.)")
|
||||
|
||||
# Сохранение в формате, который ожидает data_loader.py
|
||||
staff_excel_path = os.path.join(c_1c_dir, f"Штат_{date_str_file}.xlsx")
|
||||
absences_excel_path = os.path.join(c_1c_dir, f"Отсутствия_{date_str_file}.xlsx")
|
||||
|
||||
# Формируем структуру Excel с нужным количеством пропущенных строк (skiprows)
|
||||
with pd.ExcelWriter(staff_excel_path, engine='openpyxl') as writer:
|
||||
# Пустой заголовок (8 строк) для эмуляции отчета 1С
|
||||
dummy_headers = pd.DataFrame([[""] * 13] * 8)
|
||||
dummy_headers.to_excel(writer, index=False, header=False)
|
||||
|
||||
# Данные
|
||||
df_staff[['ФИО', 'Подразделение', 'Должность']].to_excel(writer, startrow=8, index=False)
|
||||
|
||||
with pd.ExcelWriter(absences_excel_path, engine='openpyxl') as writer:
|
||||
# Пустой заголовок (3 строки) для эмуляции отчета 1С
|
||||
dummy_headers = pd.DataFrame([[""] * 3] * 3)
|
||||
dummy_headers.to_excel(writer, index=False, header=False)
|
||||
|
||||
# Данные
|
||||
df_absences[['ФИО', 'Вид_отсутствия']].to_excel(writer, startrow=3, index=False)
|
||||
|
||||
logger.info(f"[✓] Успешно созданы файлы 1С за {target_date.strftime('%d.%m.%Y')}: {staff_excel_path}, {absences_excel_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user