V3.0.0 叠加客户亏损筛选,生成最终发放记录

新增功能:
- 读取1月/2月亏损表,按客户名称匹配考核数据
- 车辆考核追踪新增列:客户名称、客户是否亏损、考核应发、最终发放、未发放原因
- 月汇总新增亏损筛选section:亏损拦截/未匹配/最终发放/汇总
- 3月无亏损表,全部正常发放
- 亏损拦截不补发

规则:客户亏损→该客户下所有车不发;未匹配→标注待人工确认

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-04-03 09:14:37 +08:00
parent 26361f3a95
commit e814013320
3 changed files with 162 additions and 17 deletions

View File

@@ -179,6 +179,47 @@ VEHICLE_TARGET_MAP = {
('现代氢能科技(广州)有限公司', '4.5吨货车'): ('恒运50辆4.5T普货', 5000, 260), ('现代氢能科技(广州)有限公司', '4.5吨货车'): ('恒运50辆4.5T普货', 5000, 260),
} }
def read_loss_data(month):
"""读取亏损表,返回 {客户名称: ''/''} 字典。无亏损表返回None"""
import os
fp = f'{month}月.xlsx'
if not os.path.exists(fp):
return None
wb = openpyxl.load_workbook(fp, data_only=True)
ws = wb[wb.sheetnames[0]]
h = [c.value for c in next(ws.iter_rows(min_row=1, max_row=1))]
# 1月: 列名"项目","1月是否亏损"
# 2月: 列名"客户名称","是否亏损"
client_col = None; loss_col = None
for i, col_name in enumerate(h):
if col_name and ('项目' in str(col_name) or '客户' in str(col_name)):
client_col = i
if col_name and '亏损' in str(col_name):
loss_col = i
if client_col is None or loss_col is None:
wb.close()
return None
result = {}
for row in ws.iter_rows(min_row=2, values_only=True):
client = row[client_col]
loss = row[loss_col]
if client:
result[str(client).strip()] = str(loss).strip() if loss else ''
wb.close()
return result
def get_vehicle_client_map(D):
"""从考核源数据构建 {车牌号: 客户名称} 映射(取最新月的客户名)"""
plate_client = {}
for m in sorted(D.keys()):
for r in D[m]:
plate_client[r['车牌号']] = r.get('客户名称', '')
return plate_client
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

@@ -162,14 +162,44 @@ def write_calc_process_mar(wb, G1, G2, G3, feb_data):
c5,R(cbp3) if cbp3>0 else 0,R(total) if total>0 else 0]); rn+=1 c5,R(cbp3) if cbp3>0 else 0,R(total) if total>0 else 0]); rn+=1
AW(ws) AW(ws)
def write_summary_jan(wb, records): def write_summary_jan(wb, records, loss_data=None, plate_client=None):
ws = wb.create_sheet('1月汇总') ws = wb.create_sheet('1月汇总')
jan_dl=[{'车牌':r['车牌号'],'销售':r['销售经理'],'部门':r['部门名称'],'':r['奖金']} for r in records if r['是否达标']=='达标'] jan_dl=[{'车牌':r['车牌号'],'销售':r['销售经理'],'部门':r['部门名称'],'':r['奖金']} for r in records if r['是否达标']=='达标']
rn=write_sec(ws,1,'1月达标奖励',jan_dl) rn=write_sec(ws,1,'1月达标奖励(考核应发)',jan_dl)
if loss_data is not None:
# 亏损拦截
blocked = []
passed = []
unmatched = []
for d in jan_dl:
client = (plate_client or {}).get(d['车牌'], '')
status = loss_data.get(client, '未匹配') if client else '未匹配'
if status == '':
blocked.append({**d, '客户': client})
elif status == '未匹配':
unmatched.append({**d, '客户': client})
else:
passed.append(d)
rn = write_sec(ws, rn, '亏损拦截(客户亏损不发放)', blocked if blocked else [])
rn = write_sec(ws, rn, '未匹配亏损表(需人工确认)', unmatched if unmatched else [])
rn = write_sec(ws, rn, '最终发放', passed)
ws.cell(row=rn, column=1, value='汇总').font=Font(bold=True, size=11); rn+=1
total_考核 = sum(d[''] for d in jan_dl)
total_拦截 = sum(d[''] for d in blocked)
total_未匹配 = sum(d[''] for d in unmatched)
total_最终 = sum(d[''] for d in passed)
WR(ws, rn, ['考核应发', R(total_考核)]); rn+=1
WR(ws, rn, ['亏损拦截', R(total_拦截)]); rn+=1
WR(ws, rn, ['未匹配(待确认)', R(total_未匹配)]); rn+=1
WR(ws, rn, ['最终发放', R(total_最终)]); ws.cell(row=rn,column=1).font=Font(bold=True); rn+=2
write_total(ws,rn,1,{'达标':jan_dl}) write_total(ws,rn,1,{'达标':jan_dl})
AW(ws) AW(ws)
def write_summary_month(wb, month, month_data, section_names): def write_summary_month(wb, month, month_data, section_names, loss_data=None, plate_client=None):
ws = wb.create_sheet(f'{month}月汇总') ws = wb.create_sheet(f'{month}月汇总')
rn=1 rn=1
for i,cat in enumerate(section_names): for i,cat in enumerate(section_names):
@@ -178,7 +208,47 @@ def write_summary_month(wb, month, month_data, section_names):
'当月':f'{"" if month==2 else ""}{month}月当月奖励', '当月':f'{"" if month==2 else ""}{month}月当月奖励',
f'累计补发{month}':f'{"" if month==2 else ""}、累计达标补发{month}'} f'累计补发{month}':f'{"" if month==2 else ""}、累计达标补发{month}'}
rn=write_sec(ws,rn,label_map.get(cat,cat),month_data.get(cat,[])) rn=write_sec(ws,rn,label_map.get(cat,cat),month_data.get(cat,[]))
# 考核应发合计
write_total(ws,rn,month,month_data) write_total(ws,rn,month,month_data)
# 找最后一行
rn = ws.max_row + 2
if loss_data is not None:
# 合并所有发放记录
all_dl = []
for cat, dl in month_data.items():
all_dl.extend(dl)
blocked = []; passed = []; unmatched = []
for d in all_dl:
client = (plate_client or {}).get(d['车牌'], '')
status = loss_data.get(client, '未匹配') if client else '未匹配'
if status == '':
blocked.append(d)
elif status == '未匹配':
unmatched.append(d)
else:
passed.append(d)
ws.cell(row=rn, column=1, value='═══ 亏损筛选 ═══').font=Font(bold=True, size=12); rn+=2
rn = write_sec(ws, rn, '亏损拦截(客户亏损不发放)', blocked)
rn = write_sec(ws, rn, '未匹配亏损表(需人工确认)', unmatched)
rn = write_sec(ws, rn, '最终发放', passed)
total_考核 = sum(d[''] for d in all_dl)
total_拦截 = sum(d[''] for d in blocked)
total_未匹配 = sum(d[''] for d in unmatched)
total_最终 = sum(d[''] for d in passed)
ws.cell(row=rn, column=1, value='亏损筛选汇总').font=Font(bold=True, size=11); rn+=1
WR(ws, rn, ['考核应发合计', R(total_考核)]); rn+=1
WR(ws, rn, ['亏损拦截金额', R(total_拦截)]); rn+=1
WR(ws, rn, ['未匹配金额(待确认)', R(total_未匹配)]); rn+=1
WR(ws, rn, ['最终应发合计', R(total_最终)])
ws.cell(row=rn,column=1).font=Font(bold=True, size=12)
ws.cell(row=rn,column=2).font=Font(bold=True, size=12)
AW(ws) AW(ws)
# ============================================================ # ============================================================
@@ -339,7 +409,7 @@ def write_salesperson_sheet(wb, person, dept, settle_month, D, G, month_data, ve
# 新增车辆考核追踪sheet # 新增车辆考核追踪sheet
# ============================================================ # ============================================================
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, loss_data=None, plate_client=None):
ws = wb.create_sheet('车辆考核追踪') ws = wb.create_sheet('车辆考核追踪')
from calc_engine import VEHICLE_TARGET_MAP, DAYS from calc_engine import VEHICLE_TARGET_MAP, DAYS
@@ -360,7 +430,11 @@ def write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_p
headers += [f'{m}月应考核', f'{m}月实际', f'{m}月达标'] headers += [f'{m}月应考核', f'{m}月实际', f'{m}月达标']
if settle_month >= 2: if settle_month >= 2:
headers += ['累计应完成','累计实际','累计达标'] headers += ['累计应完成','累计实际','累计达标']
headers += ['本月发放金额','发放类型','已发期数','已发金额','剩余期数'] if loss_data is not None:
headers += ['客户名称','客户是否亏损','考核应发','最终发放','未发放原因']
else:
headers += ['本月发放金额','发放类型']
headers += ['已发期数','已发金额','剩余期数']
WH(ws, headers) WH(ws, headers)
ws.freeze_panes = 'H2' # 冻结车辆信息+业务员列 ws.freeze_panes = 'H2' # 冻结车辆信息+业务员列
@@ -445,12 +519,26 @@ def write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_p
# 本月发放(该业务员的) # 本月发放(该业务员的)
tp = [p for p in pays if p['结算月'] == settle_month and p['业务员'] == person] tp = [p for p in pays if p['结算月'] == settle_month and p['业务员'] == person]
if tp: 考核应发 = sum(p['金额'] for p in tp) if tp else 0
amt = sum(p['金额'] for p in tp) pay_types = ', '.join(p['类型'] for p in tp) if tp else ''
types = ', '.join(p['类型'] for p in tp)
row += [R(amt), types] if loss_data is not None:
# 有亏损表:加客户名称、亏损状态、考核应发、最终发放、未发放原因
client = (plate_client or {}).get(plate, '')
loss_status = loss_data.get(client, '未匹配') if client else '未匹配'
if loss_status == '':
final_amt = 0
reason = '客户亏损不发放' if 考核应发 > 0 else ''
elif loss_status == '未匹配':
final_amt = 0
reason = '未匹配亏损表' if 考核应发 > 0 else ''
else: else:
row += [0, ''] final_amt = 考核应发
reason = ''
row += [client, loss_status, R(考核应发), R(final_amt), reason]
else:
# 无亏损表:直接发放
row += [R(考核应发), pay_types]
# 奖金池(整车,只在第一行显示) # 奖金池(整车,只在第一行显示)
if pi == 0: if pi == 0:

30
main.py
View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""按月生成独立Excel文件 - 里程考核绩效""" """按月生成独立Excel文件 - 里程考核绩效(含亏损筛选)"""
import os, openpyxl import os, openpyxl
from calc_engine import * from calc_engine import *
from excel_writer import * from excel_writer import *
@@ -23,6 +23,22 @@ mar_data = calc_mar(G[1], G[2], G[3], feb_data)
vehicle_payments, vehicle_info = collect_vehicle_payments(G, feb_data, mar_data) vehicle_payments, vehicle_info = collect_vehicle_payments(G, feb_data, mar_data)
master_vehicles = read_master_vehicles() master_vehicles = read_master_vehicles()
# 亏损表
print("\n读取亏损表...")
loss_data = {}
for m in [1,2,3]:
ld = read_loss_data(m)
if ld:
loss_clients = sum(1 for v in ld.values() if v == '')
print(f" {m}月: {len(ld)}个客户, 其中亏损{loss_clients}")
loss_data[m] = ld
else:
print(f" {m}月: 无亏损表")
loss_data[m] = None
# 车牌→客户名称映射
plate_client = get_vehicle_client_map(D)
# 所有业务员 # 所有业务员
all_persons = {} all_persons = {}
for m in G: for m in G:
@@ -33,8 +49,8 @@ for m in G:
jan_total = sum(r['奖金'] for r in D[1] if r['是否达标']=='达标') jan_total = sum(r['奖金'] for r in D[1] if r['是否达标']=='达标')
feb_total = sum(sum(d[''] for d in v) for v in feb_data.values()) feb_total = sum(sum(d[''] for d in v) for v in feb_data.values())
mar_total = sum(sum(d[''] for d in v) for v in mar_data.values()) mar_total = sum(sum(d[''] for d in v) for v in mar_data.values())
print(f"\n1月: {jan_total:.2f}, 2月: {feb_total:.2f}, 3月: {mar_total:.2f}") print(f"\n考核应发: 1月{jan_total:.2f}, 2月{feb_total:.2f}, 3月{mar_total:.2f}")
print(f"Q1计: {jan_total + feb_total + mar_total:.2f}") print(f"Q1考核应发合计: {jan_total + feb_total + mar_total:.2f}")
# ============================================================ # ============================================================
# 按月生成 # 按月生成
@@ -68,14 +84,14 @@ for settle_month in [1, 2, 3]:
# Sheet 4: 汇总 # Sheet 4: 汇总
if settle_month == 1: if settle_month == 1:
write_summary_jan(wb, D[1]) write_summary_jan(wb, D[1], loss_data[1], plate_client)
elif settle_month == 2: elif settle_month == 2:
write_summary_month(wb, 2, feb_data, ['结转','补发1月','当月','累计补发2月']) write_summary_month(wb, 2, feb_data, ['结转','补发1月','当月','累计补发2月'], loss_data[2], plate_client)
else: else:
write_summary_month(wb, 3, mar_data, ['结转','补发1月','补发2月','当月','累计补发3月']) write_summary_month(wb, 3, mar_data, ['结转','补发1月','补发2月','当月','累计补发3月'], loss_data[3], plate_client)
# Sheet 5: 车辆考核追踪 # Sheet 5: 车辆考核追踪
write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_payments, vehicle_info) write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_payments, vehicle_info, loss_data[settle_month], plate_client)
# Sheet 6-17: 业务员 # Sheet 6-17: 业务员
for person in sorted(all_persons.keys()): for person in sorted(all_persons.keys()):