# -*- coding: utf-8 -*- """生成《响应文件》。 做法:直接抽取询比文件「第六章 响应文件格式」的原始 Word 内容(保留其全部原文、 字体、字号、缩进、对齐与表格样式),仅在原有空位上填写我方内容;不增删原文条款。 仅在原文明确允许「格式自拟」「供应商自行添加」处追加材料。 用法:python -X utf8 doc/_gen_bid_response.py """ import copy import os import shutil import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) sys.stdout.reconfigure(encoding="utf-8", errors="replace") from docx import Document from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.oxml import OxmlElement from docx.oxml.ns import qn from docx.shared import Cm from docx.table import Table from docx.text.paragraph import Paragraph from _bid_req_items import REQ_ITEMS from _gen_bid_annex import SW_COPYRIGHTS, SW_RELEVANCE DOC_DIR = os.path.dirname(os.path.abspath(__file__)) SRC_TENDER = os.path.join(DOC_DIR, "_tmp_xunbi.docx") ONEDRIVE_DIR = r"C:\Users\skygu\OneDrive\Projects\AIDOP\项目\项目招投标" OUT_NAME = "响应文件-2026年河南产互联制造业数据智能运营平台研发项目-北京智造易科技有限公司.docx" OUT_PATH = os.path.join(DOC_DIR, OUT_NAME) # ------------------------------------------------------------------ 填写内容 PROJECT_FULL = "2026年河南产互联制造业数据智能运营平台研发项目" PROJECT_NO = "0635-202611FW0036" BUYER = "河南产业互联网联合发展有限公司" COMPANY = "北京智造易科技有限公司" USCC = "91110000MA00AFPA3H" LEGAL_REP = "刘涛" LEGAL_REP_SEX = "男" LEGAL_REP_AGE = "47" # 1978-12-11 生,按投标日 2026-08-05 计 LEGAL_REP_ID = "340304197812110814" LEGAL_REP_TEL = "13955207130" AGENT = "郭峰" AGENT_ID = "320821198303277151" BANK_NAME = "中国建设银行股份有限公司北京天宝北街支行" BANK_ACCOUNT = "11050110248800000238" COMPANY_TYPE = "有限责任公司(自然人投资或控股)" # 注册地址以公共信用报告(2026-07-31 出具)载明的住所为准 ADDRESS = "北京市北京经济技术开发区文化园西路6号院18号楼6层606" OPERATE_UNTIL = "2016年12月14日至2066年12月13日" REG_CAPITAL = "1000万元人民币" # 经营范围照录营业执照(登记机关:北京经济技术开发区市场监督管理局) BUSINESS_SCOPE = ( "技术推广、技术咨询、技术服务、技术开发、技术转让;软件开发;网页设计;" "销售计算机软件及辅助设备、机械设备、电子产品、文化用品、通讯设备、" "针纺织品、五金产品(不含电动自行车);组织文化艺术交流活动;承办展览展示;" "会议服务;文艺创作;设计、制作、代理、发布广告;电脑图文设计、制作;" "摄影摄像服务;企业管理咨询;经济贸易咨询;企业策划;货物进出口;" "技术进出口;代理进出口;加工电子产品、计算机软件及辅助设备" "(限在外埠从事生产经营活动)。(市场主体依法自主选择经营项目,开展经营活动;" "依法须经批准的项目,经相关部门批准后依批准的内容开展经营活动;" "不得从事国家和本市产业政策禁止和限制类项目的经营活动。)") # 含税报价。另两家为 587,600 / 592,000,基准价=三家均价×97%; # 报价 560,000 时基准价 562,470.67,偏低 0.44%,得 39.87 分,领先次高 2.55 分。 # 分差在临界价(563,651)及以下恒为 2.55,故取整数 56 万,避免报价贴住基准价。 PRICE_CN = "伍拾陆万元整" PRICE_NUM = "560,000.00" BID_Y, BID_M, BID_D = "2026", "8", "5" PERF_COUNT = 5 # 业绩表份数 TENDER_START_KEY = ("第六章", "响应文件格式") # ------------------------------------------------------------------ 工具函数 def body_children(doc): return list(doc.element.body.iterchildren()) def para_text(el): return "".join(n.text or "" for n in el.iter(qn("w:t"))) def trim_to_chapter6(doc): """删除第六章之外的全部 body 元素,仅保留第六章(不含第七章)。""" children = body_children(doc) c6 = [i for i, el in enumerate(children) if el.tag == qn("w:p") and para_text(el).strip().startswith(TENDER_START_KEY[0]) and TENDER_START_KEY[1] in para_text(el)] c7 = [i for i, el in enumerate(children) if el.tag == qn("w:p") and para_text(el).strip().startswith("第七章")] start = c6[-1] end = next(i for i in c7 if i > start) body = doc.element.body sect = body.find(qn("w:sectPr")) for i, el in enumerate(children): if el.tag == qn("w:sectPr"): continue if i < start or i >= end: body.remove(el) if sect is not None and sect.getparent() is None: body.append(sect) return start def set_run(p, idx, text): """替换段落第 idx 个 run 的文本,完整保留该 run 的字体格式。""" runs = p.runs if idx >= len(runs): raise IndexError(f"run {idx} 不存在(共 {len(runs)} 个):{p.text!r}") runs[idx].text = text def append_run_like(p, src_idx, text): """按第 src_idx 个 run 的格式,在段落末尾追加一个新 run。""" src = p.runs[src_idx] new = copy.deepcopy(src._element) p._p.append(new) from docx.text.run import Run r = Run(new, p) r.text = text return r def cell_fill(cell, text, like_cell=None): """填写表格单元格,套用同表已有单元格的字体格式;\n 转为段内换行。 会清空该单元格原有全部段落内容(保留第一个段落及其段落格式)。 """ tmpl_run = None if like_cell is not None: for pp in like_cell.paragraphs: if pp.runs: tmpl_run = pp.runs[0]._element break paras = cell.paragraphs for extra in paras[1:]: extra._p.getparent().remove(extra._p) p = cell.paragraphs[0] for r in list(p.runs): r._element.getparent().remove(r._element) from docx.text.run import Run def new_run(): if tmpl_run is None: return p.add_run() el = copy.deepcopy(tmpl_run) for child in list(el): if child.tag in (qn("w:t"), qn("w:br"), qn("w:cr"), qn("w:tab")): el.remove(child) p._p.append(el) return Run(el, p) for i, line in enumerate(str(text).split("\n")): if i > 0: new_run()._element.append(OxmlElement("w:br")) new_run().text = line def _page_field_para(): p = OxmlElement("w:p") pPr = OxmlElement("w:pPr") st = OxmlElement("w:pStyle") st.set(qn("w:val"), "21") jc = OxmlElement("w:jc") jc.set(qn("w:val"), "center") pPr.append(st) pPr.append(jc) p.append(pPr) for kind, payload in (("begin", None), ("instr", " PAGE \\* MERGEFORMAT "), ("separate", None), ("text", "1"), ("end", None)): r = OxmlElement("w:r") if kind == "instr": it = OxmlElement("w:instrText") it.set(qn("xml:space"), "preserve") it.text = payload r.append(it) elif kind == "text": t = OxmlElement("w:t") t.text = payload r.append(t) else: fc = OxmlElement("w:fldChar") fc.set(qn("w:fldCharType"), kind) r.append(fc) p.append(r) return p def fix_footer_page_numbers(doc): """模板页脚把页码放在 wrap=none 自动缩放的文本框里,两位数会被压掉。 改为每一节都显式挂一个标准的居中 PAGE 域(视觉仍是底部居中页码), 既修掉两位数被裁的问题,也避免删除首页后各节页脚失联。 """ n = 0 for sec in doc.sections: sec.different_first_page_header_footer = False sec.footer.is_linked_to_previous = False ftr = sec.footer._element for child in list(ftr): ftr.remove(child) ftr.append(_page_field_para()) n += 1 return n def relax_row_height(row): """模板行高为「固定值」,长原文会被裁切;改为「最小值」让该行自动增高。 仅动 hRule,保留原高度作为下限,字体、边框、列宽均不变。 """ trPr = row._tr.get_or_add_trPr() th = trPr.find(qn("w:trHeight")) if th is not None: th.set(qn("w:hRule"), "atLeast") def clone_para_after(anchor_el, like_el, doc, text=None): """在 anchor_el 之后插入一个新段落,段落与首个 run 的格式均取自 like_el。""" el = copy.deepcopy(like_el) anchor_el.addnext(el) p = Paragraph(el, doc) for extra in list(p.runs)[1:]: extra._element.getparent().remove(extra._element) # 模板段落可能带自动编号或分页属性,克隆后需清掉,避免附件标题被编号/乱分页 pPr = el.find(qn("w:pPr")) if pPr is not None: numPr = pPr.find(qn("w:numPr")) if numPr is not None: pPr.remove(numPr) p.paragraph_format.page_break_before = False if p.runs: p.runs[0].text = "" if text is None else text else: p.add_run(text or "") return p ATTACH_DIR = os.path.join(DOC_DIR, "_bid_assets", "attach") CONTENT_W, CONTENT_H = 14.6, 24.6 # 模板正文区尺寸(cm) MERGE_TOKEN = "@@MERGE:%s@@" # 供合并脚本定位并替换为外部文件内容 def outline(p, lvl): """只写 w:outlineLvl,不套 Heading 样式。 导航窗格与目录域取的是「大纲级别」而非样式名,因此这样即可得到完整层级 与自动目录,而询比文件原有的字体、字号、缩进、对齐全部保持不动。 """ pPr = p._p.get_or_add_pPr() old = pPr.find(qn("w:outlineLvl")) if old is not None: pPr.remove(old) el = OxmlElement("w:outlineLvl") el.set(qn("w:val"), str(lvl - 1)) pPr.append(el) return p def fit(path, max_w, max_h): """按正文区等比缩放,返回 (宽 cm, 高 cm)。""" from PIL import Image as _Image w_px, h_px = _Image.open(path).size w, h = max_w, max_w * h_px / w_px if h > max_h: h, w = max_h, max_h * w_px / h_px return w, h def sub(cursor_el, text, like_el, doc, lvl=None, page_break=False): """插入一个从属段落(组织标题、说明或占位提示),返回段落对象。""" p = clone_para_after(cursor_el, like_el, doc, text) p.alignment = WD_ALIGN_PARAGRAPH.LEFT if page_break: p.paragraph_format.page_break_before = True if lvl: outline(p, lvl) return p def merge_mark(cursor_el, key, like_el, doc): """插入合并占位标记;合并脚本会整段删除它并在原位插入对应文件。 不加段前分页:分页由合并时插入的分节符或前面的组织标题负责, 否则会多出一页空白。 """ return clone_para_after(cursor_el, like_el, doc, MERGE_TOKEN % key)._p def attachment(cursor_el, title, pages, like_el, doc, max_w=CONTENT_W, note=None, lvl=None, page_break=True): """插入一个附件块:标题另起页,随后按 pages 逐页排图。 pages 为「每页一组图」的列表,元素形如 [(文件名, 图注 or None), ...]; 同一组内的图排在同一页。返回 (标题段, 最后一个元素),便于连续插入。 """ cur = clone_para_after(cursor_el, like_el, doc, title) cur.paragraph_format.page_break_before = page_break cur.alignment = WD_ALIGN_PARAGRAPH.LEFT if lvl: outline(cur, lvl) last = cur._p for line in ([note] if isinstance(note, str) else (note or [])): last = clone_para_after(last, like_el, doc, line)._p for gi, group in enumerate(pages): # 每页高度按该页图片数量均分,避免同页多图溢出 share = (CONTENT_H - 3.0) / max(1, len(group)) for fn, cap in group: path = os.path.join(ATTACH_DIR, fn) if not os.path.exists(path): raise FileNotFoundError(path) w, _ = fit(path, max_w, share) ip = clone_para_after(last, like_el, doc) ip.alignment = WD_ALIGN_PARAGRAPH.CENTER if gi > 0 and group[0][0] == fn: ip.paragraph_format.page_break_before = True ip.runs[0].add_picture(path, width=Cm(w)) last = ip._p if cap: cp = clone_para_after(last, like_el, doc, cap) cp.alignment = WD_ALIGN_PARAGRAPH.CENTER last = cp._p return cur, last def add_bookmark(p, name, bid): start = OxmlElement("w:bookmarkStart") start.set(qn("w:id"), str(bid)) start.set(qn("w:name"), name) end = OxmlElement("w:bookmarkEnd") end.set(qn("w:id"), str(bid)) p._p.insert(0, start) p._p.append(end) # ------------------------------------------------------------------ 主流程 shutil.copy2(SRC_TENDER, OUT_PATH + ".tmp") doc = Document(OUT_PATH + ".tmp") OFF = trim_to_chapter6(doc) ch = body_children(doc) def P(old_idx): return Paragraph(ch[old_idx - OFF], doc) def T(old_idx): return Table(ch[old_idx - OFF], doc) SEALS = [] # (bookmark, 章节, 盖章要求, 签字要求, 依据) def seal(p, key, section, stamp, sign, basis): add_bookmark(p, key, 1000 + len(SEALS)) SEALS.append((key, section, stamp, sign, basis)) # ---------------- 章节标题:补原文漏掉的编号,并设大纲级别 # 「资格审查资料:」看似缺“四、”,实为该段带自动编号(numId 52)已渲染出序号, # 不能再补文字,否则目录会显示成“四、四、资格审查资料”。 # 「包括但不限于以下资料…」确无编号,补“(三)”后目录编号才连贯。 p = P(935) set_run(p, 0, "(三)包括但不限于以下资料(") # 原文把「技术要求的汇总应答」也编成“(一)”,与上一节重号,按序改为“(二)” set_run(P(924), 0, "(二)技术要求的汇总应答") for _i in (792, 816, 838, 853, 889, 915, 942): # 一~七 outline(P(_i), 1) for _i in (794, 806, 854, 858, 890, 898, 917, 924, 935): outline(P(_i), 2) # ---------------- 目录:把原文手工目录清单换成自动目录域 # 原文第 768 段本就有「目 录」标题页,其下 770~777 是七条无页码的手工条目。 # 换成 TOC 域后可显示到四级并带页码与前导点,标题页与页面格式保持原样。 def toc_field(p, instr=' TOC \\o "1-4" \\h \\z \\u '): for r in list(p.runs): r._element.getparent().remove(r._element) for kind, payload in (("begin", None), ("instr", instr), ("separate", None), ("text", "(打开文档后请右键选择“更新域”刷新页码)"), ("end", None)): r = OxmlElement("w:r") if kind == "instr": it = OxmlElement("w:instrText") it.set(qn("xml:space"), "preserve") it.text = payload r.append(it) elif kind == "text": t = OxmlElement("w:t") t.text = payload r.append(t) else: fc = OxmlElement("w:fldChar") fc.set(qn("w:fldCharType"), kind) r.append(fc) p._p.append(r) toc_field(P(770)) for _i in range(771, 792): # 余下手工条目与撑页空段一并去掉 _el = ch[_i - OFF] if _el.getparent() is not None: _el.getparent().remove(_el) P(792).paragraph_format.page_break_before = True # 正文另起页 # 末尾「七、其他」之后的空段会多撑出一整页,去掉 _body = doc.element.body while True: _ps = _body.findall(qn("w:p")) if not _ps: break _txt = "".join(n.text or "" for n in _ps[-1].iter(qn("w:t"))) if _txt.strip(): break _body.remove(_ps[-1]) # ---------------- 封面 p = P(749) set_run(p, 0, " 2026年河南产互联制造业数据智能运营平台研发") p = P(756) set_run(p, 0, f"项目编号:{PROJECT_NO}") p = P(764) set_run(p, 1, f" {COMPANY} (盖公章)") seal(p, "SEAL01", "封面", "供应商(盖公章)", "法定代表人或其授权代表(签字或盖章)", "第六章「响应文件格式」封面署名栏原文") p = P(766) set_run(p, 0, f"日 期: {BID_Y} 年 {BID_M} 月 {BID_D} 日") # ---------------- 一(一)响应函 p = P(795) set_run(p, 1, BUYER) p = P(796) set_run(p, 1, f" {PROJECT_NO} ") set_run(p, 3, f" {PROJECT_FULL}") set_run(p, 6, f" {PRICE_CN} ") set_run(p, 8, f" {PRICE_NUM} ") p = P(799) set_run(p, 1, " 1年 ") p = P(803) set_run(p, 1, f" {COMPANY} (盖章)") seal(p, "SEAL02", "一(一)响应函", "供应商(盖章)", "法定代表人或其委托代理人(签字或盖章)", "第六章响应函署名栏原文;并为第三章形式评审项「响应函签字盖章」") p = P(805) set_run(p, 1, f" {BID_Y} ") set_run(p, 3, f" {BID_M} ") set_run(p, 5, f" {BID_D} ") # ---------------- 一(二)响应函附录 t = T(807) lab = t.cell(0, 0) cell_fill(t.cell(0, 1), PROJECT_FULL, lab) cell_fill(t.cell(1, 1), PROJECT_NO, lab) cell_fill(t.cell(2, 1), COMPANY, lab) cell_fill(t.cell(3, 1), f"大写:{PRICE_CN};\n小写:¥{PRICE_NUM} 元", lab) cell_fill(t.cell(4, 1), "2026年河南产互联制造业数据智能运营平台研发,包括故障处理、系统巡检、" "版本升级、技术咨询等", lab) cell_fill(t.cell(5, 1), "1年", lab) cell_fill(t.cell(6, 1), "系统提供不低于1年的免费质保期。", lab) cell_fill(t.cell(7, 1), "符合国家、省、市相关规定及行业要求,满足采购人工作规范要求。", lab) cell_fill(t.cell(8, 1), "采购人指定地点", lab) p = P(811) set_run(p, 1, f" {COMPANY} (盖章)") seal(p, "SEAL03", "一(二)响应函附录", "供应商(盖章)", "法定代表人或其委托代理人(签字或盖章)", "第六章响应函附录署名栏原文") p = P(813) set_run(p, 1, f" {BID_Y} ") set_run(p, 3, f" {BID_M} ") set_run(p, 5, f" {BID_D} ") # ---------------- 二 法定代表人身份证明书 set_run(P(818), 2, f" {COMPANY} ") set_run(P(819), 1, f" {COMPANY_TYPE} ") set_run(P(820), 1, f" {ADDRESS} ") p = P(821) set_run(p, 1, " 2016 ") set_run(p, 3, " 12 ") set_run(p, 5, " 14 ") set_run(P(822), 1, f" {OPERATE_UNTIL} ") p = P(823) set_run(p, 1, f" {LEGAL_REP} ") set_run(p, 3, f" {LEGAL_REP_SEX} ") set_run(p, 5, f" {LEGAL_REP_AGE} ") set_run(p, 7, "【待填:职务】") set_run(P(824), 1, f" {COMPANY} ") p = P(831) set_run(p, 1, f" {COMPANY} (盖章)") seal(p, "SEAL04", "二、法定代表人身份证明书", "供应商(盖章)", "法定代表人(签字或盖章)", "第六章署名栏原文写「法定代表人」;另原文明文「附:法定代表人身份证复印件,加盖单位公章」") p = P(833) set_run(p, 1, f" {BID_Y} ") set_run(p, 3, f" {BID_M} ") set_run(p, 5, f" {BID_D} ") # 法定代表人身份证复印件(原文「附:法定代表人身份证复印件,加盖单位公章」) tp, _ = attachment(ch[836 - OFF], "附:法定代表人身份证复印件(正面、反面)", [[("id_front.jpg", "(正面)"), ("id_back.jpg", "(反面)")]], ch[836 - OFF], doc, max_w=12.4) seal(tp, "SEALA1", "二、法定代表人身份证复印件", "加盖单位公章", "—", "第六章「附:法定代表人身份证复印件,加盖单位公章。」") # ---------------- 三 法定代表人授权委托书 p = P(840) set_run(p, 1, f" {LEGAL_REP} ") set_run(p, 3, f" {COMPANY} ") set_run(p, 5, f" {AGENT} ") set_run(p, 7, f" {COMPANY} ") set_run(p, 9, f" {BUYER} ") set_run(p, 11, f" {PROJECT_FULL} ") p = P(843) set_run(p, 1, f" {COMPANY} ") seal(p, "SEAL05", "三、法定代表人授权委托书", "供应商(盖单位章)", "法定代表人(签字或盖章)", "第六章署名栏原文写「法定代表人」;另原文明文「附:委托代理人身份证复印件,加盖单位公章」") set_run(P(845), 1, f" {LEGAL_REP_ID} ") set_run(P(846), 1, f" {AGENT} ") set_run(P(847), 1, f" {AGENT_ID} ") p = P(848) # 该段空格 run 为 0/2/4,年/月/日 为 1/3/5 set_run(p, 0, f" {BID_Y} ") set_run(p, 2, f" {BID_M} ") set_run(p, 4, f" {BID_D} ") # ---------------- 四(一)供应商基本情况表 t = T(855) lab = t.cell(0, 0) cell_fill(t.cell(0, 1), COMPANY, lab) cell_fill(t.cell(1, 1), ADDRESS, lab) cell_fill(t.cell(1, 4), "2016年12月14日", lab) cell_fill(t.cell(2, 2), LEGAL_REP, lab) cell_fill(t.cell(2, 4), LEGAL_REP_TEL, lab) cell_fill(t.cell(3, 2), "【待填:项目负责人姓名】", lab) cell_fill(t.cell(3, 4), "【待填:项目负责人电话】", lab) cell_fill(t.cell(4, 1), REG_CAPITAL, lab) cell_fill(t.cell(5, 1), USCC, lab) cell_fill(t.cell(6, 1), BANK_NAME, lab) cell_fill(t.cell(7, 1), BANK_ACCOUNT, lab) cell_fill(t.cell(8, 1), "【待填:办公场所面积】", lab) cell_fill(t.cell(9, 1), BUSINESS_SCOPE, lab) relax_row_height(t.rows[9]) # 经营范围原文较长,固定行高会裁字 cell_fill(t.cell(10, 0), "员工总人数(【待填】人)", lab) # ---------------- 四(二)供应商资格声明函 set_run(P(860), 1, f" {BUYER} ") p = P(874) set_run(p, 1, f" {COMPANY} (盖章)") seal(p, "SEAL06", "四(二)供应商资格声明函", "供应商(盖章)", "法定代表人或其委托代理人(签字或盖章)", "第六章供应商资格声明函署名栏原文") p = P(876) set_run(p, 1, f" {BID_Y} ") set_run(p, 3, f" {BID_M} ") set_run(p, 5, f" {BID_D} ") # ---------------- 四(三)证明资料:逐条对应资格条件 3.1~3.6 like = ch[884 - OFF] # 借用同节段落与字体格式 last = sub(ch[887 - OFF], "(三)证明资料", like, doc, lvl=2, page_break=True)._p # 1、营业执照(资格条件 3.1):紧跟「(三)证明资料」同页,不另起页 _, last = attachment(last, "1、营业执照", [[("license.jpg", None)]], like, doc, lvl=3, page_break=False) # 2、增值税专用发票承诺书(资格条件 3.2,明文「提供承诺书」) last = sub(last, "2、能够提供增值税专用发票的承诺书", like, doc, lvl=3, page_break=True)._p last = merge_mark(last, "invoice", like, doc) # 3、类似业绩(资格条件 3.3,同时对应「类似业绩」10 分) last = sub(last, "3、2023年1月1日以来签订的软件开发项目案例", like, doc, lvl=3, page_break=True)._p last = sub(last, "注:业绩汇总表见本响应文件「五、商务响应文件」5.1 企业业绩," "本节按每个业绩依次附合同关键页、结算发票及发票查验截图。", like, doc)._p for tag, name in (("3.1", "合同(首页、服务内容页、金额页、签字盖章页)"), ("3.2", "发票(结算发票扫描件)"), ("3.3", "发票查询(网上税务系统查验截图)")): last = sub(last, f"{tag} {name}", like, doc, lvl=4)._p last = sub(last, "【待补:本项扫描件由供应商提供后插入】", like, doc)._p # 4、不组成联合体及中选后不转包承诺书(资格条件 3.4) last = sub(last, "4、我公司在本项目不是联合体应答、中选后不转包的承诺书", like, doc, lvl=3, page_break=True)._p last = merge_mark(last, "consortium", like, doc) # 5、信用记录(资格条件 3.5,明文「提供相关网页查询截图」) last = sub(last, "5、未被列入各个网站违法失信名单的承诺书", like, doc, lvl=3, page_break=True)._p CREDIT_SITES = [ ("5.1", "未被“国家企业信用信息公示系统”列入“经营异常名录”"), ("5.2", "未被“国家企业信用信息公示系统”列入“严重违法失信名单”"), ("5.3", "未被“信用中国”网站列入“失信被执行人”"), ("5.4", "未被“信用中国”网站列入“重大税收违法失信主体”"), ("5.5", "未被“信用中国”网站列入“政府采购严重违法失信行为记录名单”"), ] for tag, name in CREDIT_SITES: last = sub(last, f"{tag} {name}", like, doc, lvl=4)._p last = sub(last, "【待补:网页查询截图由供应商提供后插入】", like, doc)._p _, last = attachment(last, "5.6 公共信用信息报告", [[(f"credit_{i:02d}.jpg", None)] for i in range(1, 8)], like, doc, lvl=4, note="说明:本报告由国家公共信用和地理空间信息中心于 2026 年 7 月 31 日出具" "(报告编号 202607311929041364K204),载明我方严重失信 0 条、" "经营异常 0 条、司法判决 0 条。") # 6、不存在下列情形之一的承诺书(资格条件 3.6,明文「提供承诺书」) last = sub(last, "6、我公司不存在下列情形之一的承诺书", like, doc, lvl=3, page_break=True)._p last = merge_mark(last, "credit", like, doc) # ---------------- 五 5.1 企业业绩(一个业绩一张表,供应商自行添加) perf_tbl = ch[891 - OFF] perf_blk = [ch[i - OFF] for i in (892, 893, 894, 895)] anchor = perf_blk[-1] for n in range(PERF_COUNT, 0, -1): if n == 1: tbl_el, blk_els = perf_tbl, perf_blk else: tbl_el = copy.deepcopy(perf_tbl) blk_els = [copy.deepcopy(e) for e in perf_blk] cur = anchor for e in [tbl_el] + blk_els: cur.addnext(e) cur = e tb = Table(tbl_el, doc) lb = tb.cell(0, 0) cell_fill(tb.cell(0, 1), f"【待填:业绩{n}·项目名称】", lb) cell_fill(tb.cell(1, 1), f"【待填:业绩{n}·项目地址】", lb) cell_fill(tb.cell(2, 1), f"【待填:业绩{n}·项目金额(不低于20万元)】", lb) cell_fill(tb.cell(3, 1), f"【待填:业绩{n}·委托人名称】", lb) cell_fill(tb.cell(4, 1), f"【待填:业绩{n}·合同签订日期(2023年1月1日以来)】", lb) cell_fill(tb.cell(5, 1), f"【待填:业绩{n}·结算发票号码及金额】", lb) pp = Paragraph(blk_els[1], doc) set_run(pp, 1, f" {COMPANY} ") seal(pp, f"SEALP{n}", f"五、5.1 企业业绩 · 业绩表 {n}", "供应商名称(盖章)", "法定代表人或其授权代表(签字或盖章)", "第六章「5.1企业业绩(一个业绩一张表,供应商自行添加)」署名栏原文") pd = Paragraph(blk_els[3], doc) set_run(pd, 1, f" {BID_Y} ") set_run(pd, 3, f" {BID_M} ") set_run(pd, 5, f" {BID_D} ") # ---------------- 五 5.2 采购活动廉洁承诺书 set_run(P(901), 0, f" {BUYER} ") set_run(P(902), 1, f" {PROJECT_FULL} ") set_run(P(910), 1, f" {COMPANY} ") p = P(912) append_run_like(p, 0, f" {COMPANY} ") seal(p, "SEAL07", "五、5.2 采购活动廉洁承诺书", "承诺单位(签章)", "法定代表人或授权代理人签字或盖章", "第六章采购活动廉洁承诺书署名栏原文") set_run(P(913), 0, f"{BID_Y} 年 {BID_M} 月 {BID_D} 日") # ---------------- 六 技术响应文件:两处「全部满足」 set_run(P(919), 3, " 全部满足 ") set_run(P(926), 3, " 全部满足 ") # 采购需求逐条应答表:在原表内追加行(原表 1 表头 + 2 空行) t = T(928) hdr = t.rows[0] tmpl_row = t.rows[1]._tr for r in list(t.rows[1:]): r._tr.getparent().remove(r._tr) for no, txt in REQ_ITEMS: new_tr = copy.deepcopy(tmpl_row) t._tbl.append(new_tr) from docx.table import _Row row = _Row(new_tr, t) cell_fill(row.cells[0], no, hdr.cells[0]) cell_fill(row.cells[1], txt, hdr.cells[0]) cell_fill(row.cells[2], "完全响应", hdr.cells[0]) cell_fill(row.cells[3], "", hdr.cells[0]) p = P(931) set_run(p, 2, f" {COMPANY} ") seal(p, "SEAL08", "六、技术响应文件(汇总应答之后)", "供应商名称(盖单位章)", "法定代表人或其委托代理人(签字)", "第六章技术响应文件署名栏原文(原文为「签字」);全章仅此一处署名栏," "同时覆盖合同条款汇总应答与采购需求汇总应答") p = P(933) set_run(p, 0, f" {BID_Y} ") set_run(p, 2, f" {BID_M} ") set_run(p, 4, f" {BID_D} ") # ---------------- 六(三)格式自拟材料:企业实力、软著、项目团队、技术方案各章 like = ch[936 - OFF] # 1、软件企业证书及软件产品证书(企业实力 6 分) _, last = attachment( ch[938 - OFF], "1、软件企业证书及软件产品证书", [[("sw_enterprise.jpg", "软件企业证书(证书编号 京 RQ-2025-1622)")], [("sw_product.jpg", "软件产品证书(证书编号 京 RC-2025-1407)")]], like, doc, lvl=3, note="说明:我方持有软件企业证书(北京软件和信息服务业协会,证书编号 京RQ-2025-1622," "2025年11月29日发证)与软件产品证书(中国软件行业协会,证书编号 京RC-2025-1407," "评估软件为透明工厂管理平台V1.0),两证均在有效期内," "证书主体均为北京智造易科技有限公司。") # 2、软件著作权登记证书(软著 9 分):先一览表,再逐份证书扫描件 last = sub(last, "2、计算机软件著作权登记证书", like, doc, lvl=3, page_break=True)._p last = merge_mark(last, "swtable", like, doc) _, last = attachment( last, "证书扫描件(共 10 份)", [[(f"swc_{i:02d}.jpg", f"{SW_COPYRIGHTS[i - 1][0]}({SW_COPYRIGHTS[i - 1][1]})")] for i in range(1, 11)], like, doc) # 3、项目团队(项目团队 2 分) last = sub(last, "3、项目团队", like, doc, lvl=3, page_break=True)._p last = merge_mark(last, "team", like, doc) last = sub(last, "【待补:上表各岗位人员姓名及与我方签署的劳务合同扫描件," "由供应商提供后插入】", like, doc)._p # 4~12:技术方案与实施服务方案各章,章标题自带序号与大纲级别 for _i in range(4, 13): last = merge_mark(last, f"ch{_i:02d}", like, doc) # 「七、其他」原为空章节:另起页并作出应答,避免标题挤在上一章末尾、末页全白。 # 该段末尾带模板自留的 (原用于收尾第六章),不去掉会把 # 下面的应答顶到新的一页。 _other = P(942) _other.paragraph_format.page_break_before = True for _r in list(_other.runs): if _r._element.findall(qn("w:br")) and not _r.text: _r._element.getparent().remove(_r._element) sub(ch[942 - OFF], "除本响应文件已列明的资格审查资料、商务响应文件与技术响应文件外," "我方无其他需要补充说明的事项。", like, doc) print("[结构] 已按截图重建四(三)证明资料 1~6、六(三)1~12 层级") # 去掉询比文件的「第六章 响应文件格式」标题页,响应文件从封面开始 title_el = ch[747 - OFF] title_el.getparent().remove(title_el) print("[标题页] 已移除询比文件「第六章 响应文件格式」标题页") n_ftr = fix_footer_page_numbers(doc) print(f"[页脚] 已修正 {n_ftr} 个页脚的页码域(模板文本框会压掉两位数)") doc.save(OUT_PATH) os.remove(OUT_PATH + ".tmp") print(f"[完成] {OUT_PATH} ({os.path.getsize(OUT_PATH):,} bytes)") print(f"[签章位] 共 {len(SEALS)} 处(均为询比文件明文要求)") import json with open(os.path.join(DOC_DIR, "_tmp_seals.json"), "w", encoding="utf-8") as f: json.dump(SEALS, f, ensure_ascii=False, indent=2) if os.path.isdir(ONEDRIVE_DIR): try: shutil.copy2(OUT_PATH, os.path.join(ONEDRIVE_DIR, OUT_NAME)) print(f"[已同步] {os.path.join(ONEDRIVE_DIR, OUT_NAME)}") except Exception as e: print(f"[同步失败] {type(e).__name__}: {e}")