Formated Client & Subctr Report

This commit is contained in:
2026-08-08 17:44:47 +05:30
parent 80d4e82f9b
commit 01f6831e9c

View File

@@ -1,5 +1,6 @@
import pandas as pd import pandas as pd
import io import io
from datetime import datetime
from flask import Blueprint, render_template, request, send_file, flash, jsonify,redirect, url_for from flask import Blueprint, render_template, request, send_file, flash, jsonify,redirect, url_for
from app.utils.helpers import login_required from app.utils.helpers import login_required
from app.utils.regex_utils import RegularExpression from app.utils.regex_utils import RegularExpression
@@ -187,6 +188,119 @@ def render_table_or_empty(df, model_key, table_class, raw_fields=None):
return html return html
# ---------------- EXCEL SHEET FORMATTING (Tr.Ex / Mh.Ex / MH & DC / Pipe Laying) ----------------
SHEET_TITLES = {
"Tr.Ex": "TRENCH EXCAVATION",
"Mh.Ex": "MANHOLE EXCAVATION",
"MH & DC": "MANHOLE DOMESTIC CHAMBER",
"Pipe Laying": "PIPE LAYING",
}
def format_detail_sheet(workbook, worksheet, df, sheet_name, contractor_name="", ra_bill_no="", report_date=""):
"""
Writes an info line (Category / Contractor / RA Bill No / Date) at the
top, followed by a bold/bordered table with sensible column widths
and a frozen header row.
Assumes df.to_excel(..., startrow=1) already wrote the header at row 1
and the data starting at row 2 - this function overwrites those rows
with formatting and fills in row 0 with the info line.
"""
if df.empty:
return
header_format = workbook.add_format({
"bold": True,
"bg_color": "#D9EAD3",
"border": 1,
"align": "center",
"valign": "vcenter",
"text_wrap": True
})
text_format = workbook.add_format({
"border": 1,
"valign": "vcenter"
})
number_format = workbook.add_format({
"border": 1,
"valign": "vcenter",
"num_format": "#,##0.00"
})
n_rows, n_cols = df.shape
header_row = 1 # matches startrow=1 used in df.to_excel()
# -----------------------------------------------------
# INFO LINE (row 0) - separate cells, not one merged block,
# so each field (RA Bill No, Date, etc.) can be clicked/copied
# on its own. Always starts at column 0, so position is
# consistent regardless of how many table columns exist.
# -----------------------------------------------------
last_col = max(n_cols - 1, 0)
report_title = SHEET_TITLES.get(sheet_name, sheet_name.upper())
label_format = workbook.add_format({"bold": True})
value_format = workbook.add_format({})
info_fields = [("Category:", report_title)]
# Client reports don't have a single contractor (data can span
# multiple subcontractors), so this field is only shown when a
# contractor name is actually passed in (Subcontractor report).
if contractor_name:
info_fields.append(("Contractor:", contractor_name))
info_fields.append(("RA Bill No:", ra_bill_no or "All"))
info_fields.append(("Date:", report_date))
col = 0
for label, value in info_fields:
worksheet.write(0, col, label, label_format)
worksheet.write(0, col + 1, value, value_format)
col += 3 # leave one blank column as a visual gap before next field
# Fill any remaining columns on row 0 with a blank bordered cell so
# the row reads cleanly if the sheet is wider than the info fields.
if last_col > col:
worksheet.write_blank(0, col, None)
# -----------------------------------------------------
# TABLE HEADER (row 1) - re-write with formatting
# -----------------------------------------------------
for col_idx, col_name in enumerate(df.columns):
worksheet.write(header_row, col_idx, col_name, header_format)
# -----------------------------------------------------
# TABLE BODY (rows 2+) - borders / number formatting
# -----------------------------------------------------
for row_idx in range(n_rows):
for col_idx, col_name in enumerate(df.columns):
value = df.iat[row_idx, col_idx]
if pd.isna(value):
worksheet.write(header_row + 1 + row_idx, col_idx, "", text_format)
elif isinstance(value, (int, float)):
worksheet.write_number(header_row + 1 + row_idx, col_idx, float(value), number_format)
else:
worksheet.write(header_row + 1 + row_idx, col_idx, str(value), text_format)
# Auto width columns based on header + content length
for col_idx, col_name in enumerate(df.columns):
max_len = len(str(col_name))
if n_rows:
col_values = df.iloc[:, col_idx].astype(str)
content_max = col_values.map(len).max()
max_len = max(max_len, content_max)
worksheet.set_column(col_idx, col_idx, min(max_len + 4, 30))
worksheet.freeze_panes(header_row + 1, 1)
worksheet.set_row(header_row, 30)
# ---------------- FETCH ---------------- # ---------------- FETCH ----------------
class SubcontractorBill: class SubcontractorBill:
def __init__(self): def __init__(self):
@@ -565,6 +679,16 @@ def report_file():
# DOWNLOAD EXCEL # DOWNLOAD EXCEL
# =================================================== # ===================================================
if action in ["excel", "excel_all"]: if action in ["excel", "excel_all"]:
# Look up the contractor's real name once, up front, so it can
# be shown in the title block of every sheet.
sc_obj = next(
(s for s in subcontractors if str(s.id) == str(subcontractor_id)),
None
)
contractor_display_name = sc_obj.subcontractor_name if sc_obj else ""
report_date = datetime.now().strftime("%d-%b-%Y")
output = io.BytesIO() output = io.BytesIO()
with pd.ExcelWriter(output,engine="xlsxwriter") as writer: with pd.ExcelWriter(output,engine="xlsxwriter") as writer:
@@ -572,7 +696,6 @@ def report_file():
abstract = AbstractReportService(subcontractor_id=subcontractor_id,ra_bill_no=ra_bill_no) abstract = AbstractReportService(subcontractor_id=subcontractor_id,ra_bill_no=ra_bill_no)
abstract.generate(workbook) abstract.generate(workbook)
sheet_map = [ sheet_map = [
(bill.df_tr, "Tr.Ex"), (bill.df_tr, "Tr.Ex"),
(bill.df_mh, "Mh.Ex"), (bill.df_mh, "Mh.Ex"),
@@ -581,17 +704,24 @@ def report_file():
] ]
for df, sheet_name in sheet_map: for df, sheet_name in sheet_map:
if not df.empty: if not df.empty:
df.to_excel(writer, sheet_name=sheet_name, index=False) # Use a copy for the export only - bill.df_tr etc. still
# need the real "Id" column later for the Edit/Delete
# buttons on the web preview table.
export_df = df.drop(columns=["Id"], errors="ignore").copy()
export_df.insert(0, "Sr No", range(1, len(export_df) + 1))
export_df.to_excel(writer, sheet_name=sheet_name, index=False, startrow=1)
worksheet = writer.sheets[sheet_name]
format_detail_sheet(
workbook, worksheet, export_df, sheet_name,
contractor_name=contractor_display_name,
ra_bill_no=ra_bill_no,
report_date=report_date
)
writer.close() writer.close()
output.seek(0) output.seek(0)
sc_name = re.sub(r'[^A-Za-z0-9_-]+', '_', contractor_display_name or "Subcontractor").strip('_')
sc_obj = next(
(s for s in subcontractors if str(s.id) == str(subcontractor_id)),
None
)
sc_name = sc_obj.subcontractor_name if sc_obj else "Subcontractor"
sc_name = re.sub(r'[^A-Za-z0-9_-]+', '_', sc_name).strip('_')
name_parts = [sc_name] name_parts = [sc_name]
@@ -820,22 +950,52 @@ def client_report():
# -------- DOWNLOAD -------- # -------- DOWNLOAD --------
if action == "download": if action == "download":
report_date = datetime.now().strftime("%d-%b-%Y")
output = io.BytesIO() output = io.BytesIO()
with pd.ExcelWriter(output, engine="xlsxwriter") as writer: with pd.ExcelWriter(output, engine="xlsxwriter") as writer:
workbook = writer.book workbook = writer.book
abstract_service.generate(workbook) abstract_service.generate(workbook)
bill_gen.df_tr.to_excel(writer, index=False, sheet_name="Tr.Ex") sheet_map = [
bill_gen.df_mh.to_excel(writer, index=False, sheet_name="Mh.Ex") (bill_gen.df_tr, "Tr.Ex"),
bill_gen.df_dc.to_excel(writer, index=False, sheet_name="MH & DC") (bill_gen.df_mh, "Mh.Ex"),
bill_gen.df_laying.to_excel(writer, index=False, sheet_name="Pipe Laying") (bill_gen.df_dc, "MH & DC"),
(bill_gen.df_laying, "Pipe Laying"),
]
for df, sheet_name in sheet_map:
if not df.empty:
# Use a copy for the export only, same as the
# Subcontractor report, so the formatted sheet gets
# a clean "Sr No" column instead of the raw Id.
export_df = df.drop(columns=["Id"], errors="ignore").copy()
export_df.insert(0, "Sr No", range(1, len(export_df) + 1))
export_df.to_excel(writer, sheet_name=sheet_name, index=False, startrow=1)
worksheet = writer.sheets[sheet_name]
format_detail_sheet(
workbook, worksheet, export_df, sheet_name,
contractor_name="",
ra_bill_no=RA_Bill_No,
report_date=report_date
)
output.seek(0) output.seek(0)
filename_parts = [f"Client_RA_{re.sub(r'[^A-Za-z0-9_-]+', '_', RA_Bill_No)}"]
if location:
filename_parts.append(re.sub(r'[^A-Za-z0-9_-]+', '_', location).strip('_'))
if category and category != "all":
filename_parts.append(category.upper())
filename = "_".join(filename_parts) + "_Report.xlsx"
return send_file( return send_file(
output, output,
download_name=f"Client_RA_{RA_Bill_No}_Report.xlsx", download_name=filename,
as_attachment=True as_attachment=True
) )