from pathlib import Path from decimal import Decimal import json,openpyxl P=Path(__file__).parent f=P/'里程任务考核_2026年9月_含缺口核算.xlsx' after=openpyxl.load_workbook(f,data_only=True) with (P/'before-layout.xlsx.bak').open('rb') as source: before=openpyxl.load_workbook(source,data_only=True) d=json.loads((P/'september_calculation.json').read_text()) controls=json.loads((P/'layout_controls.json').read_text()) def eq(a,b): if isinstance(a,(int,float)) and isinstance(b,(int,float)):return abs(a-b)<1e-7 return a==b for name in ['业务考核视图','逐日取数依据','里程缺口','规则与名单','历史结算资格','8月已发台账']: a,b=before[name],after[name] for row in a: for c in row: if name=='规则与名单' and c.coordinate=='B12':continue if name=='业务考核视图' and c.column==4 and a.cell(c.row,5).value=='刘念念': assert b[c.coordinate].value=='业务一部' continue assert eq(c.value,b[c.coordinate].value),(name,c.coordinate,c.value,b[c.coordinate].value) assert eq(after['9月汇总(新版界面)']['J2'].value,d['summary']['amount']) assert eq(after['9月汇总'][controls['oldTotal']].value,d['summary']['amount']) for i,l in enumerate(d['ledger'],2): new=[c.value for c in after['9月奖金发放记录'][i]] for k,v in {0:l['plate'],1:l['sales'],4:l['category'],5:l['assessment_month'],6:l['amount'],9:l['amount'],10:l['base'],11:l['days'],12:l['month_days']}.items():assert eq(new[k],v),(i,k,new[k],v) for i,r in enumerate(d['records'],2): s=after['里程明细9月'];assert eq(s[f'J{i}'].value,r['应考核里程(km)']);assert eq(s[f'K{i}'].value,r['实际行驶里程(km)']);assert s[f'X{i}'].value==r['缺口天数'] for p,c in controls['personControls'].items(): expected=sum(l['amount'] for l in d['ledger'] if l['sales']==p) assert eq(after[c['sheet']][c['total']].value,expected) errors=[(s.title,c.coordinate,c.value) for s in after for row in s for c in row if c.data_type=='e'] assert not errors,errors aug=openpyxl.load_workbook(P.parent/'hydrogen-history-20260916/里程任务考核_8月核算_纯氢修正版.xlsx',data_only=True) for old,new in [('里程明细8月','里程明细9月'),('8月奖金发放记录','9月奖金发放记录'),('8月计算过程','9月计算过程')]: for cell in ['A1','A2','B1']: a,b=aug[old][cell],after[new][cell] assert a.font.name==b.font.name and a.font.sz==b.font.sz,(old,new,cell,'font') assert a.fill.fgColor==b.fill.fgColor,(old,new,cell,'fill') assert a.border.left.style==b.border.left.style,(old,new,cell,'border') assert after['9月汇总(新版界面)']['A5'].fill.fgColor.rgb=='FF183D50' result=dict(status='passed',amount=d['summary']['amount'],source_and_audit_tables_preserved=6,corrected_payout_items_verified=len(d['ledger']),personal_totals_verified=len(controls['personControls']),august_style_samples_match=True,formula_errors=errors) (P/'layout_verification.json').write_text(json.dumps(result,ensure_ascii=False,indent=2));print(json.dumps(result,ensure_ascii=False,indent=2))