Add delta-based bank extraction and summary reconciliations

This commit is contained in:
A R R R Associates
2026-07-16 22:53:06 +05:30
parent d9ae1519a4
commit 46536bf96e
2 changed files with 132 additions and 4 deletions
@@ -587,6 +587,44 @@ def _safe_sheet_name(value: str) -> str:
return re.sub(r"[][\\:*?/]", "_", value)[:31]
def _write_summary_reconciliation(
worksheet,
start_row: int,
opening_balance: float,
statement_closing: float,
money_format,
text_format,
warning_format,
):
"""Add a formula-driven opening-to-closing reconciliation block."""
title_row = start_row
opening_row = start_row + 1
credit_row = start_row + 2
debit_row = start_row + 3
computed_row = start_row + 4
statement_row = start_row + 5
difference_row = start_row + 6
status_row = start_row + 7
worksheet.write(title_row, 0, "Bank Balance Reconciliation", text_format)
worksheet.write(opening_row, 0, "Opening Balance", text_format)
worksheet.write_number(opening_row, 1, float(opening_balance or 0.0), money_format)
worksheet.write(credit_row, 0, "Add: Total Credits", text_format)
worksheet.write_formula(credit_row, 1, "=SUM(tblTransactions[credit])", money_format, 0)
worksheet.write(debit_row, 0, "Less: Total Debits", text_format)
worksheet.write_formula(debit_row, 1, "=SUM(tblTransactions[debit])", money_format, 0)
worksheet.write(computed_row, 0, "Computed Closing Balance", text_format)
worksheet.write_formula(computed_row, 1, f"=B{opening_row + 1}+B{credit_row + 1}-B{debit_row + 1}", money_format, float(opening_balance or 0.0))
worksheet.write(statement_row, 0, "Statement Closing Balance", text_format)
worksheet.write_number(statement_row, 1, float(statement_closing or 0.0), money_format)
worksheet.write(difference_row, 0, "Difference", text_format)
worksheet.write_formula(difference_row, 1, f"=B{computed_row + 1}-B{statement_row + 1}", money_format, 0)
worksheet.write(status_row, 0, "Reconciliation Status", text_format)
worksheet.write_formula(status_row, 1, f'=IF(ABS(B{difference_row + 1})<=0.01,"Reconciled","RECONCILIATION ERROR")', text_format, "Reconciled")
worksheet.conditional_format(difference_row, 1, difference_row, 1, {"type": "cell", "criteria": "not between", "minimum": -0.01, "maximum": 0.01, "format": warning_format})
worksheet.conditional_format(status_row, 1, status_row, 1, {"type": "text", "criteria": "containing", "value": "ERROR", "format": warning_format})
def export_excel(output, metas, all_df, unique_df, financial_year="", selected_bank="auto", classification_enabled=True):
all_df = _ensure_analysis_columns(all_df)
unique_df = _ensure_analysis_columns(unique_df)
@@ -781,6 +819,10 @@ def export_excel(output, metas, all_df, unique_df, financial_year="", selected_b
ws.write_formula(r, 4, f'=COUNTIF(tblTransactions[final_category],A{er})', integer, 0)
ws.write_formula(r, 5, '=HYPERLINK("#\'Transaction Classification\'!A1","View / Filter")', text_fmt, "View / Filter")
if len(cat_df):
ws.autofilter(0, 0, len(cat_df), len(cat_df.columns) - 1)
_write_summary_reconciliation(ws, len(cat_df) + 3, opening, statement_closing, money, text_fmt, warning_fmt)
# Party Summary formulas.
party_df = pd.DataFrame({"Party / Counterparty": parties})
for c in ("Debit", "Credit", "Net Credit/(Debit)", "Transaction Count", "First Date", "Last Date", "View Transactions"):
@@ -797,6 +839,10 @@ def export_excel(output, metas, all_df, unique_df, financial_year="", selected_b
ws.write_formula(r, 6, f'=IFERROR(MAXIFS(tblTransactions[transaction_date],tblTransactions[final_party],A{er}),"")', date_format, "")
ws.write_formula(r, 7, '=HYPERLINK("#\'Transaction Classification\'!A1","View / Filter")', text_fmt, "View / Filter")
if len(party_df):
ws.autofilter(0, 0, len(party_df), len(party_df.columns) - 1)
_write_summary_reconciliation(ws, len(party_df) + 3, opening, statement_closing, money, text_fmt, warning_fmt)
# Category + Party formula-driven summary and trial balance detail.
pairs = tx[["auto_category", "auto_party"]].drop_duplicates().rename(columns={"auto_category": "Main Category", "auto_party": "Party / Counterparty"}) if len(tx) else pd.DataFrame(columns=["Main Category", "Party / Counterparty"])
for c in ("Debit", "Credit", "Net Credit/(Debit)", "Transaction Count", "Source Modes", "Review"):
@@ -811,6 +857,9 @@ def export_excel(output, metas, all_df, unique_df, financial_year="", selected_b
ws.write_formula(r, 5, f'=COUNTIFS(tblTransactions[final_category],A{er},tblTransactions[final_party],B{er})', integer, 0)
ws.write_formula(r, 6, f'=IF(E{er}>0,"See Transaction Classification","")', text_fmt, "See Transaction Classification")
ws.write_formula(r, 7, f'=IF(OR(A{er}="Unclassified / Review",A{er}="Other Bank Receipt / Review",A{er}="Other Bank Payment / Review"),"Review","")', text_fmt, "")
if len(pairs):
ws.autofilter(0, 0, len(pairs), len(pairs.columns) - 1)
_write_summary_reconciliation(ws, len(pairs) + 3, opening, statement_closing, money, text_fmt, warning_fmt)
# Formula-driven bank movement Trial Balance.
trial = pd.DataFrame({
@@ -880,7 +929,9 @@ def export_excel(output, metas, all_df, unique_df, financial_year="", selected_b
worksheet.freeze_panes(1, 0)
worksheet.set_row(0, 22, header)
max_col = max(0, worksheet.dim_colmax)
if worksheet.dim_rowmax >= 0 and name not in {"Transaction Classification"}:
if worksheet.dim_rowmax >= 0 and name not in {
"Transaction Classification", "Category Summary", "Party Summary", "Category Party Summary"
}:
worksheet.autofilter(0, 0, worksheet.dim_rowmax, max_col)
worksheet.set_column(0, max_col, 18)
for name in ("Category Summary", "Party Summary", "Category Party Summary", "Trial Balance"):