Add opening balance action summary to Excel export
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user