diff --git a/calc_engine.py b/calc_engine.py index 3ed24fc..c4c9b2d 100644 --- a/calc_engine.py +++ b/calc_engine.py @@ -98,6 +98,26 @@ def calc_feb(G1, G2): g2['可结转'] = fc + jr g2['2月已发'] = carry>0 or bonus2>0 or cbp2>0 g2['1月已补发'] = bp1>0 + + # 补充:1月有结转但2月无考核记录的车 → 创建虚拟2月group并发放结转 + for k, g1 in G1.items(): + if k not in G2 and g1['可结转'] >= 1: + bf = g1['奖励额'] + carry = bf + feb_data['结转'].append({'车牌':k[0],'销售':g1['销售'],'部门':g1['部门'],'额':carry}) + # 创建虚拟2月group(考核里程=满月目标,实际=0) + G2[k] = { + 'recs':[], '应考核':g1['目标km'], '实际':0, '奖金':0, '天数':DAYS[2], + '有达标':False, '目标km':g1['目标km'], '奖励额':g1['奖励额'], + '部门':g1['部门'], '销售':g1['销售'], '车牌':k[0], + 'cum_t':g1['应考核']+g1['目标km'], 'cum_a':g1['实际'], + 'cum_q': g1['实际'] >= g1['应考核']+g1['目标km'], + '结转':carry, '补发1月':0, '补发1月对应':'', + '当月奖金':0, '累计补发2月':0, '结转占位':True, + '可结转': max(0, g1['可结转'] - 1), + '2月已发':True, '1月已补发':False, + '虚拟':True, # 标记为无考核记录 + } return feb_data def calc_mar(G1, G2, G3, feb_data): @@ -134,6 +154,25 @@ def calc_mar(G1, G2, G3, feb_data): g3['结转']=carry; g3['补发1月']=bj; g3['补发1月对应']=g1['销售'] if g1 and bj>0 else '' g3['补发2月']=bf2; g3['补发2月对应']=g2['销售'] if g2 and bf2>0 else '' g3['当月奖金']=bonus3; g3['累计补发3月']=cbp3; g3['结转占位']=carry>0 + + # 补充:2月有结转但3月无考核记录的车 + for k, g2 in G2.items(): + if k not in G3 and g2.get('可结转', 0) >= 1: + bf = g2['奖励额'] + carry = bf + mar_data['结转'].append({'车牌':k[0],'销售':g2['销售'],'部门':g2['部门'],'额':carry}) + G3[k] = { + 'recs':[], '应考核':g2['目标km'], '实际':0, '奖金':0, '天数':DAYS[3], + '有达标':False, '目标km':g2['目标km'], '奖励额':g2['奖励额'], + '部门':g2['部门'], '销售':g2['销售'], '车牌':k[0], + 'cum_t':(G1.get(k,{}).get('应考核',0))+g2['应考核']+g2['目标km'], + 'cum_a':(G1.get(k,{}).get('实际',0))+g2['实际'], + 'cum_q':False, + '结转':carry, '补发1月':0, '补发1月对应':'', + '补发2月':0, '补发2月对应':'', + '当月奖金':0, '累计补发3月':0, '结转占位':True, + '虚拟':True, + } return mar_data def collect_vehicle_payments(G, feb_data, mar_data): diff --git a/excel_writer.py b/excel_writer.py index 08e4b39..a830949 100644 --- a/excel_writer.py +++ b/excel_writer.py @@ -199,14 +199,31 @@ def write_summary_jan(wb, records, loss_data=None, plate_client=None): write_total(ws,rn,1,{'达标':jan_dl}) AW(ws) -def build_payment_records(month, month_data, loss_data, plate_client): - """构建奖金发放记录列表,每条考核应发一行,叠加亏损筛选""" +def build_payment_records(month, month_data, all_loss_data, plate_client): + """构建奖金发放记录列表,每条考核应发一行,叠加亏损筛选。 + all_loss_data: {1: loss_dict, 2: loss_dict, 3: loss_dict_or_None} + 补发X月 → 查X月盈亏;其他 → 查当月盈亏;无客户 → 正常发放 + """ + # 发放类型→查哪个月盈亏 + def get_loss_month(cat, settle_month): + if '补发1月' in cat: return 1 + if '补发2月' in cat: return 2 + if '补发3月' in cat: return 3 + return settle_month + records = [] for cat, dl in month_data.items(): + loss_month = get_loss_month(cat, month) + loss_data = all_loss_data.get(loss_month) + for d in dl: client = (plate_client or {}).get(d['车牌'], '') - if loss_data: - loss_status = loss_data.get(client, '未匹配') if client else '未匹配' + + # 无客户关联(如虚拟结转记录)→ 正常发放 + if not client: + loss_status = '否' + elif loss_data: + loss_status = loss_data.get(client, '未匹配') else: loss_status = '未匹配' # 无亏损表视为未匹配,不发放 @@ -222,8 +239,9 @@ def build_payment_records(month, month_data, loss_data, plate_client): '车牌号': d['车牌'], '业务员': d['销售'], '部门': d['部门'], - '客户名称': client, + '客户名称': client or '(无客户关联)', '发放类型': cat, + '盈亏查询月': f'{loss_month}月', '考核应发': 考核应发, '客户盈亏': loss_status, '亏损拦截': 拦截, @@ -235,7 +253,7 @@ def write_payment_record_sheet(wb, month, payment_records): """写入奖金发放记录sheet""" ws = wb.create_sheet(f'{month}月奖金发放记录') - headers = ['车牌号','业务员','部门','客户名称','发放类型', + headers = ['车牌号','业务员','部门','客户名称','发放类型','盈亏查询月', '考核应发','客户盈亏','亏损拦截','实发金额'] WH(ws, headers) @@ -246,25 +264,24 @@ def write_payment_record_sheet(wb, month, payment_records): rn = 2 for r in sorted(payment_records, key=lambda x: (x['业务员'], x['车牌号'])): WR(ws, rn, [r['车牌号'], r['业务员'], r['部门'], r['客户名称'], r['发放类型'], + r.get('盈亏查询月',''), R(r['考核应发']), r['客户盈亏'], R(r['亏损拦截']), R(r['实发金额'])]) - # 颜色 if r['客户盈亏'] == '是': - for ci in [7, 8, 9]: ws.cell(row=rn, column=ci).fill = red_fill + for ci in [8, 9, 10]: ws.cell(row=rn, column=ci).fill = red_fill elif r['客户盈亏'] == '未匹配': - for ci in [7, 8, 9]: ws.cell(row=rn, column=ci).fill = yellow_fill + for ci in [8, 9, 10]: ws.cell(row=rn, column=ci).fill = yellow_fill elif r['实发金额'] > 0: - ws.cell(row=rn, column=9).fill = green_fill + ws.cell(row=rn, column=10).fill = green_fill rn += 1 - # 合计行 total_应发 = sum(r['考核应发'] for r in payment_records) total_拦截 = sum(r['亏损拦截'] for r in payment_records) total_实发 = sum(r['实发金额'] for r in payment_records) rn += 1 - WR(ws, rn, ['', '', '', '', '合计', R(total_应发), '', R(total_拦截), R(total_实发)]) - for ci in range(5, 10): ws.cell(row=rn, column=ci).font = Font(bold=True) + WR(ws, rn, ['', '', '', '', '合计', '', R(total_应发), '', R(total_拦截), R(total_实发)]) + for ci in range(5, 11): ws.cell(row=rn, column=ci).font = Font(bold=True) - ws.auto_filter.ref = f"A1:I1" + ws.auto_filter.ref = f"A1:J1" AW(ws) return payment_records @@ -428,7 +445,10 @@ def write_salesperson_sheet(wb, person, dept, settle_month, D, G, month_data, ve if g_cur: break if not g_cur: continue - first = g_cur['recs'][0] + if g_cur['recs']: + first = g_cur['recs'][0] + else: + first = {'合同编号':'','客户名称':'(无考核记录)','考核目标':g_cur.get('考核目标','')} mkm = g_cur['目标km'] # 第1行:车辆信息 + 各月里程/目标 diff --git a/main.py b/main.py index 515b83b..f555cd7 100644 --- a/main.py +++ b/main.py @@ -85,8 +85,8 @@ for settle_month in [1, 2, 3]: # Sheet 5: 车辆考核追踪 write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_payments, vehicle_info, loss_data[settle_month], plate_client) - # Sheet 6: 奖金发放记录(叠加亏损筛选的逐条明细) - payment_records = build_payment_records(settle_month, month_data, loss_data[settle_month], plate_client) + # Sheet 6: 奖金发放记录(叠加亏损筛选,补发查对应月盈亏) + payment_records = build_payment_records(settle_month, month_data, loss_data, plate_client) write_payment_record_sheet(wb, settle_month, payment_records) # Sheet 7: 月汇总(从发放记录生成)