from __future__ import annotations from pathlib import Path from openpyxl import Workbook from openpyxl.styles import Alignment, Border, Font, PatternFill, Side from openpyxl.utils import get_column_letter OUTPUT_PATH = Path(__file__).resolve().parents[1] / "rules" / "finance-rules" / "公司差旅费报销规则.xlsx" ROWS = [ (1, "北京", "北京", "", "", 500, 450, 450, 500, ""), (2, "天津", "6 个中心城区、滨海新区、东丽区、西青区、津南区、北辰区、武清区、宝坻区、静海区、蓟县", "", "", 380, 360, 350, 380, ""), (2, "天津", "宁河区", "", "", 320, 300, 280, 320, ""), (3, "河北", "石家庄", "", "", 350, 330, 300, 350, ""), (3, "河北", "张家口、秦皇岛、廊坊、承德、保定", "张家口市;秦皇岛市;承德市", "张家口市:7-9 月、11-3 月;秦皇岛市:7-8 月;承德市:7-9 月", 350, 300, 250, 350, 420), (3, "河北", "雄安新区(不含雄县、安新县、容城县)", "", "", 450, 400, 350, 450, ""), (3, "河北", "其他地区", "", "", 310, 290, 250, 310, ""), (4, "山西", "太原", "", "", 350, 330, 300, 350, ""), (4, "山西", "大同、晋城", "", "", 350, 300, 250, 350, ""), (4, "山西", "临汾", "", "", 330, 300, 250, 330, ""), (4, "山西", "阳泉、长治、晋中", "", "", 310, 290, 250, 310, ""), (4, "山西", "其他地区", "", "", 240, 220, 200, 240, ""), (5, "内蒙古", "呼和浩特", "", "", 350, 330, 300, 350, ""), (5, "内蒙古", "海拉尔市、满洲里市、阿尔山市", "海拉尔市、满洲里市、阿尔山市", "7-9 月", 320, 300, 250, 320, 380), (5, "内蒙古", "二连浩特市", "二连浩特市", "7-9 月", 320, 300, 250, 320, 380), (5, "内蒙古", "额济纳市", "额济纳市", "9-10 月", 320, 300, 250, 320, 380), (5, "内蒙古", "其他地区", "", "", 320, 300, 250, 320, ""), (6, "辽宁", "沈阳", "", "", 350, 330, 300, 350, ""), (6, "辽宁", "其他地区", "", "", 330, 300, 250, 330, ""), (7, "大连", "大连", "大连", "7-9 月", 350, 300, 300, 350, 420), (8, "吉林", "长春", "长春", "7-9 月", 350, 330, 300, 350, 420), (8, "吉林", "吉林、延边州、长白山管理区", "吉林、延边州、长白山管理区", "7-9 月", 350, 300, 250, 350, 420), (8, "吉林", "其他地区", "", "", 300, 280, 250, 300, ""), (9, "黑龙江", "哈尔滨", "哈尔滨", "7-9 月", 350, 330, 300, 350, 420), (9, "黑龙江", "牡丹江、伊春、大兴安岭地区、黑河、佳木斯", "牡丹江、伊春、大兴安岭地区、黑河、佳木斯", "6-8 月", 300, 280, 250, 300, 360), (9, "黑龙江", "其他地区", "", "", 300, 280, 250, 300, ""), (10, "上海", "上海", "", "", 500, 450, 450, 500, ""), (11, "江苏", "南京", "", "", 380, 350, 350, 380, ""), (11, "江苏", "苏州、无锡、常州、镇江", "", "", 350, 300, 250, 380, ""), (11, "江苏", "其他地区", "", "", 350, 300, 250, 360, ""), (12, "浙江", "杭州", "", "", 400, 350, 350, 400, ""), (12, "浙江", "其他地区", "", "", 340, 300, 250, 340, ""), (13, "宁波", "宁波", "", "", 350, 300, 250, 350, ""), (14, "安徽", "合肥", "", "", 350, 330, 300, 350, ""), (14, "安徽", "其他地区", "", "", 350, 300, 250, 350, ""), (15, "福建", "福州", "", "", 380, 350, 300, 380, ""), (15, "福建", "泉州、平潭综合实验区", "", "", 350, 300, 250, 380, ""), (15, "福建", "其他地区", "", "", 350, 300, 250, 350, ""), (16, "厦门", "厦门", "", "", 400, 380, 350, 400, ""), (17, "江西", "南昌", "", "", 350, 330, 300, 350, ""), (17, "江西", "其他地区", "", "", 350, 300, 250, 350, ""), (18, "山东", "济南", "", "", 380, 350, 300, 380, ""), (18, "山东", "烟台、威海、日照", "烟台、威海、日照", "7-9 月", 350, 300, 250, 380, 450), (18, "山东", "淄博、枣庄、东营、潍坊、济宁、泰安", "", "", 350, 300, 250, 380, ""), (18, "山东", "其他地区", "", "", 350, 300, 250, 360, ""), (19, "青岛", "青岛", "青岛", "7-9 月", 350, 300, 250, 380, 450), (20, "河南", "郑州", "", "", 380, 350, 300, 380, ""), (20, "河南", "洛阳", "洛阳", "4-5 月上旬", 330, 300, 250, 330, 390), (20, "河南", "其他地区", "", "", 330, 300, 250, 330, ""), (21, "湖北", "武汉", "", "", 350, 330, 300, 350, ""), (21, "湖北", "其他地区", "", "", 320, 300, 250, 320, ""), (22, "湖南", "长沙", "", "", 350, 330, 300, 350, ""), (22, "湖南", "其他地区", "", "", 330, 300, 250, 330, ""), (23, "广东", "广州", "", "", 450, 400, 400, 450, ""), (23, "广东", "珠海", "", "", 450, 400, 350, 450, ""), (23, "广东", "佛山、东莞、中山、江门", "", "", 350, 300, 250, 450, ""), (23, "广东", "其他地区", "", "", 350, 300, 250, 420, ""), (24, "深圳", "深圳", "", "", 450, 400, 400, 450, ""), (25, "广西", "南宁", "", "", 350, 330, 300, 350, ""), (25, "广西", "桂林、北海", "桂林、北海", "1-2 月、7-9 月", 330, 300, 250, 330, 390), (25, "广西", "其他地区", "", "", 330, 300, 250, 330, ""), (26, "海南", "海口、文昌、澄迈县", "海口、文昌、澄迈县", "11-2 月", 350, 330, 310, 350, 420), (26, "海南", "琼海、万宁、陵水县、保亭县", "琼海、万宁、陵水县、保亭县", "11-3 月", 350, 330, 310, 350, 420), (26, "海南", "三沙、儋州、五指山、东方、安定县、屯昌县、临高县、白沙县、昌江县、乐东县、琼中县、洋浦开发区", "", "", 350, 330, 310, 350, ""), (26, "海南", "三亚", "三亚", "10-4 月", 400, 380, 350, 400, 480), (27, "重庆", "9 个中心城区、北部新区", "", "", 370, 350, 330, 370, ""), (27, "重庆", "其他地区", "", "", 300, 280, 260, 300, ""), (28, "四川", "成都", "", "", 370, 350, 330, 370, ""), (28, "四川", "阿坝州、甘孜州", "", "", 330, 300, 250, 330, ""), (28, "四川", "绵阳、乐山、雅安", "", "", 320, 300, 250, 320, ""), (28, "四川", "宜宾", "", "", 300, 280, 250, 300, ""), (28, "四川", "凉山州", "", "", 330, 300, 250, 330, ""), (28, "四川", "德阳、遂宁、巴中", "", "", 310, 290, 250, 310, ""), (28, "四川", "其他地区", "", "", 300, 280, 250, 300, ""), (29, "贵州", "贵阳", "", "", 370, 350, 300, 370, ""), (29, "贵州", "其他地区", "", "", 300, 280, 250, 300, ""), (30, "云南", "昆明", "", "", 380, 350, 300, 380, ""), (30, "云南", "大理州、丽江市、迪庆州、西双版纳州", "", "", 350, 300, 250, 380, ""), (30, "云南", "其他地区", "", "", 330, 300, 250, 330, ""), (31, "西藏", "拉萨", "拉萨", "5-10 月", 350, 330, 300, 350, 420), (31, "西藏", "其他地区", "其他地区", "5-10 月", 300, 280, 250, 300, 360), (32, "陕西", "西安", "", "", 350, 330, 300, 350, ""), (32, "陕西", "榆林、延安", "", "", 300, 280, 250, 300, ""), (32, "陕西", "杨凌区", "", "", 260, 240, 220, 260, ""), (32, "陕西", "咸阳、宝鸡", "", "", 260, 240, 220, 260, ""), (32, "陕西", "渭南、韩城", "", "", 260, 240, 220, 260, ""), (32, "陕西", "其他地区", "", "", 230, 210, 200, 230, ""), (33, "甘肃", "兰州", "", "", 350, 330, 300, 350, ""), (33, "甘肃", "其他地区", "", "", 310, 290, 250, 310, ""), (34, "青海", "西宁", "西宁", "6-9 月", 350, 330, 300, 350, 420), (34, "青海", "玉树州", "玉树州", "5-9 月", 300, 280, 250, 300, 360), (34, "青海", "果洛州", "", "", 300, 280, 250, 300, ""), (34, "青海", "海北州、黄南州", "海北州、黄南州", "5-9 月", 250, 230, 210, 250, 300), (34, "青海", "海东、海南州", "海东、海南州", "5-9 月", 250, 230, 210, 250, 300), (34, "青海", "海西州", "海西州", "5-9 月", 200, 200, 200, 200, 240), (35, "宁夏", "银川", "", "", 350, 330, 300, 350, ""), (35, "宁夏", "其他地区", "", "", 330, 300, 250, 330, ""), (36, "新疆", "乌鲁木齐", "", "", 350, 330, 300, 350, ""), (36, "新疆", "石河子、克拉玛依、昌吉州、伊犁州、阿勒泰地区、博州、吐鲁番、哈密地区、巴州、和田地区", "", "", 340, 300, 250, 340, ""), (36, "新疆", "克州", "", "", 320, 300, 250, 320, ""), (36, "新疆", "喀什地区", "", "", 300, 280, 250, 300, ""), (36, "新疆", "阿克苏地区", "", "", 300, 280, 250, 300, ""), (36, "新疆", "塔城地区", "", "", 300, 280, 250, 300, ""), (37, "港澳台", "香港、澳门、台湾", "", "", 450, 400, 350, 500, ""), (38, "国外", "国外", "", "", 700, 600, 500, 700, ""), ] def build_workbook() -> Workbook: workbook = Workbook() worksheet = workbook.active worksheet.title = "差旅住宿费标准" headers = [ "序号", "地区", "地区(城市)", "旺季地区", "旺季期间", "公司级管理人员、高层经理(P7及以上)", "中层经理、基层经理(P4-P6、外聘专家)", "其他员工", "超标限额", "旺季超标限额", ] worksheet.append(["差旅住宿费标准"]) worksheet.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(headers)) worksheet["A1"].font = Font(bold=True, size=16, color="FFFFFF") worksheet["A1"].fill = PatternFill("solid", fgColor="1F4E78") worksheet["A1"].alignment = Alignment(horizontal="center") worksheet.append(headers) for row in ROWS: worksheet.append(row) header_fill = PatternFill("solid", fgColor="D9EAF7") thin = Side(style="thin", color="B7C9D6") for cell in worksheet[2]: cell.font = Font(bold=True) cell.fill = header_fill cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True) cell.border = Border(left=thin, right=thin, top=thin, bottom=thin) for row in worksheet.iter_rows(min_row=3, max_row=worksheet.max_row): for cell in row: cell.alignment = Alignment(vertical="center", wrap_text=True) cell.border = Border(left=thin, right=thin, top=thin, bottom=thin) for cell in row[5:]: cell.alignment = Alignment(horizontal="center", vertical="center") worksheet.freeze_panes = "A3" worksheet.auto_filter.ref = f"A2:J{worksheet.max_row}" widths = [8, 12, 42, 28, 28, 22, 26, 12, 12, 14] for index, width in enumerate(widths, start=1): worksheet.column_dimensions[get_column_letter(index)].width = width worksheet.row_dimensions[1].height = 26 worksheet.row_dimensions[2].height = 42 for index in range(3, worksheet.max_row + 1): worksheet.row_dimensions[index].height = 36 subsidy_sheet = workbook.create_sheet("出差补助标准") subsidy_headers = [ "补助类型", "项目", "港澳台", "直辖市/特区", "西藏", "新疆-乌鲁木齐", "新疆-其他", "其他地区", "国外", ] subsidy_rows = [ ("伙食补助", "自行解决餐食", 75, 65, 65, 55, 55, 55, 140), ("基本补助", "基本出差补贴", 35, 35, 105, 75, 135, 35, 35), ("合计", "", 110, 100, 170, 130, 190, 90, 175), ] subsidy_sheet.append(["出差补助标准"]) subsidy_sheet.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(subsidy_headers)) subsidy_sheet["A1"].font = Font(bold=True, size=16, color="FFFFFF") subsidy_sheet["A1"].fill = PatternFill("solid", fgColor="1F4E78") subsidy_sheet["A1"].alignment = Alignment(horizontal="center") subsidy_sheet.append(subsidy_headers) for row in subsidy_rows: subsidy_sheet.append(row) subsidy_sheet.append(["备注", "注 1:新疆分公司同事出差至乌鲁木齐外的其他新疆地区,基本补助标准为 95 元。"]) subsidy_sheet.append(["备注", "注 2:西藏分公司同事出差至拉萨市外的其他西藏地区,基本补助标准为 35 元。"]) subsidy_sheet.merge_cells(start_row=6, start_column=2, end_row=6, end_column=len(subsidy_headers)) subsidy_sheet.merge_cells(start_row=7, start_column=2, end_row=7, end_column=len(subsidy_headers)) for cell in subsidy_sheet[2]: cell.font = Font(bold=True) cell.fill = header_fill cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True) cell.border = Border(left=thin, right=thin, top=thin, bottom=thin) for row in subsidy_sheet.iter_rows(min_row=3, max_row=7): for cell in row: cell.alignment = Alignment(vertical="center", wrap_text=True) cell.border = Border(left=thin, right=thin, top=thin, bottom=thin) for cell in subsidy_sheet[5]: cell.font = Font(bold=True) cell.fill = PatternFill("solid", fgColor="E2F0D9") subsidy_sheet.freeze_panes = "A3" subsidy_sheet.auto_filter.ref = "A2:I5" subsidy_widths = [14, 18, 12, 16, 12, 18, 16, 14, 12] for index, width in enumerate(subsidy_widths, start=1): subsidy_sheet.column_dimensions[get_column_letter(index)].width = width subsidy_sheet.row_dimensions[1].height = 26 subsidy_sheet.row_dimensions[2].height = 36 for index in range(3, 8): subsidy_sheet.row_dimensions[index].height = 28 source_sheet = workbook.create_sheet("来源说明") source_sheet.append(["来源文件", "页码", "说明"]) source_sheet.append( [ "远光软件2026费用报销说明手册.pdf", "第 13-19 页", "依据 PDF 附件 3《差旅住宿费标准》整理为默认支撑表。", ] ) source_sheet.append( [ "远光软件2026费用报销说明手册.pdf", "第 20 页", "依据 PDF 附件 4《出差补助标准》整理为默认支撑表。", ] ) for row in source_sheet.iter_rows(): for cell in row: cell.alignment = Alignment(wrap_text=True, vertical="center") cell.border = Border(left=thin, right=thin, top=thin, bottom=thin) for cell in source_sheet[1]: cell.font = Font(bold=True) cell.fill = header_fill source_sheet.column_dimensions["A"].width = 34 source_sheet.column_dimensions["B"].width = 14 source_sheet.column_dimensions["C"].width = 56 return workbook def main() -> None: OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) workbook = build_workbook() workbook.save(OUTPUT_PATH) print(OUTPUT_PATH) print(f"rows={len(ROWS)}") if __name__ == "__main__": main()