← Files ClaraARCHIVED FILE

modules/business-planning/scripts/planning_decision_report.py

4.16 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

"""Render current planning questions and funding assessments in the shared report."""

from __future__ import annotations

import html
from typing import Any, Callable

from planning_financing import FINANCING_SECTIONS
from planning_presentation import label, language

__all__ = ["render_cycle", "render_financing"]


def render_cycle(plan: dict[str, Any], paragraph: Callable[[str], str]) -> str:
    """Show the current question, outcome and next test; keep exact deltas inspectable."""
    case = plan["case"]
    cycle = case.get("cycle")
    if cycle is None or plan["status"] == "blocked":
        return ""
    tr = lambda text: label(text, language(case))
    e = html.escape
    parts = [
        f'<section id="planning-cycle"><h2>{tr("Current planning question")}</h2>',
        f'<p class="lead">{e(cycle["question"])}</p>',
    ]
    for key, heading in (
        ("analysis_ids", "What we learned"),
        ("decision_ids", "Decision in this round"),
        ("next_test_ids", "Next test or action"),
        ("reopen_when_ids", "When to revisit"),
    ):
        parts.append(f"<h3>{tr(heading)}</h3>")
        parts.extend(paragraph(i) for i in cycle[key])
    revision = plan["planning_cycle"]
    parts.append(
        f'<details class="planning-history"><summary>{tr("Planning history and input changes")}</summary>'
    )
    for item in revision["history"]:
        parts.append(f'<p>{e(item["id"])}: {e(item["question"])}</p>')
    for change in revision["changes"]:
        parts.append(f'<h4>{e(change["field"])}</h4>')
        # Exact changes are workpapers, not independent financial claims.
        import json

        parts.append(
            f"<pre>{e(json.dumps(change, ensure_ascii=False, indent=2))}</pre>"
        )
    parts.append("</details></section>")
    return "".join(parts)


def render_financing(plan: dict[str, Any], paragraph: Callable[[str], str]) -> str:
    """Present model reasoning separately from mechanical request completeness."""
    case = plan["case"]
    if plan["status"] == "blocked" or not case.get("financing", {}).get("assessments"):
        return ""
    tr = lambda text: label(text, language(case))
    e = html.escape
    results = {r["id"]: r for r in plan["financing_assessments"]}
    parts = [
        f'<section id="financing-assessment"><h2>{tr("Financing assessment")}</h2>'
    ]
    for assessment in case["financing"]["assessments"]:
        result = results[assessment["id"]]
        instrument = assessment["instrument"]
        heading = "Bank debt" if instrument == "bank_debt" else "Venture equity"
        parts.append(
            f'<h3>{tr(heading)} · {e(assessment["provider"] or tr("Provider not yet selected"))}</h3>'
        )
        parts.extend(paragraph(i) for i in assessment["request_ids"])
        conclusion = assessment["conclusion"]
        if not result["conclusion_available"] or (
            conclusion == "prepare_request"
            and (
                not result["request_ready"]
                or plan["status"] != "ready_for_professional_review"
            )
        ):
            parts.append(
                f'<p class="status">{tr("Financing conclusion pending evidence and review")}</p>'
            )
        else:
            headings = {
                "explore": "Explore financing",
                "prepare_request": "Prepare the request",
                "revise_request": "Revise the request",
                "not_suitable": "Requested financing is not suitable",
            }
            parts.append(f"<h4>{tr(headings[conclusion])}</h4>")
        parts.extend(paragraph(i) for i in assessment["rationale_ids"])
        for key, title in FINANCING_SECTIONS[instrument].items():
            parts.append(f"<h4>{tr(title)}</h4>")
            parts.extend(paragraph(i) for i in assessment["sections"][key])
        if not result["coverage_complete"]:
            parts.append(
                f'<p class="limitation">{tr("The forecast does not yet cover the stated repayment or funding milestone.")}</p>'
            )
        parts.append(
            f'<p class="limitation">{tr("This assessment does not establish lender or investor approval.")}</p>'
        )
    parts.append("</section>")
    return "".join(parts)

SHA-256: 26ed46e24534a99bc3772c198785021b7365b52cbb5b99a48f11ac56b6ecacd7