from pathlib import Path
|
from zipfile import ZipFile,ZIP_DEFLATED
|
from lxml import etree as E
|
import hashlib,json
|
from copy import deepcopy
|
root=Path('scripts/quotation-contract-20261015')
|
source=root/'reference.docx'
|
target=Path('yudao-module-crm/src/main/resources/templates/contract/sale-contract.docx');target.parent.mkdir(parents=True,exist_ok=True)
|
ns={'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'};W='{'+ns['w']+'}'
|
with ZipFile(source) as src:
|
xml=E.fromstring(src.read('word/document.xml'));body=xml.find('w:body',ns);ps=body.findall('w:p',ns)
|
for t in ps[1].findall('.//w:t',ns):
|
if '中国科学院西北高原生物研究所' in (t.text or ''):t.text=t.text.replace('中国科学院西北高原生物研究所','${customerName}')
|
metadata=ps[4]
|
for child in list(metadata):metadata.remove(child)
|
ppr=ps[1].find('w:pPr',ns)
|
if ppr is not None:metadata.append(deepcopy(ppr))
|
run=E.SubElement(metadata,W+'r');rpr=ps[1].find('w:r/w:rPr',ns)
|
if rpr is not None:run.append(deepcopy(rpr))
|
E.SubElement(run,W+'t').text='合同编号:${contractNo} 合同日期:${contractDate}'
|
body.remove(metadata);body.insert(list(body).index(ps[0])+1,metadata)
|
for paragraph in ps:
|
text=''.join(paragraph.itertext())
|
if any(text.startswith(prefix) for prefix in ['第二条','第三条','第四条','第五条','第六条','第七条']):
|
prop=paragraph.find('w:pPr',ns)
|
if prop is None:prop=E.SubElement(paragraph,W+'pPr')
|
keep=prop.find('w:keepNext',ns)
|
if keep is None:keep=E.SubElement(prop,W+'keepNext')
|
keep.set(W+'val','1')
|
table=body.find('w:tbl',ns);rows=table.findall('w:tr',ns)
|
for cell,key in zip(rows[1].findall('w:tc',ns),['index','itemName','unitName','count','price','amount']):
|
texts=cell.findall('.//w:t',ns);texts[0].text='${'+key+'}'
|
for t in texts[1:]:t.text=''
|
rowprop=rows[1].find('w:trPr',ns)
|
if rowprop is None:rowprop=E.SubElement(rows[1],W+'trPr')
|
E.SubElement(rowprop,W+'cantSplit')
|
for row in rows[2:4]:table.remove(row)
|
for row,text in [(rows[4],'合计金额:${totalPrice}${discountNote}'),(rows[5],'大写金额:${totalUpper}')]:
|
texts=row.findall('.//w:t',ns);texts[0].text=text
|
for t in texts[1:]:t.text=''
|
prop=rows[0].find('w:trPr',ns)
|
if prop is None:prop=E.SubElement(rows[0],W+'trPr')
|
E.SubElement(prop,W+'tblHeader')
|
with ZipFile(target,'w',ZIP_DEFLATED) as dst:
|
for info in src.infolist():dst.writestr(info,E.tostring(xml,xml_declaration=True,encoding='UTF-8',standalone=True) if info.filename=='word/document.xml' else src.read(info.filename))
|
original=Path('D:/WechatRecord/xwechat_files/wxid_vmeut7wztkbp22_1546/temp/RWTemp/2026-10/c40afdcfa98e2b4561823e6babbefa33/虫草购销合同-西高所.doc')
|
(root/'artifact.md').write_text('''# 合同模板执行说明
|
原始 DOC 保持不变,使用 Word 转为 reference.docx。版式以该副本为准:保留页面尺寸、字体、段落、原合同全部条款和甲方收款信息。
|
仅编辑 word/document.xml:乙方名称、标题下的合同编号与日期、产品明细行、合计和大写金额;产品行可按数量克隆,原两条空行移除,表头跨页重复,产品行禁止跨页拆分,条款标题与后文同页。
|
金额来自合同实际数据,含整单优惠时明确注明优惠金额和实付合计。签章区和签字时间保留为空,不自动代签。
|
原模板开户行写为公司名称,按原样保留,正式签署前应核对。
|
'''+f'原始文件 SHA256:{hashlib.sha256(original.read_bytes()).hexdigest()}\nDOCX SHA256:{hashlib.sha256(source.read_bytes()).hexdigest()}\n',encoding='utf-8')
|
print(target)
|