钉钉考勤报表加班时间统计

最近公司裁员,一时兴起,写了个钉钉考勤报表加班时间统计的小工具,方便打工人被裁员时快速统计自己的加班时间。这个小工具可以填写多个连续的合同日期,同时显示两中加班工资计算方式:一种是按照配置的加班属于有效加班时间计算的加班费,另一种是按照实际下班时间作为有效的加班时间计算的加班费。

import sys
import openpyxl
from datetime import datetime
import re
import os
import time
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit, QFileDialog, QApplication

class ContractCalculator(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('加班费计算器')
        # 创建按钮
        self.folder = './考勤报表'
        self.pickFolderBtn = QPushButton('选择考勤报表文件夹路径', self)
        self.pickFolderBtn.clicked.connect(self.openFolderPicker)

        self.workDate1_sum = [] #存储表格原始日期
        self.workDate2_sum = [] #存储日期
        self.workTime_sum = [] #存储打卡信息
        self.overtimeLen_sum = [] #存储每日对应的加班时长
        self.wage_sum = [] #存储每日对应的月工资
        self.workday_overtimeLenSum = {} #工作日加班时长
        self.overtimeLenSum_6_8 = {} #周日加班时长
        self.allMoney = 0
        self.overtime_start = ""
        self.overtime_end = ""
        self.contract_inputs = []
        self.contract_info = []
        self.allMoney = 0

        for i in range(5):
            start_date = QLineEdit()
            end_date = QLineEdit()
            salary = QLineEdit()
            if i == 0:
                start_date.setText("2022-07-01")
                end_date.setText("2024-06-30")
                salary.setText("15000")
                
            self.contract_inputs.append((start_date, end_date, salary))

        self.overtime_start = QLineEdit("18:00")
        self.overtime_end = QLineEdit("20:00")

        layout = QVBoxLayout()

        # 输入框
        for i in range(5):
            hbox = QHBoxLayout()
            hbox.addWidget(QLabel(f"合同{i+1}开始日期"))
            hbox.addWidget(self.contract_inputs[i][0])
            hbox.addWidget(QLabel("结束日期"))
            hbox.addWidget(self.contract_inputs[i][1])
            hbox.addWidget(QLabel("工资"))
            hbox.addWidget(self.contract_inputs[i][2])
            layout.addLayout(hbox)
        
        overtime_hbox = QHBoxLayout()
        overtime_hbox.addWidget(QLabel("加班开始时间"))
        overtime_hbox.addWidget(self.overtime_start)
        overtime_hbox.addWidget(QLabel("加班结束时间"))
        overtime_hbox.addWidget(self.overtime_end)
        layout.addLayout(overtime_hbox)

        # 显示框
        self.result_text = QTextEdit()
        layout.addWidget(self.result_text)

        # 提示框
        note_text = QTextEdit()
        note_text.setPlainText("周末正常加班按照已经进行了调休不计入加班时长,仅计算所有上班时间晚上的加班时长")
        layout.addWidget(note_text)

        # 计算按钮
        calculate_button = QPushButton("计算")
        calculate_button.clicked.connect(self.calculate_salary)
        layout.addWidget(self.pickFolderBtn)
        layout.addWidget(calculate_button)
        self.setLayout(layout)

    def openFolderPicker(self):
        self.folder = QFileDialog.getExistingDirectory(self, "选择文件夹", "/")  # 获取用户选择的文件夹
        print("选择的文件夹:", self.folder)  # 输出选择的文件夹路径

    def calculate_salary(self):
        result = ""
        self.contract_info = []
        for start_date, end_date, salary in self.contract_inputs:
            self.contract_info.append((start_date.text(), end_date.text(), salary.text()))
            result = start_date.text() + end_date.text() + salary.text()
        
        overtime_start = self.overtime_start.text()
        overtime_end = self.overtime_end.text()

        # 编写工资计算逻辑
        self.readXlsx(self.folder)
        self.writeResult()

        self.result_text.append("生成 加班明细.xlsx")
        

    def getWageByDate(self, date = "2017-01-01"):
        check_datetime = datetime.strptime(date,'%Y-%m-%d')
        for ele in self.contract_info:
            #print(ele)
            if ele[0] == '' or ele[1] == '':
                continue
            start_datetime = datetime.strptime(ele[0],'%Y-%m-%d')
            end_datetime = datetime.strptime(ele[1],'%Y-%m-%d')
            wage = ele[2]
            start_timestamp = datetime.timestamp(start_datetime)
            end_timestamp = datetime.timestamp(end_datetime)
            check_timestamp = datetime.timestamp(check_datetime)
            # print(start_datetime, check_datetime, end_datetime, wage)
            # print(start_timestamp, check_timestamp, end_timestamp, wage)
            if start_timestamp <= check_timestamp <= end_timestamp:
                #print(f"{date} 对应的月工资是{wage}")
                return wage
        print(f"{date}不在合同日期内")
        return 0
    
    def readXlsx(self, filepath= './考勤报表'):
        files_and_folders = os.listdir(filepath)
        xlsx_files = [file for file in files_and_folders if file.endswith(".xlsx")]
        for file in xlsx_files:
            self.result_text.append(f"正在解析 {file}")
            QApplication.processEvents() 
            workDate1 = []
            workDate2 = []
            workTime = []
            overtimeLen = []
            if "打卡时间表" not in file:
                continue
            # 打开工作簿  
            workbook = openpyxl.load_workbook(filepath+"/"+file)  
            
            # 选择第一个工作表(默认通常是第一个)  
            sheet = workbook.active  
            # 获取第1行(注意:索引从1开始,不是从0开始)  
            row_data = [cell.value for cell in sheet[1]]  
            month = row_data[0].split(":")[1].strip().split(" ")[0][:-2]
            # 获取第2行(注意:索引从1开始,不是从0开始)  
            row_data = [cell.value for cell in sheet[2]]   
            # 获取第3行(注意:索引从1开始,不是从0开始)  
            row_data = [cell.value for cell in sheet[3]]  
            dayNum = 0
            for ele in row_data[6:]:
                dayNum = dayNum + 1
                datestr1 = month+ele
                datestr2 = month+"{:02d}".format(dayNum) #获取日期YYYY:MM:DD
                workDate1.append(datestr1)
                self.workDate1_sum.append(datestr1)
                #print(datestr1)
                wage = self.getWageByDate(datestr2)
                self.wage_sum.append(wage)
                if str(wage) not in self.workday_overtimeLenSum:
                    self.workday_overtimeLenSum[str(wage)] = 0
                if str(wage) not in self.overtimeLenSum_6_8:
                    self.overtimeLenSum_6_8[str(wage)] = 0
                workDate2.append(datestr2)
                self.workDate2_sum.append(datestr2)

            # 获取第4行(注意:索引从1开始,不是从0开始)  
            row_data = [cell.value for cell in sheet[4]]   
            #print("len(row_data) = ", len(row_data), row_data)
            dayNum = 0
            for ele in row_data[6:]:
                # print(f"----------------------dayNum = {dayNum}------------------------------------")
                # print(ele)
                if ele is not None:
                    #print(workDate1[dayNum], workDate2[dayNum] )
                    timePeriod=ele.replace("  \n", "~").strip()
                    matches = re.findall(r'\d{2}:\d{2}', timePeriod)
                    # print(timePeriod)
                    # print(matches)
                    last_time = matches[-1]
                    workStartTime = matches[0]
                    workEndTime = last_time
                    workTime.append(timePeriod)
                    self.workTime_sum.append(timePeriod)
                    # 要判断的日期
                    # 将输入日期转换为 datetime 对象
                    date_obj = datetime.strptime(workDate2[dayNum], '%Y-%m-%d')
                    
                    # 使用weekday()方法来获得日期是星期几(星期一为0,星期天为6)
                    day_of_week = date_obj.weekday()

                    time_format = '%H:%M'
                    time1 = datetime.strptime(workEndTime, time_format)
                    time2 = datetime.strptime(self.overtime_start.text(), time_format)
                    time3 = datetime.strptime(self.overtime_end.text(), time_format)
                    if time1 > time2:
                        time_difference = time1 - time2
                        time_difference = time_difference.total_seconds() // 60
                        overtimeLen.append(time_difference)
                        self.overtimeLen_sum.append(time_difference)
                        # print(f"{date_obj}, 星期{day_of_week}, 下班时间{workEndTime}, 加班起始时间{self.overtime_start},加班时长{time_difference}")
                        # print(f"workday_overtimeLenSum[{str(self.getWageByDate(workDate2[dayNum]))}] = {self.workday_overtimeLenSum[str(self.getWageByDate(workDate2[dayNum]))]}")
                        self.workday_overtimeLenSum[str(self.getWageByDate(workDate2[dayNum]))] = self.workday_overtimeLenSum[str(self.getWageByDate(workDate2[dayNum]))] + time_difference
                        # print(f"workday_overtimeLenSum[{str(self.getWageByDate(workDate2[dayNum]))}] = {self.workday_overtimeLenSum[str(self.getWageByDate(workDate2[dayNum]))]}")
                        if time1 > time3:
                            time_difference = time3 - time2
                            time_difference = time_difference.total_seconds() // 60
                            self.overtimeLenSum_6_8[str(self.getWageByDate(workDate2[dayNum]))] = self.overtimeLenSum_6_8[str(self.getWageByDate(workDate2[dayNum]))] + time_difference
                    else:
                        overtimeLen.append(0)
                        self.overtimeLen_sum.append(0)

                else:
                    workTime.append(ele)
                    self.workTime_sum.append(ele)
                    overtimeLen.append(0)
                    self.overtimeLen_sum.append(0)
                dayNum = dayNum + 1
        self.allMoney = 0
        for key,value in self.workday_overtimeLenSum.items():
            if key == '0':
                continue
            money = (int(key)/(21.75*8))*(value/60)*1.5
            self.allMoney = self.allMoney + money
            self.result_text.append(f"工资{key} 按实际下班时间为准,加班时长为{value}分钟 -->{value/60}小时")
            print(f"工资{key} 按实际下班时间为准,加班时长为{value}分钟 -->{value/60}小时")
        print(f"按实际下班时间为准,加班费总计:{self.allMoney}")
        self.result_text.append(f"按实际下班时间为准,加班费总计:{self.allMoney}")
        self.allMoney = 0
        for key,value in self.overtimeLenSum_6_8.items():
            if key == '0':
                continue
            money = (int(key)/(21.75*8))*(value/60)*1.5
            self.allMoney = self.allMoney + money
            self.result_text.append(f"工资{key} 晚上6点起加班时长为{value}分钟 -->{value/60}小时")
            print(f"工资{key} 按晚上8点加班结束,加班时长为{value}分钟 -->{value/60}小时")

        print(f"按加班时间到8点结束为准,加班费总计:{self.allMoney}")
        self.result_text.append(f"按加班时间到8点结束为准,加班费总计:{self.allMoney}")

    def writeResult(self, file = "./加班明细.xlsx"):
        # 创建一个Workbook对象
        wb = openpyxl.Workbook()
        # 选择要写入数据的工作表
        ws = wb.active
        # 写入数据
        title = ["日期","日期","打卡时间","加班时长","月工资"]
        ws.append(title)
        data = [self.workDate1_sum, self.workDate2_sum, self.workTime_sum, self.overtimeLen_sum, self.wage_sum]
        for row in zip(*data):
            ws.append(row)
        # ''' 
        #     计算加班工资
        #     1、工作日加班:小时加班工资标准=劳动合同规定的月工资标准÷(月计薪天数21.75×8小时)×150%;
        #     2、休息日加班:小时加班工资标准=劳动合同规定的月工资标准÷(月计薪天数21.75×8小时)×200%;
        #     3、法定休假日:小时加班工资标准=劳动合同规定的月工资标准÷(月计薪天数21.75×8小时)×300%。
        # '''
        for key,value in self.workday_overtimeLenSum.items():
            if key == '0':
                continue
            money = (int(key)/(21.75*8))*(value/60)*1.5
            row = [f"工资{key} 按实际下班时间为准,加班时长为{value}分钟 -->{value/60}小时-->加班费{money}"]
            ws.append(row)

        row = [f"按实际下班时间为准,加班费总计:{self.allMoney}"]
        ws.append(row)

        self.allMoney = 0
        for key,value in self.overtimeLenSum_6_8.items():
            if key == '0':
                continue
            money = (int(key)/(21.75*8))*(value/60)*1.5
            self.allMoney = self.allMoney + money
            row = [f"工资{key} 按晚上8点加班结束,加班时长为{value}分钟 -->{value/60}小时-->加班费{money}"]
            ws.append(row)
        row = [f"按加班时间到8点结束为准,加班费总计:{self.allMoney}"]
        ws.append(row)
        # 保存Excel文件
        wb.save(file)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    calculator = ContractCalculator()
    calculator.show()
    sys.exit(app.exec_())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值