Files
mileage-bonus/may_18t_recalc.py
T

742 lines
31 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
5月 18T 绩效三种测算方案
方案1: 保留原逻辑 (1000元基准, 含结转/补发/累计)
方案2: 新测算简化版 (800/300, 仅当月, 无结转/补发/累计)
方案3: 新测算完整版 (800/300/500, 含结转/补发/累计)
"""
import os, openpyxl
from collections import defaultdict
from copy import deepcopy
os.chdir('/Users/kkfluous/Downloads')
DAYS = {1: 31, 2: 28, 3: 31, 4: 30, 5: 31}
TARGET_KM = 6000
OUTPUT_DIR = '/Users/kkfluous/Downloads/5月18T测算'
# ============================================================
# 数据读取
# ============================================================
def read_18t(filepath, month):
"""只读羚牛100辆18T记录"""
wb = openpyxl.load_workbook(filepath, data_only=True)
ws = wb['业务考核视图']
h = [c.value for c in next(ws.iter_rows(min_row=1, max_row=1))]
recs = []
for row in ws.iter_rows(min_row=2, values_only=True):
r = dict(zip(h, row))
if r.get('考核目标') != '羚牛100辆18T':
continue
r['考核天数'] = float(r.get('考核天数') or 0)
r['应考核里程(km)'] = float(r.get('应考核里程(km)') or 0)
r['实际行驶里程(km)'] = float(r.get('实际行驶里程(km)') or 0)
r['月份'] = month
t, a = r['应考核里程(km)'], r['实际行驶里程(km)']
q = a >= t and t > 0
r['是否达标'] = '达标' if q else '未达标'
r['多跑'] = max(0, a - t) if q else 0
r['可结转'] = int(r['多跑'] // TARGET_KM) if q else 0
recs.append(r)
wb.close()
return recs
def grp(recs):
"""按(车牌号, 销售经理)分组"""
gs = defaultdict(lambda: {
'recs': [], '应考核': 0, '实际': 0, '奖金': 0, '天数': 0,
'有达标': False, '目标km': TARGET_KM,
'部门': '', '销售': '', '车牌': ''
})
for r in recs:
k = (r['车牌号'], r['销售经理'])
g = gs[k]
g['recs'].append(r)
g['应考核'] += r['应考核里程(km)']
g['实际'] += r['实际行驶里程(km)']
g['奖金'] += r.get('奖金', 0)
g['天数'] += r['考核天数']
if r['是否达标'] == '达标':
g['有达标'] = True
g['部门'] = r['部门名称']
g['销售'] = r['销售经理']
g['车牌'] = r['车牌号']
return dict(gs)
# ============================================================
# 方案1: 原逻辑 (base=1000)
# ============================================================
def calc_s1():
"""方案1: 保留原逻辑,base=1000。只算18T"""
BASE = 1000
D = {}
for m in range(1, 6):
fp = f'租赁任务考核_2026年{m}月.xlsx'
if os.path.exists(fp):
D[m] = read_18t(fp, m)
# 给每条记录加上奖励额并计算奖金
for m in D:
for r in D[m]:
r['奖励额'] = BASE
t, a = r['应考核里程(km)'], r['实际行驶里程(km)']
q = a >= t and t > 0
r['是否达标'] = '达标' if q else '未达标'
r['奖金'] = BASE * (r['考核天数'] / DAYS[m]) if q else 0
r['多跑'] = max(0, a - t) if q else 0
r['可结转'] = int(r['多跑'] // TARGET_KM) if q else 0
G = {m: grp(D[m]) for m in D}
# 1月: 计算可结转 + 初始化累计
for k, g in G[1].items():
cum_q = g['实际'] >= g['应考核'] and g['应考核'] > 0
cum_over = max(0, g['实际'] - g['应考核']) if cum_q else 0
g['可结转'] = int(cum_over // TARGET_KM) if TARGET_KM > 0 else 0
g['累计已发结转'] = 0
g['1月已发'] = g['奖金'] > 0
g['奖励额'] = BASE
g['cum_t'] = g['应考核']
g['cum_a'] = g['实际']
g['cum_q'] = cum_q
# 用于追踪每个月的发放
all_data = {1: {'结转': [], '当月': []}}
# 用于追踪每个月的发放
all_data = {1: {'结转': [], '当月': []}}
for k, g in G[1].items():
if g['奖金'] > 0:
all_data[1]['当月'].append({'车牌': k[0], '销售': g['销售'], '部门': g['部门'], '额': g['奖金']})
# 逐月计算: 2→5
for month in range(2, 6):
if month not in G:
continue
data_m = {'结转': [], '当月': []}
prev_m = month - 1
days_m = DAYS[month]
for k, gm in G[month].items():
gp = G[prev_m].get(k)
bf = BASE
# 累计
cum_t = (gp['cum_t'] if gp else 0) + gm['应考核']
cum_a = (gp['cum_a'] if gp else 0) + gm['实际']
cum_q = cum_a >= cum_t and cum_t > 0
# 结转
carry = 0
p_carry = gp['可结转'] if gp else 0
if gp and p_carry >= 1:
carry = bf
data_m['结转'].append({'车牌': k[0], '销售': gp['销售'], '部门': gm['部门'], '额': carry})
# 当月达标
bonus = 0
if gm['有达标'] and carry == 0:
bonus = gm['奖金']
data_m['当月'].append({'车牌': k[0], '销售': gm['销售'], '部门': gm['部门'], '额': bonus})
# 补发历史月
for back_m in range(1, month):
label = f'补发{back_m}月'
if label not in data_m:
data_m[label] = []
for back_m in range(1, month):
label = f'补发{back_m}月'
g_bm = G[back_m].get(k)
if not g_bm:
continue
# 检查该月是否已发
bm_paid = g_bm.get(f'{back_m}月已发', g_bm['奖金'] > 0)
if bm_paid:
continue
# 检查后续月是否已补发
bm_paid_later = False
for later_m in range(back_m + 1, month):
gl = G[later_m].get(k)
if gl and gl.get(f'补发{back_m}月', 0) > 0:
bm_paid_later = True
break
if bm_paid_later:
continue
if cum_q:
bj = g_bm['奖励额'] * (g_bm['天数'] / DAYS[back_m])
data_m[label].append({'车牌': k[0], '销售': g_bm['销售'], '部门': gm['部门'], '额': bj})
gm[f'补发{back_m}月'] = bj
# 累计补发当月
label_cbp = f'累计补发{month}月'
if label_cbp not in data_m:
data_m[label_cbp] = []
cbp = 0
if not gm['有达标'] and carry == 0 and cum_q:
cbp = bf * (gm['天数'] / days_m)
data_m[label_cbp].append({'车牌': k[0], '销售': gm['销售'], '部门': gm['部门'], '额': cbp})
gm['cum_t'] = cum_t
gm['cum_a'] = cum_a
gm['cum_q'] = cum_q
gm['结转'] = carry
gm['当月奖金'] = bonus
gm['累计补发当月'] = cbp
gm['奖励额'] = BASE
gm[f'{month}月已发'] = carry > 0 or bonus > 0 or cbp > 0
# 更新可结转
cum_over = max(0, cum_a - cum_t) if cum_q else 0
earned = int(cum_over // TARGET_KM) if TARGET_KM > 0 else 0
prev_used = gp['累计已发结转'] if gp else 0
cur_used = prev_used + (1 if carry > 0 else 0)
gm['累计已发结转'] = cur_used
gm['可结转'] = max(0, earned - cur_used)
# 补充: 上个月有结转但本月无考核记录的车
for k, gp in G[prev_m].items():
if k not in G[month] and gp.get('可结转', 0) >= 1:
carry = BASE
data_m['结转'].append({'车牌': k[0], '销售': gp['销售'], '部门': gp['部门'], '额': carry})
G[month][k] = {
'recs': [], '应考核': TARGET_KM, '实际': 0, '奖金': 0, '天数': days_m,
'有达标': False, '目标km': TARGET_KM, '部门': gp['部门'], '销售': gp['销售'],
'车牌': k[0], '奖励额': BASE,
'cum_t': gp.get('cum_t', 0) + TARGET_KM, 'cum_a': gp.get('cum_a', 0),
'cum_q': False,
'结转': carry, '当月奖金': 0,
f'{month}月已发': True,
'可结转': max(0, gp['可结转'] - 1),
'累计已发结转': gp.get('累计已发结转', 0) + 1,
'虚拟': True,
}
for bm in range(1, month):
G[month][k][f'补发{bm}月'] = 0
all_data[month] = data_m
# 收集5月发放明细
pay_records = collect_payments_s1(G, all_data)
return D[5], G[5], all_data.get(5, {}), pay_records
def collect_payments_s1(G, all_data):
"""收集5月结算的所有发放记录"""
records = []
m5 = all_data.get(5, {})
for cat, items in m5.items():
for d in items:
records.append({
'车牌': d['车牌'], '销售': d['销售'], '部门': d.get('部门', ''),
'金额': d['额'], '类型': cat, '结算月': 5
})
records.sort(key=lambda x: (x['车牌'], x['类型']))
return records
# ============================================================
# 方案2: 简化版 (800/300, 仅当月)
# ============================================================
def calc_s2():
"""方案2: 仅看5月, 达标800, 未达标300, 无结转/补发/累计。按组统计(同方案1)"""
BASE_GOOD = 800
BASE_BAD = 300
fp = '租赁任务考核_2026年5月.xlsx'
recs = read_18t(fp, 5)
# 给每条记录算好奖金
for r in recs:
if r['是否达标'] == '达标':
r['奖金'] = BASE_GOOD * (r['考核天数'] / DAYS[5])
else:
r['奖金'] = BASE_BAD * (r['考核天数'] / DAYS[5])
# 按(车牌+销售)分组, 每组一笔 (同方案1)
G5 = grp(recs)
pay_records = []
for k, g in sorted(G5.items()):
total = g['奖金'] # 组内达标+未达标奖金合计
ptype = '当月达标(800)' if g['有达标'] else '当月未达标(300)'
pay_records.append({
'车牌': k[0], '销售': k[1], '部门': g['部门'],
'金额': total, '类型': ptype
})
pay_records.sort(key=lambda x: (x['车牌'], x['销售']))
return recs, pay_records
# ============================================================
# 方案3: 完整版 (800/300, 结转800, 补发/累计500)
# ============================================================
def calc_s3():
"""方案3: 当月达标800, 当月未达标300, 结转800, 补发/累计补发500"""
BASE_GOOD = 800
BASE_BAD = 300
BASE_CARRY = 800
BASE_OTHER = 500
D = {}
for m in range(1, 6):
fp = f'租赁任务考核_2026年{m}月.xlsx'
if os.path.exists(fp):
D[m] = read_18t(fp, m)
# 给每条记录加上奖励额和当月奖金(按新标准)
for m in D:
for r in D[m]:
r['奖励额'] = BASE_GOOD # 当月达标基准
r['奖励额_未达标'] = BASE_BAD # 当月未达标基准
r['奖励额_其他'] = BASE_OTHER # 结转/补发/累计基准
t, a = r['应考核里程(km)'], r['实际行驶里程(km)']
q = a >= t and t > 0
r['是否达标'] = '达标' if q else '未达标'
if q:
r['奖金'] = BASE_GOOD * (r['考核天数'] / DAYS[m])
else:
r['奖金'] = BASE_BAD * (r['考核天数'] / DAYS[m])
r['多跑'] = max(0, a - t) if q else 0
r['可结转'] = int(r['多跑'] // TARGET_KM) if q else 0
G = {m: grp(D[m]) for m in D}
# 1月: 计算可结转 + 初始化累计
for k, g in G[1].items():
g['奖励额'] = BASE_GOOD
g['奖励额_其他'] = BASE_OTHER
cum_q = g['实际'] >= g['应考核'] and g['应考核'] > 0
cum_over = max(0, g['实际'] - g['应考核']) if cum_q else 0
g['可结转'] = int(cum_over // TARGET_KM) if TARGET_KM > 0 else 0
g['累计已发结转'] = 0
# 方案3: 1月已发 = 有达标 (仅300未达标不算)
g['1月已发'] = g['有达标']
g['cum_t'] = g['应考核']
g['cum_a'] = g['实际']
g['cum_q'] = cum_q
all_data = {1: {'结转': [], '当月达标(800)': [], '当月未达标(300)': []}}
for k, g in G[1].items():
# 按组统计: 有达标→当月达标(合并金额), 全未达标→当月未达标
if g['有达标']:
all_data[1]['当月达标(800)'].append({'车牌': k[0], '销售': g['销售'], '部门': g['部门'], '额': g['奖金']})
elif g['奖金'] > 0:
all_data[1]['当月未达标(300)'].append({'车牌': k[0], '销售': g['销售'], '部门': g['部门'], '额': g['奖金']})
# 逐月计算: 2→5
for month in range(2, 6):
if month not in G:
continue
data_m = {'结转': [], '当月达标(800)': [], '当月未达标(300)': []}
prev_m = month - 1
days_m = DAYS[month]
for k, gm in G[month].items():
gp = G[prev_m].get(k)
# 累计
cum_t = (gp['cum_t'] if gp else 0) + gm['应考核']
cum_a = (gp['cum_a'] if gp else 0) + gm['实际']
cum_q = cum_a >= cum_t and cum_t > 0
# 结转: 800 (整月)
carry = 0
p_carry = gp['可结转'] if gp else 0
if gp and p_carry >= 1:
carry = BASE_CARRY
data_m['结转'].append({'车牌': k[0], '销售': gp['销售'], '部门': gm['部门'], '额': carry})
# 当月奖金: 按组统计(同方案1), 有达标→当月达标, 全未达标→当月未达标
if carry == 0 and gm['奖金'] > 0:
ptype = '当月达标(800)' if gm['有达标'] else '当月未达标(300)'
data_m[ptype].append({'车牌': k[0], '销售': gm['销售'], '部门': gm['部门'], '额': gm['奖金']})
# 补发历史月: 500基准
for back_m in range(1, month):
label = f'补发{back_m}月'
if label not in data_m:
data_m[label] = []
g_bm = G[back_m].get(k)
if not g_bm:
continue
# 方案3: 历史月已发 = 有达标/已结转/已补发/已累计补发. 仅拿300未达标不算已发.
bm_paid = g_bm.get(f'{back_m}月已发', g_bm['有达标'])
if bm_paid:
continue
bm_paid_later = False
for later_m in range(back_m + 1, month):
gl = G[later_m].get(k)
if gl and gl.get(f'补发{back_m}月', 0) > 0:
bm_paid_later = True
break
if bm_paid_later:
continue
if cum_q:
bj = BASE_OTHER * (g_bm['天数'] / DAYS[back_m])
data_m[label].append({'车牌': k[0], '销售': g_bm['销售'], '部门': gm['部门'], '额': bj})
gm[f'补发{back_m}月'] = bj
# 累计补发当月: 500基准
label_cbp = f'累计补发{month}月'
if label_cbp not in data_m:
data_m[label_cbp] = []
cbp = 0
if not gm['有达标'] and carry == 0 and cum_q:
cbp = BASE_OTHER * (gm['天数'] / days_m)
data_m[label_cbp].append({'车牌': k[0], '销售': gm['销售'], '部门': gm['部门'], '额': cbp})
gm['cum_t'] = cum_t
gm['cum_a'] = cum_a
gm['cum_q'] = cum_q
gm['结转'] = carry
gm['当月奖金'] = gm['奖金'] if carry == 0 else 0
gm['累计补发当月'] = cbp
gm['奖励额'] = BASE_GOOD
gm['奖励额_其他'] = BASE_OTHER
# 方案3: 已发 = 结转/当月有达标/累计补发 (仅300未达标不算已发,可后续补发)
gm[f'{month}月已发'] = carry > 0 or gm['有达标'] or cbp > 0
# 更新可结转
cum_over = max(0, cum_a - cum_t) if cum_q else 0
earned = int(cum_over // TARGET_KM) if TARGET_KM > 0 else 0
prev_used = gp['累计已发结转'] if gp else 0
cur_used = prev_used + (1 if carry > 0 else 0)
gm['累计已发结转'] = cur_used
gm['可结转'] = max(0, earned - cur_used)
# 补充: 上个月有结转但本月无考核记录的车
for k, gp in G[prev_m].items():
if k not in G[month] and gp.get('可结转', 0) >= 1:
carry = BASE_CARRY
data_m['结转'].append({'车牌': k[0], '销售': gp['销售'], '部门': gp['部门'], '额': carry})
G[month][k] = {
'recs': [], '应考核': TARGET_KM, '实际': 0, '奖金': 0, '天数': days_m,
'有达标': False, '目标km': TARGET_KM, '部门': gp['部门'], '销售': gp['销售'],
'车牌': k[0], '奖励额': BASE_GOOD, '奖励额_其他': BASE_OTHER,
'cum_t': gp.get('cum_t', 0) + TARGET_KM, 'cum_a': gp.get('cum_a', 0),
'cum_q': False,
'结转': carry, '当月奖金': 0,
f'{month}月已发': True,
'可结转': max(0, gp['可结转'] - 1),
'累计已发结转': gp.get('累计已发结转', 0) + 1,
'虚拟': True,
}
for bm in range(1, month):
G[month][k][f'补发{bm}月'] = 0
all_data[month] = data_m
pay_records = collect_payments_s3(G, all_data)
return D[5], G[5], all_data.get(5, {}), pay_records
def collect_payments_s3(G, all_data):
"""收集方案3的5月发放记录"""
records = []
m5 = all_data.get(5, {})
for cat, items in m5.items():
for d in items:
records.append({
'车牌': d['车牌'], '销售': d['销售'], '部门': d.get('部门', ''),
'金额': d['额'], '类型': cat, '结算月': 5
})
records.sort(key=lambda x: (x['车牌'], x['类型']))
return records
# ============================================================
# Excel 输出
# ============================================================
def write_xlsx_s1(recs_5, G5, data5, pay_records, filepath):
"""方案1输出"""
wb = openpyxl.Workbook()
# Sheet 1: 规则
ws = wb.active
ws.title = '考核规则'
ws.append(['方案1: 保留原逻辑'])
ws.append(['考核目标', '羚牛100辆18T'])
ws.append(['月度目标里程', '6000km'])
ws.append(['奖励金额(达标)', '1000元/月 (按考核天数/当月天数折算)'])
ws.append(['结转奖励', '1000元/次 (消耗1个月目标)'])
ws.append(['补发/累计补发', '1000元/月 (按考核天数/当月天数折算)'])
ws.append(['规则', '达标→1000, 结转→1000, 补发→1000×折算, 累计补发当月→1000×折算'])
set_col_widths(ws, [30, 60])
# Sheet 2: 5月里程明细
write_mileage_detail(wb, recs_5, '5月里程明细')
# Sheet 3: 计算过程
ws3 = wb.create_sheet('5月计算过程')
ws3.append(['车牌号', '销售经理', '部门', '5月应考核', '5月实际', '5月达标',
'累计应考核', '累计实际', '累计达标', '可结转', '结转发放',
'当月奖金', '累计补发5月', '补发1月', '补发2月', '补发3月', '补发4月',
'5月总发放'])
for k, g in sorted(G5.items()):
if g.get('虚拟'):
continue
cum_q_str = '是' if g.get('cum_q') else '否'
dq_str = '是' if g['有达标'] else '否'
total = (g.get('结转', 0) + g.get('当月奖金', 0) + g.get('累计补发当月', 0) +
g.get('补发1月', 0) + g.get('补发2月', 0) + g.get('补发3月', 0) +
g.get('补发4月', 0))
ws3.append([k[0], k[1], g['部门'], g['应考核'], g['实际'], dq_str,
g.get('cum_t', ''), g.get('cum_a', ''), cum_q_str,
g.get('可结转', 0), g.get('结转', 0),
g.get('当月奖金', 0), g.get('累计补发当月', 0),
g.get('补发1月', 0), g.get('补发2月', 0), g.get('补发3月', 0),
g.get('补发4月', 0), total])
set_col_widths(ws3, [14, 10, 30, 12, 12, 10, 12, 12, 10, 10, 12, 12, 14, 12, 12, 12, 12, 14])
# Sheet 4: 发放明细
ws4 = wb.create_sheet('5月发放明细')
ws4.append(['车牌号', '销售经理', '部门', '金额', '发放类型'])
for r in pay_records:
ws4.append([r['车牌'], r['销售'], r['部门'], round(r['金额'], 2), r['类型']])
# 汇总行
total = sum(r['金额'] for r in pay_records)
ws4.append([])
ws4.append(['', '', '合计', round(total, 2), ''])
set_col_widths(ws4, [14, 10, 30, 12, 20])
# Sheet 5: 汇总
ws5 = wb.create_sheet('汇总')
ws5.append(['方案1: 原逻辑 (1000元基准)'])
ws5.append(['发放类型', '辆数', '金额合计'])
from collections import defaultdict
cat_sum = defaultdict(float)
cat_cnt = defaultdict(int)
for r in pay_records:
cat_sum[r['类型']] += r['金额']
cat_cnt[r['类型']] += 1
for cat in ['当月', '结转', '累计补发5月', '补发1月', '补发2月', '补发3月', '补发4月']:
if cat in cat_sum:
ws5.append([cat, cat_cnt[cat], round(cat_sum[cat], 2)])
ws5.append(['合计', sum(cat_cnt.values()), round(sum(cat_sum.values()), 2)])
set_col_widths(ws5, [20, 10, 15])
wb.save(filepath)
print(f' ✅ {filepath}')
def write_xlsx_s2(recs_5, pay_records, filepath):
"""方案2输出"""
wb = openpyxl.Workbook()
# Sheet 1: 规则
ws = wb.active
ws.title = '考核规则'
ws.append(['方案2: 新测算简化版 (仅当月)'])
ws.append(['考核目标', '羚牛100辆18T'])
ws.append(['月度目标里程', '6000km'])
ws.append(['当月达标', '800元 × (考核天数/31天)'])
ws.append(['当月未达标', '300元 × (考核天数/31天)'])
ws.append(['结转/补发/累计', '不计算'])
ws.append(['规则', '只看5月当月,按(车牌+销售)分组统计'])
set_col_widths(ws, [30, 60])
# Sheet 2: 5月里程明细
write_mileage_detail(wb, recs_5, '5月里程明细')
# Sheet 3: 发放明细
ws3 = wb.create_sheet('5月发放明细')
ws3.append(['车牌号', '销售经理', '部门', '金额', '发放类型'])
for r in pay_records:
ws3.append([r['车牌'], r['销售'], r['部门'], round(r['金额'], 2), r['类型']])
total = sum(r['金额'] for r in pay_records)
ws3.append([])
ws3.append(['', '', '合计', round(total, 2), ''])
set_col_widths(ws3, [14, 10, 30, 12, 20])
# Sheet 4: 汇总
ws4 = wb.create_sheet('汇总')
ws4.append(['方案2: 简化版 (800/300, 仅当月)'])
ws4.append(['发放类型', '辆数', '金额合计'])
cat_sum = defaultdict(float)
cat_cnt = defaultdict(int)
for r in pay_records:
cat_sum[r['类型']] += r['金额']
cat_cnt[r['类型']] += 1
for cat in sorted(cat_sum.keys()):
ws4.append([cat, cat_cnt[cat], round(cat_sum[cat], 2)])
ws4.append(['合计', sum(cat_cnt.values()), round(sum(cat_sum.values()), 2)])
set_col_widths(ws4, [25, 10, 15])
wb.save(filepath)
print(f' ✅ {filepath}')
def write_xlsx_s3(recs_5, G5, data5, pay_records, filepath):
"""方案3输出"""
wb = openpyxl.Workbook()
# Sheet 1: 规则
ws = wb.active
ws.title = '考核规则'
ws.append(['方案3: 新测算完整版 (800/300/500, 结转800 补发500)'])
ws.append(['考核目标', '羚牛100辆18T'])
ws.append(['月度目标里程', '6000km'])
ws.append(['当月达标', '800元 × (考核天数/31天)'])
ws.append(['当月未达标', '300元 × (考核天数/31天)'])
ws.append(['结转奖励', '800元/次 (消耗1个月目标)'])
ws.append(['补发历史月', '500元 × (历史月考核天数/历史月天数)'])
ws.append(['累计补发当月', '500元 × (当月考核天数/31天)'])
ws.append(['规则', '当月达标800/未达标300 → 结转800 → 补发500 → 累计补发500'])
set_col_widths(ws, [30, 60])
# Sheet 2: 5月里程明细
write_mileage_detail(wb, recs_5, '5月里程明细')
# Sheet 3: 计算过程
ws3 = wb.create_sheet('5月计算过程')
ws3.append(['车牌号', '销售经理', '部门', '5月应考核', '5月实际', '5月达标',
'累计应考核', '累计实际', '累计达标', '可结转', '结转发放(800)',
'当月奖金', '累计补发5月(500)', '补发1月', '补发2月', '补发3月', '补发4月',
'5月总发放'])
for k, g in sorted(G5.items()):
if g.get('虚拟'):
continue
cum_q_str = '是' if g.get('cum_q') else '否'
dq_str = '是' if g['有达标'] else '否'
total = (g.get('结转', 0) + g.get('当月奖金', 0) + g.get('累计补发当月', 0) +
g.get('补发1月', 0) + g.get('补发2月', 0) + g.get('补发3月', 0) +
g.get('补发4月', 0))
ws3.append([k[0], k[1], g['部门'], g['应考核'], g['实际'], dq_str,
g.get('cum_t', ''), g.get('cum_a', ''), cum_q_str,
g.get('可结转', 0), g.get('结转', 0),
g.get('当月奖金', 0), g.get('累计补发当月', 0),
g.get('补发1月', 0), g.get('补发2月', 0), g.get('补发3月', 0),
g.get('补发4月', 0), total])
set_col_widths(ws3, [14, 10, 30, 12, 12, 10, 12, 12, 10, 10, 14, 12, 16, 12, 12, 12, 12, 14])
# Sheet 4: 发放明细
ws4 = wb.create_sheet('5月发放明细')
ws4.append(['车牌号', '销售经理', '部门', '金额', '发放类型'])
for r in pay_records:
ws4.append([r['车牌'], r['销售'], r['部门'], round(r['金额'], 2), r['类型']])
total = sum(r['金额'] for r in pay_records)
ws4.append([])
ws4.append(['', '', '合计', round(total, 2), ''])
set_col_widths(ws4, [14, 10, 30, 12, 20])
# Sheet 5: 汇总
ws5 = wb.create_sheet('汇总')
ws5.append(['方案3: 完整版 (800/300/500)'])
ws5.append(['发放类型', '辆数', '金额合计'])
cat_sum = defaultdict(float)
cat_cnt = defaultdict(int)
for r in pay_records:
cat_sum[r['类型']] += r['金额']
cat_cnt[r['类型']] += 1
for cat in ['当月达标(800)', '当月未达标(300)', '结转', '累计补发5月', '补发1月', '补发2月', '补发3月', '补发4月']:
if cat in cat_sum:
ws5.append([cat, cat_cnt[cat], round(cat_sum[cat], 2)])
ws5.append(['合计', sum(cat_cnt.values()), round(sum(cat_sum.values()), 2)])
set_col_widths(ws5, [20, 10, 15])
wb.save(filepath)
print(f' ✅ {filepath}')
def write_mileage_detail(wb, recs, sheet_name):
"""里程明细sheet"""
ws = wb.create_sheet(sheet_name)
ws.append(['车牌号', '销售经理', '部门', '客户', '考核天数', '应考核里程',
'实际里程', '是否达标', '月度目标'])
for r in recs:
ws.append([r['车牌号'], r['销售经理'], r.get('部门名称', ''),
r.get('客户名称', ''), r['考核天数'], r['应考核里程(km)'],
r['实际行驶里程(km)'], r['是否达标'], TARGET_KM])
set_col_widths(ws, [14, 10, 30, 35, 10, 12, 12, 10, 10])
def set_col_widths(ws, widths):
for i, w in enumerate(widths, 1):
ws.column_dimensions[openpyxl.utils.get_column_letter(i)].width = w
# ============================================================
# 主流程
# ============================================================
def main():
os.makedirs(OUTPUT_DIR, exist_ok=True)
# 所有计算
recs_5, G5_s1, data5_s1, pay_s1 = calc_s1()
recs_5_s2, pay_s2 = calc_s2()
recs_5_s3, G5_s3, data5_s3, pay_s3 = calc_s3()
# 汇总各方案
import unicodedata
def vw(s):
return sum(2 if unicodedata.east_asian_width(c) in ('F','W') else 1 for c in str(s))
def padr(s, w):
return ' ' * (w - vw(s)) + str(s)
def padl(s, w):
return str(s) + ' ' * (w - vw(s))
def cats(dl, *keys):
cnt = sum(sum(1 for r in dl if r['类型']==k) for k in keys)
amt = sum(sum(r['金额'] for r in dl if r['类型']==k) for k in keys)
return cnt, amt
def cell(amt, cnt):
if cnt == 0: return '—'
return f'{amt:,.2f} ({cnt}辆)'
rows = [
('当月达标', cats(pay_s1, '当月'), cats(pay_s2, '当月达标(800)'), cats(pay_s3, '当月达标(800)')),
('当月未达标', cats(pay_s1), cats(pay_s2, '当月未达标(300)'), cats(pay_s3, '当月未达标(300)')),
('结转', cats(pay_s1, '结转'), cats(pay_s2), cats(pay_s3, '结转')),
('累计补发当月', cats(pay_s1, '累计补发5月'), cats(pay_s2), cats(pay_s3, '累计补发5月')),
('补发2月', cats(pay_s1, '补发2月'), cats(pay_s2), cats(pay_s3, '补发2月')),
('补发3月', cats(pay_s1, '补发3月'), cats(pay_s2), cats(pay_s3, '补发3月')),
('补发4月', cats(pay_s1, '补发4月'), cats(pay_s2), cats(pay_s3, '补发4月')),
]
t1 = sum(r['金额'] for r in pay_s1)
t2 = sum(r['金额'] for r in pay_s2)
t3 = sum(r['金额'] for r in pay_s3)
H1, H2, H3 = '方案1: 原逻辑 (1000)', '方案2: 简化 (800/300)', '方案3: 完整 (结转800 补发500)'
W0, W1, W2, W3 = 15, 24, 24, 27
print()
print(' 三方案对比')
print(f" ┌{'─'*W0}{'─'*W1}{'─'*W2}{'─'*W3}┐")
print(f" │{padl('',W0)}{padl(H1,W1)}{padl(H2,W2)}{padl(H3,W3)}│")
for i, (label, vs1, vs2, vs3) in enumerate(rows):
if i == 2:
print(f" ├{'─'*W0}{'─'*W1}{'─'*W2}{'─'*W3}┤")
c1 = cell(vs1[1], vs1[0]); c2 = cell(vs2[1], vs2[0]); c3 = cell(vs3[1], vs3[0])
print(f" │{padl(label,W0)}{padr(c1,W1)}{padr(c2,W2)}{padr(c3,W3)}│")
print(f" ├{'─'*W0}{'─'*W1}{'─'*W2}{'─'*W3}┤")
print(f" │{padl('合计',W0)}{padr(f'{t1:,.2f}',W1)}{padr(f'{t2:,.2f}',W2)}{padr(f'{t3:,.2f}',W3)}│")
print(f" └{'─'*W0}{'─'*W1}{'─'*W2}{'─'*W3}┘")
# 写Excel
print("\n生成Excel文件...")
write_xlsx_s1(recs_5, G5_s1, data5_s1, pay_s1,
os.path.join(OUTPUT_DIR, '方案1_原逻辑_1000基准.xlsx'))
write_xlsx_s2(recs_5_s2, pay_s2,
os.path.join(OUTPUT_DIR, '方案2_简化版_800_300仅当月.xlsx'))
write_xlsx_s3(recs_5_s3, G5_s3, data5_s3, pay_s3,
os.path.join(OUTPUT_DIR, '方案3_完整版_800_300_500.xlsx'))
print(f"文件输出到: {OUTPUT_DIR}/")
if __name__ == '__main__':
main()