""" =============================================================================== FILE: services/scud_etl/sql_queries.py ROLE: Изолированные SQL-шаблоны для MS SQL Server (СКУД Орион Pro). =============================================================================== """ # 1. Основной срез СКУД с учетом турникетов и флигеля SQL_QUERY_TEMPLATE = r""" DECLARE @InputDate DATE = '{target_date}'; DECLARE @TargetDate DATE = @InputDate; DECLARE @StartDate DATETIME = CAST(@TargetDate AS DATETIME); DECLARE @EndDate DATETIME = {end_datetime_sql}; WITH PercoPassages AS ( -- Физические факты прохода (Event = 32) SELECT log.HozOrgan AS EmployeeID, log.TimeVal, CASE WHEN log.Mode = 1 THEN 'IN' WHEN log.Mode = 2 THEN 'OUT' ELSE 'OTHER' END AS Direction FROM pLogData log WITH (NOLOCK) INNER JOIN pList p WITH (NOLOCK) ON log.HozOrgan = p.ID LEFT JOIN PDivision div WITH (NOLOCK) ON p.Section = div.ID WHERE log.TimeVal BETWEEN @StartDate AND @EndDate AND log.HozOrgan IS NOT NULL AND log.HozOrgan > 0 AND log.Event = 32 AND log.Mode IN (1, 2) AND ( -- Контур 1: Левый турникет открыт для всех log.DoorIndex = 1 OR -- Контур 2: Правый турникет разрешен только для реестра двора ( log.DoorIndex = 2 AND ({turnstile_filter_sql}) ) OR -- Контур 3: Флигель 1 эт. разрешен только для реестра флигеля ( log.DoorIndex = 23 AND ({fligel_filter_sql}) ) ) ), Passages AS ( SELECT EmployeeID, MIN(TimeVal) AS FirstRawEvent, MAX(TimeVal) AS LastRawEvent, MIN(CASE WHEN Direction = 'IN' THEN TimeVal END) AS FirstIn, MAX(CASE WHEN Direction = 'OUT' THEN TimeVal END) AS FinalOut FROM PercoPassages GROUP BY EmployeeID ), EvaluatedPassages AS ( SELECT p.*, CASE WHEN p.FinalOut IS NOT NULL AND p.FirstIn IS NOT NULL AND p.FinalOut > DATEADD(MINUTE, 5, p.FirstIn) THEN p.FinalOut ELSE NULL END AS FilteredLastOut FROM Passages p ) SELECT N'ЛЕНМОРНИИПРОЕКТ' AS [Фирма], ISNULL(CAST(div.Name AS NVARCHAR(255)), N'Без подразделения') AS [Подразделение], LTRIM(RTRIM( ISNULL(CAST(p.Name AS NVARCHAR(255)), N'') + CASE WHEN p.FirstName IS NOT NULL AND CAST(p.FirstName AS NVARCHAR(255)) <> '' THEN N' ' + CAST(p.FirstName AS NVARCHAR(255)) ELSE N'' END + CASE WHEN p.MidName IS NOT NULL AND CAST(p.MidName AS NVARCHAR(255)) <> '' THEN N' ' + CAST(p.MidName AS NVARCHAR(255)) ELSE N'' END )) AS [Сотрудник], ISNULL(CAST(post.Name AS NVARCHAR(255)), N'—') AS [Должность], ISNULL(CAST(p.TabNumber AS NVARCHAR(50)), N'—') AS [Таб_№], CONVERT(VARCHAR(10), @TargetDate, 104) AS [Дата], ISNULL(CAST(CONVERT(VARCHAR(8), pass.FirstIn, 108) AS NVARCHAR(20)), N'Нет входа') AS [Начало_дня], CASE WHEN pass.FirstIn IS NULL AND pass.FirstRawEvent IS NOT NULL THEN CAST(CONVERT(VARCHAR(8), pass.FirstRawEvent, 108) AS NVARCHAR(20)) ELSE N'—' END AS [Первая_активность], CASE WHEN pass.FilteredLastOut IS NOT NULL THEN CAST(CONVERT(VARCHAR(8), pass.FilteredLastOut, 108) AS NVARCHAR(20)) ELSE N'Нет выхода' END AS [Конец_дня], CASE WHEN pass.EmployeeID IS NOT NULL AND (pass.FirstIn IS NOT NULL OR pass.FirstRawEvent IS NOT NULL) THEN RIGHT('0' + CAST(DATEDIFF(MINUTE, ISNULL(pass.FirstIn, pass.FirstRawEvent), CASE WHEN pass.FilteredLastOut IS NOT NULL THEN pass.FilteredLastOut ELSE @EndDate END) / 60 AS VARCHAR), 2) + ':' + RIGHT('0' + CAST(DATEDIFF(MINUTE, ISNULL(pass.FirstIn, pass.FirstRawEvent), CASE WHEN pass.FilteredLastOut IS NOT NULL THEN pass.FilteredLastOut ELSE @EndDate END) % 60 AS VARCHAR), 2) ELSE N'00:00' END AS [Находился_в_здании], CASE WHEN pass.EmployeeID IS NOT NULL THEN N'Присутствовал' ELSE N'Отсутствовал (Нет событий)' END AS [Статус] FROM pList p WITH (NOLOCK) LEFT JOIN PDivision div WITH (NOLOCK) ON p.Section = div.ID LEFT JOIN PPost post WITH (NOLOCK) ON p.Post = post.ID LEFT JOIN EvaluatedPassages pass ON p.ID = pass.EmployeeID WHERE ISNULL(p.StatusRecord, 0) = 0 AND p.DateTimeInArchive IS NULL AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'Аренд%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT IN (N'Без подразделения', N'') AND p.Name NOT LIKE N'бр.%' AND p.Name NOT LIKE N'Гость%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT IN (N'БГИ', N'КНР') AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'Рабоч%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'Врем%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'Практика%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'тест%' AND ISNULL(CAST(post.Name AS NVARCHAR(255)), N'') NOT LIKE N'Практикант%' ORDER BY p.Name ASC; """ # 2. Сырые события физических проходов турникетов SQL_RAW_EVENTS_QUERY = r""" DECLARE @InputDate DATE = '{target_date}'; DECLARE @StartDate DATETIME = CAST(@InputDate AS DATETIME); DECLARE @EndDate DATETIME = {end_datetime_sql}; SELECT log.TimeVal, log.HozOrgan, LTRIM(RTRIM( ISNULL(CAST(p.Name AS NVARCHAR(255)), N'') + CASE WHEN p.FirstName IS NOT NULL AND CAST(p.FirstName AS NVARCHAR(255)) <> '' THEN N' ' + CAST(p.FirstName AS NVARCHAR(255)) ELSE N'' END + CASE WHEN p.MidName IS NOT NULL AND CAST(p.MidName AS NVARCHAR(255)) <> '' THEN N' ' + CAST(p.MidName AS NVARCHAR(255)) ELSE N'' END )) AS [Сотрудник], ISNULL(CAST(div.Name AS NVARCHAR(255)), N'Без подразделения') AS [Подразделение], log.Event, log.Mode, log.DoorIndex, CASE WHEN log.Mode = 2 THEN 'OUT' WHEN log.Mode = 1 THEN 'IN' WHEN log.Event IN (2, 27, 29, 33, 55, 65) THEN 'OUT' WHEN log.Event IN (1, 21, 26, 54, 64) THEN 'IN' ELSE 'OTHER' END AS Direction FROM pLogData log WITH (NOLOCK) INNER JOIN pList p WITH (NOLOCK) ON log.HozOrgan = p.ID LEFT JOIN PDivision div WITH (NOLOCK) ON p.Section = div.ID LEFT JOIN PPost post WITH (NOLOCK) ON p.Post = post.ID WHERE log.TimeVal BETWEEN @StartDate AND @EndDate AND log.HozOrgan IS NOT NULL AND log.HozOrgan > 0 AND log.Event IN (28, 32) -- ⭐️ Фильтрация сотрудников Ленморниипроект (исключение арендаторов и гостей) AND ISNULL(p.StatusRecord, 0) = 0 AND p.DateTimeInArchive IS NULL AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'Аренд%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT IN (N'Без подразделения', N'') AND p.Name NOT LIKE N'бр.%' AND p.Name NOT LIKE N'Гость%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT IN (N'БГИ', N'КНР') AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'Рабоч%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'Врем%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'Практика%' AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'тест%' AND ISNULL(CAST(post.Name AS NVARCHAR(255)), N'') NOT LIKE N'Практикант%' ORDER BY log.TimeVal ASC; """