From 427359216017ac84659ca25493606e860446110a Mon Sep 17 00:00:00 2001 From: kkfluous Date: Thu, 2 Apr 2026 15:33:04 +0800 Subject: [PATCH] =?UTF-8?q?V2.2.0=20=E4=B8=9A=E5=8A=A1=E5=91=98sheet?= =?UTF-8?q?=E9=87=8D=E5=86=99=E4=B8=BA=E5=AF=B9=E8=B4=A6=E5=8D=95=E9=A3=8E?= =?UTF-8?q?=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 每辆车展示: - 第1行:车辆信息 + 各月里程/目标 + 累计 + 达标状态 - 下方:历史已发(哪月发的、金额、来源)+ 本月发放明细 + 奖金池 - "历史已发"列清楚展示防重复证据 - 发放说明包含关键数字(累计里程≥目标等) Co-Authored-By: Claude Opus 4.6 (1M context) --- excel_writer.py | 195 ++++++++++++++++++++++++++++-------------------- 1 file changed, 116 insertions(+), 79 deletions(-) diff --git a/excel_writer.py b/excel_writer.py index ae56d19..bccc0f6 100644 --- a/excel_writer.py +++ b/excel_writer.py @@ -187,111 +187,148 @@ 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): ws = wb.create_sheet(f'业务员_{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) - # 收集该人在1-settle_month中有记录的所有车牌 plates = set() for m in range(1, settle_month+1): for k in G.get(m, {}): if k[1] == person: plates.add(k[0]) - # 本月发放总额 person_total = sum(d['额'] for cat,dl in month_data.items() for d in dl if d['销售']==person) ws.cell(row=2,column=1,value=f'本月考核车辆: {len(plates)}辆 | 本月发放合计: {R(person_total)}元').font=Font(bold=True,size=11) - rn = 4 + # 表头:车辆信息 + 各月里程/目标 + 累计 + 奖金池 + headers = ['车牌号','考核目标','月奖励'] + for m in range(1, settle_month+1): + headers.append(f'{m}月里程/目标') + if settle_month >= 2: + headers += ['累计里程/目标'] + headers.append('达标') + WH(ws, headers, 4) + + rn = 5 for plate in sorted(plates): - # 车辆信息(取最近月的group) g_cur = None for m in range(settle_month, 0, -1): g_cur = G.get(m, {}).get((plate, person)) if g_cur: break if not g_cur: continue + first = g_cur['recs'][0] + mkm = g_cur['目标km'] - ws.cell(row=rn,column=1,value=plate).font=Font(bold=True,size=11) - ws.cell(row=rn,column=2,value=first.get('合同编号','')) - ws.cell(row=rn,column=3,value=first.get('客户名称','')) - ws.cell(row=rn,column=4,value=first.get('考核目标','')) - ws.cell(row=rn,column=5,value=f'月度目标{g_cur["目标km"]}km | 月奖励{g_cur["奖励额"]}元') - rn += 1 - - # 表头 - headers = ['月份','考核天数','应考核里程','实际里程','完成率','达标','奖金','发放说明'] - if settle_month >= 2: - headers += ['累计应完成','累计实际','累计达标'] - WH(ws, headers, rn); rn += 1 - - # 每月一行 + # 第1行:车辆信息 + 各月里程/目标 + row = [plate, first.get('考核目标',''), g_cur['奖励额']] cum_t = 0; cum_a = 0 for m in range(1, settle_month+1): gm = G.get(m, {}).get((plate, person)) - if not gm: - WR(ws, rn, [f'{m}月','','','','','无记录']); rn+=1; continue - - t=gm['应考核']; a=gm['实际']; cum_t+=t; cum_a+=a - qual='达标' if gm['有达标'] else '未达标' - rate=R(a/t*100,0) if t>0 else 0 - mkm=gm['目标km'] - - # 发放金额和说明(动态生成,包含关键数字) - pay_amt=0; desc='' - if m == settle_month: - carry=gm.get('结转',0); bonus=gm.get('当月奖金',0) - cum_bp=gm.get(f'累计补发{m}月',0) - excess=sum(r['多跑'] for r in gm['recs'] if r['是否达标']=='达标') - if carry>0: - # 结转:上月多跑够整月 - prev=m-1 - g_prev=G.get(prev,{}).get((plate,person)) - prev_excess=sum(r['多跑'] for r in g_prev['recs'] if r['是否达标']=='达标') if g_prev else 0 - desc=f'{prev}月多跑{R(prev_excess,0)}≥{mkm},结转(完整月奖金)' - pay_amt=carry - elif bonus>0: - desc='当月达标' - if excess>0 and int(excess//mkm)>=1: - desc+=f',多跑{R(excess,0)}≥{mkm}可结转' - pay_amt=bonus - elif cum_bp>0: - desc=f'1-{m}月累计{R(cum_a,0)}≥{R(cum_t,0)},累计达标补发' - pay_amt=cum_bp - elif gm.get('结转占位'): - desc='结转占位,本月不另发' - else: - desc=f'未达标(实际{R(a,0)}<目标{R(t,0)})' + if gm: + t=gm['应考核']; a=gm['实际']; cum_t+=t; cum_a+=a + row.append(f'{R(a,0)}/{R(t,0)}') else: - # 历史月份 - if gm['有达标']: - pay_amt=gm.get('奖金',0) - desc=f'({m}月已发{R(pay_amt)})' - else: - desc=f'未达标(实际{R(a,0)}<目标{R(t,0)})' - - row_data=[f'{m}月',gm['天数'],R(t),R(a),f'{rate}%',qual,R(pay_amt) if pay_amt>0 else 0,desc] - if settle_month>=2: - cum_q='达标' if (cum_a>=cum_t and cum_t>0) else '未达标' - row_data+=[R(cum_t),R(cum_a),cum_q] - WR(ws,rn,row_data); rn+=1 - - # 补发行(当前月补发之前月份) + row.append('-') if settle_month >= 2: - g_s = G.get(settle_month, {}).get((plate, person), {}) - for prev_m in range(1, settle_month): - bp_key = f'补发{prev_m}月' - bp_amt = g_s.get(bp_key, 0) if isinstance(g_s, dict) else 0 - if bp_amt > 0: - desc=f'1-{settle_month}月累计{R(cum_a,0)}≥{R(cum_t,0)},补发{prev_m}月' - row_data = [f'→补发{prev_m}月','','','','','',R(bp_amt),desc] - if settle_month>=2: row_data += ['','',''] - WR(ws,rn,row_data); rn+=1 + row.append(f'{R(cum_a,0)}/{R(cum_t,0)}') + cum_q = cum_a >= cum_t and cum_t > 0 + # 达标判断:取settle_month的group + g_s = G.get(settle_month, {}).get((plate, person)) + if g_s and g_s.get('有达标'): + row.append('达标') + elif cum_q: + row.append('累计达标') + else: + row.append('未达标') + WR(ws, rn, row) + ws.cell(row=rn, column=1).font = Font(bold=True) + rn += 1 + + # 第2-N行:发放明细(历史已发 + 本月发放) + WH(ws, ['', '发放项', '金额', '说明', '奖金池'], rn); rn += 1 - # 奖金池(只统计截至当前核算月的发放) pays = vehicle_payments.get(plate, []) pays_to_date = [p for p in pays if p['结算月'] <= settle_month] total_periods = len(pays_to_date) - plate_this = sum(p['金额'] for p in pays_to_date if p['结算月']==settle_month and p['业务员']==person) - ws.cell(row=rn,column=1,value=f'小计: {R(plate_this)}元 | 奖金池: 已发{total_periods}期/共12期, 剩余{12-total_periods}期').font=Font(italic=True) - rn += 2 + + # 历史已发(m < settle_month) + for m in range(1, settle_month): + gm = G.get(m, {}).get((plate, person)) + # 查该月是否有发放记录 + m_pays = [p for p in pays_to_date if p['结算月'] == m and p['业务员'] == person] + if m_pays: + amt = sum(p['金额'] for p in m_pays) + types = ', '.join(p['类型'] for p in m_pays) + WR(ws, rn, ['', f'{m}月: 已发', R(amt), types, '']) + elif gm: + # 有考核但未发 + # 检查是否在后续月被补发过(settle_month之前) + bp_pays = [p for p in pays_to_date if p['对应考核月'] == m and p['业务员'] == person] + if bp_pays: + amt = sum(p['金额'] for p in bp_pays) + sm = bp_pays[0]['结算月'] + WR(ws, rn, ['', f'{m}月: 已发', R(amt), f'{sm}月补发', '']) + else: + WR(ws, rn, ['', f'{m}月: 未发', 0, f'未达标(实际{R(gm["实际"],0)}<目标{R(gm["应考核"],0)})', '']) + else: + WR(ws, rn, ['', f'{m}月: 无记录', '', '', '']) + rn += 1 + + # 本月发放 + g_s = G.get(settle_month, {}).get((plate, person), {}) + if not isinstance(g_s, dict): g_s = {} + gm_s = G.get(settle_month, {}).get((plate, person)) + plate_this_month = 0 + + # 结转 + carry = g_s.get('结转', 0) + if carry > 0: + prev = settle_month - 1 + g_prev = G.get(prev, {}).get((plate, person)) + prev_excess = sum(r['多跑'] for r in g_prev['recs'] if r['是否达标'] == '达标') if g_prev else 0 + WR(ws, rn, ['', '结转', R(carry), f'{prev}月多跑{R(prev_excess,0)}≥{mkm},结转(完整月奖金)', '']) + plate_this_month += carry; rn += 1 + + # 补发过去月份 + for prev_m in range(1, settle_month): + bp_key = f'补发{prev_m}月' + bp_amt = g_s.get(bp_key, 0) + if bp_amt > 0: + WR(ws, rn, ['', f'补发{prev_m}月', R(bp_amt), f'累计{R(cum_a,0)}≥{R(cum_t,0)}达标,补发{prev_m}月', '']) + plate_this_month += bp_amt; rn += 1 + + # 当月 + bonus = g_s.get('当月奖金', 0) + if bonus > 0: + excess = sum(r['多跑'] for r in gm_s['recs'] if r['是否达标'] == '达标') if gm_s else 0 + desc = '当月达标' + if excess > 0 and mkm > 0 and int(excess // mkm) >= 1: + desc += f',多跑{R(excess,0)}≥{mkm}可结转' + WR(ws, rn, ['', f'{settle_month}月当月', R(bonus), desc, '']) + plate_this_month += bonus; rn += 1 + + # 累计补发当月 + cum_bp = g_s.get(f'累计补发{settle_month}月', 0) + if cum_bp > 0: + WR(ws, rn, ['', f'累计补发{settle_month}月', R(cum_bp), f'累计{R(cum_a,0)}≥{R(cum_t,0)}达标,补发{settle_month}月', '']) + plate_this_month += cum_bp; rn += 1 + + # 无发放 + if plate_this_month == 0 and carry == 0: + if gm_s: + a_s = gm_s['实际']; t_s = gm_s['应考核'] + if gm_s.get('结转占位'): + WR(ws, rn, ['', '本月不发', 0, '结转占位,本月不另发', '']) + else: + WR(ws, rn, ['', '本月不发', 0, f'未达标(实际{R(a_s,0)}<目标{R(t_s,0)}),累计也未达标', '']) + else: + WR(ws, rn, ['', '本月不发', 0, '本月无考核记录', '']) + rn += 1 + + # 本月合计 + 奖金池 + pool_str = f'已发{total_periods}期/共12期,剩余{12-total_periods}期' + WR(ws, rn, ['', '本月合计', R(plate_this_month), '', pool_str]) + ws.cell(row=rn, column=2).font = Font(bold=True) + ws.cell(row=rn, column=5).font = Font(italic=True) + rn += 2 # 空行 # 尾部 ws.cell(row=rn,column=1,value=f'{person} {settle_month}月合计: {len(plates)}辆车, 发放 {R(person_total)}元').font=Font(bold=True,size=12)