chore: curate monthly assessment evidence and scripts
This commit is contained in:
@@ -0,0 +1,741 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user