#!/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', '水浇地15~25°区域') 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', '水浇地15~25°区域'), '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()