from pathlib import Path
import hashlib
import html
import json
import re
import subprocess
import sys

from openpyxl import Workbook, load_workbook
from openpyxl.styles import Alignment, Font, PatternFill
from openpyxl.worksheet.datavalidation import DataValidation
from openpyxl.utils import get_column_letter

ROOT = Path(__file__).resolve().parents[2]
OUT = ROOT / 'modules/product/product-group-ai-native/ipd-ltc-itr'
EVIDENCE = Path(__file__).resolve().parent
PAKCO = Path('/Users/jack/code/099-github/pakco-html')


def tables(path):
    result, rows = [], []
    for line in path.read_text().splitlines() + ['']:
        if line.startswith('|'):
            values = [c.strip() for c in line.strip('|').split('|')]
            if not all(re.fullmatch(r':?-+:?', c) for c in values):
                rows.append(values)
        elif rows:
            result.append(rows)
            rows = []
    return result


fw = tables(OUT / 'framework.md')
im = tables(OUT / 'integration-map.md')
walkthrough = [
    ('001', '标准选配', 'LTC配置与报价，引用现有能力', '销售／商务', '需求、配置与报价快照', '3、6'),
    ('001', '共性缺口未排期', '关联IPD评审，正式交期须批准', '产品负责人', '评审决定、客户答复', '2、3、4'),
    ('001', '未知价格', '保留待核，不按0元批准', '商务审批人', '缺价项和补价依据', '3、6'),
    ('001', '改数量或范围', '生成新配置和报价版本，重新审批', '商务审批人', '新旧快照与批准记录', '6'),
    ('001', '重复诉求', '关联已有客户需求，不重复建立同目标', '销售负责人', '查重与关联记录', '2、8'),
    ('002', '两客户同一目标', '一个产品需求关联两条客户需求', '产品负责人', '多对多需求关联', '2、4'),
    ('002', '附条件评审', '整改未关不能Ready', '产品负责人', '整改验证与基线', '4、10'),
    ('002', '合同早于产品排期', '升级资源冲突，保留客户履约责任', '项目负责人', '冲突决定与客户确认', '3、4'),
    ('002', 'A紧急补丁', '回标准产品补回归并判断支持分支', '技术负责人', 'MR、回归与标准化决定', '4'),
    ('003', 'B兼容且有授权', '计划、批准、部署、验证后才已同步', 'B上线负责人', 'B版本与功能确认', '7'),
    ('003', 'B旧版需适配', '需适配，补测试后重评', '技术负责人', '适配方案与报告', '7'),
    ('003', 'B权益未包含', '待授权／待增购，转LTC', '商务审批人', '权益条款与报价', '6、7'),
    ('003', 'B客户暂缓', '保留暂缓原因与复核日期', 'B项目负责人', '客户决定与下一复核', '7'),
    ('003', 'B不适用', '产品确认场景与不适用理由', '产品负责人', '不适用证据', '7'),
    ('003', 'A专属定制', '核权利和标准化，不直接复制', '产品负责人', '标准边界与权利依据', '6、7'),
    ('003', 'B失败回退', '记失败与回退后实际版本，不标同步', 'B上线负责人', '回退与恢复验证', '7'),
    ('003', '共享实例／多实例', '逐租户权益与开关，逐实例核版本', '交付负责人', '实例租户对应与验证', '7、8'),
    ('004', '合同范围内咨询', '按履约配置或培训，不重复售授权', '项目负责人', '合同与配置结果', '2、6'),
    ('004', '新增专属接口', 'LTC变更核价批准后实施', '变更审批人', '变更、报价与任务', '3、6'),
    ('004', '客户拒绝变更', '不扩大实施，保留原基线', '项目负责人', '拒绝记录与原范围', '3'),
    ('004', '共性＋专属混合', '拆关联范围，共性化保留合同约束', '产品负责人', '范围分解与权利边界', '2、6'),
    ('005', '过保重大故障', '先受理分级，费用按约判断', '售后负责人', '影响、权益与处理任务', '5'),
    ('005', '配置咨询', 'ITR配置／培训后验证关闭', '售后负责人', '操作结果与客户确认', '5'),
    ('005', '缺陷转研发', '关联缺陷，原工单保留闭环责任', '售后负责人', '交接、修复与现场验证', '4、5'),
    ('005', '新功能转报价', '新增诉求走LTC，原问题独立闭环', '售后负责人', '原问题＋新需求关联', '2、5'),
    ('005', '恢复但未根治', '记录已恢复，根因仍跟踪', '售后负责人', '恢复证据与根因任务', '5'),
    ('005', '客户未确认', '待客户确认，约定下一跟进', '售后负责人', '验证与跟进点', '5、10'),
    ('005', '问题复发', '重新打开，补根因与回归', '售后负责人', '重开与验证记录', '5、10'),
    ('006', '部分验收', '仅标相应范围通过', '项目负责人', '范围与客户验收', '3'),
    ('006', '已验收未到账', '履约完成与回款未闭合分别显示', '销售负责人', '验收与应收差额', '3'),
    ('006', '已开票未到账', '财务核对，不把开票当收款', '财务负责人', '到账核对或待收记录', '3'),
    ('006', '分期回款', '按合同各节点核实，不等终验再记', '财务负责人', '各节点收款明细', '3'),
    ('006', '结项后持续服务', 'LTC结项与ITR服务分开承接', '项目负责人', '售后交接与服务约定', '3、5'),
]
fields = [
    ('客户需求', '需求编号／项目编号／来源', '原稳定编号优先；原件链接受限', '销售／项目负责人', '两空间关联'),
    ('客户需求', '提出阶段／处理分类／关联产品需求', '分类与主流程明确；多对多保留客户记录', '产品经理', '原需求池＋项目'),
    ('客户需求', '客户期望／内部预计／批准交期', '三种日期独立，批准日期有依据', '销售／项目负责人', '客户需求'),
    ('客户需求', '验收条件／唯一结果负责人／下一动作', '非多人共同负责；待澄清不能Ready', '当前结果责任人', '客户需求'),
    ('报价合同', '配置版本／价表版本／报价版本', '同次外发包一致；新价不追改历史', '商务', '原报价工具'),
    ('报价合同', '需求编号／合同条款／变更版本', '报价行与需求逐条对应', '销售／商务', '项目范围关联'),
    ('产品需求', '共性决策／来源需求／目标版本', '决定有理由；一个共性目标一个父项', '产品经理', '03需求池'),
    ('产品需求', 'UC／云效ID／评审与执行状态', '正文UC可读；评审状态不代执行', '产品／研发', '原需求＋云效'),
    ('标准能力', '能力ID／可售状态／兼容／授权', '目录可见不等于已批准全量销售', '产品负责人', '产品清单／矩阵'),
    ('正式发布', '构建／Tag／验证报告／资料版本', '业务档位不代替构建', '研发／产品', '原版本管理'),
    ('客户部署', '客户／租户／实例／组件', '客户可多实例；实例可服务多租户', '交付／运维', '当前部署主记录'),
    ('客户部署', '当前实际版本／配置／验收链接', '部署回读与业务验证分别记录', '交付／测试', '部署关联明细'),
    ('升级决策', '发布×实例租户×能力升级包', '同关系去重，不按客户名称强关联', '产品统筹／交付执行', '关联记录'),
    ('升级决策', '适用／权益／兼容／决定', '待评估、计划、待授权、需适配、暂缓、不适用、已验证、回退', '对应决定岗位', '关联记录'),
    ('升级决策', '理由／责任人／窗口／复核日期／证据', '暂缓不漏跟进；同步有实际证据', '交付负责人', '关联记录'),
    ('ITR工单', '原售后状态（保修权益）', '在保／过保原值不覆盖', '售后', '原售后台账'),
    ('ITR工单', '工单处理状态／恢复状态／唯一责任人', '恢复与解决、关闭分开', '售后负责人', '原表增量'),
    ('ITR工单', '合同SLA／下一更新时间／等待依赖', '等待不静默停表；没有SLA写待确认', '售后负责人', '原表增量'),
    ('ITR工单', '修复版本／现场验证／客户确认／复发', '完成时间不自动推出关闭', '售后／测试', '原表增量'),
    ('验收回款', '范围验收／遗留／付款节点／到账凭据', '财务核实；验收和收款分开', '项目／财务', '原项目＋财务源'),
]
decisions = [
    ('D01', '流程Owner、审批权和岗位实名', '管理人员／各负责人', '试点启动前', '一个决定一个A，授权范围明确'),
    ('D02', '标准增强、维护权益和升级服务收费', '产品／商务／财务', '正式报价或升级前', '合同权益与价表规则一致'),
    ('D03', '试点A/B、实际实例与当前组件版本', '产品／交付', '试点实施前', '真实范围、版本与客户授权可回查'),
    ('D04', '目标仓库分支与质量规则冲突', '技术负责人', '开发或发布前', '目标仓库现行规则及回归范围冻结'),
    ('D05', '当前部署关系的唯一事实源', '交付／运维', '客户覆盖评估前', '旧废弃表不作现网事实'),
    ('D06', '试点期限、现场窗口与SLA', '项目／售后负责人', '分派真实任务前', '有批准日期和可执行下一节点'),
    ('D07', '企微／云效／报价的系统增量与权限', '产品／IT／系统Owner', '自动化开发前', '详细UC、授权及异常恢复通过评审'),
]

wb = Workbook()
wb.remove(wb.active)


def sheet(name, rows):
    ws = wb.create_sheet(name)
    for row in rows:
        ws.append(row)
    ws.freeze_panes = 'B2'
    ws.auto_filter.ref = ws.dimensions
    for row in ws:
        for cell in row:
            cell.font = Font(name='微软雅黑', size=12, bold=cell.row == 1,
                             color='FFFFFF' if cell.row == 1 else '1E293B')
            cell.fill = PatternFill('solid', fgColor='193B57' if cell.row == 1 else ('F2F6FA' if cell.row % 2 == 0 else 'FFFFFF'))
            cell.alignment = Alignment(vertical='top', wrap_text=True)
        ws.row_dimensions[row[0].row].height = 54 if row[0].row > 1 else 32
    for col in ws.columns:
        title = str(col[0].value or '')
        width = 46 if any(t in title for t in ['产物', '说明', '准出', '处理', '范围', '依据', '规则', '增量', '证据', '输入', '功能']) else 25
        ws.column_dimensions[col[0].column_letter].width = width
    ws.sheet_view.zoomScale = 80
    ws.sheet_properties.pageSetUpPr.fitToPage = True
    ws.page_setup.orientation = 'landscape'
    ws.page_setup.paperSize = ws.PAPERSIZE_A3
    ws.page_setup.fitToWidth = 1
    ws.page_setup.fitToHeight = 0
    ws.print_title_rows = '1:1'
    return ws


sheet('使用说明', [
    ['项目', '说明'],
    ['版本与状态', 'V1.0评审稿，2026-10-09；尚未上线或改原表'],
    ['用途', '优先把字段与关联补入原体系；Excel是评审与试点模板，不作为第二套当前主账'],
    ['两套来源', '数字技术中心资料汇总＋交付实施部项目管理空间；原制度正文已读取'],
    ['模板填写', '需求／升级／工单三个模板均为空；用真实业务编号填入，不把模板当作已建任务'],
    ['状态与证据', '桌面走查仅证明文稿路由，系统UAT和客户验收未执行'],
    ['职责', '岗位表为结果责任建议；未指定人员，报价、工期、发布和客户验收继续遵守现授权'],
    ['敏感边界', '不填账号、口令、完整个人资料；合同／日志／财务凭据仅挂受限链接'],
    ['完整规则', 'framework.md、integration-map.md、use-cases.md为对应规则正文'],
] + [[r[0], '；'.join(r[1:])] for r in fw[0][1:]])
process = [['流程', '节点', '牵头／最终决定', '输入与动作', '产物／准出']]
for lane, table in zip(['LTC', 'IPD', 'ITR'], fw[1:4]):
    process.extend([[lane] + r for r in table[1:]])
sheet('流程交接', process)
sheet('岗位分工', fw[7])
sheet('原体系融合', [['现有载体', '主归属', '保留的作用', '建议增量']] + im[1][1:])
sheet('字段增量', [['对象', '字段组', '口径与规则', '维护岗位', '优先承载位置']] + fields)
templates = {
    '需求登记模板': ['需求编号', '客户项目编号', '来源链接', '提出阶段', '场景与期望', '处理分类', '合同范围依据', '结果负责人', '客户期望日期', '内部预计日期', '批准交期', '验收条件', '产品需求ID', '云效ID', '配置报价版本', '处理状态', '下一动作', '复核日期', '客户确认链接'],
    '跨客户同步模板': ['发布编号', '客户项目编号', '租户', '部署实例', '能力或升级包ID', '当前实际组件版本', '目标构建', '适用依据', '权益依据', '兼容证据', '升级决定', '结果负责人', '计划窗口', '复核日期', '实际部署版本', '功能验证链接', '客户确认链接', '失败回退记录'],
    'ITR工单模板': ['原问题编号', '客户项目编号', '部署实例', '影响与问题', '保修权益', '工单处理状态', '恢复状态', '结果负责人', '合同SLA依据', '下一更新时间', '等待依赖', '关联产品需求缺陷', '关联增购需求', '修复版本措施', '现场验证链接', '客户确认链接', '关闭依据', '复发重开记录'],
}
for name, header in templates.items():
    ws = sheet(name, [header] + [[None] * len(header) for _ in range(20)])
    ws.auto_filter.ref = f'A1:{get_column_letter(len(header))}21'
    ws.sheet_properties.tabColor = '2A7D73'
    choices = {
        '处理分类': '现有功能配置,共性新增增强,专属定制,缺陷故障,增购服务,待澄清研究',
        '处理状态': '待澄清,已分流,已评估,已确认范围,执行中,待客户验证,已验收,暂缓,拒绝,取消',
        '升级决定': '待评估,计划升级,待授权／待增购,需适配,客户暂缓,不适用,已验证同步,失败回退',
        '保修权益': '在保,过保,待确认',
        '工单处理状态': '待受理,定位中,处理中,待现场验证,待客户确认,已关闭,依赖等待,重新打开',
        '恢复状态': '待评估,未恢复,已恢复待根治,已解决,不适用',
    }
    for title, options in choices.items():
        if title in header:
            dv = DataValidation(type='list', formula1='"' + options + '"', allow_blank=True)
            dv.errorTitle = '请使用已有状态'
            dv.error = '按体系选择状态；新的业务口径先评审。'
            dv.showErrorMessage = True
            ws.add_data_validation(dv)
            letter = get_column_letter(header.index(title) + 1)
            dv.add(f'{letter}2:{letter}1001')
walk_rows = [['用例', '场景', '预期处理', '结果责任岗位', '必需证据', 'framework章节', '本次结论', '真实UAT']]
for uc, case, expected, owner, evidence, section in walkthrough:
    walk_rows.append(['UC-FLOW-' + uc, case, expected, owner, evidence, section, '文稿路由与证据要求已覆盖', '未执行'])
sheet('验收走查', walk_rows)
sheet('评审待定', [['编号', '待定事项', '决策岗位', '关闭门槛', '通过依据', '状态']] + [list(r) + ['待确认'] for r in decisions])
if (OUT / 'tool-selection.md').exists():
    tools_tables = tables(OUT / 'tool-selection.md')
    sheet('系统方案', tools_tables[1])
    sheet('流程约束', tools_tables[2])
target = OUT / 'IPD-LTC-ITR执行表.xlsx'
wb.save(target)

# Follow the pakco Webpage scaffold and inline its base/theme assets.
template = (PAKCO / 'templates/webpage.html').read_text()
base = (PAKCO / 'assets/base.css').read_text()
theme = (PAKCO / 'assets/themes/corporate-clean.css').read_text()
custom = '''
:root{--font-sans:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;--font-display:var(--font-sans);--radius:14px;--radius-lg:22px;--bg:#f4f6f9;--accent:#193b57;--accent-2:#237f77;--text-3:#64748b}
body.webpage{font-size:16px}.webpage .hero .lede{margin-left:0;margin-right:0;font-weight:400}.webpage .page{width:min(1120px,100%);max-width:1120px;padding:42px 28px 68px;margin:auto}.webpage .hero{padding:12px 0 26px;text-align:left}.webpage .h1{font-size:clamp(32px,4.3vw,54px);line-height:1.2;max-width:19ch}.webpage .h2{font-size:28px;line-height:1.35}.webpage .lede{font-size:18px;max-width:72ch;line-height:1.8}.webpage h3{font-size:22px}.webpage h4{font-size:19px}.webpage p{margin:8px 0 12px}.webpage section{margin:0 0 30px}.meta,.note{font-size:14px;color:var(--text-2)}.banner{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--accent-2);padding:18px 22px;border-radius:var(--radius);margin:18px 0 24px}.nav{display:flex;gap:8px;flex-wrap:wrap;margin:20px 0 0}.nav a,.download{display:inline-flex;align-items:center;padding:9px 15px;border:1px solid var(--border);background:var(--surface);border-radius:10px;font-size:14px}.webpage .grid{gap:16px}.webpage .card{padding:22px;box-shadow:none}.lane{border-top:4px solid var(--accent-2)}.lane:nth-child(2){border-top-color:var(--accent-2)}.lane:nth-child(3){border-top-color:var(--warn)}.route{font-size:16px;font-weight:600;line-height:2}.owner{font-size:14px;color:var(--text-2);margin-top:14px}.bridge{background:var(--accent);color:var(--surface);padding:24px;border-radius:var(--radius-lg);font-size:18px;line-height:1.8}.bridge small{display:block;color:var(--surface-2);font-size:14px;margin-top:10px}.step-list{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;counter-reset:steps}.step-list>div{background:var(--surface);padding:18px;border:1px solid var(--border);border-radius:var(--radius);counter-increment:steps}.step-list b:before{content:counter(steps,decimal-leading-zero) " ";color:var(--accent-2)}.step-list p{font-size:14px;color:var(--text-2)}details{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:17px 20px;margin:12px 0}summary{cursor:pointer;font-weight:600;line-height:1.6}details[open] summary{margin-bottom:16px}.table-wrap{overflow-x:auto;max-width:100%}table{width:100%;border-collapse:collapse;font-size:14px;text-align:left}th,td{vertical-align:top;padding:12px 10px;border-bottom:1px solid var(--border);min-width:110px}th{color:var(--accent);background:var(--bg-soft)}.role-table td:first-child{width:42%}.tabs{display:flex;gap:8px;margin:18px 0;flex-wrap:wrap}.tabs button{font:inherit;padding:9px 17px;border:1px solid var(--border);background:var(--surface);color:var(--text-1);border-radius:10px;cursor:pointer}.tabs button[aria-selected="true"]{background:var(--accent);color:var(--surface)}.tabs button:focus-visible,a:focus-visible,summary:focus-visible{outline:3px solid var(--accent-2);outline-offset:3px}.route-panel[hidden]{display:none}.route-panel{padding:22px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface)}.route-panel ol{padding-left:24px}.route-panel li{padding:5px 0}.source{overflow-wrap:anywhere}.page-footer{font-size:13px;color:var(--text-2)}@media(max-width:700px){.webpage .page{padding:24px 18px 44px}.webpage .g3,.step-list{grid-template-columns:1fr}.webpage .h2{font-size:24px}.webpage .card{padding:18px}.bridge{padding:20px;font-size:17px}.webpage .g2{grid-template-columns:1fr}.webpage section{margin-bottom:24px}.nav a{padding:8px 11px}}
@media print{.nav,.tabs,.download{display:none}.route-panel[hidden]{display:block}.webpage .page{width:100%;padding:0}.card,details,.step-list>div{break-inside:avoid}}
'''


def html_table(table):
    return '<div class="table-wrap"><table><thead><tr>' + ''.join('<th>' + html.escape(v) + '</th>' for v in table[0]) + '</tr></thead><tbody>' + ''.join('<tr>' + ''.join('<td>' + html.escape(v) + '</td>' for v in row) + '</tr>' for row in table[1:]) + '</tbody></table></div>'


body = '''<div class="page">
<section class="hero"><p class="kicker">康比特数字业务 · 团队管理体系</p><h1 class="h1">把需求、报价、开发<br>和客户使用串起来</h1><p class="lede">以 IPD、LTC、ITR 管理三条工作流，把已有研发体系和项目管理体系接成一条业务闭环。每次交接都有负责人、产物和验收依据。</p><p class="meta">V1.0 评审稿 · 2026年10月9日 · 现有两套体系已读取</p><nav class="nav" aria-label="页面导航"><a href="#flows">三条流程</a><a href="#roles">谁来负责</a><a href="#upgrade">A → B 同步</a><a href="#integration">原体系融合</a><a href="#pilot">试点与决策</a></nav></section>
<section id="flows"><h2 class="h2">三条流程，各有完成标准</h2><div class="grid g3">
<article class="card lane"><p class="kicker">IPD · 产品开发</p><h3>形成可复用能力</h3><p class="route">共性评估 → 需求与设计<br>→ 开发验证 → 正式发布</p><p>发布后更新能力、适配、价格影响和资料，逐客户检查适用性。</p><p class="owner">产品负责人对范围与优先级负责；技术与测试对实现和质量负责。</p></article>
<article class="card lane"><p class="kicker">LTC · 线索到回款</p><h3>兑现客户承诺</h3><p class="route">商机需求 → 方案报价 → 合同<br>→ 交付验收 → 回款复盘</p><p>选配报价工具承接其中一段；合同范围、变更、实际交付与财务到账继续关联。</p><p class="owner">销售负责商业结果；项目负责人负责履约；商务与财务各守其责。</p></article>
<article class="card lane"><p class="kicker">ITR · 问题到解决</p><h3>解决客户使用问题</h3><p class="route">受理分级 → 恢复定位<br>→ 处理验证 → 客户确认关闭</p><p>共性缺陷转 IPD，新增付费诉求转 LTC；原工单保留闭环责任。</p><p class="owner">售后负责人负责闭环；研发、供方和交付接关联处理任务。</p></article>
</div><div class="bridge">LTC 发现需求 → 共性需求进入 IPD → 发布后供 LTC 销售交付。<br>投产问题走 ITR，必要时推动 IPD 改进或 LTC 增购。<small>客户需求、产品需求、报价合同、正式发布、客户实际部署，通过编号与证据关联。</small></div></section>
<section><h2 class="h2">收到一条诉求，先决定怎么走</h2><p class="note">点击查看分流；这是流程说明，操作不会新建任务或修改系统。</p><div class="tabs" role="tablist" aria-label="诉求分类">
<button type="button" id="tab-config" role="tab" aria-controls="panel-config" aria-selected="true" data-tab="config">已有能力</button><button type="button" id="tab-common" role="tab" aria-controls="panel-common" aria-selected="false" data-tab="common" tabindex="-1">共性增强</button><button type="button" id="tab-custom" role="tab" aria-controls="panel-custom" aria-selected="false" data-tab="custom" tabindex="-1">专属定制</button><button type="button" id="tab-fault" role="tab" aria-controls="panel-fault" aria-selected="false" data-tab="fault" tabindex="-1">投产故障</button></div>
<div class="route-panel" id="panel-config" role="tabpanel" aria-labelledby="tab-config"><h3>配置、培训或咨询</h3><ol><li>产品核实已有能力和客户权益。</li><li>售前／交付走 LTC 实施，投产咨询走 ITR。</li><li>核对合同是否包含服务，按实际场景验证和确认。</li></ol><p class="note">已有能力不重复开发，已买授权不重复收费。</p></div>
<div class="route-panel" id="panel-common" role="tabpanel" aria-labelledby="tab-common" hidden><h3>产品评审决定共性与版本</h3><ol><li>保留每个客户需求，关联一个共性产品需求。</li><li>评审价值、范围、工期和验收；合同节点冲突交管理者裁决。</li><li>验证发布后更新目录，原客户交付与其他客户升级分别跟进。</li></ol><p class="note">客户期望日期、内部预计日期、批准交期分别记录。</p></div>
<div class="route-panel" id="panel-custom" role="tabpanel" aria-labelledby="tab-custom" hidden><h3>先对齐范围，再批准变更</h3><ol><li>拆清标准能力与客户专属部分。</li><li>评估成本、工期、价格、维护与权利约束。</li><li>批准变更后关联研发任务，按客户约定验收。</li></ol><p class="note">共性化可以复用，但不能抹去原合同或复制客户专属数据。</p></div>
<div class="route-panel" id="panel-fault" role="tabpanel" aria-labelledby="tab-fault" hidden><h3>先受理与恢复，再解决根因</h3><ol><li>售后登记影响、责任人与服务约定，保修权益单独记录。</li><li>研发／供方接处理任务，共性缺陷关联 IPD。</li><li>现场验证、客户确认后关闭；恢复服务与根因解决分别跟踪。</li></ol><p class="note">增强需求可以另走 LTC，原故障仍有负责人。</p></div></section>
<section id="roles"><h2 class="h2">谁来负责</h2><p>每个决定只有一个最终负责岗位。一个人可以兼岗，交接时仍需说清现在承担哪项责任。</p>ROLE_TABLE<details><summary>展开完整 RACI 与流程交接</summary><p><a href="framework.md">完整体系第9章</a>列明14项决定的A/R/C/I；<a href="IPD-LTC-ITR执行表.xlsx">执行表</a>提供节点与产物清单。当前为岗位建议，尚未替部门指派人员。</p></details></section>
<section id="upgrade"><h2 class="h2">A 上线以后，B 怎么办？</h2><div class="banner"><strong>A 客户上线，只证明 A 的结果。</strong><br>B 需要自己的适用、权益、兼容、升级和验收记录。</div><div class="step-list">
<div><b>判断标准范围</b><p>产品区分共性能力、通用修复与A专属定制。</p></div><div><b>找全相关客户</b><p>从当前项目与实例找受影响客户；多实例、SaaS租户分别记录。</p></div><div><b>核对授权和收费</b><p>商务核合同权益；待增购回LTC，不默认免费或自动收费。</p></div><div><b>验证兼容条件</b><p>研发测试核实际版本、硬件、接口、数据迁移与回退。</p></div><div><b>批准并实施升级</b><p>交付确认客户窗口、备份与授权；暂缓保留复核日期。</p></div><div><b>回验并更新事实</b><p>实际版本＋功能场景＋客户确认齐全，才能记已验证同步。</p></div></div><p class="note">全部客户有决定＝覆盖评估完成；计划内客户都验证通过＝实际同步完成。两项结果分开。</p></section>
<section id="integration"><h2 class="h2">装进已经建立的两套体系</h2><div class="grid g2"><article class="card"><h3>研发体系 → IPD 主干</h3><p>保留研发蓝图、需求评审、新品G0—G8、开发、测试、Git与发布制度。</p><p>需求池与版本管理连接客户需求；正式发布输出能力、适配、资料及升级清单。</p><a class="source" href="https://doc.weixin.qq.com/smartsheet/s3_Ac0AJgaVAEYCNawNO01DIS4ypXMdn?tab=ep1V85&viewId=v3qBsg">打开原研发体系</a></article><article class="card"><h3>项目空间 → LTC／ITR 承载</h3><p>保留售前采集、支撑统计、项目总表、排期、售后台账、经营分析和续费记录。</p><p>补需求与合同范围、实际部署、工单处理状态和客户确认的关联。</p><a class="source" href="https://doc.weixin.qq.com/smartsheet/s3_AHkAH3hlAHwCNUzm1hPTKQQGwNBft_a?tab=BYYqSm&viewId=vabcde">打开原项目空间</a></article></div><div class="banner"><strong>两个字段含义先分清</strong><br>“售后状态＝在保／过保”保留，另增工单处理状态。<br>“验证状态＝登录检查”保留，功能验收和客户验收另留证据。</div><details><summary>六项原制度如何归入三流程</summary>INTEGRATION_TABLE</details><details><summary>网页与支撑能力的职责</summary><p>产品清单展示IPD产出的标准能力；场景库供销售复用；选配报价工具承接LTC中的需求、配置和报价。微信登录支撑身份，Dify辅助抽取和文案。新增展示页不能代替流程管理、工单、合同或财务闭环。</p></details></section>
<section id="pilot"><h2 class="h2">先跑通一个需求、一个版本、两个客户</h2><p>用一次真实报价、一次A→B决策和一个售后工单检验交接，再决定增加哪些系统能力。</p><details><summary>启动前要确认的事项</summary>DECISION_TABLE</details><p class="note">体系文稿、执行模板和6个UC已形成；真实岗位任命、收费与升级权益、试点客户、期限和系统实施仍待业务确认。当前为本地评审包，未改在线表、未实施跨客户升级，未完成系统UAT。</p><div class="nav"><a class="download" href="IPD-LTC-ITR执行表.xlsx">执行表与3个空白模板</a><a class="download" href="framework.md">完整体系</a><a class="download" href="integration-map.md">来源融合与差异</a><a class="download" href="use-cases.md">6个Use Case／33项走查</a></div></section>
<footer class="page-footer">本页采用 pakco Webpage 模板，康比特商务阅读风格。原文与流程规则见配套Markdown；原件敏感信息保留在受限源位置。</footer></div>'''
roles = [['岗位', '核心责任'], ['销售', '客户需求事实、商机、客户沟通与收款跟进'], ['商务', '价表、报价、折扣、合同与权益边界'], ['产品', '共性判断、范围优先级、正式发布与客户覆盖评估'], ['研发测试', '可行性、实现、兼容与质量证据'], ['交付售后', '履约、客户实际版本、现场验收与问题闭环'], ['推广／财务', '推广复用批准材料；财务核实到账与成本口径']]
body = body.replace('ROLE_TABLE', html_table(roles)).replace('INTEGRATION_TABLE', html_table(im[0])).replace('DECISION_TABLE', html_table([['事项', '决策岗位', '关闭门槛']] + [[r[1], r[2], r[3]] for r in decisions]))
script = '''<script>
const tabs=[...document.querySelectorAll('[role="tab"]')];
function activate(tab){tabs.forEach(b=>{const active=b===tab;b.setAttribute('aria-selected',String(active));b.tabIndex=active?0:-1;document.getElementById(b.getAttribute('aria-controls')).hidden=!active;});}
tabs.forEach((tab,index)=>{tab.addEventListener('click',()=>activate(tab));tab.addEventListener('keydown',e=>{let n;if(e.key==='ArrowRight')n=(index+1)%tabs.length;else if(e.key==='ArrowLeft')n=(index-1+tabs.length)%tabs.length;else if(e.key==='Home')n=0;else if(e.key==='End')n=tabs.length-1;else return;e.preventDefault();activate(tabs[n]);tabs[n].focus();});});
</script>'''
page = re.sub(r'<title>.*?</title>', '<title>IPD／LTC／ITR 一体化管理体系 · 评审稿</title>', template)
page = re.sub(r'<link[^>]+rel="stylesheet"[^>]*>\s*', '', page)
page = re.sub(r'<body class="webpage">.*</body>', lambda _: '<body class="webpage">' + body + script + '</body>', page, flags=re.S)
page = page.replace('</head>', '<style>' + base + '\n' + theme + '\n' + custom + '</style></head>')
(OUT / 'review.html').write_text(page)

if (OUT / 'guide.md').exists():
    subprocess.run([sys.executable, str(EVIDENCE / 'build_progressive_guide.py')], check=True)

check = load_workbook(target)
assert len(check.sheetnames) == (12 if (OUT / 'tool-selection.md').exists() else 10)
assert check['验收走查'].max_row == 34
assert all(cell.font.name == '微软雅黑' and cell.font.sz == 12 for ws in check for row in ws for cell in row)
assert not any(cell.data_type in ['f', 'e'] for ws in check for row in ws for cell in row)
for name in templates:
    assert not any(cell.value is not None for row in check[name].iter_rows(min_row=2) for cell in row)
assert len(walkthrough) == 33
evidence = {'date': '2026-10-09', 'current_view': 'V1.1 progressive guide' if (OUT / 'guide.md').exists() else 'V1.0 review', 'workbook_sheets': check.sheetnames, 'walkthrough_cases': 33, 'real_uat': 'not_run', 'formulas': 0, 'template_rows': 20, 'template_blank_verified': True, 'font': '微软雅黑12', 'pakco_commit': 'c8ce8b6dc3de1b87926a6b6a672733f6a3feef36', 'artifacts': {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in OUT.iterdir() if p.is_file()}}
(EVIDENCE / 'artifact-validation.json').write_text(json.dumps(evidence, ensure_ascii=False, indent=2) + '\n')
print(json.dumps({'workbook': str(target), 'sheets': len(check.sheetnames), 'walkthrough_cases': 33, 'html_bytes': (OUT / 'review.html').stat().st_size}, ensure_ascii=False))
