Add opening balance action summary to Excel export

This commit is contained in:
A R R R Associates
2026-09-04 15:53:06 +05:30
parent 3ad20087be
commit 87bfc13cbd
+105 -18
View File
@@ -284,7 +284,7 @@ def page(
@router.get("/download/{run_id}") @router.get("/download/{run_id}")
def download_report(request: Request, run_id: int, client_id: int | None = None): def download_report(request: Request, run_id: int, client_id: int | None = None):
"""Download the complete opening-balance comparison as an Excel workbook.""" """Download the complete opening-balance review, including the web summary action reports."""
db = CommonSessionLocal() db = CommonSessionLocal()
try: try:
user, denied = _require_partner(request, db, "accounting.tally.view") user, denied = _require_partner(request, db, "accounting.tally.view")
@@ -298,11 +298,103 @@ def download_report(request: Request, run_id: int, client_id: int | None = None)
if not selected or (client_id and int(client_id) != int(run.client_id)): if not selected or (client_id and int(client_id) != int(run.client_id)):
return _go(error="Opening balance comparison run is not visible.") return _go(error="Opening balance comparison run is not visible.")
# Complete detailed reports.
ledger_rows = ledger_items(db, run_id=run.id) ledger_rows = ledger_items(db, run_id=run.id)
stock_rows = stock_items(db, run_id=run.id) stock_rows = stock_items(db, run_id=run.id)
# These are the same action-required populations shown on the web Summary tab.
ledger_action_rows = ledger_items(db, run_id=run.id, status="difference")
stock_action_rows = []
for action_status in (
"quantity_difference",
"value_difference",
"quantity_and_value_difference",
"unit_difference",
):
stock_action_rows.extend(stock_items(db, run_id=run.id, status=action_status))
try:
summary_counts = _summary_counts(json.loads(run.summary_json or "{}"))
except Exception:
summary_counts = _summary_counts({})
wb = Workbook() wb = Workbook()
ws = wb.active meta = wb.active
ws.title = "Balance Sheet Ledgers" meta.title = "Summary"
meta.append(["Opening Balance Review"])
meta.append(["Client", getattr(selected, "client_name", "") or getattr(selected, "name", "") or str(selected.id)])
meta.append(["Run ID", run.id])
meta.append(["Previous Tally Company", run.previous_company_name])
meta.append(["Current Tally Company", run.current_company_name])
meta.append(["Scope", "Balance Sheet ledgers and Stock Items only"])
meta.append(["Balance Sheet Ledger Rows", len(ledger_rows)])
meta.append(["Stock Item Rows", len(stock_rows)])
meta.append(["Ledger Differences — Action Required", len(ledger_action_rows)])
meta.append(["Stock Differences — Action Required", len(stock_action_rows)])
meta.append([])
meta.append(["Ledger Status", "Count"])
for label, key in (
("Matched", "matched"),
("Difference", "difference"),
("Missing in Current Year", "missing_in_current_year"),
("New in Current Year", "new_in_current_year"),
):
meta.append([label, summary_counts["ledgers"].get(key, 0)])
meta.append([])
meta.append(["Stock Item Status", "Count"])
for label, key in (
("Matched", "matched"),
("Quantity Difference", "quantity_difference"),
("Value Difference", "value_difference"),
("Quantity & Value Difference", "quantity_and_value_difference"),
("Unit Difference", "unit_difference"),
("Missing in Current Year", "missing_in_current_year"),
("New in Current Year", "new_in_current_year"),
):
meta.append([label, summary_counts["stock_items"].get(key, 0)])
meta.append([])
meta.append(["Generated", datetime.now().strftime("%Y-%m-%d %H:%M:%S")])
# Mirror the web Summary -> Ledger Differences — Action Required table.
law = wb.create_sheet("Ledger Action Summary")
ledger_action_headers = [
"Apply", "Previous Ledger", "Previous Group", "PY Closing",
"Current Ledger", "Current Group", "CY Opening", "Difference",
"Match", "Match Method", "Confidence %", "Correction Status", "Correction Note",
]
law.append(ledger_action_headers)
for row in ledger_action_rows:
law.append([
"", row.previous_name or "", row.previous_group or "",
float(row.previous_closing_balance or 0), row.current_name or "",
row.current_group or "", float(row.current_opening_balance or 0),
float(row.difference or 0), row.match_status or "", row.match_method or "",
int(row.confidence or 0), row.correction_status or "", row.correction_note or "",
])
# Mirror the web Summary -> Stock Differences — Action Required table.
saw = wb.create_sheet("Stock Action Summary")
stock_action_headers = [
"Apply", "Previous Stock Item", "Previous Group", "HSN", "Unit",
"PY Closing Qty", "PY Closing Value", "Current Stock Item", "Current Group",
"Current HSN", "Current Unit", "CY Opening Qty", "CY Opening Value",
"Qty Difference", "Value Difference", "Status", "Match Method",
"Confidence %", "Correction Status", "Correction Note",
]
saw.append(stock_action_headers)
for row in stock_action_rows:
saw.append([
"", row.previous_name or "", row.previous_group or "", row.previous_hsn or "",
row.previous_unit or "", float(row.previous_closing_qty or 0),
float(row.previous_closing_value or 0), row.current_name or "",
row.current_group or "", row.current_hsn or "", row.current_unit or "",
float(row.current_opening_qty or 0), float(row.current_opening_value or 0),
float(row.quantity_difference or 0), float(row.value_difference or 0),
row.match_status or "", row.match_method or "", int(row.confidence or 0),
row.correction_status or "", row.correction_note or "",
])
ws = wb.create_sheet("Balance Sheet Ledgers")
headers = [ headers = [
"Previous Ledger", "Previous Group", "PY Closing", "Current Ledger", "Previous Ledger", "Previous Group", "PY Closing", "Current Ledger",
"Current Group", "CY Opening", "Difference", "Status", "Current Group", "CY Opening", "Difference", "Status",
@@ -337,32 +429,27 @@ def download_report(request: Request, run_id: int, client_id: int | None = None)
row.correction_status or "", row.correction_note or "", row.correction_status or "", row.correction_note or "",
]) ])
meta = wb.create_sheet("Summary", 0)
meta.append(["Opening Balance Review"])
meta.append(["Client", getattr(selected, "client_name", "") or getattr(selected, "name", "") or str(selected.id)])
meta.append(["Run ID", run.id])
meta.append(["Previous Tally Company", run.previous_company_name])
meta.append(["Current Tally Company", run.current_company_name])
meta.append(["Scope", "Balance Sheet ledgers and Stock Items only"])
meta.append(["Balance Sheet Ledger Rows", len(ledger_rows)])
meta.append(["Stock Item Rows", len(stock_rows)])
meta.append(["Generated", datetime.now().strftime("%Y-%m-%d %H:%M:%S")])
header_fill = PatternFill("solid", fgColor="DCE6F1") header_fill = PatternFill("solid", fgColor="DCE6F1")
for sheet in (ws, sws): action_fill = PatternFill("solid", fgColor="FCE4D6")
for sheet in (law, saw, ws, sws):
sheet.freeze_panes = "A2" sheet.freeze_panes = "A2"
sheet.auto_filter.ref = sheet.dimensions sheet.auto_filter.ref = sheet.dimensions
for cell in sheet[1]: for cell in sheet[1]:
cell.font = Font(bold=True) cell.font = Font(bold=True)
cell.fill = header_fill cell.fill = action_fill if sheet in (law, saw) else header_fill
cell.alignment = Alignment(vertical="center", wrap_text=True) cell.alignment = Alignment(vertical="center", wrap_text=True)
for col in sheet.columns: for col in sheet.columns:
letter = col[0].column_letter letter = col[0].column_letter
width = min(38, max(10, max(len(str(c.value or "")) for c in col[:200]) + 2)) width = min(42, max(10, max(len(str(c.value or "")) for c in col[:200]) + 2))
sheet.column_dimensions[letter].width = width sheet.column_dimensions[letter].width = width
meta["A1"].font = Font(bold=True, size=14) meta["A1"].font = Font(bold=True, size=14)
meta.column_dimensions["A"].width = 28 meta.column_dimensions["A"].width = 38
meta.column_dimensions["B"].width = 48 meta.column_dimensions["B"].width = 48
for row_no in (12, 18):
for cell in meta[row_no]:
cell.font = Font(bold=True)
cell.fill = header_fill
output = BytesIO() output = BytesIO()
wb.save(output) wb.save(output)