5 Commits

Author SHA1 Message Date
kkfluous
f2de5d5500 feat: 车辆考核追踪sheet移到业务员sheet前面
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:01:13 +08:00
kkfluous
54ecbc352f feat: 车辆考核追踪sheet业务员名加部门前缀
考核明细和发放明细中的业务员统一显示为"X部-姓名"格式。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:00:27 +08:00
kkfluous
cdc4cec2ff feat: 业务员sheet命名加部门前缀(如"二部-刘念念")
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:53:03 +08:00
kkfluous
8c2f4e73dd fix: 车辆追踪补全所有492辆车的考核目标和月度奖励
通过(归属公司+车型)→考核目标映射,补全109辆无考核记录车辆的考核目标。
含全角/半角括号兼容(现代氢能科技)。
现在492辆车全部有考核目标和月度奖励金额。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:47:41 +08:00
kkfluous
e47cc00b3d V2.3.0 车辆考核追踪:多人多条用单元格内换行显示
同车多个销售经理在同一单元格内换行展示:
- X月考核明细: "刘念念: 4834/2903 达标\n董剑煜: 1294/2710 未达标"
- 本月发放明细: "赵连飞: 150(结转)\n董剑煜: 260(当月达标)"
设置wrap_text自动换行,一车一行不拆分。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:51:16 +08:00
3 changed files with 66 additions and 22 deletions

View File

@@ -169,6 +169,16 @@ def collect_vehicle_payments(G, feb_data, mar_data):
for i, r in enumerate(records): r['期数'] = i + 1 for i, r in enumerate(records): r['期数'] = i + 1
return payments, info return payments, info
# (归属公司+车型) → 考核目标 映射,用于补全无考核记录的车辆
VEHICLE_TARGET_MAP = {
('广州开发区交投氢能运营管理有限公司', '4.5吨冷链车'): ('交投190辆4.5T冷链车', 3000, 150),
('广州开发区交投氢能运营管理有限公司', '4.5吨货车'): ('交投40辆4.5T普货', 3000, 150),
('羚牛氢能科技(广东)有限公司', '4.5吨冷链车'): ('羚牛136辆4.5T冷链车', 5000, 260),
('羚牛氢能科技(广东)有限公司', '18吨双飞翼货车'): ('羚牛100辆18T', 6000, 1000),
('现代氢能科技(广州)有限公司', '4.5吨货车'): ('恒运50辆4.5T普货', 5000, 260),
('现代氢能科技(广州)有限公司', '4.5吨货车'): ('恒运50辆4.5T普货', 5000, 260),
}
def read_master_vehicles(fp='里程任务考核_Q1汇总.xlsx'): def read_master_vehicles(fp='里程任务考核_Q1汇总.xlsx'):
"""从现有Q1汇总文件读取全量车辆台账""" """从现有Q1汇总文件读取全量车辆台账"""
wb = openpyxl.load_workbook(fp, data_only=True) wb = openpyxl.load_workbook(fp, data_only=True)

View File

@@ -186,7 +186,8 @@ def write_summary_month(wb, month, month_data, section_names):
# ============================================================ # ============================================================
def write_salesperson_sheet(wb, person, dept, settle_month, D, G, month_data, vehicle_payments): def write_salesperson_sheet(wb, person, dept, settle_month, D, G, month_data, vehicle_payments):
ws = wb.create_sheet(f'业务员_{person}') short_dept = dept.replace('业务','') if '业务' in dept else dept
ws = wb.create_sheet(f'{short_dept}-{person}')
ws.cell(row=1,column=1,value=f'{person} | {dept} | {settle_month}月绩效对账单').font=Font(bold=True,size=14) ws.cell(row=1,column=1,value=f'{person} | {dept} | {settle_month}月绩效对账单').font=Font(bold=True,size=14)
plates = set() plates = set()
@@ -340,46 +341,79 @@ def write_salesperson_sheet(wb, person, dept, settle_month, D, G, month_data, ve
def write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_payments, vehicle_info): def write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_payments, vehicle_info):
ws = wb.create_sheet('车辆考核追踪') ws = wb.create_sheet('车辆考核追踪')
wrap_align = Alignment(wrap_text=True, vertical='top')
# 表头:每月用一列"业务员/里程/目标/达标"(多人换行)
headers = ['车牌号','车架号','归属公司','车型','考核目标','月度奖励金额'] headers = ['车牌号','车架号','归属公司','车型','考核目标','月度奖励金额']
for m in range(1, settle_month+1): for m in range(1, settle_month+1):
headers += [f'{m}月业务员',f'{m}考核',f'{m}月实际',f'{m}月达标'] headers.append(f'{m}月考核明细')
if settle_month >= 2: if settle_month >= 2:
headers += ['累计应完成','累计实际','累计达标'] headers += ['累计里程/目标','累计达标']
headers += ['本月发放金额','发放给谁','发放类型','累计已发期数','累计已发金额','剩余可发期数'] headers += ['本月发放明细','累计已发期数','累计已发金额','剩余可发期数']
WH(ws, headers) WH(ws, headers)
from calc_engine import VEHICLE_TARGET_MAP, RULES
rn=2 rn=2
for mv in master_vehicles: for mv in master_vehicles:
plate=mv['车牌号'] plate=mv['车牌号']
info=vehicle_info.get(plate, {}) info=vehicle_info.get(plate, {})
pays=vehicle_payments.get(plate, []) pays=vehicle_payments.get(plate, [])
# 补全缺失的考核目标
target_name = info.get('考核目标','')
monthly_bonus = info.get('月度奖励',0)
if not target_name:
company = mv.get('归属公司','')
vtype = mv.get('车型确定','')
mapped = VEHICLE_TARGET_MAP.get((company, vtype))
if mapped:
target_name, _, monthly_bonus = mapped
row=[plate,mv.get('车架号',''),mv.get('归属公司',''),mv.get('车型确定',''), row=[plate,mv.get('车架号',''),mv.get('归属公司',''),mv.get('车型确定',''),
info.get('考核目标',''),info.get('月度奖励',0) or ''] target_name or '',monthly_bonus or '']
cum_t=0; cum_a=0 cum_t=0; cum_a=0
for m in range(1, settle_month+1): for m in range(1, settle_month+1):
mgs=[(k,g) for k,g in G.get(m,{}).items() if k[0]==plate] mgs=[(k,g) for k,g in G.get(m,{}).items() if k[0]==plate]
if mgs: if mgs:
persons=', '.join(sorted(set(g['销售'] for _,g in mgs))) # 每个(车牌,销售)组一行,换行显示
ts=sum(g['应考核'] for _,g in mgs); As=sum(g['实际'] for _,g in mgs) lines = []
q='达标' if any(g['有达标'] for _,g in mgs) else '未达标' for _,g in sorted(mgs, key=lambda x: x[0][1]):
cum_t+=ts; cum_a+=As t=g['应考核']; a=g['实际']
row+=[persons,R(ts),R(As),q] cum_t+=t; cum_a+=a
q='达标' if g['有达标'] else '未达标'
sd = g['部门'].replace('业务','') if '业务' in g.get('部门','') else g.get('部门','')
lines.append(f"{sd}-{g['销售']}: {R(a,0)}/{R(t,0)} {q}")
row.append('\n'.join(lines))
else: else:
row+=['','','',''] row.append('')
if settle_month>=2:
row+=[R(cum_t),R(cum_a),'达标' if (cum_a>=cum_t and cum_t>0) else '未达标']
# 本月发放(截至settle_month if settle_month>=2:
cum_q='达标' if (cum_a>=cum_t and cum_t>0) else '未达标'
row+=[f'{R(cum_a,0)}/{R(cum_t,0)}', cum_q]
# 本月发放明细(多人多类型换行)
tp=[p for p in pays if p['结算月']==settle_month] tp=[p for p in pays if p['结算月']==settle_month]
if tp: if tp:
row+=[R(sum(p['金额'] for p in tp)),', '.join(sorted(set(p['业务员'] for p in tp))), pay_lines = []
', '.join(sorted(set(p['类型'] for p in tp)))] for p in sorted(tp, key=lambda x: x['业务员']):
pd = p.get('部门','').replace('业务','') if '业务' in p.get('部门','') else p.get('部门','')
pay_lines.append(f"{pd}-{p['业务员']}: {R(p['金额'])}({p['类型']})")
row.append('\n'.join(pay_lines))
else: else:
row+=[0,'',''] row.append('')
# 奖金池截至settle_month # 奖金池截至settle_month
pays_to_date=[p for p in pays if p['结算月']<=settle_month] pays_to_date=[p for p in pays if p['结算月']<=settle_month]
tp_count=len(pays_to_date); tp_amt=sum(p['金额'] for p in pays_to_date) tp_count=len(pays_to_date); tp_amt=sum(p['金额'] for p in pays_to_date)
row+=[tp_count,R(tp_amt) if tp_amt>0 else 0,12-tp_count] row+=[tp_count,R(tp_amt) if tp_amt>0 else 0,12-tp_count]
WR(ws,rn,row); rn+=1
WR(ws,rn,row)
# 对含换行的单元格设置自动换行
for ci in range(len(row)):
cell = ws.cell(row=rn, column=ci+1)
if isinstance(cell.value, str) and '\n' in cell.value:
cell.alignment = wrap_align
rn+=1
AW(ws) AW(ws)

View File

@@ -74,13 +74,13 @@ for settle_month in [1, 2, 3]:
else: else:
write_summary_month(wb, 3, mar_data, ['结转','补发1月','补发2月','当月','累计补发3月']) write_summary_month(wb, 3, mar_data, ['结转','补发1月','补发2月','当月','累计补发3月'])
# Sheet 5-16: 业务员 # Sheet 5: 车辆考核追踪
write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_payments, vehicle_info)
# Sheet 6-17: 业务员
for person in sorted(all_persons.keys()): for person in sorted(all_persons.keys()):
write_salesperson_sheet(wb, person, all_persons[person], settle_month, D, G, month_data, vehicle_payments) write_salesperson_sheet(wb, person, all_persons[person], settle_month, D, G, month_data, vehicle_payments)
# Sheet 17: 车辆考核追踪
write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_payments, vehicle_info)
# 删除默认空sheet # 删除默认空sheet
if 'Sheet' in wb.sheetnames: if 'Sheet' in wb.sheetnames:
del wb['Sheet'] del wb['Sheet']