sozsoft-platform/configs/seeds/host/sql/object/Hr_S_LeaveRequestAfterSave.sql
2026-10-05 00:18:13 +03:00

363 lines
20 KiB
Transact-SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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ı, silme) 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); onaylı rapor türü Sağlık Kartı istirahatiyle eşlenir; durum değiştiyse bildirim
-- gider; puantaj yeniden hesaplanır.
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), @IsDeleted bit;
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], @IsDeleted = IsDeleted
FROM Hr_T_LeaveRequest
WHERE Id = @RequestId;
IF @EmployeeId IS NULL
RETURN;
-- Puantajı kapanmış aya düşen talep eklenemez, değiştirilemez, silinemez (eski tarihleri
-- Hr_S_LeaveRequestGuard denetler).
IF dbo.Hr_F_TimesheetPeriodClosed(@TenantId, CAST(@StartDate AS date), CAST(@EndDate AS date)) = 1
THROW 51118, N'İzin tarihleri kapalı bir puantaj dönemine düşüyor. Önce İnsan Kaynakları dönemi açmalıdır.', 1;
IF @IsDeleted = 1
BEGIN
-- Silinen talebin istirahati de kalkar; rapora denk gelen izin günleri ve puantaj yenilenir.
IF @SickLeaveId IS NOT NULL
BEGIN
UPDATE Adm_T_UserSickLeave
SET IsDeleted = 1, DeletionTime = GETUTCDATE(), DeleterId = @ActorId
WHERE Id = @SickLeaveId AND IsDeleted = 0;
EXEC dbo.Hr_S_LeaveDaysRefresh @TenantId, @EmployeeId;
END
EXEC dbo.Hr_S_TimesheetCalculate @TenantId, @EmployeeId, @StartDate, @EndDate, @ActorId;
RETURN;
END
DECLARE @Message nvarchar(1024);
---------------------------------------------------------------------------------------------
-- Çalışan ve departman müdürü
---------------------------------------------------------------------------------------------
DECLARE @EmployeeName nvarchar(256), @DepartmentId uniqueidentifier, @JobPositionId uniqueidentifier,
@Gender nvarchar(16), @HireDate date, @TerminationDate date;
SELECT @EmployeeName = CONCAT(Name, N' ', Surname), @DepartmentId = DepartmentId,
@JobPositionId = JobPositionId, @Gender = Gender,
@HireDate = CAST(HireDate AS date), @TerminationDate = CAST(TerminationDate AS date)
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, @ExceedAction = BalanceExceedAction
FROM Hr_T_LeaveType
WHERE Id = @LeaveTypeId;
-- Rapor türü kendi çalışma günlerini sayar; diğer türlerde rapora denk gelen gün izinden düşmez.
DECLARE @WorkDays decimal(9, 1) = dbo.Hr_F_LeaveWorkDays(@TenantId, CAST(@StartDate AS date), CAST(@EndDate AS date));
DECLARE @Days decimal(9, 1) = CASE WHEN @TypeCode = N'Sick' THEN @WorkDays
ELSE dbo.Hr_F_LeaveNetDays(@TenantId, @EmployeeId, CAST(@StartDate AS date), CAST(@EndDate AS date)) END;
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 @WorkDays <= 0
THROW 51115, N'Seçilen tarih aralığında çalışma günü yok.', 1;
IF (@HireDate IS NOT NULL AND CAST(@StartDate AS date) < @HireDate)
OR (@TerminationDate IS NOT NULL AND CAST(@EndDate AS date) > @TerminationDate)
THROW 51119, N'İzin tarihleri çalışanın işe giriş ve işten çıkış tarihleri arasında olmalıdır.', 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). İstirahat onayla oluşur: onay
-- bekleyen rapor talebi sağlık kartında ve puantajda görünmez, izin günlerini düşürmez.
---------------------------------------------------------------------------------------------
IF @TypeCode = N'Sick' AND @Status = @StatusApproved
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, onayı beklemeye dönen 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
---------------------------------------------------------------------------------------------
-- Rapor, çalışanın diğer izinlerinin gün sayısını değiştirir. Puantaj: talebin günleri yeniden
-- hesaplanır (onaylı izin İzinli, rapor Raporlu gün olur); tarihi değişen talebin artık
-- karşılıksız kalan eski günlerini hesap kendisi aralığa katar. İleri tarih yazılmaz.
---------------------------------------------------------------------------------------------
IF @TypeCode = N'Sick' OR @SickLeaveId IS NOT NULL
EXEC dbo.Hr_S_LeaveDaysRefresh @TenantId, @EmployeeId;
EXEC dbo.Hr_S_TimesheetCalculate @TenantId, @EmployeeId, @StartDate, @EndDate, @ActorId;
END
GO