CREATE OR REPLACE PROCEDURE public."Hr_S_AttendanceProcess"( p_FailOnClosed boolean DEFAULT TRUE ) LANGUAGE plpgsql AS $$ DECLARE v_Key record; BEGIN -- İşlenmemiş hareketleri (ekran, Excel import, PDKS ucu; silinenler dahil) işler: çalışanı kullanıcı -- kartındaki PDKS Kart No'dan (yoksa Sicil No'dan) bulur, hareketin iş gününü vardiyaya göre belirler ve etkilenen günlerin puantajını -- yeniden hesaplar. Ekrandan gelen değişiklik kapalı döneme düşerse kayıt geri alınır -- (p_FailOnClosed = TRUE); PDKS ve gece işinde satır hatalı işaretlenir. -- Kart/sicil eşleşmesi SQL Server harmanlaması gibi büyük/küçük harf duyarsızdır. DROP TABLE IF EXISTS ap_log; CREATE TEMP TABLE ap_log ( Id uuid PRIMARY KEY, TenantId uuid NULL, EmployeeId uuid NULL, CardNo varchar(64) NULL, EventTime timestamp, IsDeleted boolean, OldWorkDate date NULL, NewWorkDate date NULL, Error varchar(32) NULL ) ON COMMIT DROP; INSERT INTO ap_log (Id, TenantId, EmployeeId, CardNo, EventTime, IsDeleted, OldWorkDate) SELECT "Id", "TenantId", "EmployeeId", "CardNo", "EventTime", "IsDeleted", "WorkDate" FROM public."Hr_T_AttendanceLog" WHERE "IsProcessed" = FALSE; IF NOT EXISTS (SELECT 1 FROM ap_log) THEN RETURN; END IF; UPDATE ap_log a SET EmployeeId = u.Id FROM ( SELECT a2.Id AS LogId, ( SELECT x."Id" FROM public."AbpUsers" x WHERE (lower(x."PdksCardNo") = lower(a2.CardNo) OR lower(x."RegistrationNo") = lower(a2.CardNo)) AND x."IsDeleted" = FALSE AND (x."TenantId" = a2.TenantId OR (x."TenantId" IS NULL AND a2.TenantId IS NULL)) ORDER BY CASE WHEN lower(x."PdksCardNo") = lower(a2.CardNo) THEN 0 ELSE 1 END, x."IsActive" DESC NULLS LAST LIMIT 1) AS Id FROM ap_log a2 WHERE a2.EmployeeId IS NULL AND a2.CardNo IS NOT NULL AND a2.CardNo <> '' ) u WHERE u.LogId = a.Id AND u.Id IS NOT NULL; UPDATE ap_log SET Error = 'UnknownCard' WHERE EmployeeId IS NULL AND IsDeleted = FALSE; UPDATE ap_log a SET NewWorkDate = (a.EventTime - s."DayOffset" * INTERVAL '1 minute')::date FROM public."Hr_V_AttendanceShift" s WHERE s."EmployeeId" = a.EmployeeId AND a.IsDeleted = FALSE AND a.Error IS NULL; -- Kapalı dönem: hareketin eski ya da yeni günü kapalı aya düşüyorsa. UPDATE ap_log a SET Error = 'ClosedPeriod' WHERE EXISTS ( SELECT 1 FROM public."Hr_T_TimesheetPeriod" p WHERE p."IsDeleted" = FALSE AND p."Status" = 'Closed' AND (p."TenantId" = a.TenantId OR (p."TenantId" IS NULL AND a.TenantId IS NULL)) AND ((p."PeriodYear" = EXTRACT(YEAR FROM a.OldWorkDate)::integer AND p."PeriodMonth" = EXTRACT(MONTH FROM a.OldWorkDate)::integer) OR (p."PeriodYear" = EXTRACT(YEAR FROM a.NewWorkDate)::integer AND p."PeriodMonth" = EXTRACT(MONTH FROM a.NewWorkDate)::integer))); IF p_FailOnClosed = TRUE AND EXISTS (SELECT 1 FROM ap_log WHERE Error = 'ClosedPeriod') THEN RAISE EXCEPTION 'Kapalı puantaj dönemine ait hareket eklenemez, değiştirilemez ya da silinemez. Dönemi İnsan Kaynakları açabilir.' USING ERRCODE = 'P0001', DETAIL = '51200'; END IF; UPDATE public."Hr_T_AttendanceLog" l SET "EmployeeId" = a.EmployeeId, "WorkDate" = CASE WHEN a.Error = 'ClosedPeriod' THEN l."WorkDate" ELSE a.NewWorkDate END, "ProcessError" = a.Error, "Source" = COALESCE(l."Source", 'Import'), "IsProcessed" = TRUE FROM ap_log a WHERE a.Id = l."Id"; -- Etkilenen günler: hareketin eski günü (taşındıysa ya da silindiyse) ve yeni günü. DROP TABLE IF EXISTS ap_keys; CREATE TEMP TABLE ap_keys (TenantId uuid NULL, EmployeeId uuid, FromDate date, ToDate date) ON COMMIT DROP; INSERT INTO ap_keys (TenantId, EmployeeId, FromDate, ToDate) SELECT k.TenantId, k.EmployeeId, MIN(k.D), MAX(k.D) FROM ( SELECT TenantId, EmployeeId, OldWorkDate AS D FROM ap_log WHERE EmployeeId IS NOT NULL AND OldWorkDate IS NOT NULL AND COALESCE(Error, '') <> 'ClosedPeriod' UNION ALL SELECT TenantId, EmployeeId, NewWorkDate FROM ap_log WHERE EmployeeId IS NOT NULL AND NewWorkDate IS NOT NULL AND Error IS NULL ) k GROUP BY k.TenantId, k.EmployeeId; FOR v_Key IN SELECT TenantId, EmployeeId, FromDate, ToDate FROM ap_keys LOOP CALL public."Hr_S_TimesheetCalculate"(v_Key.TenantId, v_Key.EmployeeId, v_Key.FromDate, v_Key.ToDate, NULL); END LOOP; END; $$;