CREATE OR ALTER PROCEDURE [dbo].[Hr_S_LeaveRequestAfterSave] @RequestId uniqueidentifier, @ActorId uniqueidentifier, @Actor nvarchar(16) AS BEGIN SET NOCOUNT ON; -- İzin talebinin her kaydından (ekleme, güncelleme, Grid Workflow kararı) sonra çalışır: -- çalışan bilgisi, talep no, müdür, gün sayısı tamamlanır; kurallar doğrulanır (THROW kaydı -- geri alır); rapor türü Sağlık Kartı istirahatiyle eşlenir; durum değiştiyse bildirim gider. DECLARE @StatusStart nvarchar(50) = N'Onaya Gönderildi', @StatusManager nvarchar(50) = N'Müdür Onayı', @StatusHr nvarchar(50) = N'İK Onayı', @StatusApproved nvarchar(50) = N'Onaylandı', @StatusRejected nvarchar(50) = N'Reddedildi'; DECLARE @TenantId uniqueidentifier, @EmployeeId uniqueidentifier, @LeaveTypeId uniqueidentifier, @StartDate datetime2, @EndDate datetime2, @IsHalfDay bit, @HalfDayPart nvarchar(16), @Document nvarchar(MAX), @SickLeaveId uniqueidentifier, @Status nvarchar(50), @LastStatus nvarchar(50), @Description nvarchar(1024); SELECT @TenantId = TenantId, @EmployeeId = EmployeeId, @LeaveTypeId = LeaveTypeId, @StartDate = StartDate, @EndDate = EndDate, @IsHalfDay = IsHalfDay, @HalfDayPart = HalfDayPart, @Document = DocumentContent, @SickLeaveId = SickLeaveId, @Status = ApprovalStatus, @LastStatus = LastNotifiedStatus, @Description = [Description] FROM Hr_T_LeaveRequest WHERE Id = @RequestId AND IsDeleted = 0; IF @EmployeeId IS NULL RETURN; DECLARE @Message nvarchar(1024); --------------------------------------------------------------------------------------------- -- Çalışan ve departman müdürü --------------------------------------------------------------------------------------------- DECLARE @EmployeeName nvarchar(256), @DepartmentId uniqueidentifier, @JobPositionId uniqueidentifier, @Gender nvarchar(16); SELECT @EmployeeName = CONCAT(Name, N' ', Surname), @DepartmentId = DepartmentId, @JobPositionId = JobPositionId, @Gender = Gender FROM AbpUsers WHERE Id = @EmployeeId; -- Müdür = çalışanın departmanındaki en üst pozisyon(lar)ın sahibi (üst pozisyonu başka -- departmanda olan ya da üstü olmayan pozisyon). Çalışan bu pozisyondaysa ya da pozisyon boşsa -- bir üst pozisyona çıkılır. DECLARE @Positions TABLE (Id uniqueidentifier PRIMARY KEY); DECLARE @Managers TABLE (Id uniqueidentifier PRIMARY KEY, UserName nvarchar(256)); DECLARE @Parents TABLE (Id uniqueidentifier PRIMARY KEY); DECLARE @Level int = 0; INSERT INTO @Positions (Id) SELECT p.Id FROM Hr_T_JobPosition p LEFT JOIN Hr_T_JobPosition parent ON parent.Id = p.ParentId WHERE p.IsDeleted = 0 AND p.DepartmentId = @DepartmentId AND (p.TenantId = @TenantId OR (p.TenantId IS NULL AND @TenantId IS NULL)) AND (p.ParentId IS NULL OR parent.DepartmentId <> p.DepartmentId); IF EXISTS (SELECT 1 FROM @Positions WHERE Id = @JobPositionId) BEGIN DELETE FROM @Positions; INSERT INTO @Positions (Id) SELECT ParentId FROM Hr_T_JobPosition WHERE Id = @JobPositionId AND ParentId IS NOT NULL; END WHILE @Level < 10 AND EXISTS (SELECT 1 FROM @Positions) BEGIN INSERT INTO @Managers (Id, UserName) SELECT u.Id, u.UserName FROM AbpUsers u WHERE u.IsDeleted = 0 AND u.IsActive = 1 AND u.Id <> @EmployeeId AND (u.TenantId = @TenantId OR (u.TenantId IS NULL AND @TenantId IS NULL)) AND u.JobPositionId IN (SELECT Id FROM @Positions); IF EXISTS (SELECT 1 FROM @Managers) BREAK; DELETE FROM @Parents; INSERT INTO @Parents (Id) SELECT DISTINCT p.ParentId FROM Hr_T_JobPosition p WHERE p.Id IN (SELECT Id FROM @Positions) AND p.ParentId IS NOT NULL; DELETE FROM @Positions; INSERT INTO @Positions (Id) SELECT Id FROM @Parents; SET @Level += 1; END DECLARE @ManagerIds nvarchar(1024) = (SELECT STRING_AGG(CONVERT(nvarchar(36), Id), N'|') FROM @Managers); DECLARE @ManagerUserNames nvarchar(1024) = (SELECT STRING_AGG(UserName, N'|') FROM @Managers); -- Pozisyonu en üstte olan (üst pozisyonu olmayan) çalışanın onaylayacak müdürü yoktur; müdür adımı -- atlanır ve talep İK onayına gider. Akış bu kolona bakan karşılaştırma düğümüyle dallanır. DECLARE @RequiresManager int = CASE WHEN @ManagerIds IS NULL AND EXISTS (SELECT 1 FROM Hr_T_JobPosition WHERE Id = @JobPositionId AND ParentId IS NULL AND IsDeleted = 0) THEN 0 ELSE 1 END; IF @ManagerIds IS NULL AND @RequiresManager = 1 AND (@Status IS NULL OR @Status IN (@StatusStart, @StatusManager)) THROW 51110, N'Çalışanın departman müdürü bulunamadı. Kullanıcı kartında departman ve pozisyon tanımlı olmalı, departmanın müdür pozisyonunda aktif bir kullanıcı bulunmalıdır.', 1; --------------------------------------------------------------------------------------------- -- Yöneticinin yapabileceği geçişler: müdür onayından İK onayına ya da redde. Başka bir -- geçiş (ör. doğrudan Onaylandı) önceki duruma geri alınır. --------------------------------------------------------------------------------------------- IF @Actor = N'Manager' AND COALESCE(@Status, N'') <> COALESCE(@LastStatus, N'') AND NOT (@LastStatus = @StatusManager AND @Status IN (@StatusHr, @StatusRejected)) BEGIN SET @Status = @LastStatus; UPDATE Hr_T_LeaveRequest SET ApprovalStatus = @LastStatus WHERE Id = @RequestId; END -- Çalışanın açtığı talep doğrudan müdür onayına düşer (persona ekranında akış başlatılmaz); -- müdürü olmayan en üst pozisyonda İK onayına. IF @Status IS NULL AND @Actor = N'Employee' SET @Status = CASE WHEN @RequiresManager = 1 THEN @StatusManager ELSE @StatusHr END; -- Adıma girerken onaycı kayıttan çözülür: müdür adımında departman müdürü, İK adımında İK -- ekranının akışındaki İK düğümü (tek kaynak; diğer ekranların düğümü yalnız geçişi yapar). -- Adım içindeki kayıtta (karar anı) motorun yazdığı karar vereni ezmemek için dokunulmaz. DECLARE @HrApprover nvarchar(1024) = (SELECT TOP 1 Approver FROM Sas_H_ListFormWorkflow WHERE ListFormCode = N'App.Hr.LeaveRequest' AND Kind = N'Approval' AND Title = @StatusHr); UPDATE Hr_T_LeaveRequest SET EmployeeName = @EmployeeName, DepartmentId = @DepartmentId, JobPositionId = @JobPositionId, ManagerIds = COALESCE(@ManagerIds, ManagerIds), ManagerUserNames = COALESCE(@ManagerUserNames, ManagerUserNames), RequiresManagerApproval = @RequiresManager, ApprovalStatus = @Status, ApprovalUser = CASE WHEN @Status = COALESCE(@LastStatus, N'') THEN ApprovalUser WHEN @Status = @StatusManager THEN COALESCE(@ManagerUserNames, ApprovalUser) WHEN @Status = @StatusHr AND @HrApprover IS NOT NULL THEN @HrApprover ELSE ApprovalUser END, HalfDayPart = CASE WHEN @IsHalfDay = 1 THEN COALESCE(@HalfDayPart, N'Morning') ELSE NULL END, RequestNo = CASE WHEN RequestNo IS NULL OR RequestNo = N'' THEN N'IZN-' + CAST(YEAR(CreationTime) AS nvarchar(4)) + N'-' + RIGHT(N'00000' + CAST( (SELECT COALESCE(MAX(TRY_CAST(RIGHT(x.RequestNo, 5) AS int)), 0) + 1 FROM Hr_T_LeaveRequest x WHERE (x.TenantId = @TenantId OR (x.TenantId IS NULL AND @TenantId IS NULL)) AND x.RequestNo LIKE N'IZN-' + CAST(YEAR(CreationTime) AS nvarchar(4)) + N'-%') AS nvarchar(10)), 5) ELSE RequestNo END WHERE Id = @RequestId; --------------------------------------------------------------------------------------------- -- Gün sayısı ve kurallar (sonuçlanmış talepte yeniden doğrulanmaz) --------------------------------------------------------------------------------------------- DECLARE @TypeCode nvarchar(32), @TypeGender nvarchar(16), @MaxDaysPerYear int, @Deducts bit, @RequiresDocument bit, @AllowHalfDay bit, @ExceedAction nvarchar(16); SELECT @TypeCode = Code, @TypeGender = Gender, @MaxDaysPerYear = MaxDaysPerYear, @Deducts = DeductsFromAnnualLeave, @RequiresDocument = RequiresDocument, @AllowHalfDay = AllowHalfDay FROM Hr_T_LeaveType WHERE Id = @LeaveTypeId; -- Kolon Hr_T_LeaveType.sql'de sonradan eklenir; migrate prosedürleri tablolardan önce -- derlediği için kurulu bir veritabanında doğrudan başvuru derlemeyi düşürürdü. EXEC sp_executesql N'SELECT @Action = BalanceExceedAction FROM Hr_T_LeaveType WHERE Id = @Id', N'@Action nvarchar(16) OUTPUT, @Id uniqueidentifier', @Action = @ExceedAction OUTPUT, @Id = @LeaveTypeId; DECLARE @Days decimal(9, 1) = dbo.Hr_F_LeaveWorkDays(@TenantId, CAST(@StartDate AS date), CAST(@EndDate AS date)); IF @IsHalfDay = 1 SET @Days = CASE WHEN @Days > 0.5 THEN 0.5 ELSE @Days END; UPDATE Hr_T_LeaveRequest SET Days = @Days WHERE Id = @RequestId; IF @Status IS NULL OR @Status IN (@StatusStart, @StatusManager, @StatusHr) BEGIN IF @TypeGender IS NOT NULL AND @TypeGender <> N'All' AND COALESCE(@Gender, N'') <> @TypeGender THROW 51111, N'Bu izin türü çalışanın cinsiyetine uygun değil.', 1; IF @IsHalfDay = 1 AND @AllowHalfDay = 0 THROW 51112, N'Bu izin türü yarım gün kullanılamaz.', 1; IF @IsHalfDay = 1 AND CAST(@StartDate AS date) <> CAST(@EndDate AS date) THROW 51113, N'Yarım gün izinde başlangıç ve bitiş aynı gün olmalıdır.', 1; IF @RequiresDocument = 1 AND (@Document IS NULL OR @Document = N'') THROW 51114, N'Bu izin türü için belge (rapor vb.) eklenmelidir.', 1; IF @Days <= 0 THROW 51115, N'Seçilen tarih aralığında çalışma günü yok.', 1; -- Çakışma: reddedilmemiş başka bir talep ya da talebe bağlı olmayan bir istirahat aynı -- güne düşüyorsa. Aynı günün iki farklı yarısı birlikte alınabilir. IF EXISTS ( SELECT 1 FROM Hr_T_LeaveRequest o WHERE o.EmployeeId = @EmployeeId AND o.Id <> @RequestId AND o.IsDeleted = 0 AND (o.ApprovalStatus IS NULL OR o.ApprovalStatus <> @StatusRejected) AND CAST(o.StartDate AS date) <= CAST(@EndDate AS date) AND CAST(o.EndDate AS date) >= CAST(@StartDate AS date) AND NOT (o.IsHalfDay = 1 AND @IsHalfDay = 1 AND COALESCE(o.HalfDayPart, N'Morning') <> COALESCE(@HalfDayPart, N'Morning'))) OR EXISTS ( SELECT 1 FROM Adm_T_UserSickLeave s WHERE s.UserId = @EmployeeId AND s.IsDeleted = 0 AND (@SickLeaveId IS NULL OR s.Id <> @SickLeaveId) AND NOT EXISTS (SELECT 1 FROM Hr_T_LeaveRequest l WHERE l.SickLeaveId = s.Id AND l.IsDeleted = 0) AND CAST(s.StartDate AS date) <= CAST(@EndDate AS date) AND CAST(s.EndDate AS date) >= CAST(@StartDate AS date)) THROW 51116, N'Bu tarihlerle çakışan başka bir izin ya da rapor kaydı var.', 1; DECLARE @Warning nvarchar(512) = NULL; IF @Deducts = 1 BEGIN DECLARE @Remaining decimal(9, 1) = COALESCE((SELECT RemainingDays FROM Hr_V_LeaveSummary WHERE Id = @EmployeeId), 0); DECLARE @OtherPending decimal(9, 1) = COALESCE(( SELECT SUM(o.Days) FROM Hr_T_LeaveRequest o INNER JOIN Hr_T_LeaveType ot ON ot.Id = o.LeaveTypeId WHERE o.EmployeeId = @EmployeeId AND o.Id <> @RequestId AND o.IsDeleted = 0 AND ot.DeductsFromAnnualLeave = 1 AND (o.ApprovalStatus IS NULL OR o.ApprovalStatus IN (@StatusStart, @StatusManager, @StatusHr))), 0); IF @Days > @Remaining - @OtherPending SET @Warning = CONCAT(N'Yıllık izin bakiyesi yetersiz: kullanılabilir ', FORMAT(@Remaining - @OtherPending, '0.#'), N' gün, talep ', FORMAT(@Days, '0.#'), N' gün.'); END IF @Warning IS NULL AND @MaxDaysPerYear IS NOT NULL BEGIN DECLARE @UsedThisYear decimal(9, 1) = COALESCE(( SELECT SUM(o.Days) FROM Hr_T_LeaveRequest o WHERE o.EmployeeId = @EmployeeId AND o.Id <> @RequestId AND o.IsDeleted = 0 AND o.LeaveTypeId = @LeaveTypeId AND YEAR(o.StartDate) = YEAR(@StartDate) AND (o.ApprovalStatus IS NULL OR o.ApprovalStatus <> @StatusRejected)), 0); IF @UsedThisYear + @Days > @MaxDaysPerYear SET @Warning = CONCAT(N'Bu izin türünün yıllık sınırı ', @MaxDaysPerYear, N' gün; bu yıl kullanılan/bekleyen ', FORMAT(@UsedThisYear, '0.#'), N' gün, talep ', FORMAT(@Days, '0.#'), N' gün.'); END IF @Warning IS NOT NULL AND COALESCE(@ExceedAction, N'Block') = N'Block' BEGIN SET @Message = @Warning; THROW 51117, @Message, 1; END UPDATE Hr_T_LeaveRequest SET BalanceWarning = @Warning WHERE Id = @RequestId; END --------------------------------------------------------------------------------------------- -- Rapor türü = Sağlık Kartı › İstirahatler satırı (tek kayıt) --------------------------------------------------------------------------------------------- IF @TypeCode = N'Sick' AND COALESCE(@Status, N'') <> @StatusRejected BEGIN IF @SickLeaveId IS NULL OR NOT EXISTS (SELECT 1 FROM Adm_T_UserSickLeave WHERE Id = @SickLeaveId) BEGIN SET @SickLeaveId = NEWID(); INSERT INTO Adm_T_UserSickLeave (Id, TenantId, UserId, StartDate, EndDate, Days, SickLeaveType, Institution, Clinic, SickLeaveReason, CreationTime, CreatorId, IsDeleted) VALUES (@SickLeaveId, @TenantId, @EmployeeId, CAST(@StartDate AS date), CAST(@EndDate AS date), DATEDIFF(day, CAST(@StartDate AS date), CAST(@EndDate AS date)) + 1, N'Disease', NULL, NULL, LEFT(@Description, 512), GETUTCDATE(), @ActorId, 0); UPDATE Hr_T_LeaveRequest SET SickLeaveId = @SickLeaveId WHERE Id = @RequestId; END ELSE BEGIN UPDATE Adm_T_UserSickLeave SET StartDate = CAST(@StartDate AS date), EndDate = CAST(@EndDate AS date), Days = DATEDIFF(day, CAST(@StartDate AS date), CAST(@EndDate AS date)) + 1, IsDeleted = 0, DeletionTime = NULL, DeleterId = NULL, LastModificationTime = GETUTCDATE(), LastModifierId = @ActorId WHERE Id = @SickLeaveId; END END ELSE IF @SickLeaveId IS NOT NULL BEGIN -- Reddedilen ya da türü rapordan başka bir türe çevrilen talebin istirahati kalkar. UPDATE Adm_T_UserSickLeave SET IsDeleted = 1, DeletionTime = GETUTCDATE(), DeleterId = @ActorId WHERE Id = @SickLeaveId AND IsDeleted = 0; IF @TypeCode <> N'Sick' UPDATE Hr_T_LeaveRequest SET SickLeaveId = NULL WHERE Id = @RequestId; END --------------------------------------------------------------------------------------------- -- Bildirim: yalnızca bildirim taşıyan durumlara geçişte, bir kez --------------------------------------------------------------------------------------------- IF @Status IN (@StatusManager, @StatusHr, @StatusApproved, @StatusRejected) AND COALESCE(@LastStatus, N'') <> @Status BEGIN DECLARE @Recipients TABLE (Id uniqueidentifier PRIMARY KEY); IF @Status = @StatusManager INSERT INTO @Recipients (Id) SELECT Id FROM @Managers; ELSE IF @Status = @StatusHr INSERT INTO @Recipients (Id) SELECT DISTINCT u.Id FROM STRING_SPLIT(COALESCE(@HrApprover, N''), N'|') a INNER JOIN AbpUsers u ON (u.UserName = LTRIM(RTRIM(a.value)) OR u.Email = LTRIM(RTRIM(a.value))) AND u.IsDeleted = 0 AND (u.TenantId = @TenantId OR (u.TenantId IS NULL AND @TenantId IS NULL)) WHERE LTRIM(RTRIM(a.value)) <> N''; ELSE INSERT INTO @Recipients (Id) VALUES (@EmployeeId); DECLARE @Type nvarchar(64) = CASE WHEN @Status IN (@StatusManager, @StatusHr) THEN N'IzinTalebiOnayBekliyor' ELSE N'IzinTalebiSonuclandi' END; DECLARE @Subject nvarchar(128) = CASE WHEN @Status IN (@StatusManager, @StatusHr) THEN N'App.Hr.Leave.Notification.Approval.Subject' ELSE N'App.Hr.Leave.Notification.Result.Subject' END; DECLARE @Body nvarchar(128) = CASE WHEN @Status IN (@StatusManager, @StatusHr) THEN N'App.Hr.Leave.Notification.Approval.Message' ELSE N'App.Hr.Leave.Notification.Result.Message' END; DECLARE @Recipient uniqueidentifier; DECLARE recipient_cursor CURSOR LOCAL FAST_FORWARD FOR SELECT Id FROM @Recipients; OPEN recipient_cursor; FETCH NEXT FROM recipient_cursor INTO @Recipient; WHILE @@FETCH_STATUS = 0 BEGIN EXEC dbo.Hr_S_LeaveNotify @RequestId, @Recipient, @Type, @Subject, @Body, @ActorId; FETCH NEXT FROM recipient_cursor INTO @Recipient; END CLOSE recipient_cursor; DEALLOCATE recipient_cursor; UPDATE Hr_T_LeaveRequest SET LastNotifiedStatus = @Status WHERE Id = @RequestId; END END GO