志同道合月度工资表生成
触发条件
用户提供当月考勤表文件(宁夏志同道合牧业有限公司_打卡时间_YYYYMMDD-YYYYMMDD.xlsx),要求生成工资表。
输入文件
| 文件 | 来源 | 说明 |
|---|---|---|
| 当月考勤表 | 微信拖入 | 打卡时间_YYYYMMDD-YYYYMMDD.xlsx,Sheet名"打卡时间" |
| 上月工资表 | workspace内 | 获取员工名单、岗位、工资基数 |
输出文件
2026年X月工资表.xlsx,存放在workspace根目录。
执行步骤
Step 1: 读取考勤表,统计每人出勤天数
import openpyxl, re
wb = openpyxl.load_workbook("考勤文件.xlsx", data_only=True)
ws = wb.active
def count_attendance(row_data):
count = 0
for val in row_data[6:]: # 从第7列开始是打卡时间
if val is not None:
val_str = str(val).strip()
if re.match(r'\d{1,2}:\d{2}', val_str):
count += 1
return count
attendance = {}
for row_idx in range(5, ws.max_row + 1):
row_data = [cell.value for cell in ws[row_idx]]
name = row_data[0]
if name is None: continue
attendance[name] = count_attendance(row_data)
考勤表结构:
- 行1-2: 标题/生成时间
- 行3: 表头(姓名、考勤组、部门、工号、职位、UserId、打卡时间)
- 行4: 日期行(1, 2, 3, 六, 清明, 6, ...)
- 行5+: 员工数据,第7列起每列对应一天的打卡时间
Step 2: 构建员工工资数据
固定工资员工(8人,始终排最前)
出勤天数填0,岗位为空,不受考勤影响全额发放:
| 序号 | 姓名 | 月工资 | 基本 | 岗位 | 绩效 | 工龄 | 扣款 | 社保 |
|---|---|---|---|---|---|---|---|---|
| 1 | 马阳俊 | 6000 | 5000 | 1000 | 0 | 0 | 0 | - |
| 2 | 杨学红 | 5000 | 5000 | 0 | 0 | 0 | 0 | - |
| 3 | 马凯 | 10000 | 5000 | 3000 | 2000 | 0 | 0 | - |
| 4 | 马阳峰 | 5000 | 5000 | 0 | 0 | 0 | 0 | - |
| 5 | 马海燕 | 4000 | 4000 | 0 | 0 | 0 | 0 | 532.35 |
| 6 | 马国连 | 5000 | 5000 | 0 | 0 | 0 | 0 | - |
| 7 | 蔡玉梅 | 5000 | 5000 | 0 | 0 | 0 | 0 | - |
| 8 | 杨丽芸 | 3000 | 3000 | 0 | 0 | 0 | 0 | 532.35 |
考勤员工(从4月最终名单取工资基数)
4月名单(43人,固定8+考勤35):
| 序号 | 姓名 | 岗位 | 月工资 | 基本 | 岗位 | 绩效 | 工龄 | 扣款 |
|---|---|---|---|---|---|---|---|---|
| 9 | 丁少鹏 | 主管 | 7000 | 5000 | 1000 | 1000 | 0 | 0 |
| 10 | 赫羿钤 | 库管 | 5000 | 3000 | 1000 | 1000 | 0 | 0 |
| 11 | 马学仁 | 修理工 | 6000 | 5000 | 1000 | 0 | 0 | 0 |
| 12 | 马志强 | 接产员 | 5000 | 2000 | 3000 | 0 | 0 | 0 |
| 13 | 杨自花 | 挤奶工 | 3500 | 3000 | 500 | 100 | 0 | 0 |
| 14 | 马进兰 | 挤奶工 | 3500 | 3000 | 500 | 400 | 0 | 0 |
| 15 | 马泽莲 | 挤奶工 | 3500 | 3000 | 500 | 400 | 0 | 0 |
| 16 | 徐建玲 | 挤奶工 | 4000 | 3000 | 1000 | 300 | 0 | 0 |
| 17 | 马燕 | 挤奶工 | 4000 | 3000 | 1000 | 300 | 0 | 0 |
| 18 | 白金花 | 挤奶工 | 3500 | 3000 | 500 | 200 | 0 | 0 |
| 19 | 马建梅 | 挤奶工 | 3500 | 3000 | 500 | 200 | 0 | 0 |
| 20 | 马志远 | 挤奶工 | 3700 | 3000 | 700 | 0 | 0 | 0 |
| 21 | 段永东 | 挤奶工 | 3700 | 3000 | 700 | 0 | 0 | 0 |
| 22 | 柳春琴 | 挤奶工 | 3600 | 3000 | 600 | 0 | 0 | 0 |
| 23 | 马秀娟 | 挤奶工 | 4400 | 3000 | 1000 | 400 | 0 | 0 |
| 24 | 马龙 | 赶牛工 | 4500 | 2000 | 2000 | 500 | 0 | 0 |
| 25 | 马风伏 | 赶牛工 | 4500 | 2000 | 2500 | 0 | 0 | 0 |
| 26 | 马志珍 | 赶牛工 | 4500 | 2000 | 2500 | 0 | 0 | 0 |
| 27 | 纳国连 | 洗槽工 | 4000 | 3500 | 500 | 0 | 0 | 0 |
| 28 | 彭巧能 | 卧床工 | 4000 | 3000 | 1000 | 0 | 0 | 0 |
| 29 | 张新胜 | 兽医 | 8500 | 5000 | 3000 | 500 | 0 | 0 |
| 30 | 但哲 | 繁育主配 | 8000 | 3000 | 3000 | 2000 | 0 | 0 |
| 31 | 王兴林 | 粉料工 | 5000 | 4500 | 500 | 0 | 0 | 0 |
| 32 | 虎彦成 | 接产员 | 5000 | 2000 | 3000 | 0 | 0 | 0 |
| 33 | 李学萍 | 挤奶工 | 3600 | 2000 | 1600 | 0 | 0 | 0 |
| 34 | 马生宝 | 推料工 | 4500 | 2000 | 2500 | 0 | 0 | 0 |
| 35 | 马有花 | 犊牛饲喂 | 4500 | 2000 | 2500 | 0 | 0 | 0 |
| 36 | 周龙 | 日粮工 | 5500 | 3000 | 2500 | 0 | 0 | 0 |
| 37 | 周凤云 | 清粪工 | 5000 | 3000 | 2000 | 0 | 0 | 0 |
| 38 | 姬满梅 | 挤奶工 | 3600 | 3000 | 600 | 0 | 0 | 0 |
| 39 | 焦小玲 | 挤奶工 | 3600 | 3000 | 600 | 0 | 0 | 0 |
| 40 | 苟永刚 | 日粮工 | 5500 | 3000 | 2000 | 500 | 0 | 0 |
| 41 | 马存花 | 犊牛饲喂 | 4500 | 2000 | 2000 | 500 | 0 | 0 |
| 42 | 马志涛 | 赶牛工 | 4500 | 2000 | 2000 | 500 | 0 | 0 |
重要:生成新月份时,先从上月工资表读取最新工资基数,而非硬编码。以上为4月基准参考。
Step 3: 对比考勤与上月名单,识别变动
prev_month_names = set(上月所有员工名)
current_kq_names = set(attendance.keys())
new_employees = current_kq_names - prev_month_names # 新增
departed = prev_month_names - current_kq_names # 可能离职
- 新增员工:标黄,工资基数需人工确认
- 考勤0天的老员工:可能是离职,备注"X月未出勤",需人工确认
- 考勤0天的固定工资员工:正常,全额发放
Step 4: 确定满勤/非满勤公式
满勤判断无统一规则,每次需人工逐人确认。4月的判断(作为参考):
| 公式 | 条件(近似) |
|---|---|
满勤 =F+G+H+I-J |
出勤天数足够(通常≥28天),且企业确认按全勤发 |
非满勤 =E/30*D |
出勤不足、中途请假、新入职 |
⚠️ 挤奶工和赶牛工即使有"无公休"标注,实际仍可能按非满勤处理(如4月的杨自花28天、马龙29天等)。以企业逐人确认为准。
Step 5: 生成Excel工资表
from openpyxl.styles import Font, Border, PatternFill, Alignment, Side
# === 样式 ===
title_font = Font(name='宋体', size=14, bold=True)
date_font = Font(name='宋体', size=11)
header_font = Font(name='宋体', size=11, bold=True)
data_font = Font(name='宋体', size=11)
center_align = Alignment(horizontal='center', vertical='center')
thin_border = Border(left=Side(style='thin'), right=Side(style='thin'),
top=Side(style='thin'), bottom=Side(style='thin'))
yellow_fill = PatternFill(start_color='FFFFFF00', end_color='FFFFFF00', fill_type='solid')
# === 表结构 ===
# 行1: 标题(合并A1:Q1) "宁夏志同道合牧业有限公司X月份职工工资表"
# 行2: 日期(合并A2:Q2) "2026.X.15"
# 行3: 表头
# 行4+: 数据行(固定8人 → 考勤员工 → 新员工)
# 合计行: SUM公式
# 制表人/领导签字行
# 备注行: "挤奶工倒班制无公休;赶牛工无公休;兽医2天休;…"
# === 17列 ===
headers = ['序号','姓名','岗位','出勤天数','月工资','基本工资','岗位工资',
'绩效工资','工龄工资','扣考核罚款','应发工资合计','扣除个人社保',
'扣除个税','扣款小计','实发工资','签名','领导签字']
# === 列宽 ===
col_widths = {'A':6,'B':10,'C':13,'D':13,'E':13,'F':13,'G':13,'H':13,
'I':13,'J':13,'K':12,'L':13,'M':10,'N':13,'O':12,'P':8,'Q':10}
每行写入逻辑
for idx, emp in enumerate(all_employees, 1):
row = current_row
# A-P列写入数据值
ws.cell(row=row, column=1, value=idx)
ws.cell(row=row, column=2, value=emp["name"])
# ... 依次填入各列
# K列公式 - 关键!
if is_fixed_salary or is_full_attend:
ws.cell(row=row, column=11).value = f"=F{row}+G{row}+H{row}+I{row}-J{row}"
else:
ws.cell(row=row, column=11).value = f"=E{row}/30*D{row}"
# N列: 扣款小计(仅有社保或个税时写公式)
if emp.get("social") or emp.get("tax"):
ws.cell(row=row, column=14).value = f"=L{row}+M{row}"
# O列: 实发
ws.cell(row=row, column=15).value = f"=K{row}-N{row}"
# Q列: 备注(领导签字列,用于标注离职/请假等)
ws.cell(row=row, column=17, value=emp.get("remark"))
# 边框、对齐、新员工标黄
for c in range(1, 18):
ws.cell(row=row, column=c).border = thin_border
ws.cell(row=row, column=c).alignment = center_align
if emp["name"] in new_employee_names:
for c in range(1, 18):
ws.cell(row=row, column=c).fill = yellow_fill
合计行
data_start = 4 # 数据起始行
data_end = total_row - 1 # 数据结束行
# J列(扣考核罚款)用SUM,其余数值列用SUM
for col_idx, col_letter in enumerate(['E','F','G','H','I','J','K','L','M','N','O'], 5):
ws.cell(row=total_row, column=col_idx).value = f"=SUM({col_letter}{data_start}:{col_letter}{data_end})"
Step 6: 验证
生成后用 data_only=True 重新打开,验证:
1. 所有行K列有公式
2. 非满勤员工K值 ≈ 月工资/30×出勤天数
3. 满勤员工K值 = 基本+岗位+绩效+工龄-扣款
4. 合计行SUM公式范围正确
5. 新员工已标黄
关键注意事项
⚠️ 绩效工资 vs 扣考核罚款
| H列(绩效工资) | J列(扣考核罚款) |
|---|---|
| 含工龄性质的绩效补贴 | 仅真正的扣罚 |
| 杨自花100、马进兰400、马泽莲400等 | 通常为0 |
⚠️ 计薪基数
30天(非21.75天法定计薪天数)
⚠️ 非满勤公式
=E/30*D 中的E是月工资列、D是出勤天数列
⚠️ 社保/个税
- 固定工资员工:马海燕和杨丽芸各扣社保532.35元
- 考勤员工:目前均无社保个税,N列留空
⚠️ 人员变动处理
- 从3月到4月:白国雄→马秀娟顶替、马林/陈旦子/马海洋/穆萍离职
- 每月生成时需对比上月名单,识别增减
用户确认流程
生成后需向用户列出:
1. 新增员工:姓名、岗位(假设)、工资基数(待确认)
2. 考勤异常:0天员工是否离职、出勤显著偏低者
3. 扣款/绩效变动:如有与上月不一致的,提醒确认
等待用户修正后重新生成最终版。