init: 土地平整工程竣工结算资料(部分)生成套件 v1.0.0

从土地平整工程单项工程结算表中提取清理表土、修平田地、
田埂修筑三项工序的工程量,自动生成 T3 田块单元工程
竣工结算资料套表(计量报验单、工序工程量统计表、3份工序工程量签证)。

- 支持 .xls / .xlsx 格式输入
- 输出 5 份 .xlsx 文档,含合并单元格、边框、表头蓝色底色
- 参数可覆盖项目名称/承包单位/合同段/监理/业主等元信息
This commit is contained in:
2026-07-21 14:45:08 +08:00
commit 3cf5ba655c
2 changed files with 776 additions and 0 deletions
+682
View File
@@ -0,0 +1,682 @@
#!/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}' 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()