#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""商机预算导入模板 2027 — 销售员填写指导（自包含 HTML）"""
import io
import sys

sys.path.insert(0, '/Users/liuxinyuan/Desktop/Hermes输出-工作类/脚本')
import brand_kit as BK  # noqa: E402

OUT = '/Users/liuxinyuan/Desktop/Hermes输出-工作类/销售分析/商机预算导入_销售填写指导_2027.html'

COLS = [
    ('年份', '可选', '整数年份，如 2027', '留空＝页面顶部年度（本次 2027）；同一份表可混填不同年份，按行落库', '填 27 或 2027年'),
    ('销售', '可选', '文本', '仅供参考，导入时用商机所属人（beforesales）覆盖', '以为填了就能改归属'),
    ('部门', '可选', '文本', '仅供参考，导入时用商机所属部门覆盖', '同上'),
    ('客户名称', '可选', '文本', '仅供参考，导入时用商机所属客户覆盖', '同上'),
    ('商机名称', '可选', '文本', '仅供参考，导入时用飞书商机名称覆盖', '同上'),
    ('商机ID(飞书)', '必填', '纯数字', '飞书商机列表里的商机 ID；必须真实存在', '填名称、填 0、填占位符'),
    ('业务线', '必填', '一级/二级[/三级]', '以系统【业务线管理】字典为准；重名必须写全路径', '只写“仪器”这类重名二级'),
    ('年度目标合计(元)', '可选', '整数元', '可不填，系统自动汇总 12 个月', '手填一个和月度加总不一致的数'),
    ('1月(元) ~ 12月(元)', '可选', '整数元', '0 或留空＝该月不预测；负数归零、小数取整', '填 10 万 / 100000元 / 1.2 万'),
]

SCENARIOS = [
    ('商机ID 在飞书存在', '导入成功', '客户/商机/销售员/部门从飞书回填', 'ok'),
    ('商机ID 在飞书不存在', '该行报错跳过', '提示：该商机ID在飞书系统中不存在', 'bad'),
    ('同年同商机ID 已有预算', '覆盖更新', '月度目标与业务线被新值替换，不叠加', 'warn'),
    ('商机所属人（beforesales）为空', '用 Excel 里的销售', '仅此时 Excel 的“销售”才起作用', 'warn'),
    ('业务线留空', '沿用原值或报错', '该商机已有业务线→沿用；从来没有→该行报错（业务线必填）', 'warn'),
]

ERRORS = [
    ('该商机ID在飞书系统中不存在', 'ID 抄错 / 商机已删除 / 填了商机名称', '回飞书商机列表重新复制 ID（纯数字）'),
    ('业务线必填', '业务线整列留空，且该商机此前也没维护过', '按字典填 一级/二级；不确定就查【业务线管理】'),
    ('业务线不在字典中 / 已被禁用', '写了简称、错字，或该业务线已停用', '改成字典里的全路径；停用的找销售支持换一条'),
    ('第 N 行必填列缺失', '商机ID 或 业务线 为空', '补全该行；整行都不填就删掉，别留空行'),
    ('年份非法', '填了 27、2027年、文本', '只写 4 位整数年份，或直接留空'),
    ('月度目标为负 / 带小数', '填了 -5000 或 12345.67', '系统自动归零/取整，但建议自己改对再导入'),
]

FAQ = [
    ('“年度目标合计”要不要填？', '不用。系统按 12 个月自动汇总。你手填的数只作对照，不参与计算；填错反而让自己核对时误判。'),
    ('某个月不确定，能空着吗？', '能。0 和留空都表示该月不预测。但 12 个月全 0 的行没有意义，建议直接不填这一行。'),
    ('一个商机能不能拆成多行？', '不能。唯一键是「年份 + 商机ID」，同键多行会互相覆盖，最后只剩一行。要拆请在飞书里拆成多个商机。'),
    ('跨年商机怎么填？', '按年拆行：2027 的 12 个月填一行，2028 的另起一行，年份列分别写清楚。'),
    ('销售/部门/客户填错了要紧吗？', '不影响落库，导入时会用飞书数据覆盖。但请尽量填对，方便你自己核对。'),
    ('业务线不知道确切名字？', '打开系统【业务线管理】照抄；二级名重名（如“仪器”）必须写全路径，例如 生命科学/仪器。'),
]

rows_cols = ''.join(
    '<tr><td><b>%s</b></td><td class="%s">%s</td><td>%s</td><td>%s</td><td class="bad">%s</td></tr>' % (
        c[0], 'must' if c[1] == '必填' else 'opt', c[1], c[2], c[3], c[4]) for c in COLS)

rows_sc = ''.join('<tr><td>%s</td><td><span class="pill %s">%s</span></td><td>%s</td></tr>' % (s[0], s[3], s[1], s[2]) for s in SCENARIOS)
rows_err = ''.join('<tr><td><code>%s</code></td><td>%s</td><td>%s</td></tr>' % e for e in ERRORS)
rows_faq = ''.join('<details><summary>%s</summary><p>%s</p></details>' % f for f in FAQ)

page = """<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>商机预算导入模板 2027 · 销售填写指导</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#f4f6fa;font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;color:#1b2430;font-size:13.5px;line-height:1.7}
.wrap{max-width:1120px;margin:0 auto;padding:0 18px 42px}
h1{font-size:22px;margin:16px 0 4px}
.sub{color:#5f6b7a;font-size:12.6px;margin-bottom:14px}
.card{background:#fff;border:1px solid #e3e8f1;border-radius:11px;padding:15px 17px;margin-bottom:14px}
.card h2{font-size:15.5px;margin:0 0 10px;padding-left:10px;border-left:3px solid #1F4E79}
.card h2 span{font-size:11px;color:#8a94a3;font-weight:600;margin-left:6px}
.hero{background:linear-gradient(120deg,#0E2E63,#1852B6);color:#fff;border-radius:12px;padding:16px 18px;margin-bottom:14px}
.hero b{font-size:15px}.hero p{margin:6px 0 0;font-size:12.8px;color:#D7E4F7}
.rules{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:12px}
.rule{background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.22);border-radius:9px;padding:9px 11px;font-size:12.2px}
.rule i{display:block;font-style:normal;font-size:10.5px;color:#FFD479;letter-spacing:.06em;margin-bottom:2px}
table{width:100%;border-collapse:collapse;font-size:12.1px}
th{background:#1F4E79;color:#fff;padding:7px 8px;text-align:left;font-weight:600}
td{padding:6px 8px;border-bottom:1px solid #eef1f6;vertical-align:top}
tr:nth-child(even) td{background:#fafbfe}
.must{color:#B3261E;font-weight:700;white-space:nowrap}
.opt{color:#8a94a3;white-space:nowrap}
.bad{color:#8a5a12;font-size:11.6px}
code{background:#EEF3FA;border-radius:4px;padding:1px 5px;font-size:11.6px;color:#0E2E63}
.pill{border-radius:20px;padding:1px 9px;font-size:11px;white-space:nowrap;font-weight:600}
.pill.ok{background:#E3F4EA;color:#1B7A43}.pill.warn{background:#FFF3DC;color:#8A5A12}.pill.bad{background:#FDE7E5;color:#B3261E}
.steps{counter-reset:s;display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
.step{position:relative;background:#f8fafd;border:1px solid #e3e8f1;border-radius:9px;padding:10px 12px 10px 44px}
.step:before{counter-increment:s;content:counter(s);position:absolute;left:12px;top:10px;width:22px;height:22px;border-radius:50%;background:#1852B6;color:#fff;font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center}
.step b{display:block;font-size:13px;margin-bottom:2px}
.step span{font-size:11.9px;color:#5f6b7a}
.flow{display:flex;align-items:stretch;gap:6px;flex-wrap:wrap}
.flow div{flex:1;min-width:110px;background:#f7f9fd;border:1px solid #dfe6f2;border-radius:8px;padding:8px 10px;font-size:11.8px}
.flow div b{display:block;color:#1852B6;font-size:12px;margin-bottom:2px}
.check{background:#f8fafd;border:1px solid #dfe6f2;border-radius:9px;padding:11px 13px}
.check label{display:flex;gap:8px;align-items:flex-start;padding:4px 0;font-size:12.4px;cursor:pointer}
.check input{margin-top:3px;accent-color:#1852B6}
.bar{height:6px;background:#e6ecf6;border-radius:6px;overflow:hidden;margin:6px 0 8px}
.bar i{display:block;height:100%;width:0;background:#1852B6;transition:width .25s}
details{border-bottom:1px solid #eef1f6;padding:7px 0}
details summary{cursor:pointer;font-weight:600;font-size:12.8px;color:#0E2E63}
details p{margin:6px 0 0;font-size:12.4px;color:#3d4a5a}
.note{background:#FFF6E5;border:1px solid #F3D9A6;border-radius:9px;padding:10px 12px;font-size:12.4px}
.two{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.tpl{overflow-x:auto;border:1px solid #e3e8f1;border-radius:8px}
.tw{overflow-x:auto;max-width:100%}
.tw table{min-width:520px}
.tpl table{min-width:760px;font-size:11.4px}
.tpl td,.tpl th{padding:5px 6px;white-space:nowrap}
@media(max-width:900px){.rules,.steps,.two{grid-template-columns:1fr}.hero b{font-size:14px}.bior-tagwrap{display:none!important}.tpl{overflow-x:auto;-webkit-overflow-scrolling:touch}}
</style></head><body><div class="wrap">
<h1>商机预算导入模板 2027 · 销售填写指导</h1>
<div class="sub">适用对象：全体销售员 ｜ 模板文件：商机预算导入模板_2027.xlsx ｜ 唯一键：年份 + 商机ID ｜ 更新于 2026-10-17</div>

<div class="hero">
<b>先记住三件事，能避开 90% 的退回</b>
<div class="rules">
<div class="rule"><i>必填 1</i>商机ID 必须是飞书里的<b>纯数字 ID</b>，不是商机名称、不是占位符</div>
<div class="rule"><i>必填 2</i>业务线必须能在系统字典里匹配；<b>重名要写全路径</b></div>
<div class="rule"><i>格式</i>月度金额填<b>整数元</b>：100000，不写“10万”</div>
</div>
<p>其余列（销售/部门/客户/商机名称）填了也白填——导入时一律以飞书商机数据为准。</p>
</div>

<div class="card"><h2>填写五步 <span>照着做，10 分钟填完</span></h2>
<div class="steps">
<div class="step"><b>打开飞书商机列表</b><span>找到你负责的商机，确认它已存在、且你是所属人（beforesales）。</span></div>
<div class="step"><b>复制商机ID</b><span>只要那串纯数字，粘到 F 列。不要复制商机名称或链接。</span></div>
<div class="step"><b>填 12 个月目标</b><span>I~T 列，单位元、整数。不确定的月份填 0 或留空，别空着整行。</span></div>
<div class="step"><b>填业务线</b><span>G 列写 一级/二级（如 诊断原料/PCR），三级可选；查字典照抄。</span></div>
<div class="step"><b>自查后导入</b><span>过一遍下面 8 条自查清单，再上传；导入后看预览报错行号逐条改。</span></div>
</div>
</div>

<div class="card"><h2>20 列逐列说明 <span>红字＝必填</span></h2>
<div class="tpl"><table>
<thead><tr><th style="width:14%">列</th><th style="width:8%">必填</th><th style="width:16%">格式</th><th>怎么填 / 系统怎么处理</th><th style="width:20%">常见错误</th></tr></thead>
<tbody>__COLS__</tbody></table></div>
</div>

<div class="card"><h2>业务线怎么填 <span>这一列最容易被打回</span></h2>
<div class="two">
<div>
<p><b>三种写法都认：</b></p>
<ul style="margin:4px 0 0 18px;padding:0">
<li><code>诊断原料/PCR</code>（一级/二级）</li>
<li><code>诊断原料/PCR/定量</code>（一级/二级/三级）</li>
<li><code>PCR</code>（只写二级，全字典唯一时自动匹配）</li>
</ul>
</div>
<div class="note"><b>重名必须写全路径。</b>例如“仪器”在多个一级下都存在，只写“仪器”无法判断，必须写 <code>生命科学/仪器</code>。取值以系统【业务线管理】为准，写错或已停用都会被阻断。</div>
</div>
</div>

<div class="card"><h2>一行填对长什么样 <span>对照着抄</span></h2>
<div class="tw"><table>
<thead><tr><th style="width:20%">商机ID(飞书)</th><th style="width:18%">业务线</th><th>1~12 月（元）</th><th style="width:24%">结果</th></tr></thead>
<tbody>
<tr><td><code>7382910xxxx</code>（纯数字）</td><td>诊断原料/PCR</td><td>100000 × 6 个月，其余 0</td><td><span class="pill ok">导入成功</span></td></tr>
<tr><td><code>7382910xxxx</code></td><td><b>空着</b></td><td>100000 × 6</td><td><span class="pill bad">该行报错：业务线必填</span></td></tr>
<tr><td>写成 <b>“明德新冠商机”</b></td><td>诊断原料/PCR</td><td>100000 × 6</td><td><span class="pill bad">该行报错：商机ID不存在</span></td></tr>
<tr><td>同 ID 再导一次</td><td>诊断原料/PCR/定量</td><td>改成 120000 × 6</td><td><span class="pill warn">覆盖更新，不叠加</span></td></tr>
</tbody></table></div>
<div style="margin-top:8px;font-size:12.2px;color:#5f6b7a">ID 为占位示意，实际请从飞书商机列表复制；金额一律整数元。</div>
</div>

<div class="card"><h2>导入后会发生什么 <span>五种场景</span></h2>
<table><thead><tr><th style="width:30%">场景</th><th style="width:16%">系统处理</th><th>说明</th></tr></thead><tbody>__SC__</tbody></table>
<div style="margin-top:9px;font-size:12.2px;color:#5f6b7a">注意：同一年、同一个商机ID 再导一次＝<b>覆盖</b>，不是追加。改预算直接重导即可，不用先删。</div>
</div>

<div class="card"><h2>报错对照表 <span>看到提示直接查这里</span></h2>
<table><thead><tr><th style="width:30%">报错原文</th><th style="width:32%">原因</th><th>怎么改</th></tr></thead><tbody>__ERR__</tbody></table>
</div>

<div class="card"><h2>提交前自查 <span>8 条全过再上传</span></h2>
<div class="check" id="ck">
<div class="bar"><i id="bar"></i></div>
<label><input type="checkbox">每一行的商机ID 都是飞书里的纯数字，且确实存在</label>
<label><input type="checkbox">没有把商机名称、链接或“示例ID”留在 F 列</label>
<label><input type="checkbox">每一行都填了业务线，且能在【业务线管理】里对上</label>
<label><input type="checkbox">重名的二级（如“仪器”）写成了全路径</label>
<label><input type="checkbox">月度金额都是整数元，没有“万”、没有小数点、没有负号</label>
<label><input type="checkbox">同一年 + 同一商机ID 没有重复出现两行</label>
<label><input type="checkbox">空行已删除，不留只有格式没有内容的行</label>
<label><input type="checkbox">整行 12 个月全是 0 的，已确认要保留（否则删掉）</label>
<div id="ckmsg" style="margin-top:6px;font-size:12px;color:#5f6b7a">已完成 0 / 8</div>
</div>
</div>

<div class="card"><h2>常见问题</h2>__FAQ__</div>

<div class="card"><h2>导入流程 <span>谁做什么</span></h2>
<div class="flow">
<div><b>① 销售填写</b>按本页规则填 20 列，自查 8 条</div>
<div><b>② 上传导入</b>系统逐行校验，给出行号与原因</div>
<div><b>③ 看预览</b>成功行落库、失败行跳过，不影响其他行</div>
<div><b>④ 改错重导</b>只改失败行，重导同年同 ID＝覆盖</div>
<div><b>⑤ 核对</b>回页面看月度目标是否与表格一致</div>
</div>
</div>
__FOOTER__
</div>
<script>
(function(){
  var box=document.getElementById('ck'); if(!box) return;
  var ins=box.querySelectorAll('input[type=checkbox]'), bar=document.getElementById('bar'), msg=document.getElementById('ckmsg');
  function up(){
    var n=0; ins.forEach(function(i){ if(i.checked) n++; });
    bar.style.width=(n/ins.length*100)+'%';
    msg.textContent = n===ins.length ? '✅ 8 项全过，可以导入' : ('已完成 '+n+' / '+ins.length);
    msg.style.color = n===ins.length ? '#1B7A43' : '#5f6b7a';
  }
  ins.forEach(function(i){ i.addEventListener('change', up); });
})();
</script></body></html>"""

page = (page.replace('__COLS__', rows_cols).replace('__SC__', rows_sc)
        .replace('__ERR__', rows_err).replace('__FAQ__', rows_faq))
page = BK.inject(page, theme='dark', tag='商机预算导入 · 销售填写指导',
                 footer_extra='内部资料 · 模板以 system 内最新版为准')
page = page.replace('__FOOTER__', '')
page = page.replace('<table>', '<div class="tw"><table>').replace('</table>', '</table></div>')
io.open(OUT, 'w', encoding='utf-8').write(page)
print('✅', OUT, '%.0f KB' % (len(page.encode()) / 1024))
