| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 |
- """A reusable, blank-input decision model, separate from empirical company data."""
- import csv
- import json
- FIELDS = [
- ('R0','原方案净实现收入','元/销售单位','财务/渠道结算'),
- ('V0','原方案变动成本','元/销售单位','财务/已核BOM及履约'),
- ('Rn','新方案净实现收入','元/销售单位','财务/渠道结算'),
- ('Vn','新方案变动成本','元/销售单位','已核配方、包材、制造、装箱、物流和预期报损;不重复计项'),
- ('F','一次增量投入','元','报价、打样、必要验证、试产和印版'),
- ('Qn','真正新增销量','销售单位/同一分析期','研究或经营估计;不含从旧品转移'),
- ('Qc','从原方案转移销量','销售单位/同一分析期','经营估计;与新增互斥'),
- ]
- SCRIPT = r'''(() => {
- const names = ['R0','V0','Rn','Vn','F','Qn','Qc'];
- const state = document.querySelector('#model-state');
- let origin = 'blank';
- const calculate = (v) => {
- const u0=v.R0-v.V0, un=v.Rn-v.Vn, du=un-u0;
- const value=v.Qn*un+v.Qc*du-v.F;
- return {u0,un,du,value,breakEven:du>0 ? Math.ceil(v.F/du) : null};
- };
- const fmt = n => Number(n).toLocaleString('zh-CN',{maximumFractionDigits:2});
- function update() {
- const rows=names.map(k=>[k,document.getElementById(k).value.trim()]);
- if(rows.some(([k,v])=>v==='')) { state.textContent='输入未齐,不计算;空值不按0处理。';document.querySelector('#sensitivity').innerHTML='';return; }
- const v=Object.fromEntries(rows.map(([k,x])=>[k,Number(x)]));
- if(Object.values(v).some(x=>!Number.isFinite(x)||x<0)||!Number.isInteger(v.Qn)||!Number.isInteger(v.Qc)) {state.textContent='输入应为非负有限数;销量为非负整数。';document.querySelector('#sensitivity').innerHTML='';return;}
- const result=calculate(v);
- const badge=origin==='synthetic'?'算术假设示例,非企业参数':'本地输入试算,参数真实性未核验';
- state.textContent=badge+'。原单位贡献 '+fmt(result.u0)+';新单位贡献 '+fmt(result.un)+';单位增量 '+fmt(result.du)+' 元。该分析期增量贡献 '+fmt(result.value)+' 元。'+(result.breakEven===null?'同销量替代下,单位增量≤0,不能靠增加替代销量覆盖正的一次投入。':'仅同销量替代情景,覆盖F至少需要 '+fmt(result.breakEven)+' 个销售单位。')+'此模型未含资金时间价值及未声明的固定运营变化,不等于量产或盈利验收。';
- const scenarios=[['当前输入',v],['新净收入降低10%',{...v,Rn:v.Rn*.9}],['新变动成本增加10%',{...v,Vn:v.Vn*1.1}],['新增销量减半',{...v,Qn:Math.floor(v.Qn*.5)}],['无真正新增销量',{...v,Qn:0}]];
- document.querySelector('#sensitivity').innerHTML='<div class="table-wrap"><table><thead><tr><th>单因素敏感性(其他条件固定)</th><th>新单位贡献/元</th><th>分析期增量贡献/元</th></tr></thead><tbody>'+scenarios.map(([name,x])=>{const z=calculate(x);return '<tr><td>'+name+'</td><td>'+fmt(z.un)+'</td><td>'+fmt(z.value)+'</td></tr>'}).join('')+'</tbody></table></div>';
- window.modelResult={...result,origin,inputs:v,empiricalVerified:false};
- }
- names.forEach(k=>document.getElementById(k).addEventListener('input',()=>{origin='user-unverified';delete window.modelResult;update();}));
- document.getElementById('example').onclick=()=>{
- const v={R0:20,V0:15,Rn:21,Vn:15.5,F:1000,Qn:100,Qc:1000};
- names.forEach(k=>document.getElementById(k).value=v[k]);origin='synthetic';update();
- };
- document.getElementById('reset').onclick=()=>{names.forEach(k=>document.getElementById(k).value='');origin='blank';delete window.modelResult;update();};
- update();
- })();'''
- def build_model(out,shell):
- model={'schema':'ksf.incremental-feasibility.v1','unit':'元/统一销售单位,销量为同一分析期',
- 'empiricalCompanyData':False,'fields':{k:{'label':label,'value':None,'unit':unit,'sourceNeeded':source} for k,label,unit,source in FIELDS},
- 'formulas':{'u0':'R0-V0','un':'Rn-Vn','deltaUnit':'un-u0','incrementalContribution':'Qn*un+Qc*(un-u0)-F',
- 'sameVolumeBreakEven':'ceil(F/(un-u0)) only when un-u0>0; Qn=0'},
- 'limitations':['不以零售价代净实现收入','费用口径互斥,不能重复计税/运费/报损','新销量与旧品转移销量分开','仅单期贡献模型,未含资金时间价值和额外固定运营变化']}
- (out/'商业回推模型.json').write_text(json.dumps(model,ensure_ascii=False,indent=2)+'\n',encoding='utf8')
- with (out/'商业回推输入模板.csv').open('w',encoding='utf-8-sig',newline='') as f:
- w=csv.writer(f);w.writerow(['变量','含义','实际值(留空待核)','单位','需要的依据'])
- for k,label,unit,source in FIELDS:w.writerow([k,label,'',unit,source])
- inputs=''.join(f'<label for="{k}">{label}<br><small>{unit}</small><input id="{k}" type="number" min="0" step="'+('1' if k in ['Qn','Qc'] else 'any')+'" placeholder="待核,不自动填值"></label>' for k,label,unit,_ in FIELDS)
- body='''<article><h2>先统一口径,再看增量</h2><p>O1拆配比与装箱,O2拆印版及旧包材消化,O3拆拣货/错发/物流;依报价归入变动成本或一次投入,已含费用不重复加入。所有输入默认为空;本页仅本地计算、不保存、不上传。</p><p>统一使用同一销售单位及分析期。u₀=R₀−V₀,uₙ=Rₙ−Vₙ;增量贡献=Qₙ×uₙ+Q꜀×(uₙ−u₀)−F。若只替代原销量,且单位增量为正,盈亏临界=⌈F/(uₙ−u₀)⌉。</p><p><a href="商业回推输入模板.csv">空白输入模板 CSV</a> · <a href="商业回推模型.json">模型及口径 JSON</a> · <a href="reports/feasibility.html">返回可行性报告</a></p><div class="calcgrid">'''+inputs+'''</div><p><button class="switch" id="example">载入算术假设示例(非企业参数)</button> <button class="switch" id="reset">清空输入</button></p><output id="model-state" aria-live="polite"></output><div id="sensitivity"></div><p class="muted">算术示例专供检验公式:20/15 → 21/15.5,F=1000,新增100、转移1000。不是公司收入、成本、销量预测或售价建议。用户修改参数后的真实性仍需另核。</p></article><script>'''+SCRIPT+'</script>'
- (out/'商业回推演算器.html').write_text(shell('商业回推|空值模型与敏感性试算',body),encoding='utf8')
|