Files
settlement-docs/scripts/generate.py
T
XuHengJie 3cf5ba655c init: 土地平整工程竣工结算资料(部分)生成套件 v1.0.0
从土地平整工程单项工程结算表中提取清理表土、修平田地、
田埂修筑三项工序的工程量,自动生成 T3 田块单元工程
竣工结算资料套表(计量报验单、工序工程量统计表、3份工序工程量签证)。

- 支持 .xls / .xlsx 格式输入
- 输出 5 份 .xlsx 文档,含合并单元格、边框、表头蓝色底色
- 参数可覆盖项目名称/承包单位/合同段/监理/业主等元信息
2026-07-21 14:45:08 +08:00

683 lines
30 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
土地平整工程竣工结算资料(部分)生成
从单项工程结算表中提取数据,生成T3田块竣工结算资料套表。
"""
import sys
import json
import os
import re
from copy import copy
# ── helpers for openpyxl ──────────────────────────────────────────────
try:
import openpyxl
from openpyxl.styles import (
Font, Border, Side, Alignment, PatternFill, NamedStyle
)
from openpyxl.utils import get_column_letter
except ImportError:
print(json.dumps({"error": "openpyxl 未安装,请在环境安装: pip install openpyxl"}))
sys.exit(1)
# ── also try xlrd for .xls input ──────────────────────────────────────
try:
import xlrd
HAS_XLRD = True
except ImportError:
HAS_XLRD = False
# ══════════════════════════════════════════════════════════════════════
# Style definitions
# ══════════════════════════════════════════════════════════════════════
THIN_BORDER = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin'),
)
NO_BORDER = Border()
TITLE_FONT = Font(name='宋体', size=16, bold=True)
HEADER_FONT = Font(name='宋体', size=10, bold=True)
NORMAL_FONT = Font(name='宋体', size=10)
SIGN_FONT = Font(name='宋体', size=10, bold=True)
SMALL_FONT = Font(name='宋体', size=9)
CENTER_ALIGN = Alignment(horizontal='center', vertical='center', wrap_text=True)
LEFT_ALIGN = Alignment(horizontal='left', vertical='center', wrap_text=True)
LEFT_TOP = Alignment(horizontal='left', vertical='top', wrap_text=True)
HEADER_FILL = PatternFill(start_color='D9E2F3', end_color='D9E2F3', fill_type='solid')
LIGHT_YELLOW = PatternFill(start_color='FFF2CC', end_color='FFF2CC', fill_type='solid')
def apply_cell(ws, row, col, value, font=None, alignment=None, border=None, fill=None, number_format=None):
"""Helper to set a cell value + style."""
cell = ws.cell(row=row, column=col, value=value)
if font:
cell.font = font
if alignment:
cell.alignment = alignment
if border:
cell.border = border
if fill:
cell.fill = fill
if number_format:
cell.number_format = number_format
return cell
def merge_and_set(ws, r1, r2, c1, c2, value, font=None, alignment=None, border=None, fill=None):
"""Merge cells and set value + style in the top-left cell."""
ws.merge_cells(start_row=r1, start_column=c1, end_row=r2, end_column=c2)
return apply_cell(ws, r1, c1, value, font=font, alignment=alignment, border=border, fill=fill)
def thin_border_range(ws, r1, r2, c1, c2):
"""Apply thin border to all cells in range."""
for r in range(r1, r2 + 1):
for c in range(c1, c2 + 1):
ws.cell(row=r, column=c).border = THIN_BORDER
def set_col_widths(ws, widths):
"""Set column widths: widths is a dict {col_index: width_in_chars} (1-based)"""
for col_idx, w in widths.items():
ws.column_dimensions[get_column_letter(col_idx)].width = w
def set_row_heights(ws, heights):
"""Set row heights: heights is a dict {row_index: height_in_points} (1-based)"""
for row_idx, h in heights.items():
ws.row_dimensions[row_idx].height = h
# ══════════════════════════════════════════════════════════════════════
# Input reader
# ══════════════════════════════════════════════════════════════════════
def read_input(input_path):
"""
Read the 单项工程结算表 and return a dict with:
- project_name
- contractor
- quantities: { '清理表土': float, '修平田地': float, '田埂修筑': float }
- date_value (if available)
"""
ext = os.path.splitext(input_path)[1].lower()
result = {
'project_name': '',
'contractor': '',
'contract_section': '',
'quantities': {},
'date_value': None,
}
if ext in ('.xls',):
if not HAS_XLRD:
raise RuntimeError(
"输入文件为 .xls 格式,但环境未安装 xlrd 库。"
"请将文件另存为 .xlsx 格式后重试。"
)
wb = xlrd.open_workbook(input_path)
ws = wb.sheet_by_index(0)
# Row 0: title
# Row 1: project name (col 1)
# Row 2: contractor (col 1)
# Row 3: header row
# Row 4-8: process rows
result['project_name'] = str(ws.cell(1, 1).value or '')
result['contractor'] = str(ws.cell(2, 1).value or '')
# 合同段 at (1, 8)
result['contract_section'] = str(ws.cell(1, 9).value or '')
# Quantity cell: col 8 (0-indexed) in rows 4,5,6
process_map = {
'清理表土': (4, 9),
'修平田地': (5, 9),
'田埂修筑': (6, 9),
}
for pname, (r, c) in process_map.items():
cv = ws.cell(r, c).value
try:
result['quantities'][pname] = float(cv) if cv else 0.0
except (ValueError, TypeError):
result['quantities'][pname] = 0.0
# Date
for r in range(ws.nrows):
for c in range(ws.ncols):
if ws.cell(r, c).ctype == 3:
dt = xlrd.xldate_as_tuple(ws.cell(r, c).value, wb.datemode)
result['date_value'] = dt
break
if result['date_value']:
break
elif ext in ('.xlsx',):
wb = openpyxl.load_workbook(input_path, data_only=True)
ws = wb.active
result['project_name'] = str(ws.cell(2, 2).value or '')
result['contractor'] = str(ws.cell(3, 2).value or '')
result['contract_section'] = str(ws.cell(2, 10).value or '')
process_map = {
'清理表土': (5, 10),
'修平田地': (6, 10),
'田埂修筑': (7, 10),
}
for pname, (r, c) in process_map.items():
cv = ws.cell(r, c).value
try:
result['quantities'][pname] = float(cv) if cv else 0.0
except (ValueError, TypeError):
result['quantities'][pname] = 0.0
else:
raise ValueError(f"不支持的文件格式: {ext},请提供 .xls 或 .xlsx 文件")
return result
# ══════════════════════════════════════════════════════════════════════
# Output document generators
# ══════════════════════════════════════════════════════════════════════
def generate_report_form(wb, data, params):
"""
输出文件1:计量报验单
文件名: 01_{field_name}单元工程计量报验单.xlsx
Template: 3.3.3-01.01.01.001.1
"""
field = params['field_name']
proj = params.get('project_name') or data.get('project_name', '')
contractor = params.get('contractor') or data.get('contractor', '')
contract_section = params.get('contract_section') or data.get('contract_section', '')
supervision = params.get('supervision_company', '')
ws = wb.active
ws.title = '计量报验单'
# Column widths (1-indexed)
set_col_widths(ws, {1: 16, 2: 16, 3: 16, 4: 14, 5: 14, 6: 12, 7: 12, 8: 10, 9: 10})
set_row_heights(ws, {1: 36, 2: 24, 3: 24, 4: 30, 5: 24, 6: 24, 7: 24, 8: 24,
9: 24, 10: 24, 11: 24, 12: 30, 13: 24, 14: 24, 15: 24})
# Row 1: Title
merge_and_set(ws, 1, 1, 1, 9,
f'{field}单元工程计量报验单',
font=Font(name='宋体', size=16, bold=True),
alignment=CENTER_ALIGN)
# Row 2: 项目名称 & 合同段
merge_and_set(ws, 2, 2, 1, 2, '项目名称', font=SIGN_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 2, 2, 3, 7, proj, font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 2, 2, 8, 9, f'合同段 {contract_section}', font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 3: 致 监理单位
merge_and_set(ws, 3, 3, 1, 1, '致:', font=SIGN_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 3, 3, 2, 9, supervision, font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 4: 已完成...单元工程
merge_and_set(ws, 4, 4, 1, 4,
'我方按承包合同约定,已完成了',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 4, 4, 5, 7, field, font=NORMAL_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 4, 4, 8, 9,
'单元工程(工序)的施工,其',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 5: 工程质量...
merge_and_set(ws, 5, 5, 1, 9,
'工程质量已经检查合格,并对工程量进行了计量测量,现提交计量结果,请予以核准。',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 6: 附件:
merge_and_set(ws, 6, 6, 1, 9, '附件:', font=SIGN_FONT, alignment=LEFT_ALIGN)
# Row 7-9: Attachment items
attachments = [
('1、工序工程计量统计表', '1'),
('2、工程量测量资料', ''),
('3、工序工程计量签证表', '3'),
]
for i, (desc, pages) in enumerate(attachments):
r = 7 + i
merge_and_set(ws, r, r, 1, 1, '', font=NORMAL_FONT) # spacing
merge_and_set(ws, r, r, 2, 6, desc, font=NORMAL_FONT, alignment=LEFT_ALIGN)
if pages:
apply_cell(ws, r, 8, float(pages), font=NORMAL_FONT, alignment=CENTER_ALIGN)
apply_cell(ws, r, 9, '页', font=NORMAL_FONT, alignment=CENTER_ALIGN)
# Row 10: 承包单位
merge_and_set(ws, 10, 10, 1, 3,
f'承包单位(盖章): {contractor}',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 10, 10, 4, 9, '', font=NORMAL_FONT)
# Row 11: 项目经理
merge_and_set(ws, 11, 11, 1, 3,
'项目经理(签字):',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 11, 11, 4, 9, '', font=NORMAL_FONT)
# Row 12: 日期
merge_and_set(ws, 12, 12, 1, 3,
'日 期:',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 12, 12, 4, 9, '', font=NORMAL_FONT)
# Row 13: 审查意见
merge_and_set(ws, 13, 13, 1, 5,
'监理机构审查意见:同意。',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 13, 13, 6, 9,
'项目承担单位审查意见:同意。',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 14: 签字
merge_and_set(ws, 14, 14, 1, 3,
'监理工程师(签字):',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 14, 14, 6, 7,
'现场负责人(签字):',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 15: 日期
merge_and_set(ws, 15, 15, 1, 3,
'日 期:',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 15, 15, 4, 5, '', font=NORMAL_FONT)
merge_and_set(ws, 15, 15, 6, 7,
'日 期:',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
apply_cell(ws, 14, 8, '', font=NORMAL_FONT)
apply_cell(ws, 14, 9, '', font=NORMAL_FONT)
apply_cell(ws, 15, 8, '', font=NORMAL_FONT)
apply_cell(ws, 15, 9, '', font=NORMAL_FONT)
def generate_statistics_table(wb, data, params):
"""
输出文件2:工序工程量统计表
文件名: 02_{field_name}单元工程工序工程量统计表.xlsx
Template: 3.3.3-01.01.01.001.2
"""
field = params['field_name']
proj = params.get('project_name') or data.get('project_name', '')
contractor = params.get('contractor') or data.get('contractor', '')
contract_section = params.get('contract_section') or data.get('contract_section', '')
qty = data['quantities']
ws = wb.active
ws.title = '工序工程量统计表'
set_col_widths(ws, {1: 8, 2: 16, 3: 10, 4: 14, 5: 14, 6: 14,
7: 8, 8: 8, 9: 8, 10: 8, 11: 8, 12: 8, 13: 8, 14: 8, 15: 8})
set_row_heights(ws, {1: 36, 2: 24, 3: 24, 4: 30, 5: 24, 6: 24, 7: 24,
8: 24, 9: 24, 10: 24, 11: 24})
# Row 1: Title
merge_and_set(ws, 1, 1, 1, 15,
f'{field}单元工程工序工程量统计表',
font=Font(name='宋体', size=16, bold=True),
alignment=CENTER_ALIGN)
# Row 2: 项目名称
merge_and_set(ws, 2, 2, 1, 2, '项目名称', font=SIGN_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 2, 2, 3, 12, proj, font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 2, 2, 14, 15, f'合同段 {contract_section}',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
apply_cell(ws, 2, 13, '', font=NORMAL_FONT)
# Row 3: 承包单位
merge_and_set(ws, 3, 3, 1, 2, '承包单位', font=SIGN_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 3, 3, 3, 15, contractor, font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 4: Table header row 1
merge_and_set(ws, 4, 5, 1, 1, '序号', font=HEADER_FONT, alignment=CENTER_ALIGN,
border=THIN_BORDER, fill=HEADER_FILL)
merge_and_set(ws, 4, 5, 2, 2, '工程验收段名称及部位', font=HEADER_FONT,
alignment=CENTER_ALIGN, border=THIN_BORDER, fill=HEADER_FILL)
merge_and_set(ws, 4, 4, 3, 3, '工程量', font=HEADER_FONT,
alignment=CENTER_ALIGN, border=THIN_BORDER, fill=HEADER_FILL)
merge_and_set(ws, 4, 4, 4, 6, '工 序 工 程 量', font=HEADER_FONT,
alignment=CENTER_ALIGN, border=THIN_BORDER, fill=HEADER_FILL)
# fill remaining header cells
for c in range(7, 16):
ws.cell(row=4, column=c).border = THIN_BORDER
ws.cell(row=4, column=c).fill = HEADER_FILL
for c in range(7, 16):
ws.cell(row=5, column=c).border = THIN_BORDER
ws.cell(row=5, column=c).fill = HEADER_FILL
# Row 5: Sub-header for 清理表土, 修平田地, 田埂修筑
apply_cell(ws, 5, 3, '', font=HEADER_FONT, alignment=CENTER_ALIGN,
border=THIN_BORDER, fill=HEADER_FILL)
sub_headers = {4: '清理表土', 5: '修平田地', 6: '田埂修筑'}
for c, text in sub_headers.items():
apply_cell(ws, 5, c, text, font=HEADER_FONT, alignment=CENTER_ALIGN,
border=THIN_BORDER, fill=HEADER_FILL)
# Row 6: Data
hm2 = ''
apply_cell(ws, 6, 1, 1, font=NORMAL_FONT, alignment=CENTER_ALIGN, border=THIN_BORDER)
apply_cell(ws, 6, 2, field, font=NORMAL_FONT, alignment=CENTER_ALIGN, border=THIN_BORDER)
apply_cell(ws, 6, 3, hm2, font=NORMAL_FONT, alignment=CENTER_ALIGN, border=THIN_BORDER)
for c_idx, pname in [(4, '清理表土'), (5, '修平田地'), (6, '田埂修筑')]:
val = qty.get(pname, 0)
apply_cell(ws, 6, c_idx, val if val else '',
font=NORMAL_FONT, alignment=CENTER_ALIGN, border=THIN_BORDER,
number_format='#,##0.00' if val else None)
for c in range(7, 16):
apply_cell(ws, 6, c, '', font=NORMAL_FONT, alignment=CENTER_ALIGN, border=THIN_BORDER)
# Row 7: 合计
merge_and_set(ws, 7, 7, 1, 2, '合 计', font=SIGN_FONT, alignment=CENTER_ALIGN,
border=THIN_BORDER)
apply_cell(ws, 7, 3, '', font=SIGN_FONT, alignment=CENTER_ALIGN, border=THIN_BORDER)
for c_idx, pname in [(4, '清理表土'), (5, '修平田地'), (6, '田埂修筑')]:
val = qty.get(pname, 0)
apply_cell(ws, 7, c_idx, val if val else '',
font=SIGN_FONT, alignment=CENTER_ALIGN, border=THIN_BORDER,
number_format='#,##0.00' if val else None)
for c in range(7, 16):
apply_cell(ws, 7, c, '', font=SIGN_FONT, alignment=CENTER_ALIGN, border=THIN_BORDER)
# Row 8-11: Signature area
sig_labels = [
('承包单位(盖章):', '监理机构(盖章):', '项目承担单位(盖章):'),
('技术负责人:', '监理工程师:', '现场负责人:'),
('日 期:', '日 期:', '日 期:'),
]
block_cols = [(1, 5), (6, 10), (11, 15)]
for ri, (l1, l2, l3) in enumerate(sig_labels):
r = 8 + ri
merge_and_set(ws, r, r, 1, 5, l1, font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, r, r, 6, 10, l2, font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, r, r, 11, 15, l3, font=NORMAL_FONT, alignment=LEFT_ALIGN)
def generate_certificate(wb, data, params, process_name, quantity, date_val=None):
"""
输出文件3.x:工序工程量签证
Template: 3.3.3-01.01.01.001.3.01.xx
"""
field = params['field_name']
proj = params.get('project_name') or data.get('project_name', '')
contractor = params.get('contractor') or data.get('contractor', '')
contract_section = params.get('contract_section') or data.get('contract_section', '')
supervision = params.get('supervision_company', '')
project_owner = params.get('project_owner', '')
sub_project = params.get('sub_project_name', '水浇地1525°区域')
unit_eng = params.get('unit_engineer_name', '田块修筑')
ws = wb.active
ws.title = '工程量签证'
set_col_widths(ws, {1: 16, 2: 12, 3: 14, 4: 14, 5: 14,
6: 12, 7: 12, 8: 12, 9: 12, 10: 10, 11: 10, 12: 10})
set_row_heights(ws, {1: 36, 2: 24, 3: 24, 4: 24, 5: 24, 6: 24,
7: 30, 8: 30, 9: 24, 10: 24, 11: 24, 12: 24,
13: 24, 14: 24, 15: 36, 16: 36, 17: 30, 18: 24,
19: 24, 20: 24, 21: 24, 22: 24})
# Row 1: Title
merge_and_set(ws, 1, 1, 1, 12,
'工序(验收段)工程量签证',
font=Font(name='宋体', size=16, bold=True),
alignment=CENTER_ALIGN)
# Row 2: 项目名称 / 合同段
merge_and_set(ws, 2, 2, 1, 2, '项目名称', font=SIGN_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 2, 2, 3, 8, proj, font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 2, 2, 10, 12, f'合同段 {contract_section}',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
apply_cell(ws, 2, 9, '', font=NORMAL_FONT)
# Row 3: 单项/分部/单元/工程名称
merge_and_set(ws, 3, 3, 1, 2, '单项工程名称', font=SIGN_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 3, 3, 3, 5, '土地平整工程', font=NORMAL_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 3, 3, 6, 8, '分部工程名称', font=SIGN_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 3, 3, 9, 12, sub_project, font=NORMAL_FONT, alignment=CENTER_ALIGN)
# Row 4: 单位/单元工程
merge_and_set(ws, 4, 4, 1, 2, '单位工程名称', font=SIGN_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 4, 4, 3, 5, unit_eng, font=NORMAL_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 4, 4, 6, 8, '单元工程名称', font=SIGN_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 4, 4, 9, 12, field, font=NORMAL_FONT, alignment=CENTER_ALIGN)
# Row 5-6: 致 业主/监理
merge_and_set(ws, 5, 5, 1, 1, '致:', font=SIGN_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 5, 5, 2, 12, project_owner, font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 6, 6, 1, 1, '', font=NORMAL_FONT)
merge_and_set(ws, 6, 6, 2, 12, supervision, font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 7: 我方按设计要求完成...
merge_and_set(ws, 7, 7, 1, 4,
'我方按设计要求完成',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 7, 7, 5, 9, process_name, font=NORMAL_FONT, alignment=CENTER_ALIGN)
merge_and_set(ws, 7, 7, 10, 12,
'工序验收段施工,',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 8: 经工序...
merge_and_set(ws, 8, 8, 1, 12,
'经工序质量评定合格,请予以对其工程量进行核定。',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 9: 承包单位
merge_and_set(ws, 9, 9, 1, 3, '承包单位(盖章):', font=SIGN_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 9, 9, 4, 12, contractor, font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 10: 项目经理
merge_and_set(ws, 10, 10, 1, 3, '项目经理(签字):', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 10, 10, 4, 6, '', font=NORMAL_FONT)
merge_and_set(ws, 10, 10, 7, 12, '', font=NORMAL_FONT)
# Row 11: 日期
merge_and_set(ws, 11, 11, 1, 3, '日 期:', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 11, 11, 4, 12, '', font=NORMAL_FONT)
# Row 12: 签证内容及图示
merge_and_set(ws, 12, 12, 1, 12,
'签证内容及图示:',
font=SIGN_FONT, alignment=LEFT_ALIGN)
# Row 13: 1、田块
merge_and_set(ws, 13, 13, 1, 4, f'1、{field}', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 13, 13, 5, 12,
'验收段的工序工程量',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 14: 2、图示
merge_and_set(ws, 14, 14, 1, 12,
'2、图示详见施工图册',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 15-16: blank spacing for diagram
merge_and_set(ws, 15, 15, 1, 12, '', font=NORMAL_FONT)
merge_and_set(ws, 16, 16, 1, 12, '', font=NORMAL_FONT)
# Row 17: 工程量计算
merge_and_set(ws, 17, 17, 1, 12,
'工程量计算:',
font=SIGN_FONT, alignment=LEFT_ALIGN)
# Row 18: 1、工序名称 + 数量
quantity_str = f'{quantity:.2f} m³' if quantity else '0.00 m³'
merge_and_set(ws, 18, 18, 1, 4, f'1、{process_name}', font=SIGN_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 18, 18, 5, 12, quantity_str, font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 19: 2、表达式
merge_and_set(ws, 19, 19, 1, 4, '2、工程量计算表达式', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 19, 19, 5, 12, '', font=NORMAL_FONT)
# Row 20: Signatures row 1
merge_and_set(ws, 20, 20, 1, 4, '承包单位(盖章):', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 20, 20, 5, 8, '监理机构(盖章):', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 20, 20, 9, 12, '项目承担单位(盖章):',
font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 21: Signatures row 2
merge_and_set(ws, 21, 21, 1, 2, '技术负责人:', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 21, 21, 5, 6, '监理工程师:', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 21, 21, 9, 10, '现场负责人:', font=NORMAL_FONT, alignment=LEFT_ALIGN)
# Row 22: Signatures row 3 (dates)
merge_and_set(ws, 22, 22, 1, 2, '日 期:', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 22, 22, 3, 4, '', font=NORMAL_FONT)
merge_and_set(ws, 22, 22, 5, 6, '日 期:', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 22, 22, 7, 8, '', font=NORMAL_FONT)
merge_and_set(ws, 22, 22, 9, 10, '日 期:', font=NORMAL_FONT, alignment=LEFT_ALIGN)
merge_and_set(ws, 22, 22, 11, 12, '', font=NORMAL_FONT)
# Apply full borders to all rows (for visual consistency)
for r in range(1, 23):
for c in range(1, 13):
cell = ws.cell(row=r, column=c)
if cell.border == Border():
cell.border = THIN_BORDER
# ══════════════════════════════════════════════════════════════════════
# Main
# ══════════════════════════════════════════════════════════════════════
def main():
if len(sys.argv) > 1:
params_raw = sys.argv[1]
try:
p = json.loads(params_raw)
except json.JSONDecodeError:
print(json.dumps({"error": f"无法解析参数 JSON: {params_raw}"}))
sys.exit(1)
elif 'PARAMS_FILE' in os.environ:
with open(os.environ['PARAMS_FILE'], 'r') as f:
p = json.load(f)
else:
p = {}
# For development: use env vars as fallback
for key in ('input_path', 'output_dir', 'field_name', 'sub_project_name',
'unit_engineer_name', 'project_name', 'contractor',
'contract_section', 'supervision_company', 'project_owner'):
if key.upper() in os.environ:
p[key] = os.environ[key.upper()]
required = ['input_path', 'output_dir']
missing = [k for k in required if k not in p or not p[k]]
if missing:
print(json.dumps({
"error": f"缺少必要参数: {', '.join(missing)}",
"usage": "python generate.py '{\"input_path\": \"...\", \"output_dir\": \"...\"}'"
}))
sys.exit(1)
input_path = p['input_path']
output_dir = p['output_dir']
os.makedirs(output_dir, exist_ok=True)
# ── 1. Read input ──────────────────────────────────────────────
print(json.dumps({"step": "read_input", "status": "start", "path": input_path}))
try:
data = read_input(input_path)
except Exception as e:
print(json.dumps({"error": f"读取结算表失败: {str(e)}"}))
sys.exit(1)
print(json.dumps({
"step": "read_input",
"status": "ok",
"project_name": data['project_name'],
"contractor": data['contractor'],
"quantities": data['quantities'],
}))
qty = data['quantities']
# ── 2. Collect params with fallback ─────────────────────────────
params = {
'field_name': p.get('field_name', 'T3田块'),
'sub_project_name': p.get('sub_project_name', '水浇地1525°区域'),
'unit_engineer_name': p.get('unit_engineer_name', '田块修筑'),
'project_name': p.get('project_name') or data.get('project_name', ''),
'contractor': p.get('contractor') or data.get('contractor', ''),
'contract_section': p.get('contract_section') or data.get('contract_section', ''),
'supervision_company': p.get('supervision_company', '曲靖南天监理有限公司'),
'project_owner': p.get('project_owner', '巧家县国土资源局'),
}
# ── 3. Generate output documents ────────────────────────────────
generated = []
# 3.1 计量报验单
wb1 = openpyxl.Workbook()
generate_report_form(wb1, data, params)
f1 = os.path.join(output_dir, f'01_{params["field_name"]}单元工程计量报验单.xlsx')
wb1.save(f1)
generated.append(f1)
print(json.dumps({"step": "generate", "file": f1, "status": "ok"}))
# 3.2 工序工程量统计表
wb2 = openpyxl.Workbook()
generate_statistics_table(wb2, data, params)
f2 = os.path.join(output_dir, f'02_{params["field_name"]}单元工程工序工程量统计表.xlsx')
wb2.save(f2)
generated.append(f2)
print(json.dumps({"step": "generate", "file": f2, "status": "ok"}))
# 3.3 清理表土签证
proc_name = '清理表土'
q_val = qty.get(proc_name, 0)
wb31 = openpyxl.Workbook()
generate_certificate(wb31, data, params, proc_name, q_val)
f31 = os.path.join(output_dir, f'03.01_{proc_name}工序工程量签证.xlsx')
wb31.save(f31)
generated.append(f31)
print(json.dumps({"step": "generate", "file": f31, "status": "ok"}))
# 3.4 修平田地签证
proc_name = '修平田地'
q_val = qty.get(proc_name, 0)
wb32 = openpyxl.Workbook()
generate_certificate(wb32, data, params, proc_name, q_val)
f32 = os.path.join(output_dir, f'03.02_{proc_name}工序工程量签证.xlsx')
wb32.save(f32)
generated.append(f32)
print(json.dumps({"step": "generate", "file": f32, "status": "ok"}))
# 3.5 田埂修筑签证
proc_name = '田埂修筑'
q_val = qty.get(proc_name, 0)
wb33 = openpyxl.Workbook()
generate_certificate(wb33, data, params, proc_name, q_val)
f33 = os.path.join(output_dir, f'03.03_{proc_name}工序工程量签证.xlsx')
wb33.save(f33)
generated.append(f33)
print(json.dumps({"step": "generate", "file": f33, "status": "ok"}))
# ── Done ────────────────────────────────────────────────────────
result = {
"status": "success",
"output_dir": output_dir,
"files": generated,
"summary": {
"field": params['field_name'],
"project": params['project_name'],
"contractor": params['contractor'],
"quantities": {
"清理表土": round(qty.get('清理表土', 0), 2),
"修平田地": round(qty.get('修平田地', 0), 2),
"田埂修筑": round(qty.get('田埂修筑', 0), 2),
}
}
}
print(json.dumps(result))
if __name__ == '__main__':
main()