diff --git a/src/app/dashboard/financial/delivery-notes/flags/[collection]/[recordId]/flag-decision.test.tsx b/src/app/dashboard/financial/delivery-notes/flags/[collection]/[recordId]/flag-decision.test.tsx
new file mode 100644
index 0000000..ded166c
--- /dev/null
+++ b/src/app/dashboard/financial/delivery-notes/flags/[collection]/[recordId]/flag-decision.test.tsx
@@ -0,0 +1,41 @@
+import {expect,it,vi} from 'vitest';
+import {renderToStaticMarkup} from 'react-dom/server';
+import * as React from 'react';
+
+vi.mock('next/navigation',()=>({useRouter:()=>({push:vi.fn(),refresh:vi.fn()})}));
+
+import {FlagDecision} from './flag-decision';
+
+const render=(props:Partial>={})=>renderToStaticMarkup();
+const text=(html:string)=>html.replace(/<[^>]*>/g,' ').replace(/\s+/g,' ').trim();
+/** The opening tag of the button reading `label`, so its own `disabled` can be read. */
+const buttonTag=(html:string,label:string):string=>html.split('')))}))
+ .find(button=>button.label.includes(label))?.open??'';
+
+it('offers both decisions when the record can be deleted',()=>{
+ const html=render();
+ expect(text(html)).toContain('Keep');
+ expect(text(html)).toContain('Delete');
+ expect(html).toContain('
;
+
+ async function decide(action:'delete'|'keep'){
+ if(busy)return;
+ if(action==='delete'&&!window.confirm('Delete this record? A copy is saved so it can be restored.'))return;
+ setBusy(true);setError('');
+ try{
+ const response=await fetch(`/api/deletion-flags/${collection}/${recordId}`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action,reason})});
+ if(!response.ok){
+ const body=await response.json().catch(()=>({}));
+ setError(String(body.detail||body.error||`Could not save (${response.status}).`));
+ return;
+ }
+ router.push(INBOX);
+ }catch{
+ setError('Could not save. Check the connection and try again.');
+ }finally{
+ setBusy(false);
+ }
+ }
+
+ return
+
+
+
+
+
+ {deleteBlock&&
{deleteBlock}
}
+ {error&&
{error}
}
+
;
+}
\ No newline at end of file
diff --git a/src/app/dashboard/financial/delivery-notes/flags/[collection]/[recordId]/page.tsx b/src/app/dashboard/financial/delivery-notes/flags/[collection]/[recordId]/page.tsx
new file mode 100644
index 0000000..98266d8
--- /dev/null
+++ b/src/app/dashboard/financial/delivery-notes/flags/[collection]/[recordId]/page.tsx
@@ -0,0 +1,73 @@
+import Link from 'next/link';
+import {Badge} from '@/components/ui/badge';
+import {Card,CardContent,CardHeader} from '@/components/ui/card';
+import {Topbar} from '@/components/layout/sidebar';
+import {pbAdmin} from '@/lib/pb';
+import {requirePageUser} from '@/lib/auth/guard';
+import {canDecide,deletionBlock,describeFlag} from '@/lib/purchasing/deletion-decisions';
+import {FlagDecision as FlagDecisionForm} from './flag-decision';
+
+export const dynamic='force-dynamic';
+
+const INBOX='/dashboard/financial/delivery-notes/inbox';
+
+/**
+ * One deletion flag, in words, with the decision underneath it.
+ *
+ * Everything a person needs is on this page — what the record is, why it was
+ * flagged, and what else it touches — so nobody has to go and read the raw
+ * record to decide whether it should go. The agent that raised it cannot delete
+ * it; this page and its two buttons are the whole of what a person can do.
+ */
+export default async function FlagPage({params}:{params:Promise<{collection:string;recordId:string}>}){
+ const user=await requirePageUser('financial');
+ const {collection,recordId}=await params;
+ const pb=await pbAdmin();
+ const detail=await describeFlag(pb,collection,recordId);
+
+ if(!detail){
+ return <>
+
+