کانال نوحه و مداحی ناب

Stored Procedure های کامل برای مدیریت جداول سیستم HRM

در اینجا Stored Procedure های کامل برای مدیریت جداول سیستم HRM ارائه می‌دهم:

1. Stored Procedure برای مدیریت کارکنان

1.1. گرفتن لیست کارکنان

sql
DELIMITER $$

CREATE PROCEDURE sp_GetEmployees(
    IN p_CompanyID INT,
    IN p_DepartmentID INT,
    IN p_PositionID INT,
    IN p_IsActive BOOLEAN,
    IN p_Status ENUM('Active', 'Suspended', 'Leave', 'Terminated'),
    IN p_SearchTerm NVARCHAR(100),
    IN p_Page INT,
    IN p_PageSize INT,
    OUT p_TotalRecords INT,
    OUT p_TotalPages INT
)
BEGIN
    -- محاسبه رکوردهای کل
    SELECT COUNT(*) INTO p_TotalRecords
    FROM Employees e
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND (p_PositionID IS NULL OR e.PositionID = p_PositionID)
    AND (p_IsActive IS NULL OR e.IsActive = p_IsActive)
    AND (p_Status IS NULL OR e.Status = p_Status)
    AND (p_SearchTerm IS NULL OR 
         e.FirstName LIKE CONCAT('%', p_SearchTerm, '%') OR 
         e.LastName LIKE CONCAT('%', p_SearchTerm, '%') OR
         e.EmployeeCode LIKE CONCAT('%', p_SearchTerm, '%') OR
         e.NationalCode LIKE CONCAT('%', p_SearchTerm, '%'));
    
    SET p_TotalPages = CEIL(p_TotalRecords / p_PageSize);
    
    -- گرفتن لیست کارکنان
    SELECT 
        e.EmployeeID,
        e.EmployeeCode,
        e.NationalCode,
        e.FirstName,
        e.LastName,
        e.FatherName,
        e.BirthDate,
        e.Gender,
        e.MaritalStatus,
        e.PersonalMobile,
        e.WorkEmail,
        e.DepartmentID,
        d.DepartmentName,
        e.PositionID,
        p.PositionTitle,
        e.EmploymentType,
        e.EmploymentDate,
        e.EmploymentEndDate,
        e.BasicSalary,
        e.IsActive,
        e.Status,
        e.ProfileImage,
        DATEDIFF(CURDATE(), e.EmploymentDate) as EmploymentDays,
        TIMESTAMPDIFF(YEAR, e.BirthDate, CURDATE()) as Age,
        ws.ShiftName,
        CONCAT(mgr.FirstName, ' ', mgr.LastName) as ManagerName
    FROM Employees e
    LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID
    LEFT JOIN Positions p ON e.PositionID = p.PositionID
    LEFT JOIN WorkShifts ws ON e.WorkShiftID = ws.ShiftID
    LEFT JOIN Employees mgr ON d.ManagerID = mgr.EmployeeID
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND (p_PositionID IS NULL OR e.PositionID = p_PositionID)
    AND (p_IsActive IS NULL OR e.IsActive = p_IsActive)
    AND (p_Status IS NULL OR e.Status = p_Status)
    AND (p_SearchTerm IS NULL OR 
         e.FirstName LIKE CONCAT('%', p_SearchTerm, '%') OR 
         e.LastName LIKE CONCAT('%', p_SearchTerm, '%') OR
         e.EmployeeCode LIKE CONCAT('%', p_SearchTerm, '%') OR
         e.NationalCode LIKE CONCAT('%', p_SearchTerm, '%'))
    ORDER BY e.FirstName, e.LastName
    LIMIT p_PageSize OFFSET (p_Page - 1) * p_PageSize;
END$$

DELIMITER ;

1.2. گرفتن اطلاعات کامل یک کارمند

sql
DELIMITER $$

CREATE PROCEDURE sp_GetEmployeeInfo(
    IN p_EmployeeID INT
)
BEGIN
    -- اطلاعات اصلی کارمند
    SELECT 
        e.*,
        d.DepartmentName,
        p.PositionTitle,
        ws.ShiftName,
        CONCAT(mgr.FirstName, ' ', mgr.LastName) as ManagerName,
        TIMESTAMPDIFF(YEAR, e.BirthDate, CURDATE()) as Age,
        DATEDIFF(CURDATE(), e.EmploymentDate) as EmploymentDays
    FROM Employees e
    LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID
    LEFT JOIN Positions p ON e.PositionID = p.PositionID
    LEFT JOIN WorkShifts ws ON e.WorkShiftID = ws.ShiftID
    LEFT JOIN Employees mgr ON d.ManagerID = mgr.EmployeeID
    WHERE e.EmployeeID = p_EmployeeID;
    
    -- سوابق تحصیلی
    SELECT 
        ed.EducationID,
        ed.EducationLevel,
        ed.FieldOfStudy,
        ed.University,
        ed.StartDate,
        ed.EndDate,
        ed.GPA,
        ed.IsCurrent,
        ed.Description,
        ed.DocumentFile
    FROM Educations ed
    WHERE ed.EmployeeID = p_EmployeeID
    ORDER BY ed.StartDate DESC;
    
    -- سوابق کاری
    SELECT 
        we.ExperienceID,
        we.CompanyName,
        we.JobTitle,
        we.StartDate,
        we.EndDate,
        we.IsCurrent,
        we.Salary,
        we.LeaveReason,
        we.Achievements
    FROM WorkExperiences we
    WHERE we.EmployeeID = p_EmployeeID
    ORDER BY we.StartDate DESC;
    
    -- مهارت‌ها
    SELECT 
        es.EmployeeSkillID,
        s.SkillID,
        s.SkillName,
        s.SkillCategory,
        es.ProficiencyLevel,
        es.YearsOfExperience,
        es.CertificateFile
    FROM EmployeeSkills es
    INNER JOIN Skills s ON es.SkillID = s.SkillID
    WHERE es.EmployeeID = p_EmployeeID
    ORDER BY s.SkillCategory, s.SkillName;
    
    -- دوره‌های آموزشی
    SELECT 
        et.EmployeeTrainingID,
        t.TrainingName,
        t.TrainingType,
        t.Provider,
        t.StartDate,
        t.EndDate,
        et.EnrollmentDate,
        et.CompletionDate,
        et.Status,
        et.Score,
        et.CertificateFile
    FROM EmployeeTrainings et
    INNER JOIN Trainings t ON et.TrainingID = t.TrainingID
    WHERE et.EmployeeID = p_EmployeeID
    ORDER BY et.EnrollmentDate DESC;
END$$

DELIMITER ;

1.3. ایجاد کارمند جدید

sql
DELIMITER $$

CREATE PROCEDURE sp_CreateEmployee(
    IN p_CompanyID INT,
    IN p_EmployeeCode VARCHAR(20),
    IN p_NationalCode VARCHAR(10),
    IN p_FirstName NVARCHAR(50),
    IN p_LastName NVARCHAR(50),
    IN p_FatherName NVARCHAR(50),
    IN p_BirthDate DATE,
    IN p_BirthPlace NVARCHAR(100),
    IN p_Gender ENUM('Male', 'Female'),
    IN p_MaritalStatus ENUM('Single', 'Married', 'Divorced', 'Widowed'),
    IN p_MilitaryStatus ENUM('Exempt', 'Completed', 'EducationalExempt', 'NotApplicable'),
    IN p_InsuranceNumber VARCHAR(20),
    IN p_PersonalMobile VARCHAR(11),
    IN p_WorkEmail VARCHAR(100),
    IN p_Address TEXT,
    IN p_DepartmentID INT,
    IN p_PositionID INT,
    IN p_EmploymentType ENUM('Permanent', 'Contractual', 'Temporary', 'ProjectBased'),
    IN p_EmploymentDate DATE,
    IN p_WorkShiftID INT,
    IN p_BasicSalary DECIMAL(12,2),
    IN p_BankName NVARCHAR(100),
    IN p_BankAccountNumber VARCHAR(30),
    IN p_ProfileImage VARCHAR(255),
    IN p_CreatedBy INT,
    OUT p_EmployeeID INT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE v_Count INT DEFAULT 0;
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در ایجاد کارمند';
        SET p_EmployeeID = -1;
    END;
    
    START TRANSACTION;
    
    -- بررسی تکراری نبودن کد کارمند
    SELECT COUNT(*) INTO v_Count 
    FROM Employees 
    WHERE CompanyID = p_CompanyID AND EmployeeCode = p_EmployeeCode;
    
    IF v_Count > 0 THEN
        SET p_ResultMessage = 'کد کارمند تکراری است';
        SET p_EmployeeID = -1;
        ROLLBACK;
    ELSE
        -- بررسی تکراری نبودن کد ملی
        IF p_NationalCode IS NOT NULL THEN
            SELECT COUNT(*) INTO v_Count 
            FROM Employees 
            WHERE CompanyID = p_CompanyID AND NationalCode = p_NationalCode;
            
            IF v_Count > 0 THEN
                SET p_ResultMessage = 'کد ملی تکراری است';
                SET p_EmployeeID = -1;
                ROLLBACK;
            ELSE
                -- ایجاد کارمند
                INSERT INTO Employees (
                    CompanyID, EmployeeCode, NationalCode, FirstName, LastName,
                    FatherName, BirthDate, BirthPlace, Gender, MaritalStatus,
                    MilitaryStatus, InsuranceNumber, PersonalMobile, WorkEmail,
                    Address, DepartmentID, PositionID, EmploymentType, EmploymentDate,
                    WorkShiftID, BasicSalary, BankName, BankAccountNumber, ProfileImage,
                    CreatedBy
                ) VALUES (
                    p_CompanyID, p_EmployeeCode, p_NationalCode, p_FirstName, p_LastName,
                    p_FatherName, p_BirthDate, p_BirthPlace, p_Gender, p_MaritalStatus,
                    p_MilitaryStatus, p_InsuranceNumber, p_PersonalMobile, p_WorkEmail,
                    p_Address, p_DepartmentID, p_PositionID, p_EmploymentType, p_EmploymentDate,
                    p_WorkShiftID, p_BasicSalary, p_BankName, p_BankAccountNumber, p_ProfileImage,
                    p_CreatedBy
                );
                
                SET p_EmployeeID = LAST_INSERT_ID();
                SET p_ResultMessage = 'کارمند با موفقیت ایجاد شد';
                COMMIT;
            END IF;
        ELSE
            -- ایجاد کارمند بدون کد ملی
            INSERT INTO Employees (
                CompanyID, EmployeeCode, NationalCode, FirstName, LastName,
                FatherName, BirthDate, BirthPlace, Gender, MaritalStatus,
                MilitaryStatus, InsuranceNumber, PersonalMobile, WorkEmail,
                Address, DepartmentID, PositionID, EmploymentType, EmploymentDate,
                WorkShiftID, BasicSalary, BankName, BankAccountNumber, ProfileImage,
                CreatedBy
            ) VALUES (
                p_CompanyID, p_EmployeeCode, p_NationalCode, p_FirstName, p_LastName,
                p_FatherName, p_BirthDate, p_BirthPlace, p_Gender, p_MaritalStatus,
                p_MilitaryStatus, p_InsuranceNumber, p_PersonalMobile, p_WorkEmail,
                p_Address, p_DepartmentID, p_PositionID, p_EmploymentType, p_EmploymentDate,
                p_WorkShiftID, p_BasicSalary, p_BankName, p_BankAccountNumber, p_ProfileImage,
                p_CreatedBy
            );
            
            SET p_EmployeeID = LAST_INSERT_ID();
            SET p_ResultMessage = 'کارمند با موفقیت ایجاد شد';
            COMMIT;
        END IF;
    END IF;
END$$

DELIMITER ;

1.4. به‌روزرسانی کارمند

sql
DELIMITER $$

CREATE PROCEDURE sp_UpdateEmployee(
    IN p_EmployeeID INT,
    IN p_FirstName NVARCHAR(50),
    IN p_LastName NVARCHAR(50),
    IN p_FatherName NVARCHAR(50),
    IN p_BirthDate DATE,
    IN p_BirthPlace NVARCHAR(100),
    IN p_Gender ENUM('Male', 'Female'),
    IN p_MaritalStatus ENUM('Single', 'Married', 'Divorced', 'Widowed'),
    IN p_MilitaryStatus ENUM('Exempt', 'Completed', 'EducationalExempt', 'NotApplicable'),
    IN p_InsuranceNumber VARCHAR(20),
    IN p_PersonalMobile VARCHAR(11),
    IN p_WorkEmail VARCHAR(100),
    IN p_Address TEXT,
    IN p_DepartmentID INT,
    IN p_PositionID INT,
    IN p_EmploymentType ENUM('Permanent', 'Contractual', 'Temporary', 'ProjectBased'),
    IN p_EmploymentEndDate DATE,
    IN p_WorkShiftID INT,
    IN p_BasicSalary DECIMAL(12,2),
    IN p_BankName NVARCHAR(100),
    IN p_BankAccountNumber VARCHAR(30),
    IN p_ProfileImage VARCHAR(255),
    IN p_IsActive BOOLEAN,
    IN p_Status ENUM('Active', 'Suspended', 'Leave', 'Terminated'),
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در به‌روزرسانی کارمند';
    END;
    
    START TRANSACTION;
    
    -- به‌روزرسانی کارمند
    UPDATE Employees 
    SET 
        FirstName = p_FirstName,
        LastName = p_LastName,
        FatherName = p_FatherName,
        BirthDate = p_BirthDate,
        BirthPlace = p_BirthPlace,
        Gender = p_Gender,
        MaritalStatus = p_MaritalStatus,
        MilitaryStatus = p_MilitaryStatus,
        InsuranceNumber = p_InsuranceNumber,
        PersonalMobile = p_PersonalMobile,
        WorkEmail = p_WorkEmail,
        Address = p_Address,
        DepartmentID = p_DepartmentID,
        PositionID = p_PositionID,
        EmploymentType = p_EmploymentType,
        EmploymentEndDate = p_EmploymentEndDate,
        WorkShiftID = p_WorkShiftID,
        BasicSalary = p_BasicSalary,
        BankName = p_BankName,
        BankAccountNumber = p_BankAccountNumber,
        ProfileImage = p_ProfileImage,
        IsActive = p_IsActive,
        Status = p_Status,
        UpdatedAt = CURRENT_TIMESTAMP
    WHERE EmployeeID = p_EmployeeID;
    
    SET p_ResultMessage = 'کارمند با موفقیت به‌روزرسانی شد';
    COMMIT;
END$$

DELIMITER ;

2. Stored Procedure برای مدیریت دپارتمان‌ها

2.1. گرفتن لیست دپارتمان‌ها

sql
DELIMITER $$

CREATE PROCEDURE sp_GetDepartments(
    IN p_CompanyID INT,
    IN p_ParentDepartmentID INT,
    IN p_IsActive BOOLEAN,
    IN p_IncludeTree BOOLEAN,
    OUT p_TotalRecords INT
)
BEGIN
    -- محاسبه رکوردهای کل
    SELECT COUNT(*) INTO p_TotalRecords
    FROM Departments 
    WHERE CompanyID = p_CompanyID
    AND (p_ParentDepartmentID IS NULL OR ParentDepartmentID = p_ParentDepartmentID)
    AND (p_IsActive IS NULL OR IsActive = p_IsActive);
    
    IF p_IncludeTree = TRUE THEN
        -- ساختار درختی دپارتمان‌ها
        SELECT 
            d1.DepartmentID,
            d1.DepartmentCode,
            d1.DepartmentName,
            d1.ParentDepartmentID,
            d1.ManagerID,
            d1.CostCenter,
            d1.Location,
            d1.Phone,
            d1.Email,
            d1.IsActive,
            d1.SortOrder,
            d2.DepartmentName as ParentDepartmentName,
            CONCAT(mgr.FirstName, ' ', mgr.LastName) as ManagerName,
            COUNT(e.EmployeeID) as EmployeeCount
        FROM Departments d1
        LEFT JOIN Departments d2 ON d1.ParentDepartmentID = d2.DepartmentID
        LEFT JOIN Employees mgr ON d1.ManagerID = mgr.EmployeeID
        LEFT JOIN Employees e ON d1.DepartmentID = e.DepartmentID AND e.IsActive = 1
        WHERE d1.CompanyID = p_CompanyID
        AND (p_IsActive IS NULL OR d1.IsActive = p_IsActive)
        GROUP BY d1.DepartmentID
        ORDER BY d1.SortOrder, d1.DepartmentName;
    ELSE
        -- لیست ساده دپارتمان‌ها
        SELECT 
            d.DepartmentID,
            d.DepartmentCode,
            d.DepartmentName,
            d.ParentDepartmentID,
            d.ManagerID,
            d.CostCenter,
            d.Location,
            d.Phone,
            d.Email,
            d.IsActive,
            d.SortOrder,
            CONCAT(mgr.FirstName, ' ', mgr.LastName) as ManagerName,
            COUNT(e.EmployeeID) as EmployeeCount
        FROM Departments d
        LEFT JOIN Employees mgr ON d.ManagerID = mgr.EmployeeID
        LEFT JOIN Employees e ON d.DepartmentID = e.DepartmentID AND e.IsActive = 1
        WHERE d.CompanyID = p_CompanyID
        AND (p_ParentDepartmentID IS NULL OR d.ParentDepartmentID = p_ParentDepartmentID)
        AND (p_IsActive IS NULL OR d.IsActive = p_IsActive)
        GROUP BY d.DepartmentID
        ORDER BY d.SortOrder, d.DepartmentName;
    END IF;
END$$

DELIMITER ;

2.2. ایجاد دپارتمان جدید

sql
DELIMITER $$

CREATE PROCEDURE sp_CreateDepartment(
    IN p_CompanyID INT,
    IN p_DepartmentCode VARCHAR(20),
    IN p_DepartmentName NVARCHAR(100),
    IN p_ParentDepartmentID INT,
    IN p_ManagerID INT,
    IN p_CostCenter VARCHAR(50),
    IN p_Location NVARCHAR(200),
    IN p_Phone VARCHAR(15),
    IN p_Email VARCHAR(100),
    IN p_SortOrder INT,
    OUT p_DepartmentID INT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE v_Count INT DEFAULT 0;
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در ایجاد دپارتمان';
        SET p_DepartmentID = -1;
    END;
    
    START TRANSACTION;
    
    -- بررسی تکراری نبودن کد دپارتمان
    SELECT COUNT(*) INTO v_Count 
    FROM Departments 
    WHERE CompanyID = p_CompanyID AND DepartmentCode = p_DepartmentCode;
    
    IF v_Count > 0 THEN
        SET p_ResultMessage = 'کد دپارتمان تکراری است';
        SET p_DepartmentID = -1;
        ROLLBACK;
    ELSE
        -- ایجاد دپارتمان
        INSERT INTO Departments (
            CompanyID, DepartmentCode, DepartmentName, ParentDepartmentID,
            ManagerID, CostCenter, Location, Phone, Email, SortOrder
        ) VALUES (
            p_CompanyID, p_DepartmentCode, p_DepartmentName, p_ParentDepartmentID,
            p_ManagerID, p_CostCenter, p_Location, p_Phone, p_Email, p_SortOrder
        );
        
        SET p_DepartmentID = LAST_INSERT_ID();
        SET p_ResultMessage = 'دپارتمان با موفقیت ایجاد شد';
        COMMIT;
    END IF;
END$$

DELIMITER ;

3. Stored Procedure برای مدیریت مرخصی‌ها

3.1. گرفتن لیست مرخصی‌ها

sql
DELIMITER $$

CREATE PROCEDURE sp_GetEmployeeLeaves(
    IN p_CompanyID INT,
    IN p_EmployeeID INT,
    IN p_Status ENUM('Pending', 'Approved', 'Rejected', 'Cancelled'),
    IN p_LeaveTypeID INT,
    IN p_StartDate DATE,
    IN p_EndDate DATE,
    IN p_Page INT,
    IN p_PageSize INT,
    OUT p_TotalRecords INT,
    OUT p_TotalPages INT
)
BEGIN
    -- محاسبه رکوردهای کل
    SELECT COUNT(*) INTO p_TotalRecords
    FROM EmployeeLeaves el
    INNER JOIN Employees e ON el.EmployeeID = e.EmployeeID
    WHERE e.CompanyID = p_CompanyID
    AND (p_EmployeeID IS NULL OR el.EmployeeID = p_EmployeeID)
    AND (p_Status IS NULL OR el.Status = p_Status)
    AND (p_LeaveTypeID IS NULL OR el.LeaveTypeID = p_LeaveTypeID)
    AND (p_StartDate IS NULL OR el.StartDate >= p_StartDate)
    AND (p_EndDate IS NULL OR el.EndDate <= p_EndDate);
    
    SET p_TotalPages = CEIL(p_TotalRecords / p_PageSize);
    
    -- گرفتن لیست مرخصی‌ها
    SELECT 
        el.LeaveID,
        el.EmployeeID,
        e.EmployeeCode,
        CONCAT(e.FirstName, ' ', e.LastName) as EmployeeName,
        d.DepartmentName,
        el.LeaveTypeID,
        lt.LeaveTypeName,
        el.StartDate,
        el.EndDate,
        el.StartTime,
        el.EndTime,
        el.TotalDays,
        el.Reason,
        el.Status,
        el.RequestedAt,
        el.ApprovedBy,
        el.ApprovedAt,
        CONCAT(app.FirstName, ' ', app.LastName) as ApproverName,
        el.RejectedBy,
        el.RejectedAt,
        CONCAT(rej.FirstName, ' ', rej.LastName) as RejecterName,
        el.RejectionReason
    FROM EmployeeLeaves el
    INNER JOIN Employees e ON el.EmployeeID = e.EmployeeID
    INNER JOIN LeaveTypes lt ON el.LeaveTypeID = lt.LeaveTypeID
    LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID
    LEFT JOIN Employees app ON el.ApprovedBy = app.EmployeeID
    LEFT JOIN Employees rej ON el.RejectedBy = rej.EmployeeID
    WHERE e.CompanyID = p_CompanyID
    AND (p_EmployeeID IS NULL OR el.EmployeeID = p_EmployeeID)
    AND (p_Status IS NULL OR el.Status = p_Status)
    AND (p_LeaveTypeID IS NULL OR el.LeaveTypeID = p_LeaveTypeID)
    AND (p_StartDate IS NULL OR el.StartDate >= p_StartDate)
    AND (p_EndDate IS NULL OR el.EndDate <= p_EndDate)
    ORDER BY el.StartDate DESC, el.LeaveID DESC
    LIMIT p_PageSize OFFSET (p_Page - 1) * p_PageSize;
END$$

DELIMITER ;

3.2. ایجاد درخواست مرخصی

sql
DELIMITER $$

CREATE PROCEDURE sp_CreateLeaveRequest(
    IN p_EmployeeID INT,
    IN p_LeaveTypeID INT,
    IN p_StartDate DATE,
    IN p_EndDate DATE,
    IN p_StartTime TIME,
    IN p_EndTime TIME,
    IN p_Reason TEXT,
    IN p_ContactDuringLeave VARCHAR(100),
    IN p_RequestedBy INT,
    OUT p_LeaveID INT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE v_TotalDays DECIMAL(4,2);
    DECLARE v_AvailableBalance DECIMAL(4,2);
    DECLARE v_Year INT;
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در ایجاد درخواست مرخصی';
        SET p_LeaveID = -1;
    END;
    
    START TRANSACTION;
    
    -- محاسبه تعداد روزهای مرخصی
    IF p_StartTime IS NULL AND p_EndTime IS NULL THEN
        -- مرخصی روز کامل
        SET v_TotalDays = DATEDIFF(p_EndDate, p_StartDate) + 1;
    ELSE
        -- مرخصی ساعتی
        SET v_TotalDays = (DATEDIFF(p_EndDate, p_StartDate) + 1) * 0.5; -- فرض: نیم روز
    END IF;
    
    -- بررسی موجودی مرخصی
    SET v_Year = YEAR(p_StartDate);
    
    SELECT RemainingBalance INTO v_AvailableBalance
    FROM EmployeeLeaveBalances
    WHERE EmployeeID = p_EmployeeID 
    AND LeaveTypeID = p_LeaveTypeID 
    AND Year = v_Year;
    
    IF v_AvailableBalance IS NULL THEN
        SET v_AvailableBalance = 0;
    END IF;
    
    IF v_TotalDays > v_AvailableBalance THEN
        SET p_ResultMessage = 'موجودی مرخصی کافی نیست';
        SET p_LeaveID = -1;
        ROLLBACK;
    ELSE
        -- ایجاد درخواست مرخصی
        INSERT INTO EmployeeLeaves (
            EmployeeID, LeaveTypeID, StartDate, EndDate, StartTime, EndTime,
            TotalDays, Reason, ContactDuringLeave, RequestedBy
        ) VALUES (
            p_EmployeeID, p_LeaveTypeID, p_StartDate, p_EndDate, p_StartTime, p_EndTime,
            v_TotalDays, p_Reason, p_ContactDuringLeave, p_RequestedBy
        );
        
        SET p_LeaveID = LAST_INSERT_ID();
        SET p_ResultMessage = 'درخواست مرخصی با موفقیت ایجاد شد';
        COMMIT;
    END IF;
END$$

DELIMITER ;

3.3. تایید یا رد درخواست مرخصی

sql
DELIMITER $$

CREATE PROCEDURE sp_ApproveRejectLeave(
    IN p_LeaveID INT,
    IN p_Status ENUM('Approved', 'Rejected'),
    IN p_ApprovedBy INT,
    IN p_RejectionReason TEXT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE v_EmployeeID INT;
    DECLARE v_LeaveTypeID INT;
    DECLARE v_TotalDays DECIMAL(4,2);
    DECLARE v_Year INT;
    DECLARE v_StartDate DATE;
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در به‌روزرسانی وضعیت مرخصی';
    END;
    
    START TRANSACTION;
    
    -- گرفتن اطلاعات مرخصی
    SELECT EmployeeID, LeaveTypeID, TotalDays, StartDate
    INTO v_EmployeeID, v_LeaveTypeID, v_TotalDays, v_StartDate
    FROM EmployeeLeaves
    WHERE LeaveID = p_LeaveID;
    
    SET v_Year = YEAR(v_StartDate);
    
    IF p_Status = 'Approved' THEN
        -- به‌روزرسانی وضعیت به تایید شده
        UPDATE EmployeeLeaves 
        SET 
            Status = 'Approved',
            ApprovedBy = p_ApprovedBy,
            ApprovedAt = CURRENT_TIMESTAMP,
            RejectedBy = NULL,
            RejectedAt = NULL,
            RejectionReason = NULL
        WHERE LeaveID = p_LeaveID;
        
        -- کسر از موجودی مرخصی
        UPDATE EmployeeLeaveBalances 
        SET 
            UsedBalance = UsedBalance + v_TotalDays,
            RemainingBalance = RemainingBalance - v_TotalDays,
            UpdatedAt = CURRENT_TIMESTAMP
        WHERE EmployeeID = v_EmployeeID 
        AND LeaveTypeID = v_LeaveTypeID 
        AND Year = v_Year;
        
        SET p_ResultMessage = 'مرخصی با موفقیت تایید شد';
    ELSE
        -- به‌روزرسانی وضعیت به رد شده
        UPDATE EmployeeLeaves 
        SET 
            Status = 'Rejected',
            RejectedBy = p_ApprovedBy,
            RejectedAt = CURRENT_TIMESTAMP,
            ApprovedBy = NULL,
            ApprovedAt = NULL,
            RejectionReason = p_RejectionReason
        WHERE LeaveID = p_LeaveID;
        
        SET p_ResultMessage = 'مرخصی با موفقیت رد شد';
    END IF;
    
    COMMIT;
END$$

DELIMITER ;

4. Stored Procedure برای مدیریت حقوق و دستمزد

4.1. ایجاد دوره حقوق و دستمزد

sql
DELIMITER $$

CREATE PROCEDURE sp_CreatePayrollPeriod(
    IN p_CompanyID INT,
    IN p_PeriodName NVARCHAR(100),
    IN p_PeriodYear INT,
    IN p_PeriodMonth INT,
    IN p_StartDate DATE,
    IN p_EndDate DATE,
    IN p_PaymentDate DATE,
    IN p_CreatedBy INT,
    OUT p_PeriodID INT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE v_Count INT DEFAULT 0;
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در ایجاد دوره حقوق';
        SET p_PeriodID = -1;
    END;
    
    START TRANSACTION;
    
    -- بررسی تکراری نبودن دوره
    SELECT COUNT(*) INTO v_Count 
    FROM PayrollPeriods 
    WHERE CompanyID = p_CompanyID 
    AND PeriodYear = p_PeriodYear 
    AND PeriodMonth = p_PeriodMonth;
    
    IF v_Count > 0 THEN
        SET p_ResultMessage = 'دوره حقوق برای این ماه و سال قبلاً ایجاد شده است';
        SET p_PeriodID = -1;
        ROLLBACK;
    ELSE
        -- ایجاد دوره
        INSERT INTO PayrollPeriods (
            CompanyID, PeriodName, PeriodYear, PeriodMonth,
            StartDate, EndDate, PaymentDate, CreatedBy
        ) VALUES (
            p_CompanyID, p_PeriodName, p_PeriodYear, p_PeriodMonth,
            p_StartDate, p_EndDate, p_PaymentDate, p_CreatedBy
        );
        
        SET p_PeriodID = LAST_INSERT_ID();
        SET p_ResultMessage = 'دوره حقوق با موفقیت ایجاد شد';
        COMMIT;
    END IF;
END$$

DELIMITER ;

4.2. محاسبه حقوق کارکنان

sql
DELIMITER $$

CREATE PROCEDURE sp_CalculatePayroll(
    IN p_PeriodID INT,
    IN p_EmployeeIDs JSON,
    IN p_CalculatedBy INT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE v_EmployeeCount INT DEFAULT 0;
    DECLARE i INT DEFAULT 0;
    DECLARE v_EmployeeID INT;
    DECLARE v_BasicSalary DECIMAL(12,2);
    DECLARE v_Allowances JSON;
    DECLARE v_Deductions JSON;
    DECLARE v_Bonuses JSON;
    DECLARE v_GrossSalary DECIMAL(12,2);
    DECLARE v_TotalDeductions DECIMAL(12,2);
    DECLARE v_NetSalary DECIMAL(12,2);
    DECLARE v_WorkingDays INT DEFAULT 30;
    DECLARE v_ActualDays INT DEFAULT 30;
    DECLARE v_AbsenceDays INT DEFAULT 0;
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در محاسبه حقوق';
    END;
    
    START TRANSACTION;
    
    SET v_EmployeeCount = JSON_LENGTH(p_EmployeeIDs);
    SET i = 0;
    
    WHILE i < v_EmployeeCount DO
        SET v_EmployeeID = JSON_EXTRACT(p_EmployeeIDs, CONCAT('$[', i, ']'));
        
        -- گرفتن اطلاعات حقوق پایه
        SELECT BasicSalary INTO v_BasicSalary
        FROM Employees
        WHERE EmployeeID = v_EmployeeID AND IsActive = 1;
        
        IF v_BasicSalary IS NOT NULL THEN
            -- محاسبه روزهای غیبت
            SELECT COALESCE(SUM(TotalDays), 0) INTO v_AbsenceDays
            FROM EmployeeLeaves
            WHERE EmployeeID = v_EmployeeID
            AND Status = 'Approved'
            AND (
                (StartDate BETWEEN (SELECT StartDate FROM PayrollPeriods WHERE PeriodID = p_PeriodID) 
                 AND (SELECT EndDate FROM PayrollPeriods WHERE PeriodID = p_PeriodID))
                OR
                (EndDate BETWEEN (SELECT StartDate FROM PayrollPeriods WHERE PeriodID = p_PeriodID) 
                 AND (SELECT EndDate FROM PayrollPeriods WHERE PeriodID = p_PeriodID))
            );
            
            SET v_ActualDays = v_WorkingDays - v_AbsenceDays;
            
            -- محاسبه حقوق
            SET v_GrossSalary = (v_BasicSalary / v_WorkingDays) * v_ActualDays;
            
            -- محاسبه کسورات (نمونه)
            SET v_Deductions = JSON_OBJECT(
                'tax', v_GrossSalary * 0.1,
                'insurance', v_GrossSalary * 0.07,
                'other', 0
            );
            
            SET v_TotalDeductions = 
                JSON_EXTRACT(v_Deductions, '$.tax') +
                JSON_EXTRACT(v_Deductions, '$.insurance') +
                JSON_EXTRACT(v_Deductions, '$.other');
            
            -- محاسبه اضافه کار و پاداش (نمونه)
            SET v_Bonuses = JSON_OBJECT(
                'overtime', v_BasicSalary * 0.05,
                'performance', v_BasicSalary * 0.03,
                'other', 0
            );
            
            SET v_Allowances = JSON_OBJECT(
                'housing', 2000000,
                'transportation', 500000,
                'other', 0
            );
            
            SET v_NetSalary = v_GrossSalary - v_TotalDeductions + 
                JSON_EXTRACT(v_Bonuses, '$.overtime') +
                JSON_EXTRACT(v_Bonuses, '$.performance') +
                JSON_EXTRACT(v_Allowances, '$.housing') +
                JSON_EXTRACT(v_Allowances, '$.transportation');
            
            -- درج یا به‌روزرسانی حقوق
            INSERT INTO EmployeePayroll (
                PeriodID, EmployeeID, BasicSalary, Allowances, Deductions, Bonuses,
                GrossSalary, TotalDeductions, NetSalary, CalculatedBy, CalculatedAt
            ) VALUES (
                p_PeriodID, v_EmployeeID, v_BasicSalary, v_Allowances, v_Deductions, v_Bonuses,
                v_GrossSalary, v_TotalDeductions, v_NetSalary, p_CalculatedBy, CURRENT_TIMESTAMP
            )
            ON DUPLICATE KEY UPDATE
                BasicSalary = VALUES(BasicSalary),
                Allowances = VALUES(Allowances),
                Deductions = VALUES(Deductions),
                Bonuses = VALUES(Bonuses),
                GrossSalary = VALUES(GrossSalary),
                TotalDeductions = VALUES(TotalDeductions),
                NetSalary = VALUES(NetSalary),
                CalculatedBy = VALUES(CalculatedBy),
                CalculatedAt = VALUES(CalculatedAt);
        END IF;
        
        SET i = i + 1;
    END WHILE;
    
    -- به‌روزرسانی وضعیت دوره
    UPDATE PayrollPeriods 
    SET Status = 'Calculated' 
    WHERE PeriodID = p_PeriodID;
    
    SET p_ResultMessage = 'حقوق کارکنان با موفقیت محاسبه شد';
    COMMIT;
END$$

DELIMITER ;

5. Stored Procedure برای گزارشات

5.1. گزارش حضور و غیاب

sql
DELIMITER $$

CREATE PROCEDURE sp_GetAttendanceReport(
    IN p_CompanyID INT,
    IN p_DepartmentID INT,
    IN p_StartDate DATE,
    IN p_EndDate DATE,
    IN p_Page INT,
    IN p_PageSize INT,
    OUT p_TotalRecords INT
)
BEGIN
    -- محاسبه رکوردهای کل
    SELECT COUNT(*) INTO p_TotalRecords
    FROM Employees e
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND e.IsActive = 1;
    
    -- گزارش حضور و غیاب
    SELECT 
        e.EmployeeID,
        e.EmployeeCode,
        CONCAT(e.FirstName, ' ', e.LastName) as EmployeeName,
        d.DepartmentName,
        p.PositionTitle,
        COUNT(da.DailyAttendanceID) as WorkingDays,
        SUM(da.TotalWorkMinutes) as TotalWorkMinutes,
        SUM(da.OvertimeMinutes) as TotalOvertime,
        SUM(da.LateMinutes) as TotalLate,
        SUM(da.ShortageMinutes) as TotalShortage,
        SUM(CASE WHEN da.Status = 'Present' THEN 1 ELSE 0 END) as PresentDays,
        SUM(CASE WHEN da.Status = 'Absent' THEN 1 ELSE 0 END) as AbsentDays,
        SUM(CASE WHEN da.Status = 'Leave' THEN 1 ELSE 0 END) as LeaveDays,
        ROUND(SUM(da.TotalWorkMinutes) / 60, 2) as TotalWorkHours,
        ROUND(SUM(da.OvertimeMinutes) / 60, 2) as TotalOvertimeHours
    FROM Employees e
    LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID
    LEFT JOIN Positions p ON e.PositionID = p.PositionID
    LEFT JOIN DailyAttendance da ON e.EmployeeID = da.EmployeeID 
        AND da.AttendanceDate BETWEEN p_StartDate AND p_EndDate
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND e.IsActive = 1
    GROUP BY e.EmployeeID
    ORDER BY d.DepartmentName, e.FirstName, e.LastName
    LIMIT p_PageSize OFFSET (p_Page - 1) * p_PageSize;
END$$

DELIMITER ;

5.2. گزارش مرخصی‌ها

sql
DELIMITER $$

CREATE PROCEDURE sp_GetLeaveReport(
    IN p_CompanyID INT,
    IN p_DepartmentID INT,
    IN p_StartDate DATE,
    IN p_EndDate DATE
)
BEGIN
    SELECT 
        e.EmployeeID,
        e.EmployeeCode,
        CONCAT(e.FirstName, ' ', e.LastName) as EmployeeName,
        d.DepartmentName,
        lt.LeaveTypeName,
        COUNT(el.LeaveID) as LeaveCount,
        SUM(el.TotalDays) as TotalDays,
        MIN(el.StartDate) as FirstLeaveDate,
        MAX(el.StartDate) as LastLeaveDate
    FROM Employees e
    INNER JOIN Departments d ON e.DepartmentID = d.DepartmentID
    INNER JOIN EmployeeLeaves el ON e.EmployeeID = el.EmployeeID
    INNER JOIN LeaveTypes lt ON el.LeaveTypeID = lt.LeaveTypeID
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND el.Status = 'Approved'
    AND el.StartDate BETWEEN p_StartDate AND p_EndDate
    GROUP BY e.EmployeeID, lt.LeaveTypeID
    ORDER BY d.DepartmentName, e.FirstName, e.LastName, lt.LeaveTypeName;
    
    -- جمع‌بندی بر اساس نوع مرخصی
    SELECT 
        lt.LeaveTypeName,
        COUNT(el.LeaveID) as TotalRequests,
        SUM(el.TotalDays) as TotalDays,
        AVG(el.TotalDays) as AverageDays
    FROM EmployeeLeaves el
    INNER JOIN LeaveTypes lt ON el.LeaveTypeID = lt.LeaveTypeID
    INNER JOIN Employees e ON el.EmployeeID = e.EmployeeID
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND el.Status = 'Approved'
    AND el.StartDate BETWEEN p_StartDate AND p_EndDate
    GROUP BY lt.LeaveTypeID
    ORDER BY TotalDays DESC;
END$$

DELIMITER ;

6. نحوه فراخوانی در PHP

php
<?php
// فراخوانی Stored Procedure برای گرفتن لیست کارکنان
$companyId = 1;
$departmentId = null;
$page = 1;
$pageSize = 20;

$query = "CALL sp_GetEmployees(?, ?, ?, NULL, NULL, ?, ?, @totalRecords, @totalPages)";
$stmt = $conn->prepare($query);
$stmt->bind_param("iiiii", $companyId, $departmentId, $isActive, $page, $pageSize);
$stmt->execute();
$result = $stmt->get_result();

$employees = [];
while ($row = $result->fetch_assoc()) {
    $employees[] = $row;
}

// گرفتن خروجی‌ها
$output = $conn->query("SELECT @totalRecords as total_records, @totalPages as total_pages");
$pagination = $output->fetch_assoc();

// فراخوانی برای ایجاد کارمند جدید
$employeeData = [
    'CompanyID' => 1,
    'EmployeeCode' => 'EMP1001',
    'NationalCode' => '1234567890',
    'FirstName' => 'علی',
    'LastName' => 'محمدی',
    'DepartmentID' => 1,
    'PositionID' => 1,
    'EmploymentDate' => '2024-01-15'
];

$query = "CALL sp_CreateEmployee(?, ?, ?, ?, ?, NULL, NULL, 'Male', 'Single', NULL, NULL, NULL, NULL, NULL, ?, ?, 'Permanent', ?, NULL, 0, NULL, NULL, NULL, 1, @employeeId, @resultMessage)";
$stmt = $conn->prepare($query);
$stmt->bind_param(
    "isssssiis",
    $employeeData['CompanyID'],
    $employeeData['EmployeeCode'],
    $employeeData['NationalCode'],
    $employeeData['FirstName'],
    $employeeData['LastName'],
    $employeeData['DepartmentID'],
    $employeeData['PositionID'],
    $employeeData['EmploymentDate']
);
$stmt->execute();

// گرفتن نتیجه
$result = $conn->query("SELECT @employeeId as employee_id, @resultMessage as message");
$output = $result->fetch_assoc();

if ($output['employee_id'] > 0) {
    echo "کارمند با شناسه " . $output['employee_id'] . " ایجاد شد";
} else {
    echo "خطا: " . $output['message'];
}

// فراخوانی برای ایجاد درخواست مرخصی
$leaveData = [
    'EmployeeID' => 1,
    'LeaveTypeID' => 1,
    'StartDate' => '2024-02-01',
    'EndDate' => '2024-02-03',
    'RequestedBy' => 1
];

$query = "CALL sp_CreateLeaveRequest(?, ?, ?, ?, NULL, NULL, NULL, NULL, ?, @leaveId, @resultMessage)";
$stmt = $conn->prepare($query);
$stmt->bind_param(
    "iiiss",
    $leaveData['EmployeeID'],
    $leaveData['LeaveTypeID'],
    $leaveData['StartDate'],
    $leaveData['EndDate'],
    $leaveData['RequestedBy']
);
$stmt->execute();

$result = $conn->query("SELECT @leaveId as leave_id, @resultMessage as message");
$output = $result->fetch_assoc();
?>

این Stored Procedure های کامل تمام نیازهای اصلی برای مدیریت رکوردهای سیستم HRM را پوشش می‌دهند و می‌توانند بر اساس نیازهای خاص سازمان توسعه داده شوند.

 
کانال نوحه و مداحی ناب

نظرات (۰)
هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی