import fs from 'node:fs/promises'; import {Workbook,SpreadsheetFile} from '@oai/artifact-tool'; import {fileURLToPath} from 'node:url'; import path from 'node:path'; const dir=path.dirname(fileURLToPath(import.meta.url)); const d=JSON.parse(await fs.readFile(path.join(dir,'september_calculation.json'),'utf8')); const w=Workbook.create(),previews=[]; const money='#,##0.00;[Red](#,##0.00);0.00'; const col=n=>{let s='';for(;n;n=Math.floor((n-1)/26))s=String.fromCharCode(65+(n-1)%26)+s;return s;}; const esc=s=>String(s).replaceAll('"','""'); const put=(s,r,a)=>s.getRange(`A${r}:${col(a.length)}${r}`).values=[a.map(x=>x===undefined?null:x)]; function sh(name,cols,rows){const s=w.worksheets.add(name);s.showGridLines=false;s.getRange(`A1:${col(cols)}${rows}`).format={font:{name:'宋体',size:11,color:'#172D3D'},columnWidth:16,rowHeight:22,verticalAlignment:'center'};return s;} function widths(s,vals,rows=20){for(const [c,v] of Object.entries(vals))s.getRange(`${c}1:${c}${rows}`).format.columnWidth=v;} function head(s,r,a){put(s,r,a);s.getRange(`A${r}:${col(a.length)}${r}`).format={fill:'#D9E1F2',font:{bold:true},wrapText:true,horizontalAlignment:'center',rowHeight:38};} function table(s,h,rs,ws={},nums=[]){head(s,1,h);if(rs.length)s.getRange(`A2:${col(h.length)}${rs.length+1}`).values=rs;for(const c of nums)s.getRange(`${c}2:${c}${rs.length+1}`).setNumberFormat(money);widths(s,ws,rs.length+1);s.freezePanes.freezeRows(1);} const sumSheet=sh('9月汇总',14,38),ledger=sh('9月奖金发放记录',14,d.ledger.length+4),calc=sh('9月计算过程',22,d.groups.length+1),track=sh('车辆考核追踪',26,d.groups.length+1),detail=sh('业务考核视图',31,d.records.length+1),daily=sh('逐日取数依据',16,d.daily.length+1),gap=sh('里程缺口',10,d.gaps.length+1),rules=sh('规则与名单',9,205),history=sh('历史结算资格',9,d.historical_months.length+1),aug=sh('8月已发台账',10,d.august_ledger.length+3); const personal={};for(const p of d.people)personal[p]=sh(p+'-9月对账',14,d.groups.filter(x=>x.sales===p).length*8+20); const dr=d.daily.length+1,lr=d.ledger.length+1,rr=d.records.length+1; const L=c=>`'9月奖金发放记录'!$${c}$2:$${c}$${lr}`; const pay=(p,plate=null,cat=null)=>`SUMIFS(${L('J')},${L('B')},${p}${plate?`,${L('A')},${plate}`:''}${cat?`,${L('D')},"${cat}"`:''})`; table(rules,['项目','确认口径'],[ ['测试差值阈值(km)',50],['本月自然日数',30],['锁定8月实际已发(元)',45065.47], ['测试日判定','仅9月名单车辆:每日仪表−GPS严格大于50公里。'], ['测试日里程','取当日GPS。'],['非测试日里程','有纯氢(含真实0)取纯氢,无纯氢取仪表。'], ['候选无法判定','缺仪表或GPS时不强判;有纯氢取纯氢,无纯氢兜底GPS。'], ['数据缺失','来源空白保留缺口,按已有里程累计;不自动将普通车辆改取GPS。'], ['重叠交接日','交接日计前段,后段次日起算;调整天数、目标及奖金折算。'], ['历史结算','1—8月已发及原8月扣减锁定,不追扣、不重复补发。'], ['历史里程','承接1—6月纯氢修正和原7—8月里程;8月原有缺口仍保留。'], ['新政策车辆','沿用新合同+新提车.xls原始39辆名单,本月考核匹配36辆。'], ['人员归属','按9月考核记录负责人分组;不因车辆转交自动转移旧负责人历史。'], ['离职及亏损','沿用原规则:伍仲文、岑彦、赵连飞、钱婧不发不转交;亏损暂不处理。'], ['补数更新','取数、里程、金额及汇总含公式。新增数据后需重新运行完整核算,以重新生成发放资格。'] ],{A:25,B:91}); head(rules,19,['9月新政策考核目标','月奖励(元)']); [['交投40辆4.5T普货',150],['交投190辆4.5T冷链车',150],['羚牛136辆4.5T冷链车',300],['恒运50辆4.5T普货',300],['羚牛100辆18T',150]].forEach((r,i)=>put(rules,20+i,r)); head(rules,27,['新政策车牌','9月是否有考核']);d.policy_plates.forEach((p,i)=>put(rules,28+i,[p,d.records.some(r=>r['车牌号']===p)?'是':'否'])); rules.getRange('D1:E1').values=[['9月测试候选车牌','9月是否有考核']];rules.getRange('D1:E1').format={fill:'#D9E1F2',font:{bold:true},rowHeight:38}; rules.getRange(`D2:E${d.tests.length+1}`).values=d.tests.map(p=>[p,d.records.some(r=>r['车牌号']===p)?'是':'否']);widths(rules,{D:23,E:23,G:25,H:82},205); rules.getRange('G1:H1').values=[['数据来源','文件']];let sr=2;for(const [key,v]of Object.entries(d.sources))rules.getRange(`G${sr}:H${sr++}`).values=[[key,path.basename(v.path)]]; rules.getRange('G10:H10').values=[['8月基线','里程任务考核_8月核算_纯氢修正版.xlsx,按用户确认锁定45,065.47元。']]; previews.push(['规则与名单','A1:B16']); table(daily,['考核源表行','车牌号','业务负责人','日期','仪表(km)','GPS(km)','纯氢(km)','测试候选','仪表−GPS(km)','测试日判定','采用来源','计入里程(km)','缺口标识','仪表来源单元格','GPS来源单元格','纯氢来源单元格'],d.daily.map(x=>[x.source_row,x.plate,x.sales,new Date(x.date+'T00:00:00Z'),x.meter,x.gps,x.hydrogen,x.test_candidate?'是':'否',null,null,null,null,null,x.provenance.meter||'未覆盖',x.provenance.gps||'未覆盖',x.provenance.hydrogen||'未覆盖']),{A:13,B:17,C:14,D:15,E:16,F:16,G:16,H:12,I:20,J:16,K:27,L:20,M:13,N:90,O:90,P:90},['E','F','G','I','L']); daily.getRange(`D2:D${dr}`).setNumberFormat('yyyy-mm-dd'); daily.getRange(`I2:M${dr}`).formulas=d.daily.map((_,i)=>{const r=i+2;return[ `=IF(AND(ISNUMBER(E${r}),ISNUMBER(F${r})),E${r}-F${r},"")`, `=IF(H${r}="否","不适用",IF(AND(ISNUMBER(E${r}),ISNUMBER(F${r})),IF(I${r}>'规则与名单'!$B$2,"是","否"),"未判定"))`, `=IF(J${r}="是",IF(ISNUMBER(F${r}),"测试日GPS","缺失"),IF(ISNUMBER(G${r}),"非测试日纯氢",IF(AND(H${r}="是",J${r}="未判定"),IF(ISNUMBER(F${r}),"测试名单无纯氢GPS","缺失"),IF(ISNUMBER(E${r}),"非测试日仪表","缺失"))))`, `=IF(K${r}="缺失","",IF(K${r}="非测试日纯氢",G${r},IF(K${r}="非测试日仪表",E${r},F${r})))`, `=IF(K${r}="缺失",1,0)`];}); daily.freezePanes.freezeColumns(3);previews.push(['逐日取数依据','A1:M12']); const recordHeaders=[...d.headers,'月度目标里程','奖励基准','缺口天数','新政策','测试候选','已有里程口径','核算源行','原考核天数','原应考核里程','取数说明']; table(detail,recordHeaders,d.records.map(r=>[...d.headers.map(h=>['实际行驶里程(km)','完成率(%)','是否达标','应考核里程(km)'].includes(h)?null:r[h]),r['月度目标里程'],r['月奖励'],null,r['新政策']?'是':'否',r['测试候选']?'是':'否',null,r['源表行'],r['原考核天数'],r['原应考核里程(km)'],r['取数方式']]),{A:18,D:16,E:14,F:35,G:32,H:36,J:30,K:22,L:22,N:20,O:22,P:16,Q:25,V:20,W:18,X:15,Y:12,Z:14,AA:48,AB:14,AC:16,AD:22,AE:65},['N','O','W','AD']); detail.getRange(`N2:Q${rr}`).formulas=d.records.map((r,i)=>{let j=i+2;return[`=ROUND(V${j}*M${j}/'规则与名单'!$B$3,2)`,`=ROUND(SUMIFS('逐日取数依据'!$L$2:$L$${dr},'逐日取数依据'!$A$2:$A$${dr},AB${j}),2)`,`=ROUND(O${j}/N${j}*100,2)`,`=IF(X${j}=M${j},"待补里程",IF(O${j}>=N${j},"达标",IF(X${j}>0,"已有里程未达标","未达标")))`];}); detail.getRange(`X2:X${rr}`).formulas=d.records.map((_,i)=>[`=SUMIFS('逐日取数依据'!$M$2:$M$${dr},'逐日取数依据'!$A$2:$A$${dr},AB${i+2})`]); detail.getRange(`AA2:AA${rr}`).formulas=d.records.map((_,i)=>[`=IF(X${i+2}>0,"仅累计已有数据,缺失不代表实际零里程","数据齐全")`]); detail.getRange(`Q2:Q${rr}`).conditionalFormats.add('containsText',{text:'待补',format:{fill:'#FFF2CC',font:{color:'#9C5700'}}}); detail.freezePanes.freezeColumns(1);previews.push(['业务考核视图','J1:Q12']); table(ledger,['车牌号','业务负责人','部门','发放类别','对应考核月','奖励基准(元)','折算分子','折算分母','核算金额(元)','本期列入(元)','本月缺口天数','8月缺口天数','依据状态','本月无考核记录'],d.ledger.map(l=>[l.plate,l.sales,l.department,l.category,l.assessment_month,l.base,l.days,l.month_days,null,null,l.gap_days,l.history_gap_days,l.status,l.virtual?'是':'否']),{A:18,B:16,C:18,D:22,E:16,F:20,G:16,H:16,I:22,J:22,K:18,L:18,M:24,N:22},['F','I','J']); ledger.getRange(`I2:J${lr}`).formulas=d.ledger.map((_,i)=>[`=ROUND(F${i+2}*G${i+2}/H${i+2},2)`,`=I${i+2}`]);put(ledger,lr+1,['合计']);ledger.getRange(`I${lr+1}:J${lr+1}`).formulas=[[`=SUM(I2:I${lr})`,`=SUM(J2:J${lr})`]];ledger.getRange(`I${lr+1}:J${lr+1}`).setNumberFormat(money);previews.push(['9月奖金发放记录','A1:J12']); table(calc,['车牌号','业务负责人','部门','9月目标(km)','9月已有里程(km)','截至8月累计目标','截至8月累计里程','累计目标(km)','累计里程(km)','累计是否达标','上期可用结转','本期结转余额','月度目标(km)','奖励基准(元)','9月缺口天数','8月缺口天数','无本月考核','当月(元)','结转(元)','历史补发(元)','累计补发9月(元)','本月合计(元)'],d.groups.map(g=>[g.plate,g.sales,g.department,g.target,g.actual,Math.round((g.cum_target-g.target)*100)/100,Math.round((g.cum_actual-g.actual)*100)/100,null,null,null,g.previous_carry,g.carry_left,g.monthly_target,g.reward,g.gap_days,g.history_gap_days,g.virtual?'是':'否',null,null,null,null,null]),{A:18,B:16,C:18,D:20,E:23,F:24,G:24,H:20,I:22,J:20,K:20,L:20,M:21,N:20,O:18,P:18,Q:19,R:19,S:19,T:22,U:24,V:22},['D','E','F','G','H','I','N','R','S','T','U','V']); calc.getRange(`H2:J${d.groups.length+1}`).formulas=d.groups.map((_,i)=>{const r=i+2;return[`=F${r}+D${r}`,`=G${r}+E${r}`,`=IF(AND(H${r}>0,I${r}>=H${r}),"达标","已有里程未达标")`];}); calc.getRange(`R2:V${d.groups.length+1}`).formulas=d.groups.map((_,i)=>{const r=i+2;return[`=${pay('B'+r,'A'+r,'当月')}`,`=${pay('B'+r,'A'+r,'结转')}`,`=${Array.from({length:8},(_,m)=>pay('B'+r,'A'+r,`补发${m+1}月`)).join('+')}`,`=${pay('B'+r,'A'+r,'累计补发9月')}`,`=SUM(R${r}:U${r})`];}); calc.freezePanes.freezeColumns(2);previews.push(['9月计算过程','A1:J12']); table(track,['车牌号','负责人',...Array.from({length:9},(_,i)=>[`${i+1}月目标(km)`,`${i+1}月里程(km)`]).flat(),'累计目标(km)','累计已有里程(km)','上期结转','本期结转余额','9月核算金额','9月缺口天数'],d.groups.map(g=>[g.plate,g.sales,...g.history.flat(),g.cum_target,g.cum_actual,g.previous_carry,g.carry_left,null,g.gap_days]),{A:18,B:15,U:22,V:24,W:18,X:22,Y:22,Z:20},['U','V','Y']); track.getRange(`Y2:Y${d.groups.length+1}`).formulas=d.groups.map((_,i)=>[`=${pay('B'+(i+2),'A'+(i+2))}`]);track.freezePanes.freezeColumns(2);previews.push(['车辆考核追踪','A1:H12']); table(gap,['车牌号','业务负责人','日期','考核源表行','需补来源','仪表(km)','GPS(km)','纯氢(km)','测试候选','缺口原因'],d.gaps.map(x=>[x.plate,x.sales,new Date(x.date+'T00:00:00Z'),x.source_row,x.needed,x.meter,x.gps,x.hydrogen,x.test_candidate?'是':'否',x.reason]),{A:18,B:16,C:17,D:17,E:15,F:18,G:18,H:18,I:16,J:68},['F','G','H']);gap.getRange(`C2:C${d.gaps.length+1}`).setNumberFormat('yyyy-mm-dd');previews.push(['里程缺口','A1:J12']); table(history,['考核月','车牌号','原业务负责人','考核天数','当时奖励基准','截至8月已结算','考核目标(km)','已修正里程(km)','结算含义'],d.historical_months.map(x=>[x.month,x.plate,x.sales,x.days,x.reward,x.settled?'是':'否',x.target,x.actual,x.settled?'已发或已核定扣减,不重复补发':'可按后续累计达标判定补发']),{A:13,B:18,C:18,D:16,E:22,F:22,G:22,H:22,I:48},['E','G','H']);previews.push(['历史结算资格','A1:I12']); table(aug,['车牌号','业务负责人','发放类别','考核月','原应发(元)','原扣减(元)','实际已发(元)','奖励基准','折算分子','折算分母'],d.august_ledger.map(l=>[l.plate,l.sales,l.category,l.assessment_month,l.gross_amount,l.withheld,l.amount,l.base,l.days,l.month_days]),{A:18,B:16,C:22,D:13,E:20,F:20,G:22,H:18,I:16,J:16},['E','F','G','H']);put(aug,d.august_ledger.length+2,['合计']);aug.getRange(`G${d.august_ledger.length+2}`).formulas=[[`=SUM(G2:G${d.august_ledger.length+1})`]];previews.push(['8月已发台账','A1:J12']); widths(sumSheet,{A:18,B:26,C:20,D:20,E:20,F:20,G:20,H:20,I:20,J:20,K:20,L:20,M:22,N:22},38); put(sumSheet,2,['2026年9月里程绩效核算']);sumSheet.getRange('A2').format.font={size:16,bold:true}; put(sumSheet,4,['按现有里程核算(元)']);sumSheet.getRange('C4').formulas=[[`=SUM(${L('J')})`]];sumSheet.getRange('C4').format.font={size:16,bold:true};sumSheet.getRange('C4').setNumberFormat(money); put(sumSheet,5,[`本月${d.summary.gap_days}个车辆日缺少所需来源,涉及${d.summary.gap_vehicles}辆车;补数后可能改变达标、结转和补发。`]); put(sumSheet,6,['8月已发45,065.47元锁定;交接日计前段;本月新政策按上月名单。金额按每条发放项取分后汇总。']); head(sumSheet,8,['业务负责人','部门',...d.categories.map(x=>x+'(元)'),'合计(元)']); d.people.forEach((p,i)=>{const r=i+9;const dep=[...new Set(d.groups.filter(g=>g.sales===p).map(g=>g.department))].join('、');put(sumSheet,r,[p,dep,...Array(12).fill(null)]);sumSheet.getRange(`C${r}:M${r}`).formulas=[d.categories.map(cat=>`=${pay('A'+r,null,cat)}`)];sumSheet.getRange(`N${r}`).formulas=[[`=SUM(C${r}:M${r})`]];if(i%2===0)sumSheet.getRange(`A${r}:N${r}`).format.fill='#EDF3FA';}); const totalRow=9+d.people.length;put(sumSheet,totalRow,['合计']);sumSheet.getRange(`C${totalRow}:N${totalRow}`).formulas=[Array.from({length:12},(_,i)=>`=SUM(${col(i+3)}9:${col(i+3)}${totalRow-1})`)];sumSheet.getRange(`C9:N${totalRow}`).setNumberFormat(money);sumSheet.getRange(`A${totalRow}:N${totalRow}`).format.font={bold:true}; put(sumSheet,totalRow+3,['考核记录',d.summary.records,'考核车辆',d.summary.vehicles,'测试日',d.summary.test_days]);put(sumSheet,totalRow+4,['本月缺口天数',d.summary.gap_days,'整段缺里程记录',d.summary.wholly_missing_records,'新政策车辆',d.summary.new_policy_vehicles]); put(sumSheet,totalRow+6,['缺口明细见“里程缺口”,逐日取数可查仪表/GPS/纯氢及原始单元格。现有未达标不等于补齐后仍未达标。']); previews.push(['9月汇总',`A1:N${totalRow+6}`]); for(const p of d.people){const s=personal[p];const gs=d.groups.filter(g=>g.sales===p);widths(s,{A:18,B:28,C:18,D:23,E:23,F:23,G:23,H:23,I:23,J:23,K:23,L:23,M:27,N:20},gs.length*8+20);put(s,1,[p+' 9月绩效对账单']);s.getRange('A1').format.font={size:14,bold:true};put(s,2,['按现有里程核算(元)']);s.getRange('C2').formulas=[[`=${pay('"'+esc(p)+'"')}`]];s.getRange('C2').setNumberFormat(money);head(s,4,['车牌号','考核目标','9月奖励基准',...Array.from({length:9},(_,i)=>`${i+1}月里程/目标`),'累计里程/目标','9月缺口天数']);let row=5; for(const g of gs){const r=d.records.find(r=>r['车牌号']===g.plate&&r['销售经理']===p);put(s,row,[g.plate,r?.['考核目标']||'无本月考核,承接历史结转',g.reward,...g.history.map(([t,a])=>t===null?'—':`${a.toFixed(2)}/${t.toFixed(2)}`),`${g.cum_actual.toFixed(2)}/${g.cum_target.toFixed(2)}`,g.gap_days]);s.getRange(`A${row}:N${row}`).format={fill:'#EDF3FA',rowHeight:32,wrapText:true};row++;const ls=d.ledger.filter(l=>l.plate===g.plate&&l.sales===p); if(!ls.length){put(s,row++,['','本月未列入',0,g.gap_days?'已有里程不足,待补数据后再判定':'现有条件未达到奖励资格']);} for(const l of ls){put(s,row,['',l.category,null,l.status]);s.getRange(`C${row}`).formulas=[[`='9月奖金发放记录'!J${l.id+1}`]];row++;} put(s,row++,['','结转余额',g.carry_left,`上期可用${g.previous_carry}期,本期剩余${g.carry_left}期`]);row++; }s.getRange(`C5:C${row}`).setNumberFormat(money);previews.push([s.name,'A1:N13']);} w.recalculate(); const oldMeter=daily.getRange('G2').values[0][0]; const oldMileage=detail.getRange('O2').values[0][0]; const oldBase=ledger.getRange('F2').values[0][0]; const oldTotal=sumSheet.getRange('C4').values[0][0]; daily.getRange('G2').values=[[oldMeter+10]]; ledger.getRange('F2').values=[[oldBase+30]]; w.recalculate(); if(Math.abs(detail.getRange('O2').values[0][0]-oldMileage-10)>1e-7)throw Error('Mileage recalc failed'); if(Math.abs(sumSheet.getRange('C4').values[0][0]-oldTotal-30)>1e-7)throw Error('Payment recalc failed'); daily.getRange('G2').values=[[oldMeter]]; ledger.getRange('F2').values=[[oldBase]]; w.recalculate(); if(Math.abs(sumSheet.getRange('C4').values[0][0]-d.summary.amount)>1e-7)throw Error('Restore failed'); await fs.writeFile(path.join(dir,'recalculation_probe.json'),JSON.stringify({passed:true,mileage_input_change:10,payment_input_change:30,restored:true,output_file_unchanged:true})); console.log('Recalculation probes passed, restored inputs; final workbook unchanged.');