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

Stored Procedure های کامل برای مدیریت جداول سیستم حضور و غیاب

حتماً! در اینجا Stored Procedure های کامل برای مدیریت جداول سیستم حضور و غیاب ارائه می‌دهم:

1. Stored Procedure برای مدیریت دستگاه‌های حضور و غیاب

1.1. گرفتن لیست دستگاه‌ها

sql
DELIMITER $$

CREATE PROCEDURE sp_GetAttendanceDevices(
    IN p_CompanyID INT,
    IN p_IsActive BOOLEAN,
    IN p_DeviceType ENUM('Fingerprint', 'FaceRecognition', 'Card', 'Mobile'),
    IN p_Page INT,
    IN p_PageSize INT,
    OUT p_TotalRecords INT
)
BEGIN
    -- محاسبه رکوردهای کل
    SELECT COUNT(*) INTO p_TotalRecords
    FROM AttendanceDevices 
    WHERE CompanyID = p_CompanyID
    AND (p_IsActive IS NULL OR IsActive = p_IsActive)
    AND (p_DeviceType IS NULL OR DeviceType = p_DeviceType);
    
    -- گرفتن لیست دستگاه‌ها
    SELECT 
        DeviceID,
        DeviceCode,
        DeviceName,
        DeviceType,
        IPAddress,
        Port,
        Location,
        IsActive,
        LastSync,
        CreatedAt,
        DATEDIFF(CURDATE(), LastSync) as DaysSinceLastSync,
        CASE 
            WHEN LastSync IS NULL THEN 'Never'
            WHEN DATEDIFF(CURDATE(), LastSync) = 0 THEN 'Today'
            WHEN DATEDIFF(CURDATE(), LastSync) = 1 THEN 'Yesterday'
            WHEN DATEDIFF(CURDATE(), LastSync) <= 7 THEN 'This Week'
            ELSE 'Older'
        END as SyncStatus
    FROM AttendanceDevices
    WHERE CompanyID = p_CompanyID
    AND (p_IsActive IS NULL OR IsActive = p_IsActive)
    AND (p_DeviceType IS NULL OR DeviceType = p_DeviceType)
    ORDER BY DeviceName
    LIMIT p_PageSize OFFSET (p_Page - 1) * p_PageSize;
END$$

DELIMITER ;

1.2. ایجاد دستگاه جدید

sql
DELIMITER $$

CREATE PROCEDURE sp_CreateAttendanceDevice(
    IN p_CompanyID INT,
    IN p_DeviceCode VARCHAR(50),
    IN p_DeviceName NVARCHAR(100),
    IN p_DeviceType ENUM('Fingerprint', 'FaceRecognition', 'Card', 'Mobile'),
    IN p_IPAddress VARCHAR(15),
    IN p_Port INT,
    IN p_Location NVARCHAR(200),
    OUT p_DeviceID 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_DeviceID = -1;
    END;
    
    START TRANSACTION;
    
    -- بررسی تکراری نبودن کد دستگاه
    SELECT COUNT(*) INTO v_Count 
    FROM AttendanceDevices 
    WHERE CompanyID = p_CompanyID AND DeviceCode = p_DeviceCode;
    
    IF v_Count > 0 THEN
        SET p_ResultMessage = 'کد دستگاه تکراری است';
        SET p_DeviceID = -1;
        ROLLBACK;
    ELSE
        -- ایجاد دستگاه
        INSERT INTO AttendanceDevices (
            CompanyID, DeviceCode, DeviceName, DeviceType,
            IPAddress, Port, Location
        ) VALUES (
            p_CompanyID, p_DeviceCode, p_DeviceName, p_DeviceType,
            p_IPAddress, p_Port, p_Location
        );
        
        SET p_DeviceID = LAST_INSERT_ID();
        SET p_ResultMessage = 'دستگاه با موفقیت ایجاد شد';
        COMMIT;
    END IF;
END$$

DELIMITER ;

2. Stored Procedure برای ثبت تردد

2.1. ثبت رکورد تردد

sql
DELIMITER $$

CREATE PROCEDURE sp_CreateAttendanceRecord(
    IN p_EmployeeCode VARCHAR(20),
    IN p_DeviceID INT,
    IN p_RecordType ENUM('CheckIn', 'CheckOut', 'BreakStart', 'BreakEnd'),
    IN p_VerifyMethod ENUM('Fingerprint', 'Card', 'Face', 'PIN'),
    IN p_Location VARCHAR(100),
    OUT p_RecordID BIGINT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE v_EmployeeID INT;
    DECLARE v_LastRecordType ENUM('CheckIn', 'CheckOut', 'BreakStart', 'BreakEnd');
    DECLARE v_LastRecordTime DATETIME;
    DECLARE v_CurrentDate DATE;
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در ثبت تردد';
        SET p_RecordID = -1;
    END;
    
    START TRANSACTION;
    
    -- پیدا کردن EmployeeID از کد کارمند
    SELECT EmployeeID INTO v_EmployeeID
    FROM Employees 
    WHERE EmployeeCode = p_EmployeeCode AND IsActive = 1;
    
    IF v_EmployeeID IS NULL THEN
        SET p_ResultMessage = 'کارمند یافت نشد';
        SET p_RecordID = -1;
        ROLLBACK;
    ELSE
        SET v_CurrentDate = CURDATE();
        
        -- پیدا کردن آخرین رکورد امروز
        SELECT RecordType, RecordDateTime INTO v_LastRecordType, v_LastRecordTime
        FROM AttendanceRecords 
        WHERE EmployeeID = v_EmployeeID 
        AND DATE(RecordDateTime) = v_CurrentDate
        ORDER BY RecordDateTime DESC 
        LIMIT 1;
        
        -- اعتبارسنجی منطقی تردد
        IF v_LastRecordType IS NOT NULL THEN
            CASE 
                WHEN p_RecordType = 'CheckIn' AND v_LastRecordType IN ('CheckIn', 'BreakStart') THEN
                    SET p_ResultMessage = 'ورود قبلاً ثبت شده است';
                    SET p_RecordID = -1;
                    ROLLBACK;
                WHEN p_RecordType = 'CheckOut' AND v_LastRecordType IN ('CheckOut', 'BreakEnd') THEN
                    SET p_ResultMessage = 'خروج قبلاً ثبت شده است';
                    SET p_RecordID = -1;
                    ROLLBACK;
                WHEN p_RecordType = 'BreakStart' AND v_LastRecordType IN ('BreakStart', 'CheckOut') THEN
                    SET p_ResultMessage = 'شروع استراحت قبلاً ثبت شده است';
                    SET p_RecordID = -1;
                    ROLLBACK;
                WHEN p_RecordType = 'BreakEnd' AND v_LastRecordType IN ('BreakEnd', 'CheckIn') THEN
                    SET p_ResultMessage = 'پایان استراحت قبلاً ثبت شده است';
                    SET p_RecordID = -1;
                    ROLLBACK;
                ELSE
                    -- ثبت رکورد
                    INSERT INTO AttendanceRecords (
                        EmployeeID, DeviceID, RecordDateTime, RecordType,
                        VerifyMethod, Location
                    ) VALUES (
                        v_EmployeeID, p_DeviceID, NOW(), p_RecordType,
                        p_VerifyMethod, p_Location
                    );
                    
                    SET p_RecordID = LAST_INSERT_ID();
                    SET p_ResultMessage = 'تردد با موفقیت ثبت شد';
                    COMMIT;
            END CASE;
        ELSE
            -- اولین رکورد روز باید CheckIn باشد
            IF p_RecordType != 'CheckIn' THEN
                SET p_ResultMessage = 'اولین تردد روز باید ورود باشد';
                SET p_RecordID = -1;
                ROLLBACK;
            ELSE
                -- ثبت رکورد
                INSERT INTO AttendanceRecords (
                    EmployeeID, DeviceID, RecordDateTime, RecordType,
                    VerifyMethod, Location
                ) VALUES (
                    v_EmployeeID, p_DeviceID, NOW(), p_RecordType,
                    p_VerifyMethod, p_Location
                );
                
                SET p_RecordID = LAST_INSERT_ID();
                SET p_ResultMessage = 'تردد با موفقیت ثبت شد';
                COMMIT;
            END IF;
        END IF;
    END IF;
END$$

DELIMITER ;

2.2. ثبت دستی تردد

sql
DELIMITER $$

CREATE PROCEDURE sp_CreateManualAttendance(
    IN p_EmployeeID INT,
    IN p_RecordDate DATE,
    IN p_RecordTime TIME,
    IN p_RecordType ENUM('CheckIn', 'CheckOut', 'BreakStart', 'BreakEnd'),
    IN p_ManualReason TEXT,
    IN p_CreatedBy INT,
    OUT p_RecordID BIGINT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE v_RecordDateTime DATETIME;
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در ثبت دستی تردد';
        SET p_RecordID = -1;
    END;
    
    START TRANSACTION;
    
    SET v_RecordDateTime = CONCAT(p_RecordDate, ' ', p_RecordTime);
    
    -- ثبت رکورد دستی
    INSERT INTO AttendanceRecords (
        EmployeeID, RecordDateTime, RecordType,
        IsManual, ManualReason, CreatedBy
    ) VALUES (
        p_EmployeeID, v_RecordDateTime, p_RecordType,
        TRUE, p_ManualReason, p_CreatedBy
    );
    
    SET p_RecordID = LAST_INSERT_ID();
    
    -- به‌روزرسانی خلاصه حضور و غیاب
    CALL sp_UpdateDailyAttendance(p_EmployeeID, p_RecordDate);
    
    SET p_ResultMessage = 'تردد دستی با موفقیت ثبت شد';
    COMMIT;
END$$

DELIMITER ;

3. Stored Procedure برای گزارش‌گیری تردد

3.1. گزارش تردد روزانه

sql
DELIMITER $$

CREATE PROCEDURE sp_GetDailyAttendanceReport(
    IN p_CompanyID INT,
    IN p_ReportDate DATE,
    IN p_DepartmentID INT,
    IN p_IncludeAbsent BOOLEAN,
    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,
        ws.ShiftName,
        ws.StartTime as ShiftStartTime,
        ws.EndTime as ShiftEndTime,
        -- زمان ورود
        (SELECT RecordTime 
         FROM AttendanceRecords 
         WHERE EmployeeID = e.EmployeeID 
         AND DATE(RecordDateTime) = p_ReportDate 
         AND RecordType = 'CheckIn'
         ORDER BY RecordDateTime ASC LIMIT 1) as CheckInTime,
        
        -- زمان خروج
        (SELECT RecordTime 
         FROM AttendanceRecords 
         WHERE EmployeeID = e.EmployeeID 
         AND DATE(RecordDateTime) = p_ReportDate 
         AND RecordType = 'CheckOut'
         ORDER BY RecordDateTime DESC LIMIT 1) as CheckOutTime,
        
        -- محاسبه تاخیر
        CASE 
            WHEN (SELECT RecordTime 
                  FROM AttendanceRecords 
                  WHERE EmployeeID = e.EmployeeID 
                  AND DATE(RecordDateTime) = p_ReportDate 
                  AND RecordType = 'CheckIn'
                  ORDER BY RecordDateTime ASC LIMIT 1) > ws.StartTime 
            THEN TIMESTAMPDIFF(MINUTE, ws.StartTime, 
                 (SELECT RecordTime 
                  FROM AttendanceRecords 
                  WHERE EmployeeID = e.EmployeeID 
                  AND DATE(RecordDateTime) = p_ReportDate 
                  AND RecordType = 'CheckIn'
                  ORDER BY RecordDateTime ASC LIMIT 1))
            ELSE 0 
        END as LateMinutes,
        
        -- محاسبه تعجیل در خروج
        CASE 
            WHEN (SELECT RecordTime 
                  FROM AttendanceRecords 
                  WHERE EmployeeID = e.EmployeeID 
                  AND DATE(RecordDateTime) = p_ReportDate 
                  AND RecordType = 'CheckOut'
                  ORDER BY RecordDateTime DESC LIMIT 1) < ws.EndTime 
            THEN TIMESTAMPDIFF(MINUTE, 
                 (SELECT RecordTime 
                  FROM AttendanceRecords 
                  WHERE EmployeeID = e.EmployeeID 
                  AND DATE(RecordDateTime) = p_ReportDate 
                  AND RecordType = 'CheckOut'
                  ORDER BY RecordDateTime DESC LIMIT 1), ws.EndTime)
            ELSE 0 
        END as EarlyDepartureMinutes,
        
        -- وضعیت حضور
        CASE 
            WHEN EXISTS (SELECT 1 FROM AttendanceRecords 
                        WHERE EmployeeID = e.EmployeeID 
                        AND DATE(RecordDateTime) = p_ReportDate) THEN 'Present'
            ELSE 'Absent'
        END as AttendanceStatus,
        
        -- مدت زمان کار
        CASE 
            WHEN (SELECT RecordTime FROM AttendanceRecords 
                  WHERE EmployeeID = e.EmployeeID 
                  AND DATE(RecordDateTime) = p_ReportDate 
                  AND RecordType = 'CheckIn' LIMIT 1) IS NOT NULL
                 AND
                 (SELECT RecordTime FROM AttendanceRecords 
                  WHERE EmployeeID = e.EmployeeID 
                  AND DATE(RecordDateTime) = p_ReportDate 
                  AND RecordType = 'CheckOut' LIMIT 1) IS NOT NULL
            THEN TIMESTAMPDIFF(MINUTE,
                 (SELECT RecordTime FROM AttendanceRecords 
                  WHERE EmployeeID = e.EmployeeID 
                  AND DATE(RecordDateTime) = p_ReportDate 
                  AND RecordType = 'CheckIn' LIMIT 1),
                 (SELECT RecordTime FROM AttendanceRecords 
                  WHERE EmployeeID = e.EmployeeID 
                  AND DATE(RecordDateTime) = p_ReportDate 
                  AND RecordType = 'CheckOut' LIMIT 1))
            ELSE 0 
        END as WorkMinutes
        
    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
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND e.IsActive = 1
    AND (p_IncludeAbsent = TRUE OR EXISTS (
        SELECT 1 FROM AttendanceRecords 
        WHERE EmployeeID = e.EmployeeID 
        AND DATE(RecordDateTime) = p_ReportDate
    ))
    ORDER BY d.DepartmentName, e.FirstName, e.LastName
    LIMIT p_PageSize OFFSET (p_Page - 1) * p_PageSize;
END$$

DELIMITER ;

3.2. گزارش تردد ماهانه

sql
DELIMITER $$

CREATE PROCEDURE sp_GetMonthlyAttendanceReport(
    IN p_CompanyID INT,
    IN p_ReportYear INT,
    IN p_ReportMonth INT,
    IN p_DepartmentID INT
)
BEGIN
    DECLARE v_StartDate DATE;
    DECLARE v_EndDate DATE;
    
    SET v_StartDate = CONCAT(p_ReportYear, '-', LPAD(p_ReportMonth, 2, '0'), '-01');
    SET v_EndDate = LAST_DAY(v_StartDate);
    
    -- گزارش خلاصه ماهانه
    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.EarlyDepartureMinutes) as TotalEarlyDeparture,
        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 = 'Late' THEN 1 ELSE 0 END) as LateDays,
        SUM(CASE WHEN da.Status = 'HalfDay' THEN 1 ELSE 0 END) as HalfDays,
        SUM(CASE WHEN da.Status = 'Leave' THEN 1 ELSE 0 END) as LeaveDays,
        SUM(CASE WHEN da.Status = 'Holiday' THEN 1 ELSE 0 END) as HolidayDays,
        
        -- میانگین‌ها
        ROUND(AVG(da.TotalWorkMinutes), 2) as AvgWorkMinutesPerDay,
        ROUND(AVG(da.LateMinutes), 2) as AvgLateMinutesPerDay,
        ROUND(AVG(da.OvertimeMinutes), 2) as AvgOvertimePerDay
        
    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 v_StartDate AND v_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;
    
    -- جمع‌بندی کلی
    SELECT 
        COUNT(DISTINCT e.EmployeeID) as TotalEmployees,
        SUM(CASE WHEN da.Status = 'Present' THEN 1 ELSE 0 END) as TotalPresentDays,
        SUM(CASE WHEN da.Status = 'Absent' THEN 1 ELSE 0 END) as TotalAbsentDays,
        ROUND(AVG(da.TotalWorkMinutes), 2) as CompanyAvgWorkMinutes,
        SUM(da.OvertimeMinutes) as TotalCompanyOvertime,
        SUM(da.LateMinutes) as TotalCompanyLate
    FROM Employees e
    LEFT JOIN DailyAttendance da ON e.EmployeeID = da.EmployeeID 
        AND da.AttendanceDate BETWEEN v_StartDate AND v_EndDate
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND e.IsActive = 1;
END$$

DELIMITER ;

4. Stored Procedure برای مدیریت خلاصه حضور و غیاب

4.1. به‌روزرسانی خلاصه روزانه

sql
DELIMITER $$

CREATE PROCEDURE sp_UpdateDailyAttendance(
    IN p_EmployeeID INT,
    IN p_AttendanceDate DATE
)
BEGIN
    DECLARE v_CheckInTime TIME;
    DECLARE v_CheckOutTime TIME;
    DECLARE v_ShiftID INT;
    DECLARE v_ShiftStart TIME;
    DECLARE v_ShiftEnd TIME;
    DECLARE v_RequiredWorkMinutes INT;
    DECLARE v_ActualWorkMinutes INT;
    DECLARE v_LateMinutes INT;
    DECLARE v_EarlyDepartureMinutes INT;
    DECLARE v_OvertimeMinutes INT;
    DECLARE v_ShortageMinutes INT;
    DECLARE v_AttendanceStatus ENUM('Present', 'Absent', 'Late', 'HalfDay', 'Holiday', 'Leave');
    
    -- گرفتن اطلاعات شیفت
    SELECT WorkShiftID INTO v_ShiftID
    FROM Employees WHERE EmployeeID = p_EmployeeID;
    
    SELECT StartTime, EndTime, TIMESTAMPDIFF(MINUTE, StartTime, EndTime) 
    INTO v_ShiftStart, v_ShiftEnd, v_RequiredWorkMinutes
    FROM WorkShifts WHERE ShiftID = v_ShiftID;
    
    -- پیدا کردن زمان ورود و خروج
    SELECT MIN(RecordTime) INTO v_CheckInTime
    FROM AttendanceRecords 
    WHERE EmployeeID = p_EmployeeID 
    AND DATE(RecordDateTime) = p_AttendanceDate
    AND RecordType = 'CheckIn';
    
    SELECT MAX(RecordTime) INTO v_CheckOutTime
    FROM AttendanceRecords 
    WHERE EmployeeID = p_EmployeeID 
    AND DATE(RecordDateTime) = p_AttendanceDate
    AND RecordType = 'CheckOut';
    
    -- محاسبه تاخیر
    IF v_CheckInTime IS NOT NULL AND v_CheckInTime > v_ShiftStart THEN
        SET v_LateMinutes = TIMESTAMPDIFF(MINUTE, v_ShiftStart, v_CheckInTime);
    ELSE
        SET v_LateMinutes = 0;
    END IF;
    
    -- محاسبه تعجیل در خروج
    IF v_CheckOutTime IS NOT NULL AND v_CheckOutTime < v_ShiftEnd THEN
        SET v_EarlyDepartureMinutes = TIMESTAMPDIFF(MINUTE, v_CheckOutTime, v_ShiftEnd);
    ELSE
        SET v_EarlyDepartureMinutes = 0;
    END IF;
    
    -- محاسبه مدت زمان کار واقعی
    IF v_CheckInTime IS NOT NULL AND v_CheckOutTime IS NOT NULL THEN
        SET v_ActualWorkMinutes = TIMESTAMPDIFF(MINUTE, v_CheckInTime, v_CheckOutTime);
        
        -- کسر زمان استراحت (فرض: 60 دقیقه)
        SET v_ActualWorkMinutes = v_ActualWorkMinutes - 60;
        
        -- محاسبه اضافه کار و کسری
        IF v_ActualWorkMinutes > v_RequiredWorkMinutes THEN
            SET v_OvertimeMinutes = v_ActualWorkMinutes - v_RequiredWorkMinutes;
            SET v_ShortageMinutes = 0;
        ELSE
            SET v_OvertimeMinutes = 0;
            SET v_ShortageMinutes = v_RequiredWorkMinutes - v_ActualWorkMinutes;
        END IF;
    ELSE
        SET v_ActualWorkMinutes = 0;
        SET v_OvertimeMinutes = 0;
        SET v_ShortageMinutes = v_RequiredWorkMinutes;
    END IF;
    
    -- تعیین وضعیت حضور
    IF v_CheckInTime IS NULL AND v_CheckOutTime IS NULL THEN
        SET v_AttendanceStatus = 'Absent';
    ELSEIF v_LateMinutes > 30 OR v_ShortageMinutes > 120 THEN
        SET v_AttendanceStatus = 'HalfDay';
    ELSEIF v_LateMinutes > 0 THEN
        SET v_AttendanceStatus = 'Late';
    ELSE
        SET v_AttendanceStatus = 'Present';
    END IF;
    
    -- درج یا به‌روزرسانی رکورد
    INSERT INTO DailyAttendance (
        EmployeeID, AttendanceDate, ShiftID,
        PlannedEntryTime, PlannedExitTime,
        ActualEntryTime, ActualExitTime,
        EntryDelay, ExitEarly,
        TotalWorkMinutes, RequiredWorkMinutes,
        OvertimeMinutes, ShortageMinutes,
        Status
    ) VALUES (
        p_EmployeeID, p_AttendanceDate, v_ShiftID,
        v_ShiftStart, v_ShiftEnd,
        v_CheckInTime, v_CheckOutTime,
        v_LateMinutes, v_EarlyDepartureMinutes,
        v_ActualWorkMinutes, v_RequiredWorkMinutes,
        v_OvertimeMinutes, v_ShortageMinutes,
        v_AttendanceStatus
    )
    ON DUPLICATE KEY UPDATE
        ActualEntryTime = VALUES(ActualEntryTime),
        ActualExitTime = VALUES(ActualExitTime),
        EntryDelay = VALUES(EntryDelay),
        ExitEarly = VALUES(ExitEarly),
        TotalWorkMinutes = VALUES(TotalWorkMinutes),
        OvertimeMinutes = VALUES(OvertimeMinutes),
        ShortageMinutes = VALUES(ShortageMinutes),
        Status = VALUES(Status);
END$$

DELIMITER ;

4.2. محاسبه خودکار خلاصه ماهانه

sql
DELIMITER $$

CREATE PROCEDURE sp_CalculateMonthlyAttendance(
    IN p_CompanyID INT,
    IN p_Year INT,
    IN p_Month INT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE v_StartDate DATE;
    DECLARE v_EndDate DATE;
    DECLARE v_CurrentDate DATE;
    DECLARE v_EmployeeID INT;
    DECLARE done INT DEFAULT FALSE;
    
    DECLARE cur_employees CURSOR FOR 
        SELECT EmployeeID 
        FROM Employees 
        WHERE CompanyID = p_CompanyID AND IsActive = 1;
    
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    SET v_StartDate = CONCAT(p_Year, '-', LPAD(p_Month, 2, '0'), '-01');
    SET v_EndDate = LAST_DAY(v_StartDate);
    SET v_CurrentDate = v_StartDate;
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در محاسبه خلاصه ماهانه';
    END;
    
    START TRANSACTION;
    
    OPEN cur_employees;
    
    -- پیمایش روی تمام کارکنان
    read_loop: LOOP
        FETCH cur_employees INTO v_EmployeeID;
        IF done THEN
            LEAVE read_loop;
        END IF;
        
        -- پیمایش روی تمام روزهای ماه
        WHILE v_CurrentDate <= v_EndDate DO
            -- به‌روزرسانی خلاصه روزانه برای هر کارمند
            CALL sp_UpdateDailyAttendance(v_EmployeeID, v_CurrentDate);
            SET v_CurrentDate = DATE_ADD(v_CurrentDate, INTERVAL 1 DAY);
        END WHILE;
        
        SET v_CurrentDate = v_StartDate; -- بازنشانی برای کارمند بعدی
    END LOOP;
    
    CLOSE cur_employees;
    
    SET p_ResultMessage = 'خلاصه حضور و غیاب ماهانه با موفقیت محاسبه شد';
    COMMIT;
END$$

DELIMITER ;

5. Stored Procedure برای مدیریت شیفت‌های کاری

5.1. گرفتن لیست شیفت‌ها

sql
DELIMITER $$

CREATE PROCEDURE sp_GetWorkShifts(
    IN p_CompanyID INT,
    IN p_IsActive BOOLEAN
)
BEGIN
    SELECT 
        ShiftID,
        ShiftName,
        ShiftCode,
        StartTime,
        EndTime,
        GracePeriodBefore,
        GracePeriodAfter,
        IsActive,
        TIMESTAMPDIFF(MINUTE, StartTime, EndTime) as DurationMinutes,
        CASE 
            WHEN StartTime < '12:00:00' THEN 'صبح'
            WHEN StartTime < '17:00:00' THEN 'عصر'
            ELSE 'شب'
        END as ShiftType
    FROM WorkShifts
    WHERE CompanyID = p_CompanyID
    AND (p_IsActive IS NULL OR IsActive = p_IsActive)
    ORDER BY StartTime;
END$$

DELIMITER ;

5.2. اختصاص شیفت به کارمند

sql
DELIMITER $$

CREATE PROCEDURE sp_AssignEmployeeShift(
    IN p_EmployeeID INT,
    IN p_ShiftID INT,
    IN p_EffectiveDate DATE,
    IN p_AssignedBy INT,
    OUT p_ResultMessage VARCHAR(500)
)
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_ResultMessage = 'خطا در اختصاص شیفت';
    END;
    
    START TRANSACTION;
    
    -- غیرفعال کردن شیفت قبلی
    UPDATE EmployeeShifts 
    SET IsActive = 0, ExpiryDate = DATE_SUB(p_EffectiveDate, INTERVAL 1 DAY)
    WHERE EmployeeID = p_EmployeeID AND IsActive = 1;
    
    -- اختصاص شیفت جدید
    INSERT INTO EmployeeShifts (
        EmployeeID, ShiftID, EffectiveDate, IsActive
    ) VALUES (
        p_EmployeeID, p_ShiftID, p_EffectiveDate, 1
    );
    
    -- به‌روزرسانی شیفت در پروفایل کارمند
    UPDATE Employees 
    SET WorkShiftID = p_ShiftID
    WHERE EmployeeID = p_EmployeeID;
    
    SET p_ResultMessage = 'شیفت با موفقیت به کارمند اختصاص داده شد';
    COMMIT;
END$$

DELIMITER ;

6. Stored Procedure برای گزارش‌های مدیریتی

6.1. گزارش تردد لحظه‌ای

sql
DELIMITER $$

CREATE PROCEDURE sp_GetLiveAttendance(
    IN p_CompanyID INT,
    IN p_DepartmentID INT
)
BEGIN
    SELECT 
        e.EmployeeID,
        e.EmployeeCode,
        CONCAT(e.FirstName, ' ', e.LastName) as EmployeeName,
        d.DepartmentName,
        p.PositionTitle,
        ar.RecordDateTime as LastPunchTime,
        ar.RecordType as LastPunchType,
        ar.DeviceID,
        ad.DeviceName,
        ad.Location as DeviceLocation,
        TIMESTAMPDIFF(MINUTE, ar.RecordDateTime, NOW()) as MinutesAgo,
        CASE 
            WHEN TIMESTAMPDIFF(MINUTE, ar.RecordDateTime, NOW()) <= 5 THEN 'Online'
            WHEN TIMESTAMPDIFF(MINUTE, ar.RecordDateTime, NOW()) <= 30 THEN 'Recent'
            ELSE 'Offline'
        END as Status
    FROM AttendanceRecords ar
    INNER JOIN (
        SELECT EmployeeID, MAX(RecordDateTime) as LatestRecord
        FROM AttendanceRecords 
        WHERE DATE(RecordDateTime) = CURDATE()
        GROUP BY EmployeeID
    ) latest ON ar.EmployeeID = latest.EmployeeID AND ar.RecordDateTime = latest.LatestRecord
    INNER JOIN Employees e ON ar.EmployeeID = e.EmployeeID
    LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID
    LEFT JOIN Positions p ON e.PositionID = p.PositionID
    LEFT JOIN AttendanceDevices ad ON ar.DeviceID = ad.DeviceID
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND e.IsActive = 1
    ORDER BY ar.RecordDateTime DESC;
END$$

DELIMITER ;

6.2. گزارش کارکنان غایب

sql
DELIMITER $$

CREATE PROCEDURE sp_GetAbsentEmployees(
    IN p_CompanyID INT,
    IN p_AbsentDate DATE,
    IN p_DepartmentID INT
)
BEGIN
    SELECT 
        e.EmployeeID,
        e.EmployeeCode,
        CONCAT(e.FirstName, ' ', e.LastName) as EmployeeName,
        d.DepartmentName,
        p.PositionTitle,
        e.PersonalMobile,
        e.WorkEmail,
        CONCAT(mgr.FirstName, ' ', mgr.LastName) as ManagerName,
        DATEDIFF(p_AbsentDate, 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 Employees mgr ON d.ManagerID = mgr.EmployeeID
    WHERE e.CompanyID = p_CompanyID
    AND (p_DepartmentID IS NULL OR e.DepartmentID = p_DepartmentID)
    AND e.IsActive = 1
    AND e.Status = 'Active'
    AND NOT EXISTS (
        SELECT 1 FROM AttendanceRecords 
        WHERE EmployeeID = e.EmployeeID 
        AND DATE(RecordDateTime) = p_AbsentDate
    )
    AND p_AbsentDate >= e.EmploymentDate
    AND (e.EmploymentEndDate IS NULL OR p_AbsentDate <= e.EmploymentEndDate)
    ORDER BY d.DepartmentName, e.FirstName, e.LastName;
END$$

DELIMITER ;

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

php
<?php
// فراخوانی برای ثبت تردد
$employeeCode = 'EMP1001';
$deviceId = 1;
$recordType = 'CheckIn';

$query = "CALL sp_CreateAttendanceRecord(?, ?, ?, 'Fingerprint', NULL, @recordId, @resultMessage)";
$stmt = $conn->prepare($query);
$stmt->bind_param("sis", $employeeCode, $deviceId, $recordType);
$stmt->execute();

$result = $conn->query("SELECT @recordId as record_id, @resultMessage as message");
$output = $result->fetch_assoc();

if ($output['record_id'] > 0) {
    echo "تردد با شناسه " . $output['record_id'] . " ثبت شد";
} else {
    echo "خطا: " . $output['message'];
}

// فراخوانی برای گزارش روزانه
$companyId = 1;
$reportDate = '2024-01-15';
$departmentId = null;
$page = 1;
$pageSize = 50;

$query = "CALL sp_GetDailyAttendanceReport(?, ?, ?, TRUE, ?, ?, @totalRecords)";
$stmt = $conn->prepare($query);
$stmt->bind_param("isiii", $companyId, $reportDate, $departmentId, $page, $pageSize);
$stmt->execute();
$result = $stmt->get_result();

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

// فراخوانی برای گزارش ماهانه
$query = "CALL sp_GetMonthlyAttendanceReport(1, 2024, 1, NULL)";
$result = $conn->query($query);

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

// رفتن به نتیجه بعدی (جمع‌بندی)
if ($conn->more_results()) {
    $conn->next_result();
    $summary = $conn->store_result()->fetch_assoc();
}

// فراخوانی برای محاسبه خلاصه ماهانه
$query = "CALL sp_CalculateMonthlyAttendance(1, 2024, 1, @resultMessage)";
$conn->query($query);
$result = $conn->query("SELECT @resultMessage as message");
$output = $result->fetch_assoc();
echo $output['message'];
?>

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

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

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

ارسال نظر

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