From 51e75187edc2ee0092d0488a4be676817409eabd Mon Sep 17 00:00:00 2001 From: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:10:35 +0200 Subject: [PATCH] feat(pitch-deck): add force recompute to bypass stale pitch_fm_results cache Adds `force: true` body param to POST /api/financial-model/compute that skips the cached results check and recomputes from assumptions directly. Exposes this via a "Force Recompute" button on the scenario edit admin page, so updating assumptions directly in the DB can be followed by a cache bust without touching the UI assumption flow. Co-Authored-By: Claude Sonnet 4.6 --- .../app/api/financial-model/compute/route.ts | 6 +-- .../financial-model/[scenarioId]/page.tsx | 45 ++++++++++++++----- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/pitch-deck/app/api/financial-model/compute/route.ts b/pitch-deck/app/api/financial-model/compute/route.ts index 4008acf..32055b4 100644 --- a/pitch-deck/app/api/financial-model/compute/route.ts +++ b/pitch-deck/app/api/financial-model/compute/route.ts @@ -6,7 +6,7 @@ import { finanzplanToFMResults } from '@/lib/finanzplan/adapter' export async function POST(request: NextRequest) { try { const body = await request.json() - const { scenarioId, source } = body + const { scenarioId, source, force } = body // If source=finanzplan, use the Finanzplan engine instead if (source === 'finanzplan') { @@ -28,8 +28,8 @@ export async function POST(request: NextRequest) { const client = await pool.connect() try { - // Fast path: return cached results if they exist (avoid expensive recompute + 60 inserts) - const cached = await client.query( + // Fast path: return cached results if they exist (skip when force=true) + const cached = force ? { rows: [] } : await client.query( 'SELECT * FROM pitch_fm_results WHERE scenario_id = $1 ORDER BY month', [scenarioId] ) diff --git a/pitch-deck/app/pitch-admin/(authed)/financial-model/[scenarioId]/page.tsx b/pitch-deck/app/pitch-admin/(authed)/financial-model/[scenarioId]/page.tsx index b471286..264a493 100644 --- a/pitch-deck/app/pitch-admin/(authed)/financial-model/[scenarioId]/page.tsx +++ b/pitch-deck/app/pitch-admin/(authed)/financial-model/[scenarioId]/page.tsx @@ -3,7 +3,7 @@ import { useEffect, useState } from 'react' import { useParams } from 'next/navigation' import Link from 'next/link' -import { ArrowLeft, Save } from 'lucide-react' +import { ArrowLeft, RefreshCw, Save } from 'lucide-react' interface Assumption { id: string @@ -36,6 +36,7 @@ export default function EditScenarioPage() { const [loading, setLoading] = useState(true) const [edits, setEdits] = useState>({}) const [savingId, setSavingId] = useState(null) + const [recomputing, setRecomputing] = useState(false) const [toast, setToast] = useState(null) function flashToast(msg: string) { @@ -56,6 +57,17 @@ export default function EditScenarioPage() { useEffect(() => { if (scenarioId) load() }, [scenarioId]) + async function forceRecompute() { + setRecomputing(true) + const res = await fetch('/api/financial-model/compute', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ scenarioId, force: true }), + }) + setRecomputing(false) + flashToast(res.ok ? 'Recomputed successfully' : 'Recompute failed') + } + function setEdit(id: string, val: string) { setEdits(prev => ({ ...prev, [id]: val })) } @@ -108,17 +120,28 @@ export default function EditScenarioPage() { Back to scenarios -
-
-
-

{scenario.name}

- {scenario.is_default && ( - - Default - - )} +
+
+
+
+

{scenario.name}

+ {scenario.is_default && ( + + Default + + )} +
+ {scenario.description &&

{scenario.description}

}
- {scenario.description &&

{scenario.description}

} +