diff --git a/app/modules/accounting/opening_balance_ui.py b/app/modules/accounting/opening_balance_ui.py index 158c810..25a67ce 100644 --- a/app/modules/accounting/opening_balance_ui.py +++ b/app/modules/accounting/opening_balance_ui.py @@ -70,13 +70,48 @@ def _company_rows(tally): return rows +def _summary_counts(raw): + raw = raw or {} + ledgers = raw.get("ledgers") or {} + stock = raw.get("stock_items") or {} + return { + "ledgers": { + "matched": int(ledgers.get("matched") or 0), + "difference": int(ledgers.get("difference") or 0), + "missing_in_current_year": int(ledgers.get("missing_in_current_year") or 0), + "new_in_current_year": int(ledgers.get("new_in_current_year") or 0), + }, + "stock_items": { + "matched": int(stock.get("matched") or 0), + "quantity_difference": int(stock.get("quantity_difference") or 0), + "value_difference": int(stock.get("value_difference") or 0), + "quantity_and_value_difference": int(stock.get("quantity_and_value_difference") or 0), + "unit_difference": int(stock.get("unit_difference") or 0), + "missing_in_current_year": int(stock.get("missing_in_current_year") or 0), + "new_in_current_year": int(stock.get("new_in_current_year") or 0), + }, + } + + +def _page_rows(rows, page, per_page=50): + per_page = max(20, min(200, int(per_page or 50))) + total = len(rows) + pages = max(1, (total + per_page - 1) // per_page) + page = max(1, min(int(page or 1), pages)) + start = (page - 1) * per_page + return rows[start:start + per_page], total, page, pages + + @router.get("") def page( request: Request, client_id: int | None = None, run_id: int | None = None, - tab: str = "ledgers", + tab: str = "summary", status: str = "", + page: int = 1, + per_page: int = 50, + show_zero_new: int = 0, message: str = "", error: str = "", ): @@ -118,7 +153,11 @@ def page( selected_run = None ledgers = [] stocks = [] - summary = {} + ledger_action_rows = [] + stock_action_rows = [] + summary = _summary_counts({}) + detail_total = 0 + detail_pages = 1 if selected: runs = list_runs( @@ -134,13 +173,73 @@ def page( if selected_run: try: - summary = json.loads(selected_run.summary_json or "{}") + summary = _summary_counts(json.loads(selected_run.summary_json or "{}")) except Exception: - summary = {} - if tab == "stock": - stocks = stock_items(db, run_id=selected_run.id, status=status) + summary = _summary_counts({}) + + if tab == "summary": + ledger_action_rows = ledger_items( + db, + run_id=selected_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=selected_run.id, + status=action_status, + ) + ) + ledger_action_rows = ledger_action_rows[:100] + stock_action_rows = stock_action_rows[:100] + + elif tab == "stock": + detail_status = status or "quantity_and_value_difference" + rows = stock_items( + db, + run_id=selected_run.id, + status=detail_status, + ) + if detail_status == "new_in_current_year" and not show_zero_new: + rows = [ + row for row in rows + if abs(float(row.current_opening_qty or 0)) > 0.000001 + or abs(float(row.current_opening_value or 0)) > 0.005 + ] + stocks, detail_total, page, detail_pages = _page_rows( + rows, + page, + per_page, + ) + status = detail_status + else: - ledgers = ledger_items(db, run_id=selected_run.id, status=status) + detail_status = status or "difference" + rows = ledger_items( + db, + run_id=selected_run.id, + status=detail_status, + ) + if detail_status == "new_in_current_year" and not show_zero_new: + rows = [ + row for row in rows + if abs(float(row.current_opening_balance or 0)) > 0.005 + ] + ledgers, detail_total, page, detail_pages = _page_rows( + rows, + page, + per_page, + ) + status = detail_status + + valid_tab = tab if tab in {"summary", "ledgers", "stock"} else "summary" return templates.TemplateResponse( "modules/accounting/templates/accounting/opening_balances.html", @@ -160,9 +259,16 @@ def page( "selected_run": selected_run, "ledgers": ledgers, "stocks": stocks, + "ledger_action_rows": ledger_action_rows, + "stock_action_rows": stock_action_rows, "summary": summary, - "tab": tab if tab in {"ledgers", "stock"} else "ledgers", + "tab": valid_tab, "status_filter": status, + "page": page, + "pages": detail_pages, + "per_page": per_page, + "detail_total": detail_total, + "show_zero_new": bool(show_zero_new), "message": message, "error": command_error, }, @@ -226,7 +332,7 @@ def compare( return _go( client.id, run_id=run.id, - tab="ledgers", + tab="summary", message=( f"Opening balance comparison #{run.id} completed: " f"{run.ledger_count} ledger row(s), {run.stock_item_count} stock row(s)." @@ -368,7 +474,7 @@ async def apply_corrections( return _go( client.id, run_id=run.id, - tab="ledgers", + tab="summary", message=f"Tally correction completed: {verified} verified, {failed} failed/blocked.", ) except Exception as exc: diff --git a/app/modules/accounting/templates/accounting/opening_balances.html b/app/modules/accounting/templates/accounting/opening_balances.html index c9a9906..c55fc64 100644 --- a/app/modules/accounting/templates/accounting/opening_balances.html +++ b/app/modules/accounting/templates/accounting/opening_balances.html @@ -3,10 +3,10 @@
Accounting · Phase 21A
+Accounting · Phase 21A v2
- Compare previous-year Tally closing balances with current-year opening balances for Ledgers and Stock Items. Safe selected differences can be applied directly to current-year Tally masters and are re-read for verification. + Summary-first review of previous-year closing balances against current-year openings. Matched and new zero-balance masters stay out of the main review; actionable differences are shown immediately and retain the existing controlled Tally correction workflow.
Phase 21A v1 intentionally requires separate previous-year and current-year Tally companies. This avoids treating a current company's latest closing balance as the prior-year audited closing.
{% endif %} @@ -68,7 +67,7 @@Run #{{ selected_run.id }}
+Matched records are summarized only. Click a category when you need its detailed report.
+