Compare commits

...

43 Commits
v1.1.6 ... main

Author SHA1 Message Date
69e2b47cf1 fix: 修正职级工资规则的错误管理
- 有时间无规则时返回0
- 无时间的报错放在for外
2025-06-18 23:28:21 +08:00
feb14f2400 feat: 五年1级和两年1档计算变为上一个更改年份
- 初始年份填为起始年份
- 默认继承上一个年份
- 填写晋级晋档的时候计算
2025-06-18 23:06:56 +08:00
6aede80023 fix: 修正工龄计算字段引用以确保准确性
- 在add_history函数中将工龄计算字段从“参加工作时间”更新为“入职时间”,以确保计算逻辑的准确性
- 优化历史记录更新逻辑,提升数据一致性
2025-06-14 16:16:08 +08:00
ec3782fbdd feat: 增加工龄调整津贴计算逻辑
- 在add_history函数中新增工龄调整津贴的计算逻辑,基于参加工作时间每五年进行津贴调整
- 优化历史记录的字段更新,确保包含最新的变动后时间和变动原因
2025-06-14 06:40:39 +08:00
3f0f382d49 feat: 新增变动后津贴工资字段的计算逻辑
- 修改allowance函数中的警告信息,明确指出津贴规则缺失
- 确保在津贴规则不存在时返回0,避免抛出异常
- 优化calc_history函数,新增变动后津贴工资字段的计算逻辑
2025-06-14 06:32:14 +08:00
c374dcf40c feat: (函数化history添加和计算)添加历史记录处理函数以简化数据填充和计算逻辑
- 新增add_history和calc_history函数,整合入职、晋升、晋档和晋级记录的处理逻辑
- 优化历史记录的字段更新和复杂数据计算,提升代码可读性和维护性
- 调整历史记录填充顺序,确保数据一致性和准确性
2025-06-14 06:15:09 +08:00
30867f302c feat: 添加五年1级和两年1档年份计算字段
- 在历史记录中新增五年1级年份和两年1档年份的计算逻辑
- 更新变动前时间字段的格式化处理,确保数据一致性
2025-06-14 06:05:46 +08:00
e97448fc51 fix: 优化历史记录填充逻辑,简化字段更新和计算
- 更新历史记录填充逻辑,移除冗余字段,确保只使用必要的变动后字段
- 调整工龄计算和排序逻辑,确保数据准确性
- 修复晋级记录和工资计算中的字段引用,提升代码可读性
2025-06-14 05:54:24 +08:00
2eb841639e feat: 添加format_time_ymd函数并更新历史记录时间格式
新增format_time_ymd函数用于将时间格式化为"年.月.日"形式
修改main函数中使用format_time_ymd替代原format_time处理历史记录时间
2025-06-14 04:26:35 +08:00
17d653ee7a fix: 更新历史记录填充逻辑以使用变动后字段
将历史记录填充函数中的字段从原始字段改为变动后字段,以正确反映历史记录中的最新数据。同时保留时间字段的异常处理逻辑。
2025-06-14 04:26:24 +08:00
11eb67a1d8 fix: 调整员工历史记录中身份证和姓名的赋值位置
将身份证号码和姓名的赋值操作移到循环内部,确保每条历史记录都包含正确的个人信息
2025-06-14 04:22:41 +08:00
ced4de4d4b refactor(历史记录计算): 重构历史记录计算逻辑,优化字段顺序和继承关系
调整历史记录字段顺序,将复杂计算字段移到后面
重构数据继承逻辑,简化职务和级别档次的处理
优化晋升校验规则和工资计算流程
2025-06-14 04:21:37 +08:00
d5ef9972a1 feat: 在历史记录中添加工资合计字段并优化计算逻辑
- 在变动前后工资字段中添加"工资合计"字段
- 优化晋档和晋级记录的计算逻辑,同时更新五年1级和两年1档年份
- 添加工龄计算功能
2025-06-14 03:43:17 +08:00
2b7d8ebb4e refactor(薪资计算): 重构历史薪资记录数据结构
优化历史薪资记录DataFrame的列结构,增加更多详细信息字段
统一数据填充方式,使用列名直接赋值提高可读性
2025-06-14 03:19:34 +08:00
f87b4186bb feat: 添加根据职位和级别计算津贴的函数
新增allowance函数用于根据职位、级别和时间查询对应的津贴规则
当规则不存在时会记录警告日志并返回0
2025-06-13 16:04:11 +08:00
87c4c09a2d fix: 添加缺失的职位和职级工资规则日志警告
在role_salary和level_salary函数中添加当时间参数对应的工资规则不存在时的日志警告,提高调试时的可追溯性
2025-06-13 16:02:52 +08:00
b10d9be95f refactor: 优化数据结构并添加工龄计算功能
重构all_history数据结构以存储更多变动前后信息
新增calculate_seniority函数用于计算员工工龄
移除重复的datetime导入并合并os导入
2025-06-13 15:51:13 +08:00
b0827c2829 refactor: 将花名册填充逻辑提取为独立函数
将main函数中的花名册填充逻辑提取为独立的fill_roster函数,提高代码可维护性和复用性
2025-06-12 21:51:02 +08:00
2302dc1ece docs: 在README中添加关于工资历史排序的注意事项 2025-06-12 21:36:14 +08:00
21db947816 feat: 添加津贴规则读取功能
新增read_allowance函数用于从Excel中读取津贴规则数据,并整合到全局变量Allowance中。该功能作为规则加载的一部分被集成到load_rule函数中。
2025-06-12 21:17:34 +08:00
d9b0143a05 docs: 添加README文件说明数据格式和运行规则
添加项目README文件,包含以下内容:
1. 数据格式要求(整数和日期字段规范)
2. 程序运行规则说明
3. 打包成exe文件的详细步骤
2025-06-12 21:09:13 +08:00
f77bf1d096 refactor: 重构数据读取逻辑以提升代码结构和可读性
- 将员工数据、晋升记录、职位规则和职级规则的读取逻辑封装为独立函数,增强代码的模块化
- 更新全局变量的定义,确保数据处理的一致性和清晰性
- 移除冗余代码,提升整体代码的可维护性和可读性
2025-06-12 20:16:05 +08:00
bef29011c3 fix: 添加工龄计算字段,整理代码结构(移动函数与处理的位置)
- 新增多个工具函数以处理日期、整数转换和职级分割,增强数据处理的灵活性
- 更新工龄计算逻辑,确保准确反映员工的工龄和学龄
- 移除冗余代码,提升代码可读性和维护性
2025-06-12 15:37:16 +08:00
915bd806a7 [Build 1.4.1] fix: 修正晋升记录时间处理逻辑以确保准确性
- 更新晋升记录的时间添加逻辑,确保在记录中正确计算“工资执行时间”与“任职时间”的关系
- 提升数据的准确性和一致性
2025-06-10 23:03:14 +08:00
4db6f6c10f [Build 1.4.0] fix: 修正晋升记录添加逻辑以处理缺失备注
- 更新晋升记录的添加逻辑,确保在备注缺失时不产生多余空格
- 提升数据的准确性和可读性
2025-06-10 11:54:25 +08:00
5cdb6e3d02 fix: 优化晋升信息排序逻辑以提升准确性
- 更新晋升信息的排序方式,新增对“工资执行时间”的排序,确保获取最新的职务和任职时间
- 调整相关代码以保持一致性,提升数据处理的准确性
2025-06-10 11:49:24 +08:00
8d5931db63 feat: 增强历史记录数据结构以支持晋升备注
- 在历史记录DataFrame中新增“晋升备注”字段,以存储晋升相关的附加信息
- 调整历史记录的添加逻辑,确保在记录中包含晋升备注信息
2025-06-10 11:49:11 +08:00
f00cd31e46 [Build 1.3.2] fix: 修正晋级逻辑以处理边界条件
- 更新“五年晋级”处理逻辑,增加对级别和角色限制的检查
- 确保在晋级时,级别档次的计算符合新的业务规则
2025-06-09 17:57:34 +08:00
4def3c7dc6 fix: 改进异常处理以增强时间格式验证
- 更新异常信息,包含“晋级起始”、“晋档起始”、“晋级间隔”和“晋档间隔”字段的格式错误提示
- 确保在处理数据时,能够更清晰地识别时间格式问题
2025-06-09 15:56:00 +08:00
370131c773 [Build 1.3.1] feat: 增强日期处理逻辑以支持特定年份的设置
- 对“晋档起始”和“晋级起始”字段新增处理逻辑,将有效日期转换为特定年份的起始日期
- 确保在数据处理过程中,日期字段的格式和一致性得到提升
2025-06-05 09:39:52 +08:00
f1ba0e875f refactor: 优化日期解析逻辑以提升性能
- 将逐行处理的日期解析改为批量应用,提升数据处理效率
- 确保“入职信息”和“职务变动”表中的日期字段统一使用自定义解析函数
2025-06-05 09:26:27 +08:00
af32927f5b [Build 1.3.0] feat: 添加后备学历处理逻辑
- 新增一个后备函数用于处理学历字段,确保在缺失值情况下返回有效的学历信息
- 修改Excel输出逻辑,优先使用现学历字段,提升数据的完整性和准确性
2025-06-04 16:13:09 +08:00
9c6ded38c5 feat: 添加晋升校验逻辑
- 新增晋升校验功能,通过读取“晋升校验”工作表来验证职务晋升
2025-06-04 15:34:03 +08:00
7c6bb16d89 [Build 1.2.2] fix: 添加学龄字段的整数转换处理
- 在数据处理过程中,新增对“学龄”字段的整数转换,确保数据一致性
- 提升数据的完整性和准确性
2025-06-04 15:10:50 +08:00
2cf2862f96 fix: 格式化历史记录时间字段以确保一致性
- 在保存历史记录到Excel文件时,应用时间格式化函数,确保“时间”字段的格式符合预期
- 提升数据的可读性和一致性
2025-06-03 23:01:44 +08:00
7629dae36d [Build 1.2.1] fix: 修正职位规则读取逻辑以适应新数据格式
- 修改读取晋升级别和档次的列索引,从A:B调整为B:C,以确保正确获取数据
- 确保职位规则的处理逻辑与最新的数据结构保持一致
2025-06-03 22:51:32 +08:00
36f6fd949c feat: 更新工龄计算逻辑以包含学龄信息
- 修改工龄计算公式,增加学龄字段的影响,确保计算结果更准确
- 调整相关单元格的值设置,提升数据的完整性和准确性
2025-06-03 22:18:34 +08:00
4b72174a32 feat: 更新晋档和晋级记录描述
- 修改晋档和晋级记录的描述,分别更新为“两年晋档”和“五年晋级”
- 确保在处理历史记录时,相关字段的值设置符合新的数据结构
2025-06-03 22:16:44 +08:00
018981f42f [Build 1.2.0] feat: 更新职位规则和晋升逻辑以支持新数据结构
- 修改职位规则读取逻辑,调整列索引以适应新的数据格式
- 增加晋升级别和档次的计算逻辑,确保正确处理职务晋升
- 优化异常处理,确保在职务不存在时记录警告信息
- 更新文档以反映晋升级别档次变化的记录方式
2025-06-03 12:23:25 +08:00
cdb2716064 fix: 更新职级上限规则处理逻辑以支持新职位名称的级别限制
- 调整职位规则读取逻辑,增加对级别限制的处理
- 修改列索引以适应新的数据结构
- 优化异常处理,确保在角色不存在时记录警告信息
2025-06-02 23:37:43 +08:00
cb0275485d feat: 更新晋升和晋档逻辑以反映新规则
- 更新晋档和晋级的时间计算逻辑,使用新的起始时间和间隔
- 修改了处理晋升记录的列名,确保与新规则一致
- 调整了异常处理信息,以便更清晰地指示错误来源
2025-06-02 23:18:02 +08:00
68bd515030 feat: 保存更新历史记录数据到excel
- 修改历史记录DataFrame的列名,确保与职务和工资相关字段一致
- 添加一个空的DataFrame以存储所有人员的历史记录
- 在处理每个人员的历史记录时,将其添加到总表中并保存到Excel文件
- 记录所有人员历史记录的保存状态
2025-06-02 23:00:55 +08:00
027ae0d108 fix: 修复晋升信息缺失情况
- 在处理晋升信息时,添加了对职务2和日期2的默认值设置,确保在晋升记录不足时使用初始职务和入职时间填充相关字段。
2025-06-02 22:41:13 +08:00
3 changed files with 400 additions and 198 deletions

View File

@ -35,6 +35,12 @@
晋级晋档起始时间不会进行添加级别档次变化记录。
晋升的级别档次变化记录:正数增加,负数减小。
### 工资历史的排序问题
目前的工资历史排序是按照优先工资执行时间,后就职时间。需要注意的是,工资执行时间的排序是包含年月日的,而就职时间的排序是仅包含月日的。所以在工资执行与就职时间存在互相干涉的情况下,需要注意排序的问题。
## 3. 打包成 .exe 文件
本程序使用Win7兼容的**Python 3.8.10**,需要在电脑上使用此版本,并确保打包的环境是此版本。

592
main.py
View File

@ -3,93 +3,197 @@ from openpyxl.utils import get_column_letter
from openpyxl import load_workbook
from datetime import datetime
from dateutil.relativedelta import relativedelta
import logging
from datetime import datetime
import logging, os
# 配置日志记录
logging.basicConfig(filename='log.txt', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
P_LIMIT = 6 # 最大晋升次数
# 全局变量
## 常量
P_LIMIT = 6 # 最大晋升次数
P_START = 10 # 晋升记录开始行
H_START = 15 + P_LIMIT # 历史记录开始行
# 自定义日期解析函数
NOWTIME = datetime.now()
## 通过函数读取
BaseData = pd.DataFrame()
Promote = pd.DataFrame()
Rule_Role = []
Rule_Level = []
Rule_RoleName = []
Level_Limit = pd.DataFrame()
Promote_Level = pd.DataFrame()
Promote_verify = pd.DataFrame()
Allowance = []
## 统计量
max_promote = 0
max_history = 0
# 工具函数
def custom_date_parser(x):
try:
return datetime.strptime(x, '%Y-%m-%d')
except:
return x
BaseData = pd.read_excel("原数据.xlsx", sheet_name="入职信息")
Promote = pd.read_excel("原数据.xlsx", sheet_name="职务变动") #
Level_Limit = pd.read_excel("原数据.xlsx", sheet_name="职位规则",usecols="A:B", skiprows=2, names=["limit","role"])
for index, row in BaseData.iterrows():
for col in ["出生年月","任职年月","原职时间","参加工作时间","入职时间", "二档起始", "五档起始", "日期2"]:
BaseData.at[index, col] = custom_date_parser(row[col])
for index, row in Promote.iterrows():
for col in ["任职时间","工资执行时间"]:
Promote.at[index, col] = custom_date_parser(row[col])
logging.info("人员信息加载完成")
Rule_Role = []
col = 2
while True: # 职位规则
def format_time(dt,info=""):
try:
rule = pd.read_excel("原数据.xlsx", sheet_name="职位规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}", header=None)
Rule_Role.append({
"start":rule.iloc[0,1],
"end":rule.iloc[1,1],
"rule":pd.read_excel("原数据.xlsx", sheet_name="职位规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}",skiprows=2, names=["role","salary"])
})
col += 2
return dt.strftime("%Y.%m")
except:
break
Rule_Level = []
col = 1
while True: # 职级规则
logging.warning(f"[{info}]时间格式错误:{dt}")
return dt
def format_time_ymd(dt,info):
try:
rule = pd.read_excel("原数据.xlsx", sheet_name="职级规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}", header=None)
Rule_Level.append({
"start":rule.iloc[0,1],
"end":rule.iloc[1,1],
"rule":pd.read_excel("原数据.xlsx", sheet_name="职级规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}",skiprows=2, names=["level","salary"])
})
col += 2
return dt.strftime("%Y.%m.%d")
except:
break
Rule_RoleName = []
col = 1
while True: # 名称变化
logging.warning(f"[{info}]时间格式错误:{dt}")
return dt
def to_int(x):
try:
rule = pd.read_excel("原数据.xlsx", sheet_name="名称变化",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}", header=None)
Rule_RoleName.append({
"start":rule.iloc[0,1],
"end":rule.iloc[1,1],
"rule":pd.read_excel("原数据.xlsx", sheet_name="名称变化",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}",skiprows=2)
})
col += 2
return int(x)
except:
break
return 0
logging.info("规则加载完成")
Rule_Role = sorted(Rule_Role, key=lambda x: x['start'])
Rule_Level = sorted(Rule_Level, key=lambda x: x['start'])
Rule_RoleName = sorted(Rule_RoleName, key=lambda x: x['start'])
nowtime = datetime.now()
def fallback(x):
for i in x:
if pd.notna(i) and i != '':
return i
return ''
def split_level(level:str):
try:
parts = level.split('-')
return (int(parts[0]), int(parts[1]))
except:
logging.warning(f"职级[{level}]格式错误")
return (0, 0)
raise Exception(f"职级[{level}]格式错误")
def calculate_seniority(row, year):
return year - row["参加工作时间"].year + row["工龄调增"] - row["工龄调减"] + 1
# 读取信息
def read_base_data(): # 读取员工数据
global BaseData
BaseData = pd.read_excel("原数据.xlsx", sheet_name="入职信息")
for col in ["出生年月","任职年月","原职时间","参加工作时间","入职时间", "晋档起始", "晋级起始", "日期2"]:
BaseData[col] = BaseData[col].apply(custom_date_parser)
for col in ["晋档起始", "晋级起始"]:
BaseData[col] = BaseData[col].apply(lambda x: datetime(x.year, 1, 1) if isinstance(x, datetime) else x)
BaseData["Latest_Role"] = None
BaseData["Latest_Prom"] = None
BaseData["工龄调增"] = BaseData["工龄调增"].apply(to_int)
BaseData["工龄调减"] = BaseData["工龄调减"].apply(to_int)
BaseData["学龄"] = BaseData["学龄"].apply(to_int)
BaseData["工龄"] = BaseData.apply(lambda row: NOWTIME.year-row["参加工作时间"].year+row["工龄调增"]-row["工龄调减"]+1, axis=1)
def read_promote(): # 读取晋升记录
global Promote
Promote = pd.read_excel("原数据.xlsx", sheet_name="职务变动")
for col in ["任职时间","工资执行时间"]:
Promote[col] = Promote[col].apply(custom_date_parser)
def read_rule_role(): # 读取职位规则
global Rule_Role
col = 4
while True:
try:
rule = pd.read_excel("原数据.xlsx", sheet_name="职位规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}", header=None)
Rule_Role.append({
"start":rule.iloc[0,1],
"end":rule.iloc[1,1],
"rule":pd.read_excel("原数据.xlsx", sheet_name="职位规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}",skiprows=2, names=["role","salary"])
})
col += 2
except:
break
Rule_Role = sorted(Rule_Role, key=lambda x: x['start'])
def read_rule_level(): # 读取职级规则
global Rule_Level
col = 1
while True: # 职级规则
try:
rule = pd.read_excel("原数据.xlsx", sheet_name="职级规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}", header=None)
Rule_Level.append({
"start":rule.iloc[0,1],
"end":rule.iloc[1,1],
"rule":pd.read_excel("原数据.xlsx", sheet_name="职级规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}",skiprows=2, names=["level","salary"])
})
col += 2
except:
break
Rule_Level = sorted(Rule_Level, key=lambda x: x['start'])
def read_rule_role_name(): # 读取名称变化规则
global Rule_RoleName
col = 1
while True: # 名称变化
try:
rule = pd.read_excel("原数据.xlsx", sheet_name="名称变化",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}", header=None)
Rule_RoleName.append({
"start":rule.iloc[0,1],
"end":rule.iloc[1,1],
"rule":pd.read_excel("原数据.xlsx", sheet_name="名称变化",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}",skiprows=2)
})
col += 2
except:
break
Rule_RoleName = sorted(Rule_RoleName, key=lambda x: x['start'])
def read_level_limit(): # 读取职位对应的级别限制
global Level_Limit, Promote_Level
Level_Limit_tmp = pd.read_excel("原数据.xlsx", sheet_name="职位规则", usecols="A:A", skiprows=2, names=["limit"])
Promote_Level_tmp = pd.read_excel("原数据.xlsx", sheet_name="职位规则", usecols="B:C", skiprows=2, names=["级别","档次"])
Level_Limit = {}
Promote_Level = {}
for rule in Rule_Role:
for index, row in rule["rule"].iterrows():
Level_Limit[row["role"]] = Level_Limit_tmp.iloc[index]["limit"]
Promote_Level[row["role"]] = (Promote_Level_tmp.iloc[index]["级别"], Promote_Level_tmp.iloc[index]["档次"])
def read_promote_verify(): # 读取晋升校验
global Promote_verify
Promote_verify = pd.read_excel("原数据.xlsx", sheet_name="晋升校验", usecols="A:B")
def read_allowance(): # 读取津贴
global Allowance
col = 1
while True:
try:
rule = pd.read_excel("原数据.xlsx", sheet_name="津贴规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}", header=None)
Allowance.append({
"start":rule.iloc[0,1],
"end":rule.iloc[1,1],
"rule":pd.read_excel("原数据.xlsx", sheet_name="津贴规则",usecols=f"{get_column_letter(col)}:{get_column_letter(col+1)}",skiprows=2, names=["level","salary"])
})
col += 2
except:
break
Allowance = sorted(Allowance, key=lambda x: x['start'])
def load_people():
read_base_data()
read_promote()
logging.info("人员信息加载完成")
def load_rule():
read_rule_role()
read_rule_level()
read_rule_role_name()
read_level_limit()
read_promote_verify()
read_allowance()
logging.info("规则加载完成")
# 获取配置类函数
def role_salary(role:str, time):
for rule in Rule_Role:
@ -102,6 +206,7 @@ def role_salary(role:str, time):
logging.error("空职级")
else:
logging.warning(f"职位[{role}]在[{time}]时不存在工资规则")
logging.warning(f"时间[{time}]时不存在职位工资规则")
return 0
def level_salary(level:str, time):
@ -112,33 +217,30 @@ def level_salary(level:str, time):
return tmp["salary"]
except:
logging.warning(f"职级[{level}]在[{time}]时不存在工资规则")
return 0
logging.warning(f"时间[{time}]时不存在职级工资规则")
return 0
def role_limit(role:str):
try:
tmp = Level_Limit[Level_Limit["role"] == role].iloc[0]
return tmp["limit"]
except:
if role in Level_Limit.keys():
return Level_Limit[role]
else:
logging.warning(f"职位[{role}]不存在职级上限规则")
return -1
def format_time(dt,info):
try:
return dt.strftime("%Y.%m")
except:
logging.warning(f"[{info}]时间格式错误:{dt}")
return dt
def allowance(role:str, level:int, time):
for rule in Allowance:
if rule["start"] <= time <= rule["end"]:
try:
tmp = rule["rule"][rule["rule"]["level"] == f"{role}-{level}"].iloc[0]
return tmp["salary"]
except:
logging.warning(f"组合[{role}-{level}]在[{time}]时不存在津贴规则")
return 0
logging.warning(f"时间[{time}]时不存在津贴规则")
return 0
def to_int(x):
try:
return int(x)
except:
return 0
BaseData["工龄调增"] = BaseData["工龄调增"].apply(to_int)
BaseData["工龄调减"] = BaseData["工龄调减"].apply(to_int)
max_promote = 0
max_history = 0
# 填充类辅助函数
def fill_basic_info(ws, row):# 填充基本信息
ws.cell(row=2, column=1, value=f"部门:{row['部门']} 职务:{row['职务']}")
@ -172,135 +274,229 @@ def fill_prompt_info(ws, promote):# 填充晋升信息
ws.cell(row=P_START+index, column=2, value=prow["变动批注"])
ws.cell(row=P_START+index, column=3, value=""+prow["新职务"])
def add_history(History_pd, row, promote):
# 添加入职记录
History_pd.loc[len(History_pd), ["变动后时间","变动后职务","变动原因","变动后级别档次","五年1级年份","两年1档年份"]] = [
row["入职时间"],row["初始职务"],"套改/定级",row["入职时的初始级别"],format_time(row['晋级起始']),format_time(row['晋档起始'])]
for index, prow in promote.iterrows(): # 添加晋升记录
History_pd.loc[len(History_pd),["变动后时间","变动后职务","变动原因","晋升备注"]] = [
prow["工资执行时间"]+relativedelta(hours=prow["任职时间"].month,minutes=prow["任职时间"].day),
prow["新职务"],"晋升",f"{prow['新职务']} {prow['变动批注'] if pd.notna(prow['变动批注']) else ''}"]
try:
# 添加晋档记录
calctime=row["晋档起始"] + relativedelta(minute=1)
while True:
calctime += relativedelta(years=row["晋档间隔"])
if calctime > NOWTIME:
break
History_pd.loc[len(History_pd),["变动后时间","变动原因","两年1档年份"]] = [
calctime,"两年晋档",format_time(calctime)]
calctime=row["晋级起始"]
# 添加晋级记录
while True:
calctime += relativedelta(years=row["晋级间隔"])
if calctime > NOWTIME:
break
History_pd.loc[len(History_pd),["变动后时间","变动原因","五年1级年份"]] = [
calctime,"五年晋级",format_time(calctime)]
except:
raise Exception(f"晋级、档起始或间隔时间格式错误:{row['晋级起始']}-{row['晋档起始']}-{row['晋级间隔']}-{row['晋档间隔']}")
# 工资调标
for rule in Rule_Level:
if row["入职时间"] < rule["start"]:
History_pd.loc[len(History_pd),["变动后时间","变动原因"]] = [rule["start"], "工资调标"]
calctime = row["入职时间"]
while True:
calctime += relativedelta(years=1)
if calctime > NOWTIME:
break
elif int(calculate_seniority(row,calctime.year)) % 5 == 0:
History_pd.loc[len(History_pd),["变动后时间","变动原因"]] = \
[calctime,"调整津贴"]
History_pd["身份证号码"] = row["身份证号码"]
History_pd["姓名"] = row["姓名"]
History_pd["工龄"] = History_pd.apply(lambda x: calculate_seniority(row, x["变动后时间"].year), axis=1)
def calc_history(History_pd, row):
# 复杂数据计算
for index, hrow in History_pd.iterrows():
# 继承上一条复杂计算数据
if index > 0:
History_pd.at[index, "变动前时间"] = History_pd.at[index-1, "变动后时间"]
History_pd.at[index, "变动前职务"] = History_pd.at[index-1, "变动后职务"]
History_pd.at[index, "变动前级别档次"] = History_pd.at[index-1, "变动后级别档次"]
History_pd.at[index, "变动前职务工资"] = History_pd.at[index-1, "变动后职务工资"]
History_pd.at[index, "变动前级别工资"] = History_pd.at[index-1, "变动后级别工资"]
History_pd.at[index, "变动前津贴工资"] = History_pd.at[index-1, "变动后津贴工资"]
History_pd.at[index, "变动前工资合计"] = History_pd.at[index-1, "变动后工资合计"]
History_pd.at[index, "五年1级年份"] = History_pd.at[index-1, "五年1级年份"] if pd.isna(History_pd.at[index, "五年1级年份"]) else History_pd.at[index, "五年1级年份"]
History_pd.at[index, "两年1档年份"] = History_pd.at[index-1, "两年1档年份"] if pd.isna(History_pd.at[index, "两年1档年份"]) else History_pd.at[index, "两年1档年份"]
# 继承名称
if pd.isna(hrow["变动后职务"]):
History_pd.at[index,"变动后职务"] = History_pd.at[index,"变动前职务"]
# 名称变化
for rule in Rule_RoleName:
if rule["start"] <= hrow["变动后时间"] <= rule["end"]:
if History_pd.at[index,"变动后职务"] in rule["rule"]["原名称"].values:
History_pd.at[index,"变动后职务"] = rule["rule"][rule["rule"]["原名称"] == History_pd.at[index,"变动后职务"]]["现名称"].values[0]
# 级别档次
if index > 0:
jb, dc = split_level(History_pd.at[index,"变动前级别档次"])
if hrow["变动原因"] == "两年晋档":
History_pd.at[index, "变动后级别档次"] = f"{jb}-{dc+1}"
elif hrow["变动原因"] == "五年晋级":
if jb-1 < 1 or jb-1 < role_limit(History_pd.iloc[index]["变动后职务"]):
History_pd.at[index, "变动后级别档次"] = f"{jb}-{dc+1}"
else:
History_pd.at[index, "变动后级别档次"] = f"{jb-1}-{dc-1}"
elif hrow["变动原因"] == "工资调标":
History_pd.at[index, "变动后级别档次"] = f"{jb}-{dc}"
elif hrow["变动原因"] == "晋升":
role = History_pd.iloc[index]["变动后职务"]
if role in Promote_Level.keys():
new_jb = jb + Promote_Level[role][0]
new_dc = dc + Promote_Level[role][1]
if pd.isna(new_jb) or pd.isna(new_dc):
raise Exception(f"级别档次计算出现NaN值[{new_jb}]-[{new_dc}]({role})")
else:
new_jb = int(new_jb)
new_dc = int(new_dc)
if (History_pd.at[index,"变动后职务"] in Promote_verify.iloc[:,0].values and
role in Promote_verify.iloc[:,1].values):
logging.info(f"[{row['身份证号码']}]命中晋升校验规则[{History_pd.at[index,'变动前职务']}]->[{role}]")
History_pd.at[index, "变动后级别档次"] = f"{jb}-{dc}"
elif new_jb < role_limit(role):
History_pd.at[index, "变动后级别档次"] = f"{jb}-{dc+1}"
elif new_jb < 1 or new_dc < 1:
raise Exception(f"级别档次小于0[{new_jb}]-[{new_dc}]")
else:
History_pd.at[index, "变动后级别档次"] = f"{new_jb}-{new_dc}"
else:
logging.warning(f"职位[{role}]不存在职级上限规则")
else:
History_pd.at[index, "变动后级别档次"] = History_pd.at[index, "变动前级别档次"]
# 计算工资
History_pd.at[index, "变动后职务工资"] = role_salary(History_pd.at[index,"变动后职务"], hrow["变动后时间"])
History_pd.at[index, "变动后级别工资"] = level_salary(History_pd.at[index,"变动后级别档次"], hrow["变动后时间"])
History_pd.at[index, "变动后工资合计"] = to_int(History_pd.at[index,"变动后职务工资"]) + to_int(History_pd.at[index,"变动后级别工资"])
History_pd.at[index, "变动后津贴工资"] = allowance(History_pd.at[index,"变动后职务"], History_pd.at[index,"工龄"], History_pd.at[index,"变动后时间"])
def fill_history_info(ws, History_pd):# 填充历史记录
for index, hrow in History_pd.iterrows(): # 打印
for col in range(1, 11): # 复制样式
ws.cell(row=H_START+index, column=col)._style = ws.cell(row=H_START, column=col)._style
try:
ws.cell(row=H_START+index, column=1, value=format_time(hrow["时间"],"历史时间"))
ws.cell(row=H_START+index, column=1, value=format_time(hrow["变动后时间"],"历史时间"))
except:
logging.warning(f"历史时间格式错误:{hrow['时间']}")
ws.cell(row=H_START+index, column=2, value=hrow["职务"])
ws.cell(row=H_START+index, column=3, value=hrow["工资额1"])
ws.cell(row=H_START+index, column=4, value=hrow["级别档次"])
ws.cell(row=H_START+index, column=5, value=hrow["工资额2"])
ws.cell(row=H_START+index, column=6, value=hrow["工资合计"])
ws.cell(row=H_START+index, column=2, value=hrow["变动后职务"])
ws.cell(row=H_START+index, column=3, value=hrow["变动后职务工资"])
ws.cell(row=H_START+index, column=4, value=hrow["变动后级别档次"])
ws.cell(row=H_START+index, column=5, value=hrow["变动后级别工资"])
ws.cell(row=H_START+index, column=6, value=hrow["变动后工资合计"])
ws.cell(row=H_START+index, column=7, value=hrow["变动原因"])
# ws.cell(row=H_START+index, column=8, value=index) # Debug
BaseData["Latest_Role"] = None
BaseData["Latest_Prom"] = None
for index, row in BaseData.iterrows():
try:
logging.info(f"台账:第[{index+1}]共[{BaseData.shape[0]}]现在是[{row['身份证号码']}]")
BaseData.at[index, "Latest_Role"] = row["初始职务"]
BaseData.at[index, "Latest_Prom"] = row["入职时间"]
wb = load_workbook("模板/个人台账.xlsx")
ws = wb.active
fill_basic_info(ws, row)# 填充基本信息
# 查找晋升信息
promote = Promote[Promote["身份证号"] == row["身份证号码"]]
if not promote.empty:
promote = promote.sort_values(by="任职时间", ascending=False).reset_index(drop=True)
BaseData.at[index, "Latest_Role"] = promote.iloc[0]["新职务"]
BaseData.at[index, "Latest_Prom"] = promote.iloc[0]["任职时间"]
# 把原职务取出来
if promote.shape[0] > 1:
BaseData.at[index, "职务2"] = promote.iloc[1]["新职务"]
BaseData.at[index, "日期2"] = promote.iloc[1]["任职时间"]
promote = promote.sort_values(by="任职时间").reset_index(drop=True)
fill_prompt_info(ws, promote)# 填充晋升信息
# 根据规则匹配职级薪资
History_pd = pd.DataFrame(columns=["时间", "职务", "工资额1", "级别档次", "工资额2", "工资合计", "变动原因"])
# 添加入职记录
History_pd.loc[len(History_pd)] = [row["入职时间"], row["初始职务"], "", row["入职时的初始级别"], "", "", "套改/定级"]
for index, prow in promote.iterrows(): # 添加晋升记录
History_pd.loc[len(History_pd)] = [prow["工资执行时间"], prow["新职务"], "", "", "", "", "晋升"]
def fill_roster(): # 填充花名册
wb = load_workbook("模板/汇总名册.xlsx")
ws = wb.active
for index, row in BaseData.iterrows(): # 汇总
try:
calctime=row["二档起始"] + relativedelta(minute=1)
while True: # 添加二级记录
calctime += relativedelta(years=2)
if calctime > nowtime:
break
History_pd.loc[len(History_pd)] = [calctime, "", "", "", "", "", "两年晋档"]
calctime=row["五档起始"]
while True: # 添加五级记录
calctime += relativedelta(years=5)
if calctime > nowtime:
break
History_pd.loc[len(History_pd)] = [calctime, "", "", "", "", "", "五年晋级"]
except:
raise Exception(f"二、五档起始时间格式错误:{row['二档起始']}{row['五档起始']}")
for rule in Rule_Level: # 工资调标
if row["入职时间"] < rule["start"]:
History_pd.loc[len(History_pd)] = [rule["start"], "", "", "", "", "", "工资调标"]
History_pd = History_pd.sort_values(by="时间").reset_index(drop=True)
logging.info(f"汇总:第[{index+1}]共[{BaseData.shape[0]}]现在是[{row['身份证号码']}]")
for col in range(1,16):
ws.cell(row=6+index, column=col)._style = ws.cell(row=6, column=col)._style
ws.cell(row=6+index, column=1, value=index+1)
ws.cell(row=6+index, column=2, value=row["姓名"])
ws.cell(row=6+index, column=3, value=row["性别"])
ws.cell(row=6+index, column=4, value=format_time(row["出生年月"], "出生年月"))
ws.cell(row=6+index, column=5, value=format_time(row["参加工作时间"], "参加工作时间"))
ws.cell(row=6+index, column=6, value=fallback([row["现学历"],row["学历"]]))
ws.cell(row=6+index, column=7, value=row['工龄']+row["学龄"])
ws.cell(row=6+index, column=8, value=row['工龄'])
ws.cell(row=6+index, column=9, value=row["学龄"])
ws.cell(row=6+index, column=10, value=row["工龄调减"])
ws.cell(row=6+index, column=11, value=row["Latest_Role"])
ws.cell(row=6+index, column=12, value=format_time(row["Latest_Prom"], "Latest_Prom"))
ws.cell(row=6+index, column=13, value=row["职务2"])
ws.cell(row=6+index, column=14, value=format_time(row["日期2"], "日期2"))
except Exception as e:
logging.error(f"{row['身份证号码']}:{e}")
wb.save("汇总名册.xlsx") # 保存汇总
if History_pd.at[0,"时间"] != row["入职时间"]:
raise Exception(f"入职时间晚于其他计算的时间:{row['入职时间']} < {History_pd.at[0,'时间']} ({History_pd.at[0,'变动原因']})")
for index, hrow in History_pd.iterrows(): # 数据计算
# 调整职务职级
if index > 0 and hrow["职务"] == "":
History_pd.at[index, "职务"] = History_pd.iloc[index-1]["职务"]
for rule in Rule_RoleName: # 名称变化
if rule["start"] <= hrow["时间"] <= rule["end"]:
if History_pd.iloc[index]["职务"] in rule["rule"]["原名称"].values:
History_pd.at[index, "职务"] = rule["rule"][rule["rule"]["原名称"] == History_pd.iloc[index]["职务"]]["现名称"].values[0]
if index > 0 and hrow["级别档次"] == "":
jb, dc = split_level(History_pd.iloc[index-1]["级别档次"])
if hrow["变动原因"] == "两年晋档":
History_pd.at[index, "级别档次"] = f"{jb}-{dc+1}"
elif hrow["变动原因"] == "五年晋级":
History_pd.at[index, "级别档次"] = f"{jb-1}-{dc-1}"
elif hrow["变动原因"] == "工资调标":
History_pd.at[index, "级别档次"] = f"{jb}-{dc}"
elif hrow["变动原因"] == "晋升":
if role_limit(History_pd.iloc[index]["职务"]) == 99: # 99tag
History_pd.at[index, "级别档次"] = f"{jb}-{dc}"
elif jb == role_limit(History_pd.iloc[index]["职务"]):
History_pd.at[index, "级别档次"] = f"{jb}-{dc+1}"
else:
History_pd.at[index, "级别档次"] = f"{jb-1}-{dc-1}"
# 计算工资
History_pd.at[index, "工资额1"] = role_salary(History_pd.iloc[index]["职务"], hrow["时间"])
History_pd.at[index, "工资额2"] = level_salary(History_pd.iloc[index]["级别档次"], hrow["时间"])
History_pd.at[index, "工资合计"] = to_int(History_pd.iloc[index]["工资额1"]) + to_int(History_pd.iloc[index]["工资额2"])
def main():
fill_history_info(ws, History_pd)# 填充历史记录
wb.save(f"个人台账/{row['身份证号码']}_{row['姓名']}.xlsx")
except Exception as e:
logging.error(f"{row['身份证号码']}:{e}")
load_people()
load_rule()
wb = load_workbook("模板/汇总名册.xlsx")
ws = wb.active
for index, row in BaseData.iterrows(): # 汇总
try:
logging.info(f"汇总:第[{index+1}]共[{BaseData.shape[0]}]现在是[{row['身份证号码']}]")
for col in range(1,16):
ws.cell(row=6+index, column=col)._style = ws.cell(row=6, column=col)._style
ws.cell(row=6+index, column=1, value=index+1)
ws.cell(row=6+index, column=2, value=row["姓名"])
ws.cell(row=6+index, column=3, value=row["性别"])
ws.cell(row=6+index, column=4, value=format_time(row["出生年月"], "出生年月"))
ws.cell(row=6+index, column=5, value=format_time(row["参加工作时间"], "参加工作时间"))
ws.cell(row=6+index, column=6, value=row["学历"])
ws.cell(row=6+index, column=7, value=nowtime.year-row["参加工作时间"].year+row["工龄调增"]-row["工龄调减"]+1)
ws.cell(row=6+index, column=8, value=nowtime.year-row["参加工作时间"].year)
ws.cell(row=6+index, column=9, value=row["工龄调增"])
ws.cell(row=6+index, column=10, value=row["工龄调减"])
ws.cell(row=6+index, column=11, value=row["Latest_Role"])
ws.cell(row=6+index, column=12, value=format_time(row["Latest_Prom"], "Latest_Prom"))
ws.cell(row=6+index, column=13, value=row["职务2"])
ws.cell(row=6+index, column=14, value=format_time(row["日期2"], "日期2"))
except Exception as e:
logging.error(f"{row['身份证号码']}:{e}")
wb.save("汇总名册.xlsx") # 保存汇总
# 创建一个空的DataFrame来存储所有历史记录
all_history = pd.DataFrame(columns=[
"身份证号码", "姓名", "工龄","变动原因", "晋升备注",
"变动前时间", "变动前职务", "变动前级别档次", "变动前职务工资", "变动前级别工资", "变动前津贴工资",
"变动后时间", "变动后职务", "变动后级别档次", "变动后职务工资", "变动后级别工资", "变动后津贴工资",
"五年1级年份", "两年1档年份"])
if max_promote > 0:
logging.warning(f"最多有[{max_promote}]条晋升信息,需要调整模板。记得同时调整薪资历史的起始行和个人评价结果。")
if max_history > 0:
logging.warning(f"最多有[{max_history}]条薪资历史,需要调整模板。")
for index, row in BaseData.iterrows():
try:
logging.info(f"台账:第[{index+1}]共[{BaseData.shape[0]}]现在是[{row['身份证号码']}]")
BaseData.at[index, "Latest_Role"] = row["初始职务"]
BaseData.at[index, "Latest_Prom"] = row["入职时间"]
wb = load_workbook("模板/个人台账.xlsx")
ws = wb.active
fill_basic_info(ws, row)# 填充基本信息
# 查找晋升信息
promote = Promote[Promote["身份证号"] == row["身份证号码"]]
if not promote.empty:
promote = promote.sort_values(by=["工资执行时间", "任职时间"], ascending=[False, False]).reset_index(drop=True)
BaseData.at[index, "Latest_Role"] = promote.iloc[0]["新职务"]
BaseData.at[index, "Latest_Prom"] = promote.iloc[0]["任职时间"]
# 把原职务取出来
if promote.shape[0] > 1:
BaseData.at[index, "职务2"] = promote.iloc[1]["新职务"]
BaseData.at[index, "日期2"] = promote.iloc[1]["任职时间"]
else:
BaseData.at[index, "职务2"] = row["初始职务"]
BaseData.at[index, "日期2"] = row["入职时间"]
promote = promote.sort_values(by=["工资执行时间", "任职时间"]).reset_index(drop=True)
fill_prompt_info(ws, promote)# 填充晋升信息
# 根据规则匹配职级薪资
History_pd = pd.DataFrame(columns=[
"身份证号码", "姓名", # 统一填入
"变动后时间", "变动后职务", "变动原因", "晋升备注", # 直接填入
"工龄", "五年1级年份", "两年1档年份", # 简单计算更新
"变动前时间", "变动前职务", "变动前级别档次", "变动前职务工资", "变动前级别工资", "变动前津贴工资", "变动前工资合计", # 排序后更新
"变动后级别档次", "变动后职务工资", "变动后级别工资", "变动后津贴工资", "变动后工资合计"]) # 复杂计算更新
add_history(History_pd, row, promote)
History_pd = History_pd.sort_values(by="变动后时间").reset_index(drop=True)
if History_pd.at[0,"变动后时间"] != row["入职时间"]:
raise Exception(f"入职时间晚于其他时间:{row['入职时间']} < {History_pd.at[0,'变动后时间']} ({History_pd.at[0,'变动原因']})")
calc_history(History_pd, row)
fill_history_info(ws, History_pd)# 填充历史记录
# 将当前人员的历史记录添加到总表中
all_history = pd.concat([all_history, History_pd], ignore_index=True)
wb.save(f"个人台账/{row['身份证号码']}_{row['姓名']}.xlsx")
except Exception as e:
logging.error(f"{row['身份证号码']}:{e}")
# 保存所有历史记录到Excel文件
all_history["变动后时间"] = all_history["变动后时间"].apply(lambda x: format_time_ymd(x, "历史记录时间"))
all_history["变动前时间"] = all_history["变动前时间"].apply(lambda x: format_time_ymd(x, "历史记录时间"))
all_history.to_excel("所有人员历史记录.xlsx", index=False)
logging.info("所有人员历史记录已保存到'所有人员历史记录.xlsx'")
fill_roster()
if max_promote > 0:
logging.warning(f"最多有[{max_promote}]条晋升信息,需要调整模板。记得同时调整薪资历史的起始行和个人评价结果。")
if max_history > 0:
logging.warning(f"最多有[{max_history}]条薪资历史,需要调整模板。")
if __name__ == "__main__":
main()

View File