diff --git a/app/(marketing)/blog/page.tsx b/app/(marketing)/blog/page.tsx deleted file mode 100644 index e50f785..0000000 --- a/app/(marketing)/blog/page.tsx +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Blog Overview Page - * - * Landing page with series cards and recent posts - */ - -import Link from 'next/link' -import { getAllPosts, getSeriesWithCounts, type BlogSeries } from '@/lib/mdx/blog' - -export const metadata = { - title: 'Blog - AI Speedrun', - description: 'Artikelen over AI-gestuurde software ontwikkeling, healthcare IT en het bouwen van een EPD.', - openGraph: { - title: 'Blog - AI Speedrun', - description: 'Artikelen over AI-gestuurde software ontwikkeling, healthcare IT en het bouwen van een EPD.', - type: 'website', - siteName: 'AI Speedrun', - locale: 'nl_NL', - }, - twitter: { - card: 'summary', - title: 'Blog - AI Speedrun', - description: 'Artikelen over AI-gestuurde software ontwikkeling, healthcare IT en het bouwen van een EPD.', - }, - alternates: { - canonical: `${process.env.NEXT_PUBLIC_APP_URL || 'https://aispeedrun.vercel.app'}/blog`, - }, -} - -export default async function BlogPage() { - const series = await getSeriesWithCounts() - const posts = await getAllPosts() - - return ( -
-
- {/* Header */} -
-

- Blog -

-

- Artikelen over AI-gestuurde ontwikkeling en de reis van het bouwen van een EPD. -

-
- - {/* Series Section */} - {series.length > 0 && ( -
-

- Series -

-
- {series.map((serie) => ( - - ))} -
-
- )} - - {/* Recent Posts Section */} -
-

- Recente Posts -

- {posts.length === 0 ? ( -

- Nog geen blogposts gepubliceerd. -

- ) : ( -
- {posts.map((post) => ( - -
- - {post.seriesId.replace('-', ' ')} - - - - - {post.readingTime} min leestijd -
-

- {post.frontmatter.title} -

-

- {post.frontmatter.description} -

- {post.frontmatter.tags && post.frontmatter.tags.length > 0 && ( -
- {post.frontmatter.tags.map((tag) => ( - - {tag} - - ))} -
- )} - - ))} -
- )} -
-
-
- ) -} - -function SeriesCard({ series }: { series: BlogSeries & { postCount: number } }) { - const colorStyles = { - teal: { - bg: 'bg-teal-50', - border: 'border-teal-200 hover:border-teal-400', - badge: 'bg-teal-100 text-teal-700', - dot: 'bg-teal-500', - }, - amber: { - bg: 'bg-amber-50', - border: 'border-amber-200 hover:border-amber-400', - badge: 'bg-amber-100 text-amber-700', - dot: 'bg-amber-500', - }, - slate: { - bg: 'bg-slate-50', - border: 'border-slate-200 hover:border-slate-400', - badge: 'bg-slate-100 text-slate-700', - dot: 'bg-slate-500', - }, - } - - const styles = colorStyles[series.color] || colorStyles.slate - const statusLabels = { - completed: 'Voltooid', - active: 'Actief', - planned: 'Gepland', - } - - return ( - -
-
- - {statusLabels[series.status]} - -
-

{series.title}

-

{series.description}

-
- {series.postCount} {series.postCount === 1 ? 'deel' : 'delen'} -
- - ) -} - diff --git a/app/(marketing)/blog/serie/[serieId]/[slug]/page.tsx b/app/(marketing)/blog/serie/[serieId]/[slug]/page.tsx deleted file mode 100644 index 0c9932f..0000000 --- a/app/(marketing)/blog/serie/[serieId]/[slug]/page.tsx +++ /dev/null @@ -1,324 +0,0 @@ -/** - * Individual Blog Post Page - * - * Renders MDX content with series navigation - */ - -import { notFound } from 'next/navigation' -import Link from 'next/link' -import { MDXRemote } from 'next-mdx-remote/rsc' -import { getPost, getSeries, getPostsBySeries, getSeriesNavigation, getAllPosts } from '@/lib/mdx/blog' -import { mdxComponents } from '../../../../documentatie/components/mdx-components' - -interface PostPageProps { - params: Promise<{ serieId: string; slug: string }> -} - -export async function generateStaticParams() { - const posts = await getAllPosts() - return posts.map((post) => ({ - serieId: post.seriesId, - slug: post.slug, - })) -} - -export async function generateMetadata({ params }: PostPageProps) { - const { serieId, slug } = await params - const post = await getPost(serieId, slug) - const series = await getSeries(serieId) - - if (!post || !series) { - return { title: 'Post niet gevonden' } - } - - const siteUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://aispeedrun.vercel.app' - const postUrl = `${siteUrl}/blog/serie/${serieId}/${slug}` - // Use post-specific image if provided, otherwise fall back to default - const ogImageUrl = post.frontmatter.image - ? `${siteUrl}${post.frontmatter.image.startsWith('/') ? '' : '/'}${post.frontmatter.image}` - : `${siteUrl}/og-blog-default.png` - - return { - title: `${post.frontmatter.title} - ${series.title}`, - description: post.frontmatter.description, - authors: [{ name: 'Colin van der Heijden', url: 'https://ikbenlit.nl' }], - keywords: post.frontmatter.tags || [], - openGraph: { - title: post.frontmatter.title, - description: post.frontmatter.description, - type: 'article', - publishedTime: post.frontmatter.date, - authors: ['Colin van der Heijden'], - siteName: 'AI Speedrun', - locale: 'nl_NL', - images: [ - { - url: ogImageUrl, - width: 1200, - height: 630, - alt: post.frontmatter.title, - }, - ], - }, - twitter: { - card: 'summary_large_image', - title: post.frontmatter.title, - description: post.frontmatter.description, - images: [ogImageUrl], - }, - alternates: { - canonical: postUrl, - }, - } -} - -export default async function PostPage({ params }: PostPageProps) { - const { serieId, slug } = await params - const post = await getPost(serieId, slug) - const series = await getSeries(serieId) - - if (!post || !series) { - notFound() - } - - const navigation = await getSeriesNavigation(serieId, slug) - const { frontmatter, content, readingTime } = post - - // Generate Article structured data for SEO - const siteUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://aispeedrun.vercel.app' - const postUrl = `${siteUrl}/blog/serie/${serieId}/${slug}` - // Use post-specific image if provided, otherwise fall back to default - const articleImage = frontmatter.image - ? `${siteUrl}${frontmatter.image.startsWith('/') ? '' : '/'}${frontmatter.image}` - : `${siteUrl}/og-blog-default.png` - const articleSchema = { - '@context': 'https://schema.org', - '@type': 'Article', - headline: frontmatter.title, - description: frontmatter.description, - image: articleImage, - datePublished: frontmatter.date, - dateModified: frontmatter.date, - author: { - '@type': 'Person', - name: 'Colin van der Heijden', - url: 'https://ikbenlit.nl', - }, - publisher: { - '@type': 'Organization', - name: 'AI Speedrun', - url: siteUrl, - logo: { - '@type': 'ImageObject', - url: `${siteUrl}/images/aispeedrun-logo.webp`, - }, - }, - mainEntityOfPage: { - '@type': 'WebPage', - '@id': postUrl, - }, - articleSection: series.title, - keywords: frontmatter.tags?.join(', ') || '', - wordCount: content.split(/\s+/).length, - timeRequired: `PT${readingTime}M`, - } - - // Generate BreadcrumbList structured data - const breadcrumbSchema = { - '@context': 'https://schema.org', - '@type': 'BreadcrumbList', - itemListElement: [ - { - '@type': 'ListItem', - position: 1, - name: 'Home', - item: siteUrl, - }, - { - '@type': 'ListItem', - position: 2, - name: 'Blog', - item: `${siteUrl}/blog`, - }, - { - '@type': 'ListItem', - position: 3, - name: series.title, - item: `${siteUrl}/blog/serie/${serieId}`, - }, - { - '@type': 'ListItem', - position: 4, - name: frontmatter.title, - item: postUrl, - }, - ], - } - - const colorStyles = { - teal: { - badge: 'bg-teal-100 text-teal-700 border-teal-200', - link: 'text-teal-600 hover:text-teal-700', - }, - amber: { - badge: 'bg-amber-100 text-amber-700 border-amber-200', - link: 'text-amber-600 hover:text-amber-700', - }, - slate: { - badge: 'bg-slate-100 text-slate-700 border-slate-200', - link: 'text-slate-600 hover:text-slate-700', - }, - } - - const styles = colorStyles[series.color] || colorStyles.slate - - return ( -
- {/* Structured Data for SEO */} - + + + + + diff --git a/docs/datamodel/diagrammen/model-behandelplan-erd.html b/docs/datamodel/diagrammen/model-behandelplan-erd.html new file mode 100644 index 0000000..6d8a58d --- /dev/null +++ b/docs/datamodel/diagrammen/model-behandelplan-erd.html @@ -0,0 +1,336 @@ + + + + + +Behandelplan — entiteit-relatiediagram + + + + +
+
+
+

Behandelplan — entiteit-relatiediagram

+ model-behandelplan.md §2 · 12 entiteiten · 20 relaties · in structurele herziening — zie besluitenlog §5 B11 (één levend behandelplan met formele snapshots i.p.v. losse versie-records) +
+
+ + + + +
+
+ +
+
Diagram wordt geladen…
+
+erDiagram + ZORGEPISODE { + uuid id PK + } + DIAGNOSE { + uuid id PK + } + INTAKE { + uuid id PK + } + MEDEWERKER { + uuid id PK + } + PERSOON { + uuid id PK + } + BEHANDELPLAN { + uuid id PK + uuid zorgepisode_id FK + uuid vervangt_plan_id FK + uuid gebaseerd_op_intake_id FK + uuid opgesteld_door FK + uuid regiebehandelaar_id FK + uuid vastgesteld_door FK + integer versienummer + string status + date vastgesteld_op + } + BEHANDELPLAN_DIAGNOSE { + uuid behandelplan_id FK + uuid diagnose_id FK + } + BEHANDELDOEL { + uuid id PK + uuid behandelplan_id FK + uuid diagnose_id FK + uuid overgenomen_van_doel_id FK + string omschrijving + string prioriteit + string status + } + INTERVENTIE { + uuid id PK + uuid behandelplan_id FK + uuid uitvoerder_id FK + string interventietype + string extern_systeem + string extern_kenmerk + } + INTERVENTIE_DOEL { + uuid interventie_id FK + uuid behandeldoel_id FK + } + EVALUATIEMOMENT { + uuid id PK + uuid behandelplan_id FK + uuid uitgevoerd_door FK + string evaluatietype + date gepland_op + string uitkomst + } + BEHANDELPLAN_AKKOORD { + uuid id PK + uuid behandelplan_id FK + uuid gegeven_door_persoon_id FK + uuid vastgelegd_door FK + date datum + string wijze + string gegeven_door_type + } + + ZORGEPISODE ||--o{ BEHANDELPLAN : "zorgepisode_id" + BEHANDELPLAN |o--o| BEHANDELPLAN : "vervangt_plan_id (self)" + INTAKE |o--o{ BEHANDELPLAN : "gebaseerd_op_intake_id (0..1)" + BEHANDELPLAN ||--o{ BEHANDELPLAN_DIAGNOSE : "diagnoses" + DIAGNOSE ||--o{ BEHANDELPLAN_DIAGNOSE : "plannen" + BEHANDELPLAN ||--o{ BEHANDELDOEL : "doelen" + DIAGNOSE |o--o{ BEHANDELDOEL : "diagnose_id (optioneel)" + BEHANDELDOEL |o--o{ BEHANDELDOEL : "overgenomen_van_doel_id (self)" + BEHANDELPLAN ||--o{ INTERVENTIE : "interventies" + INTERVENTIE ||--o{ INTERVENTIE_DOEL : "doelkoppelingen" + BEHANDELDOEL ||--o{ INTERVENTIE_DOEL : "interventiekoppelingen" + BEHANDELPLAN ||--o{ EVALUATIEMOMENT : "evaluatiemomenten" + BEHANDELPLAN ||--o{ BEHANDELPLAN_AKKOORD : "akkoorden" + MEDEWERKER ||--o{ BEHANDELPLAN : "opgesteld_door" + MEDEWERKER |o--o{ BEHANDELPLAN : "regiebehandelaar_id" + MEDEWERKER |o--o{ BEHANDELPLAN : "vastgesteld_door" + MEDEWERKER |o--o{ INTERVENTIE : "uitvoerder_id" + MEDEWERKER |o--o{ EVALUATIEMOMENT : "uitgevoerd_door" + MEDEWERKER ||--o{ BEHANDELPLAN_AKKOORD : "vastgelegd_door" + PERSOON |o--o{ BEHANDELPLAN_AKKOORD : "gegeven_door_persoon_id" +
+
sleep om te pannen · scroll om te zoomen
+
+
+ + + + + + + diff --git a/docs/datamodel/diagrammen/model-diagnose-erd.html b/docs/datamodel/diagrammen/model-diagnose-erd.html new file mode 100644 index 0000000..507078d --- /dev/null +++ b/docs/datamodel/diagrammen/model-diagnose-erd.html @@ -0,0 +1,345 @@ + + + + + +Diagnose — entiteit-relatiediagram + + + + +
+
+
+

Diagnose — entiteit-relatiediagram

+ model-diagnose.md §2–§3 · 13 entiteiten · 19 relaties +
+
+ + + + +
+
+ +
+
Diagram wordt geladen…
+
+erDiagram + ZORGEPISODE { + uuid id PK + } + INTAKE { + uuid id PK + } + MEDEWERKER { + uuid id PK + } + BEHANDELPLAN { + uuid id PK + } + DIAGNOSE { + uuid id PK + uuid zorgepisode_id FK + uuid dsm_classificatie_id FK + uuid gesteld_tijdens_intake_id FK + uuid definitief_door FK + uuid geregistreerd_door FK + uuid vervangen_door_diagnose_id FK + string verificatiestatus + string klinische_status + string afgeleide_icd10_code + } + DSM_CLASSIFICATIE { + uuid id PK + uuid dsm_hoofdgroep_id FK + string dsm_code + string omschrijving + boolean selecteerbaar + } + DSM_HOOFDGROEP { + uuid id PK + string code + string omschrijving + } + DSM_ICD10_MAPPING { + uuid id PK + uuid dsm_classificatie_id FK + string icd10_code + boolean voorkeur + string lijstversie + } + HOOFDDIAGNOSE_AANWIJZING { + uuid id PK + uuid zorgepisode_id FK + uuid diagnose_id FK + uuid aangewezen_door FK + date geldig_van + date geldig_tot + } + HONOS_AFNAME { + uuid id PK + uuid zorgepisode_id FK + uuid afgenomen_door FK + string instrument + date afgenomen_op + } + HONOS_ITEMSCORE { + uuid id PK + uuid honos_afname_id FK + string honos_item + int score + } + ZORGVRAAGTYPERING { + uuid id PK + uuid zorgepisode_id FK + uuid honos_afname_id FK + uuid gekozen_door FK + string soort + string gekozen_zorgvraagtype + date gekozen_op + } + ZORGVRAAGTYPE_ADVIES { + uuid id PK + uuid zorgvraagtypering_id FK + string zorgvraagtype + int rangorde + } + + ZORGEPISODE ||--o{ DIAGNOSE : "zorgepisode_id (extern, module zorgepisode)" + INTAKE |o--o{ DIAGNOSE : "gesteld_tijdens_intake_id (extern, module intake)" + DSM_CLASSIFICATIE ||--o{ DIAGNOSE : "dsm_classificatie_id" + DSM_HOOFDGROEP ||--o{ DSM_CLASSIFICATIE : "dsm_hoofdgroep_id" + DSM_CLASSIFICATIE ||--o{ DSM_ICD10_MAPPING : "dsm_classificatie_id (mapping naar ICD-10)" + ZORGEPISODE ||--o{ HOOFDDIAGNOSE_AANWIJZING : "zorgepisode_id (extern)" + DIAGNOSE ||--o{ HOOFDDIAGNOSE_AANWIJZING : "diagnose_id" + DIAGNOSE |o--o| DIAGNOSE : "vervangen_door_diagnose_id (self)" + ZORGEPISODE ||--o{ HONOS_AFNAME : "zorgepisode_id (extern)" + HONOS_AFNAME ||--o{ HONOS_ITEMSCORE : "honos_afname_id (max 19 items)" + HONOS_AFNAME ||--o| ZORGVRAAGTYPERING : "honos_afname_id (uniek)" + ZORGEPISODE ||--o{ ZORGVRAAGTYPERING : "zorgepisode_id (extern)" + ZORGVRAAGTYPERING ||--o{ ZORGVRAAGTYPE_ADVIES : "zorgvraagtypering_id" + MEDEWERKER ||--o{ DIAGNOSE : "geregistreerd_door (extern, module medewerker)" + MEDEWERKER |o--o{ DIAGNOSE : "definitief_door (optioneel, extern)" + MEDEWERKER ||--o{ HONOS_AFNAME : "afgenomen_door (extern)" + MEDEWERKER ||--o{ ZORGVRAAGTYPERING : "gekozen_door (extern)" + MEDEWERKER ||--o{ HOOFDDIAGNOSE_AANWIJZING : "aangewezen_door (extern)" + BEHANDELPLAN }o--o{ DIAGNOSE : "adresseert diagnose (extern, module behandelplan)" +
+
sleep om te pannen · scroll om te zoomen
+
+
+ + + + + + + diff --git a/docs/datamodel/diagrammen/model-instroom-erd.html b/docs/datamodel/diagrammen/model-instroom-erd.html new file mode 100644 index 0000000..409279f --- /dev/null +++ b/docs/datamodel/diagrammen/model-instroom-erd.html @@ -0,0 +1,390 @@ + + + + + +Instroom — entiteit-relatiediagram + + + + +
+
+
+

Instroom — entiteit-relatiediagram

+ model-instroom.md §2–§3 · 20 entiteiten · 29 relaties +
+
+ + + + +
+
+ +
+
Diagram wordt geladen…
+
+erDiagram + PERSOON { + uuid id PK + } + referral_request { + uuid id PK + uuid person_id FK + timestamp initiated_at + string initiator_type + text presenting_need_text + } + referral_submission { + uuid id PK + timestamp received_at + string channel + } + submission_document { + uuid id PK + uuid submission_id FK + string document_type + } + submission_request_link { + uuid submission_id FK + uuid referral_request_id FK + timestamp established_at + } + submission_duplicate_assessment { + uuid submission_id FK + uuid duplicate_of_submission_id FK + timestamp assessed_at + } + referral_case { + uuid id PK + uuid person_id FK + timestamp opened_at + string wettelijk_kader + } + submission_case_assignment { + uuid submission_id FK + uuid referral_case_id FK + timestamp assigned_at + } + request_case_link { + uuid referral_request_id FK + uuid referral_case_id FK + date since + } + case_consolidation { + uuid primary_case_id FK + uuid related_case_id FK + timestamp designated_at + } + screening_activity { + uuid id PK + uuid referral_case_id FK + string activity_type + } + case_information_request { + uuid id PK + uuid referral_case_id FK + uuid resolved_by_submission_id FK + timestamp requested_at + } + care_acceptance_decision { + uuid id PK + uuid referral_case_id FK + uuid replaces_decision_id FK + string decision_outcome + string follow_up_route + } + case_withdrawal { + uuid id PK + uuid referral_case_id FK + timestamp withdrawn_at + } + professional_referral { + uuid id PK + uuid referral_request_id FK + uuid replaces_referral_id FK + date referral_date + } + municipal_care_assignment { + uuid id PK + uuid referral_request_id FK + uuid replaces_assignment_id FK + string legal_framework + } + legal_mandate { + uuid id PK + uuid referral_case_id FK + string mandate_type + } + crisis_encounter_note { + uuid id PK + uuid referral_case_id FK + string fact_type + } + clinical_care_episode { + uuid id PK + uuid originating_decision_id FK + uuid originating_mandate_id FK + uuid possible_continuation_of_episode_id FK + timestamp started_at + } + case_urgency_assessment { + uuid id PK + uuid referral_case_id FK + string urgency_level + } + episode_team_involvement { + uuid id PK + uuid clinical_care_episode_id FK + string team_reference + } + + PERSOON |o--o{ referral_request : "person_id" + PERSOON |o--o{ referral_case : "person_id" + referral_submission ||--o{ submission_document : "documenten" + referral_submission ||--o{ submission_request_link : "koppelingen" + referral_request ||--|{ submission_request_link : "min 1 submission" + referral_submission ||--o{ submission_duplicate_assessment : "als duplicaat" + referral_submission ||--o{ submission_duplicate_assessment : "als origineel" + referral_case ||--|{ submission_case_assignment : "min 1 toewijzing" + referral_submission ||--o{ submission_case_assignment : "toewijzingen" + referral_case ||--|{ request_case_link : "min 1 request" + referral_request ||--|{ request_case_link : "normaliter 1 case" + referral_case ||--o{ case_consolidation : "primary (leidend)" + referral_case ||--o| case_consolidation : "related (uniek)" + referral_case ||--o{ screening_activity : "activiteiten" + referral_case ||--o{ case_information_request : "informatieverzoeken" + referral_submission |o--o| case_information_request : "resolved_by (0..1)" + referral_case ||--o{ care_acceptance_decision : "besluiten" + care_acceptance_decision |o--o| care_acceptance_decision : "vervangt (self)" + referral_case ||--o{ case_withdrawal : "intrekkingen" + referral_request ||--o{ professional_referral : "verwijzingen" + professional_referral |o--o| professional_referral : "vervangt (self)" + referral_request ||--o{ municipal_care_assignment : "toewijzingen" + municipal_care_assignment |o--o| municipal_care_assignment : "vervangt (self)" + referral_case ||--o{ legal_mandate : "mandaten" + referral_case ||--o{ crisis_encounter_note : "notities" + care_acceptance_decision ||--o| clinical_care_episode : "XOR met mandaat" + legal_mandate ||--o| clinical_care_episode : "XOR met besluit" + clinical_care_episode |o--o| clinical_care_episode : "mogelijke vervolg (self)" + referral_case ||--o{ case_urgency_assessment : "beoordelingen" + clinical_care_episode ||--o{ episode_team_involvement : "team-betrokkenheid" +
+
sleep om te pannen · scroll om te zoomen
+
+
+ + + + + + + diff --git a/docs/datamodel/diagrammen/model-intake-behandeladvies-erd.html b/docs/datamodel/diagrammen/model-intake-behandeladvies-erd.html new file mode 100644 index 0000000..b2a67dd --- /dev/null +++ b/docs/datamodel/diagrammen/model-intake-behandeladvies-erd.html @@ -0,0 +1,295 @@ + + + + + +Intake en behandeladvies — entiteit-relatiediagram + + + + +
+
+
+

Intake en behandeladvies — entiteit-relatiediagram

+ model-intake-behandeladvies.md §1–§2 · 7 entiteiten · 7 relaties +
+
+ + + + +
+
+ +
+
Diagram wordt geladen…
+
+erDiagram + clinical_care_episode { + uuid id PK + } + care_program { + uuid id PK + } + clinical_intake_assessment { + uuid id PK + uuid clinical_care_episode_id FK + string initiation_reason + string status + timestamp planned_at + } + intake_contact { + uuid id PK + uuid clinical_intake_assessment_id FK + timestamp occurred_at + string contact_type + int planned_duration_minutes + } + child_safety_check { + uuid id PK + uuid clinical_intake_assessment_id FK + boolean responsible_for_minors + boolean safety_concern + boolean action_taken + } + intake_mdt_review { + uuid id PK + timestamp occurred_at + text participants + } + treatment_advice { + uuid id PK + uuid clinical_intake_assessment_id FK + uuid mdt_review_id FK + uuid replaces_advice_id FK + uuid recommended_care_program FK + string outcome + timestamp decided_at + } + + clinical_care_episode ||--o{ clinical_intake_assessment : "clinical_care_episode_id" + clinical_intake_assessment ||--o{ intake_contact : "clinical_intake_assessment_id" + clinical_intake_assessment ||--o| child_safety_check : "clinical_intake_assessment_id" + clinical_intake_assessment ||--o{ treatment_advice : "clinical_intake_assessment_id" + treatment_advice |o--o| treatment_advice : "vervangt (self)" + intake_mdt_review |o--o| treatment_advice : "mdt_review_id" + care_program |o--o{ treatment_advice : "recommended_care_program" +
+
sleep om te pannen · scroll om te zoomen
+
+
+ + + + + + + diff --git a/docs/datamodel/diagrammen/model-rapportage-erd.html b/docs/datamodel/diagrammen/model-rapportage-erd.html new file mode 100644 index 0000000..561a318 --- /dev/null +++ b/docs/datamodel/diagrammen/model-rapportage-erd.html @@ -0,0 +1,339 @@ + + + + + +Rapportage — entiteit-relatiediagram + + + + +
+
+
+

Rapportage — entiteit-relatiediagram

+ model-rapportage.md §2 · 14 entiteiten · 18 relaties · deels herzien — zie besluitenlog-datamodel-2026-07-18.md §§6, 8 +
+
+ + + + +
+
+ +
+
Diagram wordt geladen…
+
+erDiagram + ZORGEPISODE { + uuid id PK + } + MEDEWERKER { + uuid id PK + } + PERSOON { + uuid id PK + } + INTAKE { + uuid id PK + } + CONTACTMOMENT { + uuid id PK + } + BEHANDELPLAN { + uuid id PK + } + BEHANDELDOEL { + uuid id PK + } + RAPPORTAGE { + uuid id PK + uuid zorgepisode_id FK + uuid auteur_medewerker_id FK + uuid auteur_persoon_id FK + uuid bevestigd_door_medewerker_id FK + uuid vorige_versie_id FK + uuid intake_id FK + uuid contactmoment_id FK + uuid behandelplan_id FK + string rapportagetype_code + text vrije_tekst + string status_code + } + RAPPORTAGESECTIE { + uuid id PK + uuid rapportage_id FK + string sectietype_code + text tekst + } + RAPPORTAGE_DOELKOPPELING { + uuid rapportage_id FK + uuid behandeldoel_id FK + string voortgang_code + } + AI_BRONVERWIJZING { + uuid id PK + string eigenaar_entiteit + uuid eigenaar_id + string bron_entiteit + uuid bron_id + text bron_content_hash + } + INCIDENTDETAIL { + uuid rapportage_id PK, FK + string incidentcategorie_code + text aard + boolean merkbare_gevolgen + } + INCIDENT_BETROKKENE { + uuid id PK + uuid incident_rapportage_id FK + uuid medewerker_id FK + string naam_vrij + } + OVERDRACHTSSAMENVATTING { + uuid id PK + uuid zorgepisode_id FK + uuid bevestigd_door_medewerker_id FK + text samenvatting_tekst + timestamp periode_van + string status_code + } + + ZORGEPISODE ||--o{ RAPPORTAGE : "zorgepisode_id" + MEDEWERKER |o--o{ RAPPORTAGE : "auteur_medewerker_id" + PERSOON |o--o{ RAPPORTAGE : "auteur_persoon_id" + MEDEWERKER |o--o{ RAPPORTAGE : "bevestigd_door_medewerker_id" + RAPPORTAGE |o--o| RAPPORTAGE : "vorige_versie_id (self)" + CONTACTMOMENT |o--o{ RAPPORTAGE : "contactmoment_id" + INTAKE |o--o{ RAPPORTAGE : "intake_id" + BEHANDELPLAN |o--o{ RAPPORTAGE : "behandelplan_id" + RAPPORTAGE ||--o{ RAPPORTAGE_DOELKOPPELING : "rapportage_id" + BEHANDELDOEL ||--o{ RAPPORTAGE_DOELKOPPELING : "behandeldoel_id" + RAPPORTAGE ||--o{ RAPPORTAGESECTIE : "secties (max 1 per sectietype)" + RAPPORTAGE ||--o| INCIDENTDETAIL : "1-op-1 (alleen bij type incident)" + INCIDENTDETAIL ||--o{ INCIDENT_BETROKKENE : "betrokkenen" + MEDEWERKER |o--o{ INCIDENT_BETROKKENE : "medewerker_id (optioneel, xor naam_vrij)" + RAPPORTAGE |o--o{ AI_BRONVERWIJZING : "eigenaar (polymorf, indien eigenaar_entiteit=rapportage)" + OVERDRACHTSSAMENVATTING |o--o{ AI_BRONVERWIJZING : "eigenaar (polymorf, indien eigenaar_entiteit=overdrachtssamenvatting)" + ZORGEPISODE ||--o{ OVERDRACHTSSAMENVATTING : "zorgepisode_id" + MEDEWERKER |o--o{ OVERDRACHTSSAMENVATTING : "bevestigd_door_medewerker_id" +
+
sleep om te pannen · scroll om te zoomen
+
+
+ + + + + + + diff --git a/docs/datamodel/diagrammen/model-screening-erd.html b/docs/datamodel/diagrammen/model-screening-erd.html new file mode 100644 index 0000000..a716c9b --- /dev/null +++ b/docs/datamodel/diagrammen/model-screening-erd.html @@ -0,0 +1,303 @@ + + + + + +Screening — entiteit-relatiediagram (achterhaald) + + + + +
+
+
+

Screening — entiteit-relatiediagram (achterhaald)

+ model-screening.md §2 · 8 entiteiten · 11 relaties · ACHTERHAALD door B21 (screeningsbesluit = acceptatiebesluit, geen apart voorafgaand besluit meer) — zie model-instroom.md (care_acceptance_decision) +
+
+ + + + +
+
+ +
+
Diagram wordt geladen…
+
+erDiagram + AANMELDING { + uuid id PK + } + MEDEWERKER { + uuid id PK + } + VERWIJZER { + uuid id PK + } + DOCUMENT { + uuid id PK + } + SCREENING { + uuid id PK + uuid aanmelding_id FK + uuid screener_id FK + date gestart_op + string status + string urgentie_id + } + SCREENING_ACTIVITEIT { + uuid id PK + uuid screening_id FK + uuid uitgevoerd_door FK + string type_id + timestamp uitgevoerd_op + boolean crisis_signaal + } + SCREENING_BESLUIT { + uuid id PK + uuid screening_id FK + uuid genomen_door FK + string uitkomst_id + string urgentie_id + text motivatie + } + INFORMATIE_UITVRAAG { + uuid id PK + uuid screening_id FK + uuid gericht_aan_verwijzer_id FK + uuid uitgevraagd_door FK + uuid ontvangen_document_id FK + string type_id + string status + } + + AANMELDING ||--o{ SCREENING : "aanmelding_id" + SCREENING ||--o{ SCREENING_ACTIVITEIT : "activiteiten" + SCREENING ||--o| SCREENING_BESLUIT : "besluit (max 1)" + SCREENING ||--o{ INFORMATIE_UITVRAAG : "uitvragen" + MEDEWERKER ||--o{ SCREENING_ACTIVITEIT : "uitgevoerd_door" + MEDEWERKER |o--o{ SCREENING : "screener_id" + MEDEWERKER ||--o{ SCREENING_BESLUIT : "genomen_door" + MEDEWERKER ||--o{ INFORMATIE_UITVRAAG : "uitgevraagd_door" + VERWIJZER |o--o{ INFORMATIE_UITVRAAG : "gericht_aan_verwijzer_id" + DOCUMENT |o--o{ INFORMATIE_UITVRAAG : "ontvangen_document_id" + SCREENING_BESLUIT |o--o| AANMELDING : "onderbouwing uitkomst (0..1)" +
+
sleep om te pannen · scroll om te zoomen
+
+
+ + + + + + + diff --git a/docs/datamodel/diagrammen/model-wachtlijst-erd.html b/docs/datamodel/diagrammen/model-wachtlijst-erd.html new file mode 100644 index 0000000..b457bd3 --- /dev/null +++ b/docs/datamodel/diagrammen/model-wachtlijst-erd.html @@ -0,0 +1,297 @@ + + + + + +Wachtlijst — entiteit-relatiediagram + + + + +
+
+
+

Wachtlijst — entiteit-relatiediagram

+ model-wachtlijst.md §2–§3 · 9 entiteiten · 8 relaties · voorlopig onderzoeksmodel, niet bouwrijp +
+
+ + + + +
+
+ +
+
Diagram wordt geladen…
+
+erDiagram + WACHTLIJSTPLAATSING { + uuid id PK + string soort + uuid aanmelding_id FK + uuid zorgepisode_id FK + uuid afdeling_id FK + uuid zorgprogramma_id FK + date teldatum + string status + } + WACHTLIJSTACTIVITEIT { + uuid id PK + uuid wachtlijstplaatsing_id FK + uuid uitgevoerd_door FK + string type + date datum + } + WACHTLIJSTOPSCHORTING { + uuid id PK + uuid wachtlijstplaatsing_id FK + date begindatum + date einddatum + string reden + } + referral_case { + uuid id PK + } + clinical_care_episode { + uuid id PK + } + AFDELING { + uuid id PK + } + ZORGPROGRAMMA { + uuid id PK + } + MEDEWERKER { + uuid id PK + } + VESTIGING { + uuid id PK + } + + referral_case ||--o{ WACHTLIJSTPLAATSING : "aanmelding_id (soort aanmeld)" + clinical_care_episode ||--o{ WACHTLIJSTPLAATSING : "zorgepisode_id (soort behandel)" + AFDELING ||--o{ WACHTLIJSTPLAATSING : "afdeling_id" + ZORGPROGRAMMA |o--o{ WACHTLIJSTPLAATSING : "zorgprogramma_id (optioneel)" + WACHTLIJSTPLAATSING ||--o{ WACHTLIJSTACTIVITEIT : "wachtlijstplaatsing_id" + WACHTLIJSTPLAATSING ||--o{ WACHTLIJSTOPSCHORTING : "wachtlijstplaatsing_id" + MEDEWERKER ||--o{ WACHTLIJSTACTIVITEIT : "uitgevoerd_door" + VESTIGING |o--o{ AFDELING : "vestiging_id (voorgesteld, open §7.2)" +
+
sleep om te pannen · scroll om te zoomen
+
+
+ + + + + + + diff --git a/docs/datamodel/entiteitenkaarten/ehr-referral-terminology.html b/docs/datamodel/entiteitenkaarten/ehr-referral-terminology.html new file mode 100644 index 0000000..cead4ed --- /dev/null +++ b/docs/datamodel/entiteitenkaarten/ehr-referral-terminology.html @@ -0,0 +1,818 @@ + + + + + + + Engelstalige EHR-termen voor GGZ-instroom + + + +
+ + +
+

EHR terminology review

+

Engelstalige EHR-termen voor GGZ-instroom

+

+ Vergelijking van Epic, Oracle Health, athenahealth, Netsmart, NextGen, + HL7 FHIR, ONC/360X en NHS, aangescherpt voor een primair ambulante GGZ-context. +

+
+ + + +
+

Voorgesteld kernmodel

+
+
+
+

Referral Request

+ verzoek · open +
+
+

referral_request

+

+ Het inhoudelijke verzoek om zorg of beoordeling, geïnitieerd door een + professional, organisatie, cliënt of vertegenwoordiger. +

+

+ Mogelijke Engelse tegenhanger van de formele VERWIJZING. Eerst bepalen + of dit een eigen identiteit nodig heeft. +

+
+
+ +
+
+

Referral Submission

+ ontvangst +
+
+

referral_submission

+

+ Eén ontvangen indiening of aanvulling, met eigen kanaal, bron, tijdstip, + documenten en oorspronkelijke inhoud. +

+

+ Voorkeursnaam voor AANMELDSIGNAAL. Een lokale technische term, geen + universele EHR-standaard. +

+
+
+ +
+
+

Referral

+ workflow +
+
+

referral

+

+ Het beheerde instroomobject dat submissions en eventuele requests + beoordeelt, aanvult, trieert en afsluit. +

+

+ Voorkeursnaam voor AANMELDINGSTRAJECT. Sluit aan op Epic, Oracle en + behavioral-healthleverancier Netsmart. +

+
+
+
+ +
+
referral_submission
+
referral
+
acceptance_decision
+
clinical_care_episode
+
clinical_intake
+
+

+ De zorgsetting is een afzonderlijk tijdgebonden kenmerk. De keten heet dus + niet admission of pre-admission; de meeste GGZ-behandeling is ambulant. +

+
+ +
+

Beoordeling van naamparen

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NaamparenOordeelSterk puntBelangrijk risico
Referral Submission / ReferralVoorkeurIdiomatic in referral management en behavioral healthReferral moet expliciet als ontvangende lifecycle worden gedefinieerd
Referral Submission / Referral CaseSterkScheidt ontvangst en workflow zeer explicietCase kan elders juridisch, financieel of klinisch iets anders betekenen
Care Access Submission / Care Access CaseRedelijkInclusief voor zelfaanmelding en niet-formele bronnenAccess kan als dossier-, systeem- of autorisatietoegang worden gelezen
Referral Submission / Referral Intake CaseZwakSluit aan op behavioral-healthtaal als referral intakeBotst met Triqura's afzonderlijke klinische intake
Inbound Care Submission / Access Assessment CaseAfwijzenDomeinneutraalAbstract en niet herkenbaar in onderzochte EHR-documentatie
+
+
+ +
+

Wat markt en standaarden feitelijk gebruiken

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BronGepubliceerde termBetekenisGevolg voor Triqura
EpicReferralLifecycle-object met incoming/outgoing class, status, triage en historieOndersteunt referral als beheerd instroomobject
Oracle HealthReferral / Referral OrderOrder is verzoek; Referral Management beheert status, bijlagen en tijdlijnScheidt request en beheerde referral
NetsmartIncoming Referral / Referral Manager / Referral PacketBehavioral-healthinstroom uit meerdere kanalen; acceptatie naar pre-admitReferraltaal past bij de GGZ, maar pre-admit niet als generieke kernterm
HL7 FHIRServiceRequest / Task / EpisodeOfCare / EncounterVerzoek, workflowtaak, verantwoordelijkheid en contact zijn aparte conceptenLokale objecten niet naar één FHIR-resource forceren
ONC/360XReferral Request / Referral Order / Referral IdentifierClosed-loop workflow met persistente identifier tot afsluitingRequest en workflow moeten herkenbaar maar niet samengevouwen zijn
NHSReferral Request / Patient PathwayReferral Request omvat self-referral; Patient Pathway is veel brederOndersteunt een apart request, maar niet Patient Pathway voor deze fase
+
+
+ +
+
+

Termen om te vermijden

+
    +
  • signal — klinkt als alert of klinisch vroegsignaal.
  • +
  • admission — suggereert opname of reeds verleende toegang.
  • +
  • pre_admission — is opnamegericht en niet passend als ambulante standaard.
  • +
  • encounter — is een feitelijk contact, geen instroomworkflow.
  • +
  • episode_of_care — impliceert organisatorische verantwoordelijkheid.
  • +
  • patient_pathway — is breder en langduriger dan beoordeling vóór acceptatie.
  • +
  • referral_intake_case — botst met de afzonderlijke klinische intake.
  • +
+
+ +
+

Belangrijkste modelcorrectie

+

+ Het huidige AANMELDSIGNAAL bevat zowel een transportfeit als inhoud van + een zorgverzoek. Engelstalige EHR-terminologie maakt zichtbaar dat dit + twee betekenissen kunnen zijn. +

+

+ Een huisartsbrief, later ontvangen diagnostiek en een telefoontje kunnen + drie submissions binnen één referral zijn, zonder dat drie nieuwe + zorgverzoeken of trajecten ontstaan. +

+

+ Of het inhoudelijke verzoek daarom een eigen referral_request + nodig heeft, wordt in de FCO-IM-feitenronde bepaald. +

+
+
+ +
+

Primaire bronnen

+ +
+ + + +
+ Zelfstandig HTML-artefact. Geen externe scripts, fonts of stylesheets. + Bronlinks vereisen internet; alle onderzoeksinhoud is lokaal beschikbaar. +
+
+ + + + diff --git a/docs/datamodel/entiteitenkaarten/entiteitenkaart-instroom.html b/docs/datamodel/entiteitenkaarten/entiteitenkaart-instroom.html new file mode 100644 index 0000000..1cd7ca8 --- /dev/null +++ b/docs/datamodel/entiteitenkaarten/entiteitenkaart-instroom.html @@ -0,0 +1,1033 @@ + + +
+ + ECD · datamodel discovery +

Entiteitenkaart — instroomflow

+

+ Afgeleid uit de feitzinnen in datamodel-discovery.md §5.1–5.5: van persoon en aanmelding + via screening, intake en wachtlijst tot diagnose en behandelplan, met de bijbehorende waardelijsten. + Status: concept, ter review — vóór dit naar SQL gaat, checkt Colin de entiteiten, kardinaliteiten en + waardelijst-waarden. +

+ +
+

Samenhang — de keten in één oogopslag

+

+ Aanmelding en zorgepisode zijn géén gelijke niveaus: de aanmelding is de binnenkomst en het + besluitproces (daar horen screening en aanmeldwachttijd bij), de zorgepisode is de periode van zorg + die pas ontstaat als het besluit "intake" is. Alles in de onderste doos hangt aan de episode. + De pijlen binnen de episode tonen de chronologie (intake → diagnose → behandelplan); in het + datamodel hangen alle drie rechtstreeks aan de episode, met de intake als optionele herkomst + van de diagnose. +

+
+
+flowchart TB
+    CL["CLIËNT (rol op PERSOON)"] -->|"meldt zich aan — 1 op veel"| AANM
+    subgraph AANMBOX["AANMELDING — binnenkomst en besluitproces"]
+        direction TB
+        AANM["Aanmelding
+verwijzer · wettelijk kader · hulpvraag"]
+        AANM --> SCR["Screening
+activiteiten + besluit"]
+        AANM -.-> WLA["Wachtlijstplaatsing
+soort: aanmeld"]
+        SCR --> UIT{"Uitkomst
+bij status besloten"}
+    end
+    UIT -->|"afgewezen · doorverwezen"| EINDE["geen episode"]
+    UIT -->|"wachtlijst"| WLA
+    UIT ==>|"intake"| EPI
+    subgraph EPIBOX["ZORGEPISODE — periode van zorg, 0..1 per aanmelding"]
+        direction TB
+        EPI["Zorgepisode"]
+        EPI --> INT["Intake(s)
+aanleiding: regulier · intern · crisis"]
+        EPI -.-> WLB["Wachtlijstplaatsing
+soort: behandel"]
+        INT --> DIA["Diagnose(s)
+DSM-5-TR · gesteld tijdens intake"]
+        DIA --> PLAN["Behandelplan
+doelen · interventies · evaluaties"]
+    end
+
+
+
+ +
+

Diagram 1 — persoon & aanmelding (§5.1)

+

Rechthoeken met een sleutel (PK) zijn entiteiten met een eigen identiteit; entiteiten met alleen een code-sleutel zijn waardelijsten (referentiedata, geen eigen levenscyclus).

+
+
+erDiagram
+    PERSOON ||--o| CLIENT : "is cliënt sinds"
+    CLIENT ||--o{ AANMELDING : "meldt zich aan"
+    CLIENT ||--o| CLIENTPORTAAL_ACCOUNT : "heeft toegang via"
+    AANMELDING ||--o| ZORGEPISODE : "start bij uitkomst intake"
+    AANMELDING }o--|| WETTELIJK_KADER : "valt onder"
+    AANMELDING }o--|| AANMELDING_STATUS : "heeft"
+    AANMELDING }o--o| AANMELDING_UITKOMST : "heeft, bij besloten"
+    AANMELDING }o--o| VERWIJZER : "komt binnen via"
+    AANMELDING ||--o{ VERWIJSDOCUMENT : "ontvangt"
+    VERWIJSDOCUMENT }o--|| VERWIJSDOCUMENT_TYPE : "is van type"
+    VERWIJZER }o--|| VERWIJZERTYPE : "is van type"
+    VERWIJZER }o--o| PRAKTIJK_INSTELLING : "verbonden aan"
+
+    PERSOON {
+        uuid id PK
+        string naam
+        date geboortedatum
+        string bsn_encrypted "optioneel"
+    }
+    CLIENT {
+        uuid id PK
+        uuid persoon_id FK
+        string clientnummer UK
+        date sinds
+    }
+    CLIENTPORTAAL_ACCOUNT {
+        uuid id PK
+        uuid client_id FK
+        string status
+    }
+    AANMELDING {
+        uuid id PK
+        uuid client_id FK
+        date datum
+        string hulpvraag
+    }
+    ZORGEPISODE {
+        uuid id PK
+        uuid aanmelding_id FK
+        date gestart_op
+        date beeindigd_op "optioneel"
+    }
+    VERWIJZER {
+        uuid id PK
+        string naam
+        string agb_code
+    }
+    PRAKTIJK_INSTELLING {
+        uuid id PK
+        string naam
+        string agb_code
+    }
+    VERWIJSDOCUMENT {
+        uuid id PK
+        uuid aanmelding_id FK
+        date ontvangen_datum
+    }
+    VERWIJZERTYPE {
+        string code PK
+        string label
+        boolean agb_verplicht
+    }
+    WETTELIJK_KADER {
+        string code PK
+        string label
+    }
+    AANMELDING_STATUS {
+        string code PK
+        string label
+    }
+    AANMELDING_UITKOMST {
+        string code PK
+        string label
+    }
+    VERWIJSDOCUMENT_TYPE {
+        string code PK
+        string label
+    }
+
+
+

+ Niet in het diagram, maar op elke entiteit van toepassing (spelregels §3.1): id (uuid), + created_at/updated_at, deleted_at (soft delete), en een append-only + audit-event per mutatie. Weggelaten voor leesbaarheid, niet omdat ze niet gelden. +

+
+ +
+

Diagram 2 — screening & intake (§5.2)

+

+ De screening hoort bij de aanmelding (besluitproces); intakes horen bij de zorgepisode. Eén episode + kan meerdere intakes hebben — naast de reguliere intake bestaan interne intakes binnen een lopende + episode; de aanleiding legt het verschil vast. Screening vóór intake is een nudge, + geen harde volgorde-eis in het schema. +

+
+
+erDiagram
+    AANMELDING ||--o| SCREENING : "wordt gescreend in"
+    SCREENING ||--o{ SCREENING_ACTIVITEIT : "omvat"
+    SCREENING_ACTIVITEIT }o--|| SCREENING_ACTIVITEIT_TYPE : "is van type"
+    SCREENING }o--o| SCREENING_BESLUIT : "besluit, bij afronding"
+    SCREENING }o--o| AFDELING : "adviseert"
+    ZORGEPISODE ||--o{ INTAKE : "omvat"
+    INTAKE }o--|| INTAKE_AANLEIDING : "gestart vanwege"
+    INTAKE }o--|| INTAKE_STATUS : "heeft"
+    INTAKE }o--o| INTAKE_UITKOMST : "heeft, bij afgerond"
+    INTAKE }o--|| AFDELING : "op afdeling"
+    INTAKE ||--o{ CONTACTMOMENT : "omvat"
+    CONTACTMOMENT }o--|| CONTACTMOMENT_TYPE : "is van type"
+    INTAKE ||--o| KINDCHECK : "heeft"
+
+    SCREENING {
+        uuid id PK
+        uuid aanmelding_id FK
+        date gestart_op
+        date besluit_datum "optioneel"
+    }
+    SCREENING_ACTIVITEIT {
+        uuid id PK
+        uuid screening_id FK
+        date datum
+        string toelichting
+        uuid uitgevoerd_door "rollenronde"
+    }
+    INTAKE {
+        uuid id PK
+        uuid zorgepisode_id FK
+        date gestart_op
+        date afgerond_op "optioneel"
+    }
+    CONTACTMOMENT {
+        uuid id PK
+        uuid intake_id FK
+        date datum
+        uuid gevoerd_door "rollenronde"
+    }
+    KINDCHECK {
+        uuid id PK
+        uuid intake_id FK
+        date uitgevoerd_op
+        boolean thuiswonende_kinderen
+        boolean zorgen_veiligheid
+        boolean actie_ondernomen
+        string toelichting
+    }
+    SCREENING_ACTIVITEIT_TYPE {
+        string code PK
+        string label
+    }
+    SCREENING_BESLUIT {
+        string code PK
+        string label
+    }
+    AFDELING {
+        string code PK
+        string label
+    }
+    INTAKE_AANLEIDING {
+        string code PK
+        string label
+    }
+    INTAKE_STATUS {
+        string code PK
+        string label
+    }
+    INTAKE_UITKOMST {
+        string code PK
+        string label
+    }
+    CONTACTMOMENT_TYPE {
+        string code PK
+        string label
+    }
+
+
+

+ ZORGPROGRAMMA (FACT, Verslaving, Trauma, ...) is als waardelijst besloten maar heeft nog geen + relatie in dit diagram — de koppeling (aan behandeladvies of traject) volgt in de ronde + diagnose & behandeltraject. Verslagen (feitzin 9, provenance) volgen in de rapportage-ronde. +

+
+ +
+

Diagram 3 — wachtlijst, diagnose & behandelplan (§5.3–5.5)

+

+ De aanmeldwachttijd hangt aan de AANMELDING (besluitproces); diagnoses, behandelplannen en de + behandelwachttijd hangen aan de ZORGEPISODE. De diagnose wordt in DSM-5-TR geregistreerd; de + ICD-10-code wordt via een mappingtabel afgeleid. Het behandelplan is hier de kern — sessieplanning, + leefgebieden en veiligheidsplan volgen in latere rondes. +

+
+
+erDiagram
+    AANMELDING ||--o{ WACHTLIJSTPLAATSING : "aanmeldwachttijd"
+    ZORGEPISODE ||--o{ WACHTLIJSTPLAATSING : "behandelwachttijd"
+    WACHTLIJSTPLAATSING }o--|| WACHTLIJST_SOORT : "is van soort"
+    WACHTLIJSTPLAATSING }o--|| PRIORITEIT : "heeft"
+    WACHTLIJSTPLAATSING }o--|| AFDELING : "voor afdeling"
+    WACHTLIJSTPLAATSING }o--o| WACHTLIJST_EINDREDEN : "beëindigd met"
+
+    ZORGEPISODE ||--o{ DIAGNOSE : "heeft"
+    DIAGNOSE }o--|| ERNST : "heeft"
+    DIAGNOSE }o--|| DIAGNOSE_STATUS : "klinische status"
+    DIAGNOSE }o--|| VERIFICATIESTATUS : "werkdiagnose of definitief"
+    DIAGNOSE }o--o| INTAKE : "gesteld tijdens"
+    DIAGNOSE }o--|| DSM_ICD_MAPPING : "geclassificeerd als"
+
+    ZORGEPISODE ||--o{ BEHANDELPLAN : "heeft"
+    BEHANDELPLAN }o--|| PLAN_STATUS : "heeft"
+    BEHANDELPLAN }o--o{ DIAGNOSE : "adresseert"
+    BEHANDELPLAN }o--o| INTAKE : "gebaseerd op"
+    BEHANDELPLAN ||--o{ BEHANDELDOEL : "bevat"
+    BEHANDELDOEL }o--|| DOEL_STATUS : "heeft"
+    BEHANDELPLAN ||--o{ INTERVENTIE : "bevat"
+    INTERVENTIE }o--o{ BEHANDELDOEL : "werkt aan"
+    BEHANDELPLAN ||--o{ EVALUATIEMOMENT : "kent"
+    EVALUATIEMOMENT }o--|| EVALUATIE_TYPE : "is van type"
+
+    WACHTLIJSTPLAATSING {
+        uuid id PK
+        uuid aanmelding_id FK "bij soort aanmeld"
+        uuid zorgepisode_id FK "bij soort behandel"
+        date geplaatst_op
+        date telt_vanaf
+        date beeindigd_op "optioneel"
+    }
+    DIAGNOSE {
+        uuid id PK
+        uuid zorgepisode_id FK
+        string dsm_code
+        string omschrijving
+        boolean hoofddiagnose
+        date gesteld_op
+        uuid gesteld_door "rollenronde"
+    }
+    DSM_ICD_MAPPING {
+        string dsm_code PK
+        string icd10_code
+        string dsm_omschrijving
+    }
+    BEHANDELPLAN {
+        uuid id PK
+        uuid zorgepisode_id FK
+        int versie
+        date opgesteld_op
+        date client_akkoord_op "optioneel"
+    }
+    BEHANDELDOEL {
+        uuid id PK
+        uuid behandelplan_id FK
+        string titel
+        string clientversie_b1
+        string prioriteit
+        int termijn_weken
+    }
+    INTERVENTIE {
+        uuid id PK
+        uuid behandelplan_id FK
+        string naam
+        string rationale
+    }
+    EVALUATIEMOMENT {
+        uuid id PK
+        uuid behandelplan_id FK
+        int week
+        date gepland_op
+        date uitgevoerd_op "optioneel"
+    }
+    WACHTLIJST_SOORT {
+        string code PK
+        string label
+    }
+    PRIORITEIT {
+        string code PK
+        string label
+    }
+    WACHTLIJST_EINDREDEN {
+        string code PK
+        string label
+    }
+    ERNST {
+        string code PK
+        string label
+    }
+    DIAGNOSE_STATUS {
+        string code PK
+        string label
+    }
+    VERIFICATIESTATUS {
+        string code PK
+        string label
+    }
+    PLAN_STATUS {
+        string code PK
+        string label
+    }
+    DOEL_STATUS {
+        string code PK
+        string label
+    }
+    EVALUATIE_TYPE {
+        string code PK
+        string label
+    }
+
+
+

+ Aannames ter bevestiging: versiebeheer als nieuw record per versie (versie-veld) · + cliëntakkoord als datum-feit, niet als status · "max één hoofddiagnose" als constraint — maar per wat + (episode / moment) is nog open. WACHTLIJSTPLAATSING heeft twee optionele FK's; welke gevuld is volgt + uit de soort (aanmeld → aanmelding, behandel → episode). +

+
+ +
+

Waardelijsten

+

De daadwerkelijke waarden per referentietabel, zodat je kunt controleren of de lijst klopt en compleet is.

+
+ +
+

VERWIJZERTYPE

+ + + + + + + + + + + +
CodeAGB verplicht
huisartsja
medisch_specialistte bevestigen
ggz_instellingte bevestigen
bedrijfsartste bevestigen
gemeentenee
zelfaanmeldingnee
crisiste bevestigen
+
+ +
+

WETTELIJK_KADER

+ + + + + + + + + +
Code
zvw
jeugdwet
wmo
wlz
forensisch
+
+ +
+

AANMELDING_STATUS

+ + + + + + + +
Code
nieuw
in_screening
besloten
+

+ Overgangen zijn vrij — een besloten aanmelding kan terug naar in_screening bij heropening. Geen eenrichtingsflow. +

+
+ +
+

AANMELDING_UITKOMST

+ + + + + + + + +
Code
intake
afgewezen
doorverwezen
wachtlijst
+

+ Alleen gezet zodra status = besloten. +

+
+ +
+

VERWIJSDOCUMENT_TYPE

+ + + + + + +
Code
verwijsbrief
beschikking
+

+ Lijst waarschijnlijk niet compleet — open voor aanvulling. +

+
+ +
+

AFDELING

+ + + + + + + + +
Code
volwassenen
jeugd
ouderen
forensisch
+

+ Organisatorische eenheid; per instelling configureerbaar. Startlijst te bevestigen — + FACT en Verslaving zijn bewust géén afdeling (zie ZORGPROGRAMMA). +

+
+ +
+

ZORGPROGRAMMA

+ + + + + + + + +
Code
algemeen_ggz
fact
verslaving
trauma
+

+ Inhoudelijk aanbod, los van afdeling. Koppeling volgt in de ronde diagnose & behandeltraject. +

+
+ +
+

SCREENING_ACTIVITEIT_TYPE

+ + + + + + + + +
Code
telefonisch_contact
dossieronderzoek
vragenlijst
overig
+

+ Type + vrije toelichting per activiteit. Startlijst te bevestigen. +

+
+ +
+

SCREENING_BESLUIT

+ + + + + + +
Code
geschikt
niet_geschikt
+

+ Verhouding tot AANMELDING_UITKOMST is een open vraag (overlap "geschikt" ↔ "intake"). +

+
+ +
+

INTAKE_AANLEIDING

+ + + + + + + +
Code
regulier
intern
crisis
+

+ Regulier = vanuit aanmelding/screening; intern = tweede intake binnen lopende episode + (bijv. afdelingsovergang). Lijst te bevestigen. +

+
+ +
+

INTAKE_STATUS

+ + + + + + +
Code
bezig
afgerond
+

+ Uit het prototype overgenomen; mogelijk komt er nog een status bij (bijv. gepland). +

+
+ +
+

INTAKE_UITKOMST

+ + + + + + + +
Code
in_zorg
doorverwijzing
extra_diagnostiek
+

+ Alleen gezet bij status = afgerond. Waarden uit het prototype — te bevestigen. +

+
+ +
+

CONTACTMOMENT_TYPE

+ + + + + + + + + +
Code
intakegesprek
aanvullend_onderzoek
telefonisch_contact
huisbezoek
overig
+
+ +
+

WACHTLIJST_SOORT

+ + + + + + +
Code
aanmeld
behandel
+

+ Volgt de Treeknormen: aanmeldwachttijd (vóór intake) en behandelwachttijd (na intake). +

+
+ +
+

PRIORITEIT

+ + + + + + + +
Code
spoed
normaal
laag
+
+ +
+

WACHTLIJST_EINDREDEN

+ + + + + + + + +
Code
intake_gestart
behandeling_gestart
doorverwezen
teruggetrokken
+

+ Startlijst te bevestigen. +

+
+ +
+

ERNST

+ + + + + + + +
Code
licht
matig
ernstig
+
+ +
+

DIAGNOSE_STATUS

+ + + + + + + + +
Code
actief
in_remissie
opgelost
inactief
+

+ Klinische status — losse as naast VERIFICATIESTATUS. +

+
+ +
+

VERIFICATIESTATUS

+ + + + + + +
Code
werkdiagnose
definitief
+

+ Aparte status-as — te bevestigen (zat in het prototype-model maar werd niet gebruikt). +

+
+ +
+

PLAN_STATUS

+ + + + + + + + +
Code
concept
actief
afgerond
vervallen
+

+ Voorstel — de statusmachine (incl. plaats van cliëntakkoord) is nog een open vraag. +

+
+ +
+

DOEL_STATUS

+ + + + + + + + +
Code
niet_gestart
bezig
gehaald
bijgesteld
+
+ +
+

EVALUATIE_TYPE

+ + + + + + + +
Code
tussentijds
eind
crisis
+
+ +
+
+ +
+

Openstaand / geparkeerd

+
    +
  1. + AGB-validatie tegen een echte Vecozo-tabel — nu vrije invoer, geen validatie. +
    Geparkeerd tot de declaratie-ronde.
    +
  2. +
  3. + Wie mag de aanmelding-status zetten — nog geen rollen/disciplines gemodelleerd. +
    Komt terug zodra die ronde wordt gedaan.
    +
  4. +
  5. + AGB-verplichting per verwijzertype — alleen huisarts (ja) en gemeente (nee) zijn expliciet besproken; de overige vier staan als "te bevestigen" in de tabel hierboven. +
  6. +
  7. + Verwijsbrief/beschikking is een nudge, geen schema-constraint — leeft in de Protocol Rules Registry (Nudge Engine), niet zichtbaar als FK-verplichting in dit diagram. +
  8. +
  9. + CLIENTPORTAAL_ACCOUNT is nog een lege huls — alleen de relatie (1-op-0..1 op CLIENT) staat vast. +
    Authenticatiedetails (e-mail, provider-koppeling, status) volgen in de auth/ADM-ronde.
    +
  10. +
  11. + Verwijzersportaal — nog geen besluit, alleen een idee. Mogelijk krijgt een verwijzer ook toegang tot de status van zijn verwijzingen, analoog aan CLIENTPORTAAL_ACCOUNT. +
    Niet in dit diagram opgenomen — apart onderwerp voor een volgende ronde als het een echte behoefte blijkt.
    +
  12. +
  13. + BSN is optioneel op PERSOON — een medewerker is ook een persoon en heeft geen BSN in het systeem nodig. De verplichting hoort bij de rol (voor cliënten: Wabvpz). +
    Nudge ("cliënt zonder BSN") of rolgebonden eis — te bepalen in de rollenronde.
    +
  14. +
  15. + Screeningsbesluit ↔ aanmelding-uitkomst — "geschikt" (screening) en uitkomst "intake" (aanmelding) overlappen mogelijk: één feit of twee? +
    Open vraag voor een volgende sessie.
    +
  16. +
  17. + Startlijsten te bevestigen — AFDELING, ZORGPROGRAMMA, SCREENING_ACTIVITEIT_TYPE, INTAKE_AANLEIDING en INTAKE_UITKOMST zijn afgeleid uit het prototype en nog niet definitief. +
  18. +
  19. + Kindcheck-structuur — nu gemodelleerd als drie ja/nee-vlaggen + toelichting (zoals het prototype), niet als één uitkomstwaarde. +
    Bevestigen of dit de juiste registratievorm is (Meldcode-eisen meenemen).
    +
  20. +
  21. + Screening vóór intake is een nudge — geen harde volgorde-eis in het schema; protocolregel bij de eerste intake van een episode. +
  22. +
  23. + Behandelplan: statusmachine, versiebeheer-vorm en verplichting nog open — cliëntakkoord als status of feit (WGBO), nieuw record per versie of muteren met audit, en behandelplan-verplicht-vóór-behandeling als nudge of harde eis. +
  24. +
  25. + Wanneer ontstaat de ZORGEPISODE precies — pas bij aanmelding-uitkomst "intake", of al bij "wachtlijst"? +
    Bepaalt waar de behandelwachttijd van een nog-niet-gestarte episode aan hangt.
    +
  26. +
  27. + DSM-ICD-mappingtabel heeft een bron nodig — wie levert en onderhoudt de mapping (en de DSM-5-TR-lijst zelf, licentie APA)? +
    Uitzoeken vóór de schema-baseline; raakt ook de declaratie-ronde.
    +
  28. +
  29. + Wachtlijst per afdeling en/of zorgprogramma — nu alleen afdeling gemodelleerd; en de beëindigingsredenen-startlijst is te bevestigen. +
  30. +
+
+ +
+ Bron: docs/datamodel/deelmodellen/datamodel-discovery.md §5.1–5.5 · volgende stap (§6): statussen + + eigenaarschap ECD/TIP per entiteit vastleggen, daarna de latere rondes (agenda, rapportage & + overdracht, rollen/disciplines) en pas dan de schema-baseline. +
+ +
diff --git a/docs/datamodel/feitenmodellen/feitenmodel-instroom.md b/docs/datamodel/feitenmodellen/feitenmodel-instroom.md new file mode 100644 index 0000000..ef6ab91 --- /dev/null +++ b/docs/datamodel/feitenmodellen/feitenmodel-instroom.md @@ -0,0 +1,765 @@ +# Feitenmodel instroom — eerste FCO-IM-ronde + +**Status:** concept voor domeinreview, geen logisch model +**Datum:** 18 juli 2026, gesynchroniseerd 19 juli 2026 +**Scope:** zorgverzoek, afzonderlijke binnenkomst, aanmeldingstraject en acceptatie +**Autoriteit:** `../besluiten/besluitenlog-datamodel-2026-07-18.md` blijft leidend +**Aanleiding:** afgesproken vervolg in `../sessielogs/sessielog-2026-07-18.md` §13 +**Synchronisatie:** §2 (Care Acceptance Decision), §3.4–§3.5, §4 en §7 zijn op +19 juli 2026 bijgewerkt conform de screening=acceptatie-beslissing uit +`../sessielogs/sessielog-2026-07-19.md` §5–§9. `Screening Recommendation` als apart +besluitobject vervalt daarmee; de begrippenlijst en `../deelmodellen/model-aanmelding.md` +volgen in een latere synchronisatiestap. + +## 1. Doel van deze ronde + +Deze ronde formuleert elementaire feiten vóórdat entiteiten, attributen, +cardinaliteiten of SQL worden vastgelegd. De feiten worden getoetst met vier +praktijkscenario's: + +1. een huisarts verwijst een cliënt; +2. een cliënt meldt zichzelf aan; +3. later komt aanvullende informatie binnen; +4. twee binnenkomsten blijken dubbel of overlappend. + +De centrale open vraag is of `Referral Request` een eigen identiteit nodig +heeft naast `Referral Submission` en `Referral Case`. + +## 2. Werkbegrippen + +De Engelse namen zijn werktermen en worden pas na domeinreview in de +begrippenlijst vastgesteld. + +### Referral Request — `referral_request` + +Het inhoudelijke verzoek om zorg of beoordeling voor één of meer +samenhangende geuite zorgvragen. Het verzoek kan afkomstig zijn van een +professional, organisatie, cliënt of vertegenwoordiger. Splitsing is pas +nodig wanneer zorgvragen afzonderlijk moeten worden beoordeeld. + +Een Referral Request is niet: + +- de technische of administratieve ontvangst van het verzoek; +- de documentenbundel; +- het interne beoordelingstraject; +- een bewijs dat de zorg is geaccepteerd; +- noodzakelijk een formele Nederlandse verwijzing. + +### Referral Submission — `referral_submission` + +Eén afzonderlijke binnenkomst bij de instelling, via één kanaal en op één +ontvangstmoment. Een submission bewaart wat daadwerkelijk is ontvangen, +inclusief bron, documenten en oorspronkelijke formulering. + +Een Referral Submission is niet: + +- automatisch een nieuw zorgverzoek; +- automatisch een nieuw aanmeldingstraject; +- een overschrijfbare actuele versie van eerder ontvangen informatie. + +### Referral Case — `referral_case` + +Het institutionele aanmeldingstraject waarin de instelling één samenhangende +zorgvraag beoordeelt. Een case kan informatie uit meerdere submissions +gebruiken. + +Een Referral Case is niet: + +- een klinische intake; +- een zorgepisode; +- een financieel traject; +- de zorgvraag of verwijzing zelf. + +### Professional Referral — `professional_referral` + +Voorlopige naam voor de formele Nederlandse **VERWIJZING**: een door een +toegestane verwijzer uitgegeven object met verwijzer, verwijsdatum, +geldigheid en eventuele verwijsspecifieke gegevens. + +De formele verwijzing heeft een eigen identiteit en wordt gekoppeld aan het +bredere Referral Request. Een zelfaanmelding kan zo al worden beoordeeld en, +bij spoed of crisis, tot zorg leiden voordat een formele verwijzing is +ontvangen. De latere verwijzing verandert de identiteit en herkomst van het +oorspronkelijke zorgverzoek niet. + +### Care Acceptance Decision — `care_acceptance_decision` + +Het formele besluit waarin de instelling de beoordeling van een Referral Case +afrondt. Het besluit draagt zelf de onderliggende beoordelingen — inhoudelijke +match, beschikbare plek en capaciteit — samen met de besluituitkomst, de +vervolgroute en, bij afwijzing, de onderbouwing. Er bestaat geen los, +voorafgaand screeningsadvies: het screeningsbesluit **is** het +acceptatiebesluit (bevestigd in domeinreview 19 juli 2026). Een positief +besluit geeft toegang tot de zorginhoudelijke intake; het is niet het latere +behandeladvies. + +Screening blijft wel bestaan als de verzameling activiteiten (contact, +onderzoek, informatie-uitvraag) waarmee de beoordelingsfeiten tot stand +komen — alleen de uitkomst ervan wordt niet meer als apart, voorlopig advies +vastgelegd. + +Acceptatie bewijst niet automatisch: + +- een behandelovereenkomst; +- behandeltoestemming; +- zorgplicht; +- toewijzing van een behandelaar; +- organisatorische of klinische verantwoordelijkheid. + +## 3. Elementaire feitzinnen + +### 3.1 Zorgverzoek + +R1. *Zorgverzoek R-101 betreft persoon Jan de Vries.* + +R2. *Zorgverzoek R-101 is op 1 juli 2026 geïnitieerd.* + +R3. *Zorgverzoek R-101 is geïnitieerd door huisarts P. Pietersen.* + +R4. *Zorgverzoek R-101 vraagt om beoordeling voor gespecialiseerde GGZ.* + +R5. *Bij zorgverzoek R-101 is als zorgvraag geuit: “somberheidsklachten en +slaapproblemen”.* + +R6. *Professionele verwijzing V-111 betreft zorgverzoek R-101.* + +R7. *Professionele verwijzing V-111 is afgegeven op 1 juli 2026.* + +R8. *Professionele verwijzing V-111 is afgegeven door huisarts P. Pietersen.* + +R9. *Professionele verwijzing V-111 vermeldt echelon “gespecialiseerde +GGZ”.* + +R10. *Professionele verwijzing V-111 vermeldt als vermoeden “depressieve +stoornis”.* + +R11. *Zorgverzoek R-102 is op 14 juli 2026 geïnitieerd door Jan de Vries +voor zichzelf.* + +R12. *Zorgverzoek R-102 vraagt om beoordeling voor dezelfde geuite zorgvraag +als zorgverzoek R-101.* + +**Besloten in domeinreview:** + +- Eén request mag meerdere samenhangende zorgvragen bevatten. +- Zorgvragen worden gesplitst over requests wanneer ze afzonderlijk moeten + worden beoordeeld. +- Een formele verwijzing is een zelfstandig object dat aan een request wordt + gekoppeld. +- Het ontbreken van een formele verwijzing blokkeert zorg niet generiek; bij + spoed of crisis kan zorg al worden geleverd voordat de verwijzing is + ontvangen. + +**Beantwoord in domeinreview (19 juli 2026):** + +- Een request kan zonder bekende persoon bestaan. Dit komt voor bij + crisisaanmeldingen waarbij de identiteit nog niet vaststaat. Een + placeholder- of "John Doe"-persoon die later moet worden om-gehangen is + bewust geen oplossing — dat vereist het achteraf verplaatsen van alle + gekoppelde feiten naar de echte persoon, wat tegen append-only ingaat. In + plaats daarvan is `person_id` optioneel en wordt de koppeling vastgelegd + als een eigen, gedateerd en toegeschreven feit zodra de identiteit bekend + wordt. + +**Te toetsen:** + +- Is “dezelfde geuite zorgvraag” een expliciete relatie tussen verzoeken, of + wordt samenhang uitsluitend binnen de Referral Case beoordeeld? + +### 3.2 Afzonderlijke binnenkomst + +S1. *Submission S-201 is op 14 juli 2026 om 09:14 ontvangen door instelling +Triqura GGZ.* + +S2. *Submission S-201 is ontvangen via ZorgDomein.* + +S3. *Submission S-201 is verzonden door huisarts P. Pietersen.* + +S4. *Submission S-201 bevat document D-301 van type verwijsbrief.* + +S5. *Submission S-201 representeert zorgverzoek R-101.* + +S6. *Submission S-202 is op 16 juli 2026 om 11:32 ontvangen via beveiligde +e-mail.* + +S7. *Submission S-202 is verzonden door huisarts P. Pietersen.* + +S8. *Submission S-202 bevat document D-302 van type diagnostische +aanvulling.* + +S9. *Submission S-202 vult zorgverzoek R-101 aan.* + +S10. *Submission S-203 is op 16 juli 2026 om 15:05 telefonisch vastgelegd +door medewerker K. Jansen.* + +S11. *De informatiebron van submission S-203 is Jan de Vries.* + +S12. *Submission S-203 representeert zorgverzoek R-102.* + +S13. *Submission S-204 is op 17 juli 2026 als duplicaat van submission S-201 +beoordeeld.* + +S14. *Medewerker K. Jansen heeft de duplicaatbeoordeling op 17 juli 2026 +vastgelegd met reden “identieke ZorgDomein-berichtidentificatie”.* + +**Besloten in domeinreview:** + +- Eén submission kan meerdere inhoudelijke requests bevatten of + representeren. +- Ieder request krijgt een expliciete koppeling met de submission; de + oorspronkelijke binnenkomst wordt niet administratief gekopieerd of + gesplitst. + +**Te toetsen:** + +- Kan één request via meerdere submissions worden ontvangen? +- Is “vult aan” een directe relatie met een request, met een eerdere + submission, of met beide? +- Is een telefonisch geregistreerde binnenkomst altijd een submission? + +### 3.3 Aanmeldingstraject en toewijzing + +C1. *Referral Case C-401 is op 14 juli 2026 geopend voor Jan de Vries.* + +C2. *Referral Case C-401 beoordeelt de samenhangende zorgvraag +“somberheidsklachten en slaapproblemen”.* + +C3. *Submission S-201 is op 14 juli 2026 toegewezen aan Referral Case C-401.* + +C4. *Medewerker K. Jansen heeft submission S-201 aan Referral Case C-401 +toegewezen.* + +C5. *De reden voor de toewijzing van submission S-201 is “eerste binnenkomst +voor deze zorgvraag”.* + +C6. *Submission S-202 is op 16 juli 2026 toegewezen aan Referral Case C-401.* + +C7. *Submission S-203 is op 17 juli 2026 toegewezen aan Referral Case C-401.* + +C8. *Zorgverzoek R-101 wordt sinds 14 juli 2026 behandeld in Referral Case +C-401.* + +C9. *Zorgverzoek R-102 wordt sinds 17 juli 2026 behandeld in Referral Case +C-401.* + +C10. *Referral Case C-402 is op 17 juli 2026 als overlappend met Referral +Case C-401 beoordeeld.* + +C11. *Medewerker K. Jansen heeft op 17 juli 2026 Referral Case C-401 als +leidend aangewezen ten opzichte van Referral Case C-402.* + +C12. *De reden voor de leidende aanwijzing is “dezelfde zorgvraag, tweede +case onbedoeld geopend”.* + +C13. *Referral Case C-402 behoudt zijn identiteit en historie na de leidende +aanwijzing.* + +**Besloten in domeinreview:** + +- Eén request kan uitzonderlijk in meerdere Referral Cases worden behandeld + wanneer verschillende proces- of wettelijke kaders dat vereisen. +- Iedere parallelle behandeling krijgt een expliciete reden en volledige + tijdgebonden historie. + +**Te toetsen:** + +- Hoort een Referral Case bij precies één cliënt, of bij een persoon die pas + tijdens de beoordeling cliënt wordt? +- Moet een toewijzing een geldigheidsperiode hebben, of volstaan + toewijzings- en correctiegebeurtenissen? +- Is “leidend” voldoende, of zijn aparte relaties nodig voor duplicaat, + overlap, splitsing en consolidatie? + +### 3.4 Screeningsactiviteiten + +G1. *Screening G-501 wordt uitgevoerd binnen Referral Case C-401.* + +G2. *Screeningsactiviteit G-502 is op 15 juli 2026 uitgevoerd binnen +screening G-501.* + +G3. *Screeningsactiviteit G-502 is van type telefonisch contact.* + +G4. *Medewerker K. Jansen heeft screeningsactiviteit G-502 uitgevoerd.* + +Screening G-501 leidt niet tot een apart screeningsadvies. De activiteiten +binnen de screening leveren de informatie waarop de beoordelingsfeiten in +§3.5 worden vastgesteld, als onderdeel van hetzelfde acceptatiebesluit. + +**Besloten in domeinreview:** + +- Screening toetst of de instelling een match ziet met de zorgvraag. +- Screening toetst daarnaast beschikbare plek, capaciteit en inhoudelijke + passendheid. +- Tijdens screening kan beperkt contact met de cliënt of verwijzer + plaatsvinden. +- Screeningscontact is meestal niet gefinancierd, maar kan dat bij + uitzondering wel zijn. +- Het screeningsbesluit **is** het acceptatiebesluit; er is geen los + tussenliggend advies (sessielog 19 juli 2026 §5). +- Een positief besluit geeft toegang tot een zorginhoudelijke intake. Contact + binnen de intake is doorgaans wel gefinancierd. +- Uit de intake volgt een afzonderlijk behandeladvies; de mogelijke + uitkomsten daarvan zijn nog niet vastgesteld (nader onderzoek, zie §7). + +### 3.5 Beoordeling en acceptatiebesluit + +**Route 1 — inhoudelijke match en capaciteit beschikbaar** + +A1. *Beoordeling van Referral Case C-401 bevestigt op 17 juli 2026 een +inhoudelijke match met de geuite zorgvraag.* + +A2. *Beoordeling van Referral Case C-401 bevestigt op 17 juli 2026 +beschikbare capaciteit bij zorgprogramma Stemming.* + +A3. *Acceptatiebesluit A-701 betreft Referral Case C-401.* + +A4. *Acceptatiebesluit A-701 is genomen op 17 juli 2026 door medewerker +K. Jansen.* + +A5. *Medewerker K. Jansen handelde bij acceptatiebesluit A-701 op grond van +beslisbevoegdheid B-801.* + +A6. *Acceptatiebesluit A-701 heeft besluituitkomst “geaccepteerd”.* + +A7. *Acceptatiebesluit A-701 heeft vervolgroute “intake direct plannen” bij +zorgprogramma Stemming.* + +A8. *Zorgepisode E-901 is ontstaan uit acceptatiebesluit A-701.* + +A9. *Zorgepisode E-901 is gestart op 17 juli 2026.* + +A10. *Referral Case C-401 is na acceptatiebesluit A-701 afgesloten met +uitkomst “geaccepteerd”.* + +**Route 2A — inhoudelijke match, geen capaciteit: accepteren en wachten** + +A11. *Beoordeling van Referral Case C-403 bevestigt op 20 juli 2026 een +inhoudelijke match met de geuite zorgvraag.* + +A12. *Beoordeling van Referral Case C-403 constateert op 20 juli 2026 geen +beschikbare capaciteit bij zorgprogramma Trauma.* + +A13. *Acceptatiebesluit A-711 betreft Referral Case C-403.* + +A14. *Acceptatiebesluit A-711 heeft besluituitkomst “geaccepteerd”.* + +A15. *Acceptatiebesluit A-711 heeft vervolgroute “intakewachtlijst” bij +zorgprogramma Trauma.* + +A16. *Zorgepisode E-902 is ontstaan uit acceptatiebesluit A-711, ook al is de +intake nog niet gestart.* + +**Route 2B — inhoudelijke match, geen capaciteit: afwijzen** + +A17. *Beoordeling van Referral Case C-404 bevestigt op 21 juli 2026 een +inhoudelijke match met de geuite zorgvraag.* + +A18. *Beoordeling van Referral Case C-404 constateert op 21 juli 2026 geen +beschikbare capaciteit bij zorgprogramma Trauma.* + +A19. *Acceptatiebesluit A-721 betreft Referral Case C-404.* + +A20. *Acceptatiebesluit A-721 heeft besluituitkomst “afgewezen”.* + +A21. *Acceptatiebesluit A-721 motiveert de afwijzing met “geen capaciteit bij +zorgprogramma Trauma”.* + +A22. *Acceptatiebesluit A-721 heeft vervolgroute “doorverwijzen”.* + +A23. *Referral Case C-404 is na acceptatiebesluit A-721 afgesloten met +uitkomst “afgewezen”; er ontstaat geen zorgepisode.* + +Route 2A en 2B tonen dat capaciteit invoer is voor het besluit, maar de +uitkomst niet automatisch bepaalt (sessielog 19 juli 2026 §6). Bij afwijzing +is een concrete onderbouwing verplicht; “geen capaciteit” of “geen +acceptatie” zonder verdere duiding is onvoldoende, omdat dat laatste alleen +de uitkomst herhaalt. + +**Route 3 — heroverweging na afwijzing** + +A24. *Beoordeling van Referral Case C-405 constateert op 17 juli 2026 geen +inhoudelijke match met de geuite zorgvraag.* + +A25. *Acceptatiebesluit A-731 betreft Referral Case C-405.* + +A26. *Acceptatiebesluit A-731 heeft besluituitkomst “afgewezen”, motivering +“onvoldoende inhoudelijke match”.* + +A27. *Referral Case C-405 is na acceptatiebesluit A-731 afgesloten met +uitkomst “afgewezen”.* + +A28. *Bij Referral Case C-405 is op 24 juli 2026 een informatieverzoek +vastgelegd door medewerker K. Jansen, met gevraagde informatie “aanvullende +diagnostische onderbouwing”.* + +A29. *Submission S-210 is op 26 juli 2026 ontvangen als reactie op het +informatieverzoek van 24 juli 2026 en representeert het zorgverzoek van +Referral Case C-405.* + +A30. *Beoordeling van de heroverweging van Referral Case C-405 bevestigt op +27 juli 2026 alsnog een inhoudelijke match, op basis van submission S-210.* + +A31. *Acceptatiebesluit A-751 betreft Referral Case C-405, heeft +besluituitkomst “geaccepteerd” en vervangt acceptatiebesluit A-731.* + +A32. *De reden voor vervanging van acceptatiebesluit A-731 door A-751 is +“aanvullende diagnostiek toont alsnog inhoudelijke match aan”.* + +A33. *Zorgepisode E-905 is ontstaan uit acceptatiebesluit A-751.* + +Acceptatiebesluit A-731 blijft ongewijzigd bewaard; A-751 draagt de +expliciete vervangt-relatie en de reden. Wie het geldige besluit van +Referral Case C-405 wil weten, volgt de vervangt-keten naar het laatste, +niet-vervangen besluit — dat hoeft niet impliciet uit datums te worden +afgeleid (domeinreview 19 juli 2026, vraag 3). + +**Route 4 — cliënt trekt in vóór start intake** + +A34. *Referral Case C-406 is op 10 augustus 2026 afgesloten met +acceptatiebesluit A-760, uitkomst “geaccepteerd”, vervolgroute +“intakewachtlijst”.* + +A35. *Zorgepisode E-910 is ontstaan uit acceptatiebesluit A-760.* + +A36. *Referral Case C-406 is op 15 augustus 2026, vóór start van de intake, +door de cliënt ingetrokken; vastgelegd door medewerker K. Jansen.* + +A37. *Zorgepisode E-910 is op 15 augustus 2026 afgesloten met reden +“ingetrokken door cliënt vóór start intake”.* + +Cliënt-intrekking is geen Care Acceptance Decision en vereist geen +beslisbevoegdheid — het is een registratie van de cliëntkeuze, niet een +inhoudelijke herbeoordeling. Een cliënt kan een case op elk moment intrekken, +niet uitsluitend ná een positief besluit. + +**Route 5 — institutionele correctie vóór start intake** + +A38. *Referral Case C-407 is op 12 augustus 2026 afgesloten met +acceptatiebesluit A-762, uitkomst “geaccepteerd”, vervolgroute +“intakewachtlijst”.* + +A39. *Zorgepisode E-911 is ontstaan uit acceptatiebesluit A-762.* + +A40. *Acceptatiebesluit A-763 betreft Referral Case C-407, heeft +besluituitkomst “afgewezen” en vervangt acceptatiebesluit A-762, met reden +“capaciteitsinschatting bleek bij nader inzien onjuist”.* + +A41. *Medewerker M. de Boer handelde bij acceptatiebesluit A-763 op grond van +dezelfde beslisbevoegdheid als bij een regulier acceptatiebesluit.* + +A42. *Zorgepisode E-911 is op 16 augustus 2026 afgesloten met reden +“acceptatiebesluit herzien vóór start intake”.* + +Een institutionele correctie is wél een Care Acceptance Decision — hetzelfde +vervangt-mechanisme als Route 3, nu in omgekeerde richting — en vereist +dezelfde bevoegdheid als het oorspronkelijke besluit. In beide routes 4 en 5 +verdwijnt de al ontstane zorgepisode niet: zij wordt afgesloten met een +passende reden, net als iedere andere episode-afsluiting (B3). + +**Beantwoord in domeinreview (19 juli 2026):** + +- Besluituitkomst is beperkt tot `geaccepteerd` / `afgewezen`. `Doorverwijzen` + en het afsluiten van de case zijn vervolgroutes, geen besluituitkomsten. +- Elk positief besluit laat een zorgepisode ontstaan, ook wanneer de + vervolgroute intakewachtlijst is — niet pas bij de feitelijke start van de + intake. + +**Nog te toetsen:** + +- Is `aanvullende_informatie_nodig` een open processtatus vóór een definitief + besluit, of toch een derde besluituitkomst? Voorlopige aanname: open + processtatus, geen besluituitkomst (sessielog 19 juli 2026 §7). +- Kan een case meerdere opeenvolgende besluiten hebben na heroverweging? +- Welk besluit is dan geldig en hoe wordt intrekking of correctie vastgelegd? +- Kan een geaccepteerde case vóór de feitelijke start van de intake alsnog + worden ingetrokken, en wat gebeurt dan met de al ontstane zorgepisode? + +### 3.6 Zorginhoudelijke intake en behandeladvies + +I1. *Klinische intake I-1001 is gestart na acceptatiebesluit A-701.* + +I2. *Intakecontact I-1002 is op 20 juli 2026 uitgevoerd binnen klinische +intake I-1001.* + +I3. *Intakecontact I-1002 is een gefinancierd contact.* + +I4. *Klinische intake I-1001 heeft geleid tot behandeladvies B-1101.* + +I5. *Behandeladvies B-1101 adviseert behandeling binnen zorgprogramma +Stemming.* + +De financiering van een concreet contact is een afzonderlijk feit. Het type +fase bepaalt niet zonder uitzondering of een contact declarabel is. + +## 4. Voorlopige uniqueness- en optionaliteitsregels + +Deze regels zijn hypotheses voor domeinreview, geen definitieve +cardinaliteiten. + +### Referral Request + +- Elk request heeft één stabiele identiteit. +- Een request kan zonder bekende persoon bestaan (crisisaanmelding met nog + onbekende identiteit). Zodra de persoon bekend is, betreft het request + precies één persoon of cliënt; de koppeling zelf is een gedateerd, + toegeschreven feit, geen stille invulling van een placeholder (domeinreview + 19 juli 2026). +- Elk request heeft minstens één initiator. +- Elk request bevat één of meer samenhangende geuite zorgvragen. +- Zorgvragen die afzonderlijk moeten worden beoordeeld horen in afzonderlijke + requests. +- Een request kan zonder formele verwijzing bestaan. +- Een formele verwijzing heeft een eigen identiteit en wordt gekoppeld aan + het request waarop zij betrekking heeft. +- Acceptatie of zorgverlening vereist niet generiek dat de formele verwijzing + al is ontvangen; toepasselijke juridische en financiële gevolgen worden + afzonderlijk door protocolregels bewaakt. +- Een request kan door meerdere submissions worden gerepresenteerd of + aangevuld. +- Een request kan alleen bij uitzondering aan meer dan één case worden + gekoppeld, vanwege verschillende proces- of wettelijke kaders en met een + expliciete reden en tijdgebonden historie. + +### Referral Submission + +- Elke submission heeft één stabiele identiteit. +- Elke submission heeft precies één ontvangstmoment en één ontvangstkanaal. +- Elke submission heeft minstens één informatiebron of afzender, eventueel + “onbekend”. +- Een submission kan nul documenten bevatten, bijvoorbeeld bij telefoon. +- Een submission kan nul, één of meerdere requests representeren; iedere + bekende relatie wordt expliciet vastgelegd. +- Een submission wordt nooit overschreven door een latere aanvulling. +- De koppeling van een submission aan een case is historiseerbaar en wordt + door een mens bevestigd. + +### Referral Case + +- Elke case heeft één stabiele identiteit. +- Elke case behandelt één institutioneel als samenhangend beoordeelde + zorgvraag. +- Een case kan door meerdere submissions en requests worden gevoed. +- Parallelle cases zijn toegestaan met een expliciete reden. +- Logische consolidatie verwijdert geen case, request of submission. +- Een case heeft geen vaste, eenrichtings-statusmachine. De actuele stand + wordt afgeleid uit een tijdlijn van gebeurtenissen (toewijzingen, + informatieverzoeken, acceptatiebesluiten), niet uit één overschrijfbaar + statusveld (domeinreview 19 juli 2026 — zie ook B7 bij wachtlijst en de + non-lineaire aanmelding-status uit discovery §5.1 #5). +- Een informatieverzoek is een gebeurtenis: wie heeft wanneer welke + aanvullende informatie gevraagd. Deze gebeurtenis kan zich op elk moment + voordoen, ook ná een eerder acceptatiebesluit tijdens een heroverweging — + niet uitsluitend vóór het eerste besluit. +- Een afgesloten case kan bij heroverweging een nieuw acceptatiebesluit + krijgen; hoe het eerdere besluit dan gehistoriseerd wordt (vervangen versus + aanvullend) is nog niet vastgesteld (zie §7 vraag 3). + +### Care Acceptance Decision + +- Elk besluit betreft precies één Referral Case. +- Elk besluit heeft precies één beslisser en besluitdatum. +- De beslisser moet op het beslismoment de vereiste bevoegdheid hebben. +- Elk besluit heeft precies één besluituitkomst: `geaccepteerd` of + `afgewezen`. +- De onderliggende beoordelingen (inhoudelijke match, beschikbare plek, + capaciteit) zijn losse feiten bij hetzelfde besluit, geen aparte + voorafgaande beslissing. +- Elk besluit heeft precies één vervolgroute, bijvoorbeeld intake direct + plannen, intakewachtlijst, spoedroute, doorverwijzen of case afsluiten. +- Bij besluituitkomst `afgewezen` is een concrete onderbouwing verplicht; + het herhalen van de uitkomst (“geen acceptatie”) is geen geldige + onderbouwing. +- Capaciteit is invoer voor het besluit maar bepaalt de besluituitkomst niet + automatisch: bij ontbrekende capaciteit zijn zowel `geaccepteerd` met + vervolgroute intakewachtlijst als `afgewezen` met onderbouwing + capaciteitstekort geldige uitkomsten. +- Alleen een besluit met uitkomst `geaccepteerd` kan een zorgepisode laten + ontstaan, ongeacht de gekozen vervolgroute. +- Een besluit kan optioneel precies één eerder besluit van dezelfde case + vervangen, met een verplichte reden voor vervanging (heroverweging, + domeinreview 19 juli 2026, vraag 3). Het vervangen besluit blijft + ongewijzigd bewaard; vervanging is een expliciete relatie, geen mutatie. +- Het geldige besluit van een case is het laatste besluit in de + vervangt-keten; eerdere besluiten blijven raadpleegbaar maar zijn niet meer + bepalend voor de actuele stand. +- Cliënt-intrekking van een Referral Case is geen Care Acceptance Decision: + het vereist geen beslisbevoegdheid en kan op elk moment plaatsvinden, + inclusief ná een positief besluit (domeinreview 19 juli 2026, vraag 4). +- Een institutionele correctie van een eerder positief besluit ná ontstaan + van de zorgepisode volgt hetzelfde vervangt-mechanisme als heroverweging + (Route 3) en vereist dezelfde beslisbevoegdheid als het oorspronkelijke + besluit. +- Een reeds ontstane zorgepisode wordt bij intrekking of correctie nooit + verwijderd of ongedaan gemaakt; zij wordt afgesloten met een passende + reden, net als elke andere episode-afsluiting. +- Een zorgepisode verwijst naar het acceptatiebesluit waaruit zij ontstond. +- Een besluit bewijst geen behandeltoestemming of verantwoordelijkheid. +- Het behandeladvies na de intake is een afzonderlijk klinisch feit en geen + herhaling van het acceptatiebesluit; welke vervolgrichtingen het + behandeladvies kent en wie daartoe bevoegd is, is nog niet vastgesteld + (nader onderzoek, zie §7). + +## 5. Scenariotoets + +### Scenario 1 — huisartsverwijzing + +Benodigde objecten: + +- één Referral Request; +- één zelfstandig Professional Referral, gekoppeld aan het request; +- één Referral Submission; +- één Referral Case; +- na positief besluit één Care Acceptance Decision en één Clinical Care + Episode. + +Dit scenario toont dat verwijsdatum en ontvangstdatum verschillende feiten +zijn. + +### Scenario 2 — zelfaanmelding + +Benodigde objecten: + +- één Referral Request, geïnitieerd door de cliënt; +- één Referral Submission, bijvoorbeeld telefonisch of via het portaal; +- één Referral Case; +- geen Professional Referral. + +Dit scenario toont dat `Referral Request` breder moet zijn dan de Nederlandse +VERWIJZING. + +### Scenario 3 — spoed of crisis vóór formele verwijzing + +Benodigde objecten: + +- één Referral Request, bijvoorbeeld geïnitieerd door de cliënt, crisisdienst + of andere informatiebron; +- minstens één Referral Submission; +- één Referral Case; +- eventueel een positief Care Acceptance Decision en Clinical Care Episode + voordat een Professional Referral is ontvangen; +- een later ontvangen Professional Referral met een eigen Submission en een + koppeling aan hetzelfde Referral Request. + +Dit scenario toont dat een formele verwijzing geen bestaansvoorwaarde is voor +het request, de case of generiek voor zorgverlening. Financiële en juridische +vereisten blijven afzonderlijke protocolregels. + +### Scenario 4 — aanvullende informatie + +Benodigde objecten: + +- hetzelfde Referral Request; +- een nieuwe Referral Submission met eigen bron, tijdstip en documenten; +- dezelfde Referral Case, na menselijke toewijzing. + +Dit scenario toont waarom request, submission en case niet tot één record +kunnen worden samengevoegd. + +### Scenario 5 — dubbele of overlappende instroom + +Benodigde objecten: + +- oorspronkelijke requests en submissions blijven behouden; +- een mens beoordeelt duplicaat of overlap; +- submissions en requests worden aan de passende case gekoppeld; +- bij twee cases wordt één case eventueel als leidend aangewezen; +- de andere case wordt niet verwijderd of technisch samengevoegd. + +Dit scenario toont dat samenvoegen een expliciete, geaudite relatie is. + +### Scenario 6 — capaciteitstekort bij inhoudelijke match + +Benodigde objecten: + +- twee vergelijkbare Referral Cases met bevestigde inhoudelijke match en + bevestigd capaciteitstekort bij hetzelfde zorgprogramma; +- Route 2A: één Care Acceptance Decision met uitkomst `geaccepteerd` en + vervolgroute intakewachtlijst, gevolgd door een Clinical Care Episode die + al ontstaat vóór de feitelijke start van de intake; +- Route 2B: één Care Acceptance Decision met uitkomst `afgewezen`, een + concrete capaciteitsonderbouwing en vervolgroute doorverwijzen, zonder dat + een Clinical Care Episode ontstaat. + +Dit scenario toont dat capaciteit een beoordelingsfeit is en geen +automatische bepaler van de besluituitkomst, en dat "geaccepteerd" niet +gelijkstaat aan "intake gestart". + +## 6. Voorlopige conclusie over Referral Request + +`Referral Request` heeft naar verwachting een eigen identiteit nodig. + +Zonder dit object ontstaan twee problemen: + +1. meerdere submissions van hetzelfde zorgverzoek zijn alleen via vrije + interpretatie aan elkaar te relateren; +2. een formele verwijzing en een zelfaanmelding moeten kunstmatig in één + Nederlands begrip VERWIJZING worden gedwongen. + +De Nederlandse **VERWIJZING** valt daarom niet volledig samen met Referral +Request. Zij is een zelfstandig, professioneel geïnitieerd object met eigen +feiten, zoals verwijzer, verwijsdatum, echelon en geldigheid, dat aan het +zorgverzoek wordt gekoppeld. + +Hierdoor kan het zorgverzoek al bestaan en kan in spoed- of crisissituaties +zorg worden geleverd voordat de formele verwijzing binnenkomt. De latere +verwijzing vult de formele en financiële onderbouwing aan, maar herschrijft +het oorspronkelijke request niet. + +Of één request achtereenvolgens meerdere formele verwijzingen kan hebben, +bijvoorbeeld bij correctie of vervanging, wordt pas in het logische model +uitgewerkt. De append-only audit voorkomt ondertussen informatieverlies. + +## 7. Eerste domeinreview + +**Beantwoord (domeinreview 19 juli 2026):** + +- **Vraag 1.** Screeningsadvies en acceptatie zijn in de praktijk hetzelfde + vastgelegde besluit. Dit is verwerkt in §2–§5 hierboven: er is geen apart, + voorafgaand screeningsadvies meer, en het besluit draagt zelf de + onderliggende beoordelingen, de besluituitkomst, de vervolgroute en, bij + afwijzing, de onderbouwing. +- **Vraag 5.** `aanvullende_informatie_nodig` is geen besluituitkomst en geen + fase in een vaste statusmachine. Er bestaat op dat moment nog geen Care + Acceptance Decision — een besluit vereist een bevoegde beslisser die de + zaak daadwerkelijk afrondt, en dat is hier per definitie nog niet gebeurd. + In plaats daarvan is een informatieverzoek een eigen, gedateerd en + toegeschreven gebeurtenisfeit (vergelijkbaar met een screeningsactiviteit), + en wordt de actuele stand van een Referral Case afgeleid uit de tijdlijn + van gebeurtenissen in plaats van uit één overschrijfbaar statusveld. Dit + sluit aan bij eerdere keuzes om de GGZ-praktijk niet in een te lineair + proces te dwingen (B7 wachtlijst, non-lineaire aanmelding-status discovery + §5.1 #5) en is verwerkt in §4 hierboven. +- **Vraag 3.** Bij heroverweging krijgt een afgesloten case een nieuw + acceptatiebesluit. Het eerdere besluit blijft ongewijzigd bewaard en het + nieuwe besluit krijgt een expliciete "vervangt"-relatie met verplichte + reden — geen impliciete "laatste besluit is geldig"-regel op basis van + datum. Dit komt vaak genoeg voor in de praktijk om de extra structuur te + rechtvaardigen. Verwerkt in §3.5 (Route 3) en §4 hierboven. +- **Vraag 4.** Een geaccepteerde case kan vóór start van de intake worden + teruggedraaid, op twee manieren: cliënt-intrekking (geen besluit, geen + bevoegdheidseis, kan op elk moment) of institutionele correctie (een + vervangend Care Acceptance Decision, zelfde bevoegdheid als het + oorspronkelijke besluit). De al ontstane zorgepisode wordt in beide + gevallen afgesloten met een passende reden, nooit verwijderd. Verwerkt in + §3.5 (Route 4 en 5) en §4 hierboven. + +**Nog open, nader onderzoek nodig:** + +- **Vraag 2.** Welke mogelijke uitkomsten heeft het behandeladvies na de + intake, en wie is bevoegd die vast te stellen? Vastgesteld is alleen dat + het behandeladvies zelf al de vervolgrichting geeft — net als het + acceptatiebesluit is er geen apart, voorafgaand advies. De concrete + vervolgrichtingen en de bijbehorende bevoegdheid zijn geparkeerd (sessielog + 19 juli 2026), en raakt dit feitenmodel niet meer — die uitkomst hangt aan + de intake, niet aan de Referral Case. + +Vraag 2 vergt apart onderzoek (zie besluitenlog §10) en blokkeert de +volgende stappen niet langer. Nu volgen: + +1. definitieve uniqueness- en optionaliteitsconstraints (§4 hierboven is een + tussenstand, nog niet definitief bevestigd); +2. de statusmachine van Referral Case en Care Acceptance Decision; +3. afleiding van entiteiten en cardinaliteiten; +4. verwerking in `../besluiten/besluitenlog-datamodel-2026-07-18.md`; +5. synchronisatie van `../begrippenlijst-kernmodel.md` en + `../deelmodellen/model-aanmelding.md`. diff --git a/docs/datamodel/feitenmodellen/feitenmodel-intake-behandeladvies.md b/docs/datamodel/feitenmodellen/feitenmodel-intake-behandeladvies.md new file mode 100644 index 0000000..78730c0 --- /dev/null +++ b/docs/datamodel/feitenmodellen/feitenmodel-intake-behandeladvies.md @@ -0,0 +1,486 @@ +# Feitenmodel intake en behandeladvies — FCO-IM-ronde + +**Status:** concept voor domeinreview, geen logisch model +**Datum:** 19 juli 2026 +**Scope:** zorginhoudelijke intake, intakecontact, kindcheck, en het behandeladvies waarmee de intake wordt afgerond +**Autoriteit:** `../besluiten/besluitenlog-datamodel-2026-07-18.md` blijft leidend +**Aanleiding:** sessiedoel "ronde de feitenronde van intake en behandeladvies af"; vervolg op de instroom-feitenronde (`feitenmodel-instroom.md`) en op `feitenmodel-instroom.md` §7 vraag 2 (behandeladvies-uitkomsten, tot nu toe geparkeerd) +**Bronmateriaal:** het oudere `../deelmodellen/model-intake.md` bevat al een grondig doordachte, LKS-onderbouwde intake-uitkomst-structuur — deze ronde herbruikt die inhoud, corrigeert de inmiddels achterhaalde episode-timing-aanname, hernoemt naar het Engelstalige werkbegrippenkader (B20) en lost de opengebleven vraag over het behandeladvies op + +## 1. Doel van deze ronde + +Deze ronde formuleert elementaire feiten voor het vervolg ná een positief +acceptatiebesluit: de zorginhoudelijke intake, de contacten en onderzoeken +daarbinnen, de kindcheck, en de afronding van de intake met een +behandeladvies. Net als bij instroom komen eerst de feiten, dan pas +entiteiten en cardinaliteiten. + +**Centrale correctie ten opzichte van het oudere `../deelmodellen/model-intake.md`:** dat +document ging nog uit van "episode ontstaat bij aanmelding-uitkomst +`intake`". Dat is met B22 (19 juli 2026) achterhaald — de zorgepisode +ontstaat al bij het acceptatiebesluit zelf, óók bij vervolgroute +intakewachtlijst. De intake start dus niet gelijktijdig met het ontstaan van +de episode, maar op enig moment daarna, mogelijk na een periode op de +intakewachtlijst. + +**Centrale ontwerpkeuze deze ronde:** het behandeladvies wordt, net als het +eerdere acceptatiebesluit (B21), **geen apart, voorafgaand advies-object** +zonder eigen gewicht — maar wél een eigen entiteit los van de intake zelf, +naar het patroon van Care Acceptance Decision. De intake is het +onderzoekstraject (proces-container); het behandeladvies is het object dat +de intake afrondt en de vervolgrichting draagt, met dezelfde +vervangt-mogelijkheid bij heroverweging die bij het acceptatiebesluit al is +vastgesteld (B23). Dit vervangt het oudere voorstel waarin uitkomst en +advies platte velden op INTAKE zelf waren. + +## 2. Werkbegrippen + +De Engelse namen zijn werktermen en worden pas na domeinreview in de +begrippenlijst vastgesteld. Waar een werkterm al in +`../begrippenlijst-kernmodel.md` §4 staat, wordt die hergebruikt. + +### Clinical Intake Assessment — `clinical_intake_assessment` + +Een dynamisch klinisch onderzoekstraject voor één samenhangende zorgvraag, +met meerdere contacten, onderzoeken, disciplines en bevindingen. Hangt aan +de Clinical Care Episode, niet direct aan de Referral Case. + +Een Clinical Intake Assessment is niet: +- één gesprek of contact; +- de screening (die hoort bij instroom, vóór acceptatie); +- FHIR `Encounter`; +- automatisch gestart op het moment dat de episode ontstaat — er kan tijd + tussen zitten (intakewachtlijst). + +### Intake Contact — `intake_contact` + +Eén feitelijk contact (gesprek, telefonisch contact, huisbezoek, aanvullend +onderzoek, beeldcontact) binnen een Clinical Intake Assessment. + +Een Intake Contact is niet: de intake zelf, of automatisch een declarabel +consult — dat is een latere, aparte ZPM-consultregistratie die hiernaar kan +verwijzen. + +### Child Safety Check — `child_safety_check` + +De wettelijk verankerde kindcheck (Wet verplichte meldcode huiselijk geweld +en kindermishandeling; KNMG-meldcode) bij de intake van een volwassen +cliënt: is de cliënt verantwoordelijk voor minderjarigen, zijn er zorgen +over hun veiligheid, en is daarop actie ondernomen. + +Een Child Safety Check is niet: het volledige meldcode-stappenplan (dat is +proces-state, TIP-terrein) — alleen de klinische feiten (de vlaggen en +toelichtingen) horen in het ECD. + +### Treatment Advice — `treatment_advice` + +Het object dat een Clinical Intake Assessment afrondt: draagt de uitkomst +(bijvoorbeeld in zorg, terug- of doorverwijzing, aanvullende diagnostiek +nodig), het geadviseerde zorgprogramma, en de inhoudelijke toelichting. Kan +een eerder behandeladvies van dezelfde intake vervangen bij heroverweging, +met dezelfde vervangt-constructie als Care Acceptance Decision (B23). + +Een Treatment Advice is niet: +- een apart, voorafgaand advies naast een later formeel besluit — het + advies zelf ís de vervolgrichting, net als bij het acceptatiebesluit + (B21-patroon toegepast); +- het acceptatiebesluit (dat gaat over toegang tot het zorgproces, dit gaat + over de inhoudelijke richting na onderzoek); +- een behandelplan — het advies wijst een richting, het behandelplan werkt + die vervolgens uit. + +## 3. Elementaire feitzinnen + +### 3.1 Intake start en verloop + +N1. *Clinical Intake Assessment N-1201 is op 18 juli 2026 aangemaakt voor +Zorgepisode E-901, met aanleiding "regulier".* + +N2. *Clinical Intake Assessment N-1201 heeft status "gepland" sinds 18 juli +2026.* + +N3. *Clinical Intake Assessment N-1201 heeft status "bezig" sinds 22 juli +2026.* + +N4. *Clinical Intake Assessment N-1201 is toegewezen aan afdeling +"Volwassenen".* + +N5. *Binnen dezelfde Zorgepisode E-901 is op 1 oktober 2026 een tweede +Clinical Intake Assessment N-1205 gestart met aanleiding "intern", op +afdeling "Ouderen".* + +N6. *Voor Zorgepisode E-903 is op 2 augustus 2026 een Clinical Intake +Assessment N-1210 gestart met aanleiding "crisis", zonder voorafgaande +screening.* + +N7. *Clinical Intake Assessment N-1201 is op 15 augustus 2026 heropend en +heeft weer status "bezig", nadat zij eerder was afgerond.* + +N8. *Clinical Intake Assessment N-1215 is op 25 juli 2026 afgebroken met +reden "cliënt trekt zich terug".* + +**Besloten in domeinreview (hergebruikt van `../deelmodellen/model-intake.md`, tijdlijn +gecorrigeerd):** + +- Eén Clinical Intake Assessment per samenhangend onderzoekstraject; een + interne overgang (afdelingswissel) vormt geen nieuwe intake. +- Een episode kan meerdere intakes hebben: regulier, intern (bijvoorbeeld + overgang naar een andere afdeling binnen dezelfde episode), en crisis. +- Screening vóór intake blijft een nudge, geen harde eis — ook niet nu de + intake pas na het acceptatiebesluit start; crisis-instroom kan intake en + screening (bijna) gelijktijdig laten plaatsvinden. +- Heropening (afgerond/afgebroken → bezig) is toegestaan, zelfde + flexibiliteitsprincipe als bij de Referral Case (B25) — geen + eenrichtingsstatus. + +**Gecorrigeerd ten opzichte van `../deelmodellen/model-intake.md`:** + +- De intake start niet "op basis van de aanmelding-uitkomst intake", maar + ergens ná het acceptatiebesluit, aan de Clinical Care Episode. Tussen het + ontstaan van de episode en de feitelijke start van de intake kan een + intakewachtlijst-periode zitten (Route 2A, `feitenmodel-instroom.md`). + Status "gepland" dekt precies dat interval. + +### 3.2 Intakecontact + +N9. *Intake Contact N-1301 is op 22 juli 2026 uitgevoerd binnen Clinical +Intake Assessment N-1201, van type "intakegesprek", door psycholoog +M. de Boer, met toelichting "eerste gesprek, anamnese afgenomen".* + +N10. *Intake Contact N-1301 had een geplande duur van 60 minuten.* + +N11. *Intake Contact N-1302 is op 24 juli 2026 uitgevoerd binnen Clinical +Intake Assessment N-1201, van type "aanvullend onderzoek".* + +N12. *Intake Contact N-1303 is op 26 juli 2026 uitgevoerd binnen Clinical +Intake Assessment N-1201, van type "beeldcontact".* + +**Besloten in domeinreview:** + +- Meerdere contacten van hetzelfde type op dezelfde dag zijn legitiem (geen + uniciteitsbeperking). +- Contacttype-waardelijst bevat in elk geval: intakegesprek, aanvullend + onderzoek, telefonisch contact, huisbezoek, beeldcontact, overig. + Beeldcontact is toegevoegd ten opzichte van het oorspronkelijke + prototype — gangbare GGZ-contactvorm en ZPM-relevant. +- Een Intake Contact hangt in deze ronde exclusief aan één Clinical Intake + Assessment. Generalisatie naar een bredere consult-/afspraakstructuur + (agenda, behandelcontacten) is uitdrukkelijk een latere ronde. + +### 3.3 Kindcheck + +N13. *Child Safety Check N-1401 is op 22 juli 2026 uitgevoerd door +M. de Boer binnen Clinical Intake Assessment N-1201: cliënt is +verantwoordelijk voor minderjarige kinderen: nee.* + +N14. *Child Safety Check N-1402 is op 3 augustus 2026 uitgevoerd binnen +Clinical Intake Assessment N-1205: verantwoordelijk voor 2 minderjarige +kinderen (leeftijden "4 en 7"); zorgen over veiligheid: ja, met toelichting +"moeder oververmoeid, geen netwerk"; actie ondernomen: ja, met toelichting +"adviesvraag Veilig Thuis, 3 augustus".* + +N15. *Child Safety Check N-1402 registreert daarnaast: zwangerschap van de +cliënt of partner: nee.* + +**Besloten in domeinreview (hergebruikt van `../deelmodellen/model-intake.md`):** + +- Drie ja/nee-vlaggen plus verplichte toelichting bij "ja" blijft de kern — + bevestigd prototypepatroon. +- Vlag 1 is verbreed van "thuiswonende kinderen" naar "verantwoordelijk voor + minderjarigen" (KNMG-meldcode: ook co-ouderschap en andere zorgrelaties + tellen). +- Een vierde vlag "zwangerschap van cliënt of partner" wordt toegevoegd — + de KNMG-kindcheck rekent het ongeboren kind expliciet mee. *(Algemene + domeinkennis, niet uit de eerdere onderzoeksrapporten — bij twijfel apart + te verifiëren tegen de meldcode-tekst.)* +- Uitvoerder en datum zijn verplicht (provenance); de namen van eventuele + kinderen worden bewust niet als aparte persoonsrecords vastgelegd + (dataminimalisatie) — alleen aantal en leeftijden als vrije tekst. +- Maximaal één Child Safety Check per Clinical Intake Assessment; een nieuwe + intake binnen dezelfde episode krijgt een eigen, nieuwe kindcheck (de + situatie kan gewijzigd zijn). + +### 3.4 Behandeladvies (afronding van de intake) + +**Route 1 — in zorg** + +N16. *Clinical Intake Assessment N-1201 heeft op 30 juli 2026 geleid tot +Treatment Advice N-1501.* + +N17. *Treatment Advice N-1501 heeft uitkomst "in_zorg", geadviseerd +zorgprogramma "Algemeen GGZ".* + +N18. *Treatment Advice N-1501 is vastgesteld door M. de Boer, handelend als +regiebehandelaar van Zorgepisode E-901.* + +N19. *Clinical Intake Assessment N-1201 is op 30 juli 2026 afgerond met +Treatment Advice N-1501.* + +**Route 2 — doorverwijzing, gevolgd door terugverwijzing (LKS-volgorde)** + +N20. *Treatment Advice N-1503 heeft uitkomst "doorverwijzing", met +toelichting "beter passend bij aanbieder X, specialisatie trauma".* + +N20a. *Aanbieder X laat op 10 augustus 2026 weten geen plek te hebben.* + +N20b. *Treatment Advice N-1502 heeft uitkomst "terugverwijzing", met +toelichting "geen doorverwijzing gelukt; advies aan huisarts: begeleiding +POH-GGZ", en vervangt Treatment Advice N-1503.* + +Dit toont de LKS-volgorde (§3.6.2/patient journey fase 2-3): eerst een +inspanningsverplichting tot doorverwijzing naar een beter passende +aanbieder; pas als dat niets oplevert, of de cliënt komt niet in aanmerking +voor GGZ, terugverwijzing naar de verwijzer met advies. Dit zijn dus geen +twee gelijkwaardige, onafhankelijke uitkomsten maar een volgtijdelijk paar — +het vervangt-mechanisme (Route 5 hieronder) dekt deze opvolging al. + +**Route 3 — aanvullende diagnostiek** + +N22. *Treatment Advice N-1504 heeft uitkomst "extra_diagnostiek", met +toelichting "vermoeden persoonlijkheidsproblematiek, aanvullend +psychodiagnostisch onderzoek nodig".* + +**Route 4 — heroverweging van het behandeladvies** + +N23. *Treatment Advice N-1505 betreft Clinical Intake Assessment N-1210, +heeft uitkomst "extra_diagnostiek".* + +N24. *Op 20 augustus 2026 blijkt, na afronding van de aanvullende +diagnostiek, dat Treatment Advice N-1506 nodig is: uitkomst "in_zorg", +geadviseerd zorgprogramma "Trauma", en vervangt Treatment Advice N-1505.* + +N25. *De reden voor vervanging van Treatment Advice N-1505 door N-1506 is +"aanvullende diagnostiek afgerond, duidelijk beeld voor zorgprogramma +Trauma".* + +**Besloten in domeinreview, verscherpt door gericht LKS 4.0-onderzoek (19 +juli 2026 — volledige teksttoetsing, niet alleen samenvattingen):** + +- Behandeladvies is geen apart, voorafgaand advies naast een later + besluit — het draagt zelf de uitkomst, het richting-advies + (zorgprogramma) en de toelichting, analoog aan Care Acceptance Decision + (B21-patroon). +- Vier uitkomstwaarden blijven het startpunt: `in_zorg`, `terugverwijzing`, + `doorverwijzing`, `extra_diagnostiek` — maar niet als vier gelijkwaardige + alternatieven. + - **`doorverwijzing` en `terugverwijzing` zijn LKS-genormeerd en + volgtijdelijk, geen onafhankelijke keuzes** (patient journey fase 2-3, + §3.6.2): eerst een inspanningsverplichting tot doorverwijzing naar een + beter passende aanbieder; pas als dat niets oplevert (of de cliënt komt + niet in aanmerking voor GGZ), volgt terugverwijzing naar de verwijzer + met advies. Als waardelijst-codes blijven het twee losse waarden; de + volgorde wordt gedekt door het vervangt-mechanisme (zie Route 2 + hierboven), niet door een aparte sequentie-regel. + - **`extra_diagnostiek` is een praktijkgefundeerde toevoeging, geen + LKS-genormeerde uitkomst** — nergens in LKS 4.0 als zodanig benoemd. + Blijft behouden (de praktijk kent dit als reële afrondingsuitkomst), + maar expliciet gemarkeerd als zodanig, niet als landelijke norm + voorgesteld. +- `Afgewezen` is bewust geen behandeladvies-uitkomst — afwijzen gebeurt al + bij het acceptatiebesluit (instroom); wie de intake bereikt, wordt niet + meer "afgewezen". +- **Gedeeld/onduidelijk advies bij twijfel tussen behandelaren krijgt geen + eigen uitkomstwaarde.** LKS 4.0 §3.6.2 lost dit institutioneel op: bij + verschil van inzicht bepaalt de zorgaanbieder wie de doorslaggevende stem + heeft (vaak vastgelegd in het professioneel statuut) — een + escalatiemechanisme vóór afronding, geen apart feit in dit model. +- **Cliënt die afziet van het geadviseerde vervolg is een later, apart + feit, geen intake-uitkomstwaarde** — analoog aan hoe planakkoord al los + van het behandelplan wordt vastgelegd (B13). LKS 4.0 gaat uit van + overeenstemming tussen regiebehandelaar en cliënt als vertrekpunt van de + uitkomst zelf; een latere weigering hoort dus bij een vervolgfeit, niet + bij `Treatment Advice`. +- Vervolgroute bij wachtlijst voor behandelcapaciteit is geen aparte + uitkomst: uitkomst `in_zorg` kan gevolgd worden door plaatsing op een + behandelwachtlijst (apart wachtlijst-deelgebied), net zoals capaciteit bij + het acceptatiebesluit invoer is maar de besluituitkomst niet automatisch + bepaalt (Route 2A/2B-precedent). LKS 4.0 biedt hier geen aanknopingspunt, + dus dit blijft consistentie-redenering, geen LKS-citaat. + +**Bevoegdheid — LKS 4.0 maakt dit onderscheid expliciet zelf, verscherpt +ten opzichte van de eerdere aanname van een simpele FK-placeholder:** + +- LKS 4.0 onderscheidt uitdrukkelijk "wie de intake verricht" van wie + bevoegd is de uitkomst vast te stellen (§2, fase 2) — dat kán dezelfde + persoon zijn, hoeft niet. De **regiebehandelaar in de indicerende rol** + is verantwoordelijk voor het (doen) vaststellen van de uitkomst + (§3.6.2). Bij vrijgevestigden (sectie II, §4.3) is dit altijd dezelfde + persoon als de intakebehandelaar; bij instellingen (sectie III) kan dat + uiteenlopen. +- **MDT-bespreking is voor een deel van de settings een dwingende + landelijke norm, geen instellingsbeleid.** Voor settings 3 t/m 8 + (multidisciplinair ambulant, outreachend, klinisch, forensisch, + hoogspecialistisch) schrijft LKS 4.0 Tabel 2 dwingend voor dat een + art. 14 BIG-beroep betrokken is bij diagnostiek/indicatiestelling **en** + bespreking plaatsvindt in het multidisciplinair team (MDT), met de + betrokken discipline als lid. Voor setting 2 (monodisciplinair ambulant) + is een MDT niet verplicht — direct contact, een MDT, of bilaterale + afstemming volstaan. Bij sectie II (vrijgevestigd) bestaat geen MDT, + alleen een professioneel netwerk voor advies/consultatie. +- **Modelconsequentie:** bevoegdheid is niet één simpele FK naar een + persoon, maar minimaal een rol-attribuut (indicerende regiebehandelaar, + eventueel afwijkend van de intakebehandelaar) plus een optioneel, + settingafhankelijk MDT-feit — vergelijkbaar met hoe bij het + acceptatiebesluit beslisbevoegdheid al los van het besluit zelf is + gehouden (`decision_authority`). De exacte bevoegdheidsmatrix per setting + blijft een detail voor de rollenronde/bevoegdheidsmatrix (B16); wat hier + al vaststaat is dát het model ruimte moet bieden voor een optioneel + MDT-feit naast de indicerende regiebehandelaar, niet uitsluitend een + enkele beslisser. + +## 4. Voorlopige uniqueness- en optionaliteitsregels + +Deze regels zijn hypotheses voor domeinreview, geen definitieve +cardinaliteiten. + +### Clinical Intake Assessment + +- Elke intake heeft één stabiele identiteit en hangt aan precies één + Clinical Care Episode. +- Een episode kan nul, één of meerdere intakes hebben (regulier, intern, + crisis); "maximaal één lopende intake per episode" is een aan/uit-zetbare + bedrijfsregel, geen schemabeperking — een crisisintake kan naast een + lopende reguliere intake nodig zijn. +- Elke intake heeft een aanleiding (regulier/intern/crisis) en een + afdeling. +- Statussen: gepland → bezig → afgerond/afgebroken, met heropening + toegestaan vanuit afgerond of afgebroken. Niet toegestaan: rechtstreeks + gepland → afgerond (afronden zonder geregistreerd contact), of + afgerond ↔ afgebroken rechtstreeks. +- Bij status afgerond hoort precies één (het laatst geldige, niet-vervangen) + Treatment Advice; bij status afgebroken hoort een afbreekreden, geen + Treatment Advice. + +### Intake Contact + +- Elk contact hoort bij precies één Clinical Intake Assessment. +- Geen uniciteitsbeperking op type/datum-combinaties. + +### Child Safety Check + +- Maximaal één per Clinical Intake Assessment. +- Uitvoerder en uitvoeringsdatum verplicht; per vlag is de toelichting + verplicht zodra die vlag "ja" is. + +### Treatment Advice + +- Elk advies hoort bij precies één Clinical Intake Assessment. +- Elk advies heeft precies één uitkomst, één vaststeller en één + vaststellingsdatum. +- Optioneel precies één eerder advies van dezelfde intake vervangen, met + verplichte reden — zelfde constructie als Care Acceptance Decision + (uniek per vervangen exemplaar, voorkomt vertakking, onbeperkte + kettingdiepte). +- Geadviseerd zorgprogramma is verplicht bij uitkomst `in_zorg`, optioneel + anders. +- Toelichting is verplicht bij `terugverwijzing`, `doorverwijzing` en + `extra_diagnostiek` (een ontvangende partij of vervolgstap moet + navolgbaar zijn); optioneel bij `in_zorg`. +- Elk advies heeft precies één indicerende regiebehandelaar (kan dezelfde + persoon zijn als de intakebehandelaar, hoeft niet — LKS 4.0 §2/§3.6.2). + Optioneel, settingafhankelijk, een MDT-bespreking als apart feit + (deelnemers, datum) — voor settings 3 t/m 8 een verplicht feit, voor + setting 2 optioneel, voor sectie II (vrijgevestigd) niet van toepassing. + De exacte verplichtingsregel per setting is een detail voor de + bevoegdheidsronde (B16), niet voor deze feitenronde. + +## 5. Scenariotoets + +### Scenario 1 — reguliere intake, in zorg + +Benodigde objecten: één Clinical Intake Assessment (aanleiding regulier), +minstens één Intake Contact, één Child Safety Check, één Treatment Advice +met uitkomst `in_zorg`. + +Dit scenario toont de hoofdroute: acceptatie → (eventueel intakewachtlijst) +→ intake → behandeladvies. + +### Scenario 2 — crisisintake zonder voorafgaande screening + +Benodigde objecten: één Clinical Intake Assessment met aanleiding `crisis`, +gekoppeld aan een episode die is ontstaan uit een acceptatiebesluit of een +Wvggz-mandaat (`feitenmodel-instroom.md` §3.5 Route 5/B27), zonder dat +daaraan een screeningsactiviteit voorafging. + +Dit scenario toont dat screening vóór intake een nudge blijft, ook nu de +volgorde acceptatie-vóór-intake vaststaat. + +### Scenario 3 — aanvullende diagnostiek en heroverweging + +Benodigde objecten: één Clinical Intake Assessment, een eerste Treatment +Advice met uitkomst `extra_diagnostiek`, aanvullende Intake Contacten, en +een tweede Treatment Advice dat het eerste vervangt met uitkomst `in_zorg`. + +Dit scenario toont dat het vervangt-patroon uit de instroomronde (B23) +identiek toepasbaar is op het behandeladvies. + +### Scenario 4 — interne intake binnen een lopende episode + +Benodigde objecten: dezelfde Clinical Care Episode, een tweede Clinical +Intake Assessment met aanleiding `intern` op een andere afdeling, eigen +Intake Contacten en een eigen Treatment Advice. + +Dit scenario toont dat een episode meerdere, onafhankelijke intakes met elk +hun eigen behandeladvies kan hebben. + +### Scenario 5 — kindcheck met positieve vlaggen + +Benodigde objecten: één Clinical Intake Assessment, één Child Safety Check +met vlag "verantwoordelijk voor minderjarigen" = ja, verplichte toelichting, +vlag "zorgen over veiligheid" = ja met verplichte toelichting, vlag "actie +ondernomen" = ja met verplichte toelichting. + +Dit scenario toetst de conditionele verplichting van de toelichtingsvelden. + +## 6. Domeinreview + +**Beantwoord (domeinreview + gericht LKS 4.0-onderzoek, 19 juli 2026):** + +- Episode-timing gecorrigeerd: intake start ná het acceptatiebesluit, niet + gelijktijdig met een aanmelding-uitkomst die niet meer bestaat. +- Behandeladvies-uitkomsten (`feitenmodel-instroom.md` §7 vraag 2, tot nu + toe geparkeerd): vier uitkomsten bevestigd (`in_zorg`, `terugverwijzing`, + `doorverwijzing`, `extra_diagnostiek`), hergebruikt uit het al bestaande + voorstel in `../deelmodellen/model-intake.md`, met een correctie na + volledige LKS 4.0-teksttoetsing: `doorverwijzing`/`terugverwijzing` zijn + LKS-genormeerd maar volgtijdelijk (eerst doorverwijzen proberen), niet + onafhankelijk; `extra_diagnostiek` is praktijkgefundeerd, niet + LKS-genormeerd — beide nuances zijn nu expliciet in §3.4 vastgelegd. +- Behandeladvies is, net als het acceptatiebesluit, geen apart voorafgaand + advies — het object zelf geeft de vervolgrichting, met een vervangt- + mechanisme voor heroverweging. +- Bevoegdheid: LKS 4.0 maakt zelf onderscheid tussen wie de intake verricht + en wie de uitkomst vaststelt (regiebehandelaar, indicerende rol). Voor + settings 3–8 is MDT-bespreking een dwingende landelijke norm, niet + instellingsbeleid; voor setting 2 optioneel; voor vrijgevestigden niet + van toepassing. Verwerkt als rol-attribuut plus optioneel MDT-feit in §4. + +**Nog open:** + +1. Exacte bevoegdheidsmatrix per setting (welke settings precies wanneer + MDT-bespreking vereisen, en de rolinvulling daarvan) — deel van de + bredere bevoegdheidsronde (B16); het principe (rol + optioneel MDT-feit) + staat al vast, de detailinvulling niet. +2. Is `extra_diagnostiek` altijd een volledig nieuw, vervangend Treatment + Advice, of kan de bestaande intake simpelweg langer "bezig" blijven + zonder tussentijdse afronding? Beide patronen zijn nu toegestaan (zie + §3.1 N7 heropening); welke de praktijk het vaakst gebruikt is nog niet + getoetst. +3. Vierde kindcheck-vlag (zwangerschap) is toegevoegd op basis van + algemene domeinkennis, niet uit de eerder verzamelde onderzoeksrapporten + — te verifiëren tegen de actuele meldcode-tekst. +4. Contactmoment-generalisatie naar een bredere consult-/afspraakstructuur + blijft uitdrukkelijk een latere (agenda-)ronde. + +## 7. Volgende stap + +Na bevestiging van bovenstaande volgen: + +1. definitieve uniqueness- en optionaliteitsconstraints; +2. afleiding van entiteiten en cardinaliteiten (`../deelmodellen/model-intake.md` her­zien + naar Engelse namen en de gecorrigeerde episode-relatie, analoog aan hoe + `../deelmodellen/model-instroom.md` uit `feitenmodel-instroom.md` is afgeleid); +3. verwerking in `../besluiten/besluitenlog-datamodel-2026-07-18.md`; +4. synchronisatie van `../begrippenlijst-kernmodel.md` (Treatment Advice, + Child Safety Check zijn nieuw; Clinical Intake Assessment/Intake Contact + bestonden al als werkterm) en `../deelmodellen/model-intake.md`. diff --git a/docs/datamodel/onderzoek/onderzoek-engelstalige-epd-terminologie.md b/docs/datamodel/onderzoek/onderzoek-engelstalige-epd-terminologie.md new file mode 100644 index 0000000..9f84cb1 --- /dev/null +++ b/docs/datamodel/onderzoek/onderzoek-engelstalige-epd-terminologie.md @@ -0,0 +1,319 @@ +# Onderzoek Engelstalige EPD-terminologie — referral en instroom + +**Status:** onderzoeksrapport, aanbeveling nog niet als begrippenbesluit verwerkt +**Datum:** 18 juli 2026 +**Aanleiding:** de werktermen `Inbound Care Signal` en `Access Assessment Case` uit `../begrippenlijst-kernmodel.md` zijn onvoldoende vanzelfsprekend voor Engelstalige ontwikkelaars en zorgprofessionals +**Scope:** Amerikaanse EHR’s, behavioral-health EHR’s, HL7 FHIR, Amerikaanse closed-loop referral guidance en NHS-terminologie + +## 1. Onderzoeksvraag + +Welke Engelse technische termen passen het beste bij: + +1. **AANMELDSIGNAAL** — iedere afzonderlijke binnenkomst met eigen bron, ontvangsttijd, documenten en oorspronkelijk geuite zorgvraag; +2. **AANMELDINGSTRAJECT** — de institutionele workflow waarin één samenhangende zorgvraag wordt beoordeeld, eventueel gevoed door meerdere binnenkomsten, vóór klinische intake en formele acceptatie? + +## 2. Hoofdconclusie + +De twijfel bij de huidige werktermen is terecht: + +- **`Inbound Care Signal` afwijzen.** `signal` is geen gangbare EHR-term voor een ontvangen aanmelding. In zorg-IT klinkt het eerder als klinisch signaal, alert of vroegsignaal. +- **`Access Assessment Case` afwijzen als canonieke naam.** De woorden zijn begrijpelijk, maar deze combinatie komt niet herkenbaar terug in de onderzochte EHR’s of standaarden. `access assessment` beschrijft eerder een activiteit binnen een workflow dan de beheerde workflow zelf. + +Amerikaanse EHR’s gebruiken vrijwel overal **referral** als overkoepelende term voor de beheerde instroomworkflow. Ze maken in publieke documentatie alleen niet altijd scherp onderscheid tussen: + +- het zorgverzoek; +- de ontvangen verzending of documentenbundel; +- de interne workflow die het verzoek behandelt. + +Dat onderscheid is voor Triqura wel nodig. Daarom is de sterkste oplossing geen simpele hernoeming, maar een model met drie begrippen: + +1. **Referral Request — `referral_request`** + Het semantische verzoek om zorg of beoordeling, geïnitieerd door professional, organisatie, cliënt of vertegenwoordiger. +2. **Referral Submission — `referral_submission`** + Eén afzonderlijke ontvangst of indiening, via bijvoorbeeld portaal, telefoonregistratie, e-mail, eFax of koppeling, met eigen bron en documenten. +3. **Referral Case — `referral_case`** + De door de ontvangende instelling beheerde workflow waarin submissions en requests worden beoordeeld, aangevuld, getrieerd en afgesloten. + +Voor de huidige Nederlandse begrippen betekent dit voorlopig: + +- **AANMELDSIGNAAL → `Referral Submission`** +- **AANMELDINGSTRAJECT → `Referral Case`** +- **VERWIJZING → waarschijnlijk `Referral Request`**, voor zover het de formele zorgvraag/verwijzing representeert + +De precieze grens tussen `Referral Request` en de bestaande Nederlandse entiteit VERWIJZING moet in de volgende feitenronde worden vastgesteld. + +## 3. Wat de leveranciers laten zien + +### 3.1 Epic + +Epic publiceert een eersteklas `REFERRAL`-object: + +- classificeert referrals als `Internal`, `Incoming` of `Outgoing`; +- kent statussen als `New Request`, `Open`, `Pending Review`, `Authorized`, `Denied`, `Closed` en `Incomplete`; +- bewaart bron, reden, prioriteit, ontvangstdatum en besluitdatum; +- kent triage-uitkomsten zoals `Accept`, `Reject`, `Redirect`, `Information Request`, `Respond` en `Convert`; +- heeft een afzonderlijke referralhistorie. + +Dit lijkt sterk op het Nederlandse AANMELDINGSTRAJECT: één beheerd object met lifecycle en triage. Epic noemt dit eenvoudigweg een **Referral**. + +Epic publiceert daarnaast `ServiceRequest (Referral)` als FHIR-projectie en een `Incoming Referral Notification` als uitwisselingsinterface. Dat bevestigt dat een bericht, verzoek en beheerd referralobject niet noodzakelijk hetzelfde zijn. + +**Les voor Triqura:** `referral` is herkenbaar voor de workflow, maar zonder suffix te ambigu wanneer ook request en submission afzonderlijk worden gemodelleerd. + +### 3.2 Oracle Health + +Oracle Health gebruikt eveneens **Referral** voor het beheerde object: + +- actieve en eerdere referrals; +- status; +- referring en referred providers; +- attachments; +- comments; +- activity timeline; +- gekoppelde afspraken; +- requested start date en service-by date. + +Een nieuwe referral begint in de UI als **Referral Order**. De order is het verzoek; de Referral Management-functionaliteit beheert vervolgens de lifecycle. + +**Les voor Triqura:** `Referral Order` is te smal voor zelfaanmelding en informele instroom. `Referral` past bij de ontvangende workflow. + +### 3.3 athenahealth + +athenahealth publiceert vooral: + +- `Referral Order`; +- `Referral/Auth`; +- attachments gekoppeld aan een referral authorization order; +- generieke `Patient Cases`. + +`Referral/Auth` vermengt verwijzing en verzekeringsautorisatie. `Patient Case` is te breed en in de publieke documentatie niet specifiek genoeg verbonden aan instroom. + +**Les voor Triqura:** `order`, `authorization` en een ongekwalificeerde `patient_case` passen niet als generieke kernnamen. + +### 3.4 Netsmart — behavioral health + +Netsmart is voor de GGZ-context bijzonder relevant. Referral Manager: + +- verzamelt **incoming referrals** uit onder andere eFax en PDF-upload; +- organiseert referral packets, tijdlijnen, notities, communicatie, taken en wachtrijen rond één referral; +- ondersteunt acceptatie en afwijzing; +- spreekt expliciet over het **referral intake process**; +- zet een geaccepteerde referral door naar een afzonderlijke **pre-admit workflow** in het EHR. + +Netsmart gebruikt dus: + +- `incoming referral` voor de ontvangen instroom; +- `referral record`/`referral` als beheerd lifecycle-object; +- `referral packet` voor de documentenbundel; +- `referral intake` voor het proces; +- `pre-admit` pas na acceptatie. + +**Les voor Triqura:** referralterminologie past ook bij behavioral health. `intake` is daar echter breder en administratief gebruikt, terwijl Triqura `INTAKE` al reserveert voor het klinische onderzoekstraject. Daarom is `Referral Intake Case` voor Triqura onnodig verwarrend. + +### 3.5 NextGen en Qualifacts + +NextGen gebruikt vooral `Referral Order` binnen een encounter en een `Intake Template` voor gegevens binnen dat contact. `Case` heeft er een andere betekenis: het groepeert encounters voor juridische of verzekeringsdoeleinden. + +Qualifacts gebruikt publiek vooral `intake`, `case management` en configureerbare workflows, maar publiceert onvoldoende technisch detail om een zelfstandig referralobject betrouwbaar af te leiden. + +**Les voor Triqura:** `case` moet altijd worden gekwalificeerd als `referral_case`; een kaal `case` is te breed. + +## 4. Wat de standaarden laten zien + +### 4.1 HL7 FHIR + +FHIR maakt nuttige conceptuele scheidingen: + +- **`ServiceRequest`** — voorstel, plan of order om een dienst te laten uitvoeren; +- **`Task`** — administratieve coördinatie en voortgang van de uitvoering; +- **`EpisodeOfCare`** — periode waarin een organisatie verantwoordelijkheid draagt; +- **`Encounter`** — feitelijk contact of interactie. + +FHIR beschrijft een intakevoorbeeld waarin: + +1. een `ServiceRequest` wordt ontvangen; +2. eligibility en capaciteit worden beoordeeld; +3. een geplande `EpisodeOfCare` kan worden gemaakt; +4. verdere assessment volgt; +5. na acceptatie een plan ontstaat en de episode actief wordt. + +Triqura heeft bewust een andere semantische grens: de ZORGEPISODE ontstaat pas bij formele acceptatie en acceptatie impliceert nog niet automatisch alle behandelverantwoordelijkheid. Daarom moet het pre-acceptatietraject niet rechtstreeks `EpisodeOfCare` worden genoemd. + +Een Triqura Referral Submission kan bij uitwisseling bestaan uit meerdere FHIR-resources, bijvoorbeeld `ServiceRequest`, `DocumentReference`, `Communication` en provenance. Het lokale object hoeft dus geen FHIR-resourcenaam te dragen. + +### 4.2 Amerikaanse ONC/360X + +360X gebruikt: + +- `referral request`; +- `referral order`; +- een persistente `Referral Identifier`; +- een closed-loop `referral workflow`; +- statussen en terugkoppelingen tot de referral is gesloten. + +De Amerikaanse definitie van **Referral Order** is doorgaans provider-authored. Die term sluit zelfaanmelding en instroom vanuit niet-klinische bronnen onvoldoende in. + +**Les voor Triqura:** gebruik `request` in plaats van `order` voor het inhoudelijke verzoek, en modelleer de ontvangende workflow afzonderlijk. + +### 4.3 NHS + +De NHS definieert: + +- **Service Request** — verzoek om zorgdiensten; +- **Referral Request** — subtype voor een zorgdienst, inclusief expliciete zelfverwijzing; +- **Patient Pathway** — de bredere route vanaf het eerste ontvangen verzoek, mogelijk over langere tijd en meerdere behandelperioden. + +De NHS vermeldt bovendien dat een mondeling en later schriftelijk bevestigd verzoek normaliter als één referral wordt verwerkt. Dit ondersteunt het onderscheid tussen: + +- één semantisch request; +- meerdere ontvangsten of representaties van dat request. + +`Patient Pathway` is voor Triqura te breed: het kan chronische of recidiverende zorg en meerdere referral-to-treatment periods omvatten. + +## 5. Beoordeling van naamparen + +### Optie A — Referral Submission / Referral Case + +**Identifiers:** `referral_submission` / `referral_case` +**Advies:** voorkeur + +Sterke punten: + +- voor Amerikaanse developers direct herkenbaar als onderdeel van referral management; +- submission duidt één ontvangst aan; +- case duidt één beheerde workflow-instance aan; +- self-referral kan expliciet als referral request door de cliënt worden geïnitieerd; +- geen botsing met admission, encounter of EpisodeOfCare; +- laat ruimte voor een afzonderlijke Referral Request. + +Risico’s: + +- `Referral Submission` is een lokale technische term, geen universele EHR-standaardterm; +- `case` kan elders financieel of juridisch betekenen en moet daarom altijd als `referral_case` worden geschreven; +- de definitie moet expliciet maken dat referral ook self-referral en andere toegestane bronnen omvat. + +### Optie B — Care Access Submission / Care Access Case + +**Identifiers:** `care_access_submission` / `care_access_case` +**Advies:** inhoudelijk inclusief, maar minder idiomatisch + +Sterke punten: + +- neutraler voor zelfaanmelding en niet-formele bronnen; +- benadrukt toegang tot zorg in plaats van verwijzing door een professional. + +Risico’s: + +- veel minder herkenbaar in de onderzochte EHR-documentatie; +- `access` kan voor developers systeem-, dossier- of autorisatietoegang betekenen; +- het verband met referral management en closed-loop uitwisseling wordt minder zichtbaar. + +### Optie C — Referral Submission / Referral Intake Case + +**Identifiers:** `referral_submission` / `referral_intake_case` +**Advies:** niet kiezen voor Triqura + +Sterke punten: + +- zeer expliciet over de instroomfase; +- sluit aan bij behavioral-healthtaal als `referral intake process`. + +Risico’s: + +- Triqura kent al een afzonderlijke klinische INTAKE; +- Amerikaanse systemen gebruiken intake zowel administratief als klinisch; +- vergroot precies de begripsverwarring die het kernmodel probeert te voorkomen. + +### Optie D — Inbound Care Submission / Access Assessment Case + +**Identifiers:** `inbound_care_submission` / `access_assessment_case` +**Advies:** niet kiezen + +Sterke punten: + +- domeinneutraal; +- inhoudelijk te begrijpen met een definitie ernaast. + +Risico’s: + +- geen herkenbaar naamgebruik in de onderzochte EHR’s; +- te abstract en samengesteld; +- `assessment` klinkt als één activiteit; +- `inbound care` klinkt eerder als gegevenslogistiek dan als cliëntinstroom. + +## 6. Aanbevolen conceptueel model + +De terminologische onduidelijkheid komt mede doordat AANMELDSIGNAAL nu twee betekenissen bevat: de ontvangen verzending én het inhoudelijke zorgverzoek. + +De aanbevolen scheiding is: + +```text +Referral Request + het inhoudelijke verzoek om zorg of beoordeling + +Referral Submission + één ontvangen representatie of aanvulling van een request + +Referral Case + de ontvangende workflow die submissions en requests behandelt +``` + +Voorbeelden: + +- Een huisarts stuurt een verwijzing met brief: één Referral Request, ontvangen via één Referral Submission, behandeld in één Referral Case. +- De huisarts stuurt later aanvullende diagnostiek: dezelfde Referral Request en Referral Case, tweede Referral Submission. +- De cliënt belt daarnaast zelf met aanvullende informatie: derde Referral Submission bij dezelfde Referral Case. +- Twee instanties melden onafhankelijk dezelfde zorgvraag: twee requests of submissions kunnen na menselijke beoordeling aan één Referral Case worden gekoppeld. +- Een gelijktijdige crisisvraag en reguliere hulpvraag kunnen ieder een eigen Referral Case krijgen. + +De cardinaliteiten mogen nog niet uit deze voorbeelden worden afgeleid. Eerst moeten de elementaire feitzinnen worden gevalideerd. + +## 7. Aanbeveling voor de begrippenlijst + +Nog niet automatisch aanpassen; eerst inhoudelijk bevestigen: + +1. vervang `Inbound Care Signal` door **Referral Submission**; +2. vervang `Access Assessment Case` door **Referral Case**; +3. voeg **Referral Request** toe als afzonderlijk begrip; +4. herbeoordeel of de huidige VERWIJZING volledig samenvalt met Referral Request of slechts één subtype/bron daarvan is; +5. behoud **Clinical Intake Assessment** als afzonderlijk klinisch proces na of rond acceptatie; +6. behoud **Clinical Care Episode** voor de geaccepteerde zorgperiode. + +## 8. Betrouwbaarheid en beperkingen + +- Epic EHI-exporttabellen geven sterke aanwijzingen voor het logische exportmodel, maar zijn niet automatisch het operationele interne databaseschema. +- Oracle, athenahealth, Netsmart, NextGen en Qualifacts publiceren vooral product-, UI- en API-termen. +- FHIR is een uitwisselingsmodel en geen verplicht lokaal domeinmodel. +- Marketingdocumentatie is gebruikt om gangbaar taalgebruik te beoordelen, niet om cardinaliteiten of juridische betekenis af te leiden. +- Er bestaat geen universele EHR-term voor “iedere afzonderlijke binnenkomst ongeacht kanaal”. `Referral Submission` is daarom een expliciet gekozen lokale technische term. + +## 9. Bronnen + +### Amerikaanse EHR’s + +- Epic, `REFERRAL`: https://open.epic.com/EHITables/GetTable/REFERRAL.htm +- Epic, `REFERRAL_2` triage decision: https://open.epic.com/EHITables/GetTable/REFERRAL_2.htm +- Epic, `REFERRAL_HIST`: https://open.epic.com/EHITables/GetTable/REFERRAL_HIST.htm +- Epic, FHIR interfaces: https://open.epic.com/Interface/FHIR +- Oracle Health, Referrals: https://docs.oracle.com/en/industries/health/oracle-health-ehr/ehrfg/referrals.html +- Oracle Health, Create Referral: https://docs.oracle.com/en/industries/health/oracle-health-ehr/ehrfg/create-referral.html +- athenahealth, Referral Authorization: https://docs.athenahealth.com/api/api-ref/referral-authorization +- athenahealth, Patient Cases: https://docs.athenahealth.com/api/api-ref/document-type-patient-case +- Netsmart, Referral Management: https://www.ntst.com/carefabric/population-health-management/referral-management +- Netsmart, referral intake workflow: https://www.ntst.com/blog/2025/optimizing-human-services-intake-with-ai-and-automation +- NextGen, Referrals: https://docs.nextgen.com/en-US/nextgenc2ae-adaptive-content-engine-help-enterprise-8-3-1-3288208/add-referrals-251359 +- NextGen, Cases: https://docs.nextgen.com/en-US/nextgenc2ae-enterprise-ehr-help-3270157/cases-in-nextgenc2ae-enterprise-ehr-324977 +- Qualifacts, InSync EHR: https://www.qualifacts.com/about/insync-ehr-platform/ + +### Standaarden en guidance + +- HL7 FHIR ServiceRequest: https://hl7.org/fhir/R5/servicerequest.html +- HL7 FHIR Task: https://hl7.org/fhir/R5/task.html +- HL7 FHIR EpisodeOfCare: https://hl7.org/fhir/R5/episodeofcare.html +- HL7 FHIR Encounter: https://hl7.org/fhir/R5/encounter.html +- HL7 US SDOH Referral Management Task: https://hl7.org/fhir/us/sdoh-clinicalcare/STU2.3/StructureDefinition-SDOHCC-TaskForReferralManagement.html +- ONC 360X: https://oncprojectracking.healthit.gov/wiki/spaces/TechLab360X/pages/18776120/360X+Home +- US Health IT Referral Order: https://isp.healthit.gov/uscdi-data/referral-order +- NHS Service Request: https://www.datadictionary.nhs.uk/classes/service_request.html +- NHS Referral Request: https://archive.datadictionary.nhs.uk/DD%20Release%20May%202024/classes/referral_request.html +- NHS Patient Pathway: https://www.datadictionary.nhs.uk/classes/patient_pathway.html diff --git a/docs/datamodel/onderzoek/onderzoek-leveranciers.md b/docs/datamodel/onderzoek/onderzoek-leveranciers.md new file mode 100644 index 0000000..0e38ccc --- /dev/null +++ b/docs/datamodel/onderzoek/onderzoek-leveranciers.md @@ -0,0 +1,193 @@ +# Onderzoek: datamodellen en publieke API's van Nederlandse ECD/EPD-leveranciers + +**Status:** onderzoeksrapport t.b.v. datamodel-discovery (input, geen besluiten) +**Datum:** 18 juli 2026 +**Onderzoeker:** research-agent "leveranciers" +**Relatie tot discovery:** dit rapport toetst nergens besluiten uit `../deelmodellen/datamodel-discovery.md` terug — het levert vergelijkingsmateriaal per deelgebied (§5-besluiten) en markeert hooguit open vragen. + +--- + +## 1. Aanpak en betrouwbaarheid per bron + +Onderzocht via webresearch (juli 2026): Nedap Ons, Medicore, PinkRoccade Zorg (mijnCaress), Adapcare (Pluriform Zorg), en kort SDB/USER, ZilliZ en Praktijkdata. Betrouwbaarheid verschilt sterk: + +| Leverancier | Publieke API-docs | Diepgang van dit onderzoek | +|---|---|---| +| **Nedap Ons** | ✅ Volledig: [ons-api.nl](https://ons-api.nl/) + downloadbare OpenAPI-spec (2,8 MB, 772 paden) | Hoog — spec integraal geanalyseerd | +| **Medicore** | ❌ Alleen marketingpagina's over "Mint" API-platform | Laag — geen resource-niveau vindbaar | +| **PinkRoccade mijnCaress** | ❌ Geen publieke documentatie | Laag — alleen persberichten/partnerpagina's | +| **Adapcare Pluriform Zorg** | ❌ Geen publieke documentatie ("Care Connector") | Laag — alleen productpagina's | +| **SDB USER (ex-Avinty)** | ❌ Geen publieke documentatie ("Connect Service") | Laag | +| **ZilliZ / Praktijkdata** | ❌ Geen publieke documentatie | Zeer laag | + +**Belangrijkste methodische bevinding:** van de onderzochte leveranciers publiceert alleen **Nedap** zijn API-model openbaar. Al het onderstaande over de anderen is afgeleid uit marketingmateriaal en derden (integratiepartners) — dat is expliciet gemarkeerd. De Nedap-analyse is daarentegen gebaseerd op de letterlijke OpenAPI-specificatie ([ons-api.nl/assets/openapi.json](https://ons-api.nl/assets/openapi.json), opgehaald 18-07-2026). + +--- + +## 2. Nedap Ons — volledige OpenAPI-analyse + +Nedap Ons is primair een VVT/GHZ/GGZ-suite. De API ([ons-api.nl](https://ons-api.nl/)) is REST, zonder versienummers in URL's (resources worden 6 maanden vóór verwijdering gedeprecate), met certificaat-authenticatie en rate limiting ("4 requests tegelijkertijd per certificaat") — bron: [API-eigenschappen](https://ons-api.nl/techniek/apis/API_eigenschappen.html). De huidige API's vervingen op 19-11-2025 de oude per-applicatie-API's ([API-overzicht](https://ons-api.nl/techniek/apis/APIS.html)). + +### 2.1 Domeinindeling (tags in de spec) + +De spec groepeert ~250 resource-typen in modules. Relevant voor het ECD: + +| Module | Kern-resources | +|---|---| +| **administration** | Client, ClientAddress, Bsn, Agbcode, Referral, CareProvider, Practitioner (met AgbCode/BigCode/Profession), ClientContactRelation(+Type), ClientEmployeeRelation(+Type), Location, LocationAssignment (incl. **WaitingList**), Insurance, Document, Team, ExpertiseProfile/ExpertiseGroup | +| **dossier** | Report, ReportType, ReportEntry, SoapReportEntry, CarePlan, CarePlanEntry, CarePlanAgreement, CarePlanSignatureRequirement, Goal/GoalEntry, Demand/DemandEntry, Domain, Action/ActionEntry, ClientNote, MedicalNote, SystemNote, Alert, RestrictiveMeasureRegistration (dwang/vrijheidsbeperking) | +| **dossier.episodes** | Episode, SubGoal, Action | +| **dossier.medical** | Problem, MedicalSummary, PropensityToAdverseReaction (allergieën), advance_directives, involuntary_care (LegalStatus, Incompetence — Wzd/Wvggz), **dsm.Classification / dsm.ClassificationSeries** | +| **dossier.omaha** | Omaha-classificatie (VVT-verpleegkundig: Problem, InterventionCategory, InterventionTarget) | +| **agenda** | AgendaSeries, AgendaOccurrence, ClientAbsenceOccurrence, Label, Unavailability, Reservation | +| **carepath** | CarePath, Template, ModuleTemplate, IntentTemplate (zorgpaden als sjablonen) | +| **dbc.ggz** | Zorgtraject, Subtraject (DBC's — legacy), DiagnoseToekenning | +| **zpm** | CareOrderZpmDetails, ZpmGbggzProfiel, ZpmZorglabel, ZpmSetting (Zorgprestatiemodel) | +| **client_story** | Story, Category, Answer ("levensverhaal" van de cliënt) | +| **survey** | Survey, SurveyResult (vragenlijsten/ROM) | +| **finance** | CareOrder, Invoice, DeclarationRule, FinanceType, VecozoInvoiceRelation | +| **openehr** | ArchetypeWrapper, CompositionWrapper — Nedap gebruikt intern deels openEHR-composities | +| overig | medication, moves (roosteren), payroll, wlz/wmo/jw (Zorglegitimatie per wet!), groupcare, tasque (zorgtaken) | + +**Observatie:** de wettelijke kaders zijn bij Nedap aparte modules met elk een eigen "Zorglegitimatie"-resource (`wlz.WlzZorglegitimatie`, `wmo.WmoZorglegitimatie`, `jw.JwZorglegitimatie`) plus `finance.FinanceType`. Het discovery-besluit "wettelijk kader hoort bij de aanmelding" (§5.0.4) is een schonere generalisatie van hetzelfde principe: financieringskader als expliciet gegeven, niet impliciet. + +### 2.2 Cliënt, persoon en verwijzer + +- **Client** is bij Nedap één platte entiteit (geen persoon/rol-splitsing zoals discovery-besluit §5.1 #1). BSN is een **aparte resource** (`/clients/{id}/bsn`, aparte autorisatie) — zelfde gedachte als spelregel 3.1 #6: BSN afgeschermd op need-to-know, los van het gewone cliëntrecord. +- Relaties zijn expliciet getypeerd: `ClientContactRelation` + `ClientContactRelationType` (waardelijst als resource!) en `ClientEmployeeRelation` + type. Contactpersonen hebben eigen adres-resources. +- **Referral** (verwijzing) heeft: `referrer`, `referrerType`, `referrerCategory`, `agbCode`, `institution`, `specialism`, `date`, `documentId`, `clientId`. Verwijzertype en -categorie zijn strings (waardelijsten), AGB-code en instelling zijn aparte velden, en het verwijs**document** is een losse gekoppelde entiteit. Dit spiegelt de discovery-besluiten §5.1 #2-#3 vrijwel één-op-één (verwijzer ≠ praktijk, AGB op beide, verwijsdocument getypeerd los). +- In de (legacy) DBC-flow zit `SoortVerwijzer` als NZa-waardelijst met `code`, `omschrijving`, `begindatum`, `einddatum` — waardelijst-items met **geldigheidsperioden**. Dat detail (waardelijstwaarden die in de tijd geldig zijn) zit nog niet in de discovery-spelregels en is een aandachtspunt voor de referentietabellen. + +### 2.3 Zorgepisodes en trajecten: twee gescheiden werelden + +Nedap kent **twee losstaande structuren** die beide "episode/traject" heten: + +1. **`dossier.episodes.Episode`** — klinisch-inhoudelijk: `clientId`, `startDate`, `endDate`, `evaluationDate`, `title`, `goal`, `subGoals[]`, gekoppelde `problemIds[]` (medische problemen), `surveyResults[]`, `documents[]`. Plus opvallend: **autorisatie op episode-niveau** (`authorizedExpertiseProfileIds`, `authorizedExpertiseGroupIds`) — wie de episode mag inzien is een eigenschap van de episode. +2. **`dbc.ggz.Zorgtraject`** — financieel/declaratie: `identificatienummer`, `begindatum`, `einddatum` ("Set automatically based on the state of the DBCs... If null, open; if non-null, closed"), `zorgdomein` (NZa-codelijst), `primaireDiagnose`, `subtrajecten[]` (DBC's, met regiebehandelaar-AGB, directe/indirecte minuten, grouper-resultaat/prestatiecode). Voor ZPM is er een aparte module (`zpm.*`) die aan een `CareOrder` hangt. + +**Relevantie voor discovery §5.1 #9 (ZORGEPISODE):** Nedap bevestigt de scheiding klinische episode ↔ declaratietraject als twee entiteiten die naar elkaar verwijzen, niet als één ding. Ook opvallend: rapportages verwijzen naar episodes via `episodeIds[]` — **many-to-many**, één verslag kan bij meerdere episodes horen. De discovery gaat nu impliciet uit van één episode per klinisch record; dit is een beargumenteerde open vraag voor de rapportage-ronde (geen bezwaar tegen genomen besluiten). + +**Diagnose in de DBC-flow:** `DiagnoseToekenning` heeft `primair` (boolean), datum, en een `diagnoseCode` uit een NZa-tabel die per code o.a. `icd9cm`, `icd10`, prestatiecode-mappings en `selecteerbaar`/`sorteervolgorde` bevat. De mapping diagnose→declaratiecode is dus **data in een codetabel**, precies het patroon van discovery-besluit §5.4 #1 (DSM-registratie met ICD-10-mapping via tabel). + +### 2.4 DSM-classificatie (GGZ-module) + +`dossier.medical.dsm.Classification` (binnen een `ClassificationSeries`): `classificationDate`, `beginDate`/`endDate`, `status` ("status of the classification phase" — vrije string, geen enum in de spec), `diagnosisDescription`, `findings`, `evaluations`, GAF-scores (`gafStart/gafCurrent/gafEnd/gafMax`), `immutable` (vergrendeld na vaststelling), `axis3weight`. Het bredere `dossier.medical.Problem` kent `certainty` (≈ werkdiagnose vs. definitief), `severity`, `course`, `onsetApproximateDate`/`onsetPeriodOfLife`, `resolvedApproximateDate`, `snomedExpressionValue` en wederom episode-koppeling + autorisatievelden. De as "zekerheid" (certainty) naast klinische status bevestigt het discovery-patroon van twee status-assen (§5.4 open vraag: verificatiestatus + klinische status) als gangbaar. + +### 2.5 Rapportagemodel + +`dossier.Report` is het centrale verslag-record: + +- **Typering:** `reportTypeId` verwijst naar `ReportType`, maar de spec documenteert een **hardcoded lijst**: 101 Rapportage, 102 Gewicht, 103 Bloeddruk, 104 Temperatuur, 105 Bloedsuiker, 106/107 Vocht in/uit, 108 Defecatie, 109 Medisch, 110 Voortgang, 111 Familie communicatie, 112 Keten communicatie, 309 Stemming, 310 Omaha, 311 Saturatie, 314 Pijnscore, 315 SOEP, 316 Bristol, 317 Klinimetrie. `ReportType` is wél een resource met `enabled`-vlag, maar de magic numbers staan letterlijk in de API-beschrijving — het anti-patroon dat spelregel 3.1 #1 (waardelijsten als data, één bron) wil voorkomen, hier zichtbaar bij een marktleider. +- **Metingen als rapportage:** gewicht, bloeddruk enz. zijn géén aparte observatie-entiteit maar rapportagetypen met `reportEntries[]` (naam/waarde/unit, bv. `systolicPressure, 120, mm[Hg]`). Genericiteit boven aparte tabellen per meetwaarde. +- **SOEP gestructureerd:** `soapReportEntries[]` met `soapPart` (Subjective/Objective/...), `content`, en **vertrouwelijkheid + autorisatie per SOEP-deel** (`confidential`, `expertiseProfileIds`). Een verslag is dus deels afschermbaar per discipline. +- **Koppelingen:** `carePlanEntryId` (rapporteren op zorgplan-regel/doel), `episodeIds[]`, `restrictiveMeasureId`, parent/child (reacties op rapportages), `reportActions[]` (leesbevestiging/actie door discipline X). +- **Auteurschap:** basisvelden uit `AuditedBase` (`createdAt/updatedAt/createdBy`) plus `ReportAuthorType`: `employee` / `caren` (mantelzorger via Caren-portaal!) / `external`. Familie kán rapporteren; het auteurstype is expliciet. Dit is een smallere variant van de provenance-spelregel 3.1 #2 (die met mens/AI verder gaat). +- **Status:** `ReportStatus` is minimaal: 0=OPENED / 1=CLOSED, alleen gebruikt voor communicatie-draden. Rapportages zelf kennen geen workflow-status — wel `flagged`, `confidential`, `hidden` + `hidingReason` (soft delete met reden). + +### 2.6 Zorgplan (behandelplan-equivalent) + +- `CarePlan`: `clientId`, `employeeId`, `beginDate`, `endDate`, `status` — enum **`DRAFT` / `ACTIVE` / `OLD`**. Versiebeheer werkt via nieuwe plannen (endpoints `by_client/active`, `by_client/draft`, `activate`, `archive`): het oude plan wordt `OLD`, er is er hooguit één `ACTIVE`. Dit is het "nieuw record per versie"-model uit de open vraag van discovery §5.5. +- Hiërarchie: `Domain` (leefgebied) → `Demand` (zorgvraag/probleem) → `Goal` → `Action`, elk met een "Entry"-variant (de concrete invulling in dít plan: `DemandEntry`, `GoalEntry`, `ActionEntry`) — sjabloon versus instantie gescheiden. `CarePlanEntry` bundelt demand+goal+acties en draagt `percentageTarget`/`percentageRealized`. +- **Cliëntakkoord is een eigen entiteit, geen status:** `CarePlanAgreement` (carePlanId, clientId, employeeId) plus `CarePlanSignatureRequirement` met `required` en `discussionType`: `discussed` / `discussed_later` / `no_discussion` / `no_agreement`. Dit beantwoordt de open discovery-vraag §5.5 ("is cliëntakkoord een status of los feit?") vanuit de praktijk: **los feit**, met aparte registratie of/hoe het besproken is. +- `carepath.CarePath` + `Template`: zorgpaden als sjablonen met modules, geïnstantieerd per cliënt (`activeModules`, start/einddatum) — vergelijkbaar met "zorgprogramma" als inhoudelijk aanbod (discovery §5.2 #2), maar dan geoperationaliseerd. + +### 2.7 Wachtlijst + +`LocationAssignmentWaitingList` is een subtype van `LocationAssignment` (type `WAITING_LIST`), letterlijk gedocumenteerd als: *"Developed as a (temporary) solution to enable (GGZ) customers to indicate a client is on a 'waiting list'"*. Velden: `waitingFor`, `status` ("Wachtstatus (urgentie)"), `description`, `classification` (alleen Wlz, ZN-classificatie zoals `DREIGENDE_CRISIS_THUIS`), `closingReason` ("Assumed use: BI tooling"). Bij álle waardevelden staat: **"Possible values are defined by the care organisation"** — volledig instellingsconfigureerbare waardelijsten, niets hardcoded. Voorbeeldwaarden: `waitingFor: BESCHIKBARE_PLEK`, `status: ACTIEF_PLAATSEN`, `closingReason: GEPLAATST`. + +**Relevantie voor discovery §5.3:** zelfs de marktleider heeft wachtlijst als noodoplossing aan locatietoewijzing gehangen. Het discovery-besluit (WACHTLIJSTPLAATSING als eigen entiteit met soort aanmeld/behandel, prioriteit en beëindigingsreden) is structureel sterker dan wat Nedap publiek biedt; de Nedap-les die overblijft is: maak prioriteit, wachtreden én beëindigingsreden instellingsconfigureerbare waardelijsten. + +### 2.8 Agenda + +Klassiek series/occurrence-patroon: `AgendaSeries` (herhaalregels: recurrenceType, weekdagen, cycleInterval, validFrom/To) en `AgendaOccurrence` (concrete afspraak, met `clientPresent`, `registered`, `registrationComplete`, `hourTypeId` → koppeling naar tijdregistratie/declaratie, labels, uitnodigingen voor cliënt en medewerkers). Afwezigheid van cliënten (`ClientAbsenceOccurrence` + `ClientAbsenceReason` als waardelijst-resource) is een aparte structuur. `presence_logs` verbinden agenda met aanwezigheidsregistratie (klinisch/verblijf). Les: afspraak-realisatie ("registered") en de afspraak zelf zijn gescheiden assen — relevant voor de agenda-ronde i.c.m. ZPM (planning ≠ geleverde prestatie). + +### 2.9 FHIR/zibs-positie van Nedap + +De eigen API is **niet** FHIR — het is een proprietary REST-model. FHIR/zibs zit in een aparte documentatiesectie ([ZIBs en FHIR](https://ons-api.nl/techniek/FHIR%20en%20ZIBS.html)) als uitwisselingslaag ernaast (o.a. `harmony.ZorgdomeinFhirConnector` in de spec, `nuts.Consent` voor het Nuts-netwerk). Dit ondersteunt de discovery-koers (§4): eigen model als kern, FHIR als adapter aan de rand. Zelfde geldt voor Nedaps interne openEHR-gebruik: wrapper-resources, geen leidend API-model. + +--- + +## 3. Medicore (ECD/EPD, veel gebruikt in GGZ/jeugdzorg) + +**Niet publiek vindbaar:** resource-lijsten, entiteiten, statussen of een developer-portal. Wat wél uit publieke bronnen blijkt: + +- Medicore heeft een API-/integratieplatform **"Mint" (Medicore Integrated)** met "35+ partners"; genoemde integratiedomeinen: medicatie, roosterplanning, speech-to-text, eigen apps. Standaarden: HL7, **FHIR**, OpenID Connect, Azure. Bron: [API-platform](https://medicore.nl/medicore-producten/api-platform/), [Mint-oplossingenpagina](https://medicore.nl/oplossingen/api-platform/). +- De EPD-pagina noemt als product-onderdelen: Mijn Medicore, Zorgapp, UP (AI-assistent), API-platform, Wellbee-cliëntportaal — geen module- of entiteitenlijst op datamodel-niveau. Bron: [EPD-pagina](https://medicore.nl/medicore-producten/epd/). +- Medicore werkt "al jaren met open standaarden zoals MedMij en HL7 FHIR" (bron: [kennisartikel Medicore UP](https://medicore.nl/kennis/de-virtuele-ecd-epd-assistent-die-je-tijd-en-inzichten-teruggeeft/)); dat maakt aannemelijk dat externe koppelingen FHIR-resources gebruiken (MedMij-gegevensdiensten zijn per definitie FHIR), maar **hoe het interne datamodel eruitziet is niet publiek**. +- Toegang tot documentatie loopt via contact/partnerschap; er is geen publieke Swagger/OpenAPI gevonden. + +**Conclusie:** "Medicore = FHIR-based API" (aanname uit de opdracht) is publiek **niet verifieerbaar** op resource-niveau; verifieerbaar is alleen dat hun koppelplatform FHIR als standaard noemt. + +## 4. PinkRoccade Zorg — mijnCaress + +**Niet publiek vindbaar:** API-referentie, entiteiten, waardelijsten. Wat wél blijkt: + +- mijnCaress (VVT/GHZ-ECD, geen GGZ-focus) positioneert zich als "API-based... één platform" en "steeds meer open"; werken met API's wordt genoemd als voorwaarde om data "gestandaardiseerd beschikbaar te stellen voor uitwisseling in de zorgketen". Bronnen: [mijnCaress VVT](https://www.mijncaress.nl/vvt/), zoekresultaat "mijnCaress wordt steeds meer Open" (pagina zelf inmiddels 404). +- Concrete koppelingen die publiek beschreven zijn: **ZorgDomein ↔ mijnCaress via HL7 FHIR** (verwijzingen ontvangen en verwerken in het ECD) — bron: [ICT&health](https://www.icthealth.nl/nieuws/koppeling-zorgdomein-mijncaress-beperkt-administratielast/), [FMT Gezondheidszorg](https://fmtgezondheidszorg.nl/koppeling-zorgdomein-en-mijncaress-verbetert-samenwerking-in-keten/); identity-koppelingen via Tools4ever; zorgapp-koppelingen via CareConnections. +- Het [mijnCaress Platform-overzicht](https://mijncaress.nl/oplossingen/mijncaress-platform/) noemt modules (Medewerkerportaal, Zorgapp, Cliëntportaal, Financieel, Stuurinformatie, Spraakgestuurd rapporteren) maar nul technische documentatie. + +**Conclusie:** geen bruikbaar datamodel-vergelijkingsmateriaal; alleen het signaal dat verwijzingen-instroom via FHIR (ZorgDomein) sectorbreed de norm wordt — relevant voor de latere adapter/instroomkant van AANMELDING. + +## 5. Adapcare — Pluriform Zorg + +**Niet publiek vindbaar:** API-referentie of entiteitenmodel. Wat wél blijkt van de [GGZ-productpagina](https://www.adapcare.nl/pluriform-zorg/ggz/): + +- Pluriform Zorg is een "workflowgestuurd ECD" voor het "complete GGZ-behandeltraject", met als genoemde processtappen: **intake en diagnostiek → behandeling → crisis → nazorg**, voor ambulante gesprekken én klinische opnames; "ondersteunt alle financieringsvormen binnen de GGZ" (incl. forensisch). +- Integratie via de **"Adapcare Care Connector"**: "integreer je op basis van open standaarden (zoals FHIR, ZIB's en openEHR)" plus CACAO-samenwerkingsplatform. Geen documentatie van wat er achter die connector zit. + +**Conclusie:** het proces-vocabulaire (intake/diagnostiek/behandeling/crisis/nazorg als fasen van één traject) bevestigt de fasering die de discovery in AANMELDING → ZORGEPISODE modelleert, maar op datamodel-niveau valt niets te verifiëren. + +## 6. Kort: USER (SDB), ZilliZ, Praktijkdata + +- **USER** is het GGZ-EPD van **Avinty, sinds april 2023 onderdeel van SDB Groep** (níet van ZilliZ — de opdrachttekst suggereerde "Zilliz/USER", dat klopt niet). De [USER-productpagina](https://sdbzorgt.nl/user/) noemt: Agenda als "het centrale punt rondom plannen, rapporteren en registreren", multidisciplinair zorgplan waaraan de cliënt meeschrijft, rapportage-overzicht "Voortgang / Decursus" over alle disciplines, Metingen, Zorgviewer, en facturatiestromen "ZPM, WLZ, WMO en FZ". Koppelingen via een **"Connect Service"** ("fundamenteel open ecosysteem") — geen publieke API-docs. Interessant vocabulaire-detail: GGZ-rapportage heet hier **decursus**, en de agenda is expliciet de spil tussen plannen, rapporteren en (ZPM-)registreren. +- **ZilliZ** ([zilliz.nl](https://zilliz.nl/)) is een ECD voor kleinschalige zorg (rapportage, geleverde zorg boeken, facturatie, Vecozo/CAK-berichtenverkeer). API-koppelingen bestaan (REST/SOAP, via partners zoals [Brixxs](https://brixxs.com/faq/hoe-kan-ik-een-api-koppeling-maken-met-zilliz/)) maar zijn niet publiek gedocumenteerd. +- **Praktijkdata** (Telasoft) is een EPD voor GGZ-praktijken (psychologen/psychiaters/therapeuten) met koppelingen naar o.a. [SnelStart](https://www.snelstart.nl/koppelingen/praktijkdata), [BergOp](https://www.bergop.info/aanbod/functies/koppelingen-epd) (ROM) en [MyMindspace](https://www.mymindspace.nl/praktijkdata-epd-koppelt-met-mymindspace) (eHealth). Documentatie alleen op aanvraag. +- **Koppeltaal 2.0** (geen leverancier, wel hét GGZ-koppelvlak voor eHealth): FHIR R4 + zib2020, met verplichte profielen voor Patient, Practitioner, CareTeam, Organization, **Task**, **ActivityDefinition**, RelatedPerson — eHealth-modules worden als ActivityDefinition aangeboden en als Task aan een cliënt toegewezen. Bronnen: [Simplifier-package](https://simplifier.net/packages/koppeltaalv2.00/0.7.2), [Koppeltaal 2.0 Dev Guide](https://vzvz.gitbook.io/koppeltaal-2.0-dev-guide/technische-howto/resources-managen). Relevant zodra het ECD eHealth-interventies aan behandeldoelen koppelt (discovery §5.5, interventies). + +--- + +## 7. Dwarsdoorsnede: wat dit betekent voor het Triqura-datamodel + +Per discovery-deelgebied, zonder besluiten terug te draaien: + +1. **Waardelijsten (spelregel 3.1 #1)** — Nedap laat beide uitersten zien: `ReportType` met magic numbers ín de API-docs (het anti-patroon) én wachtlijstvelden die volledig "defined by the care organisation" zijn (het gewenste patroon). Extra les: NZa-achtige waardelijsten (SoortVerwijzer, Zorgdomein, diagnosecodes) dragen bij Nedap **geldigheidsperioden** (`begindatum`/`einddatum` per waarde). Overweging voor de referentietabellen: geldig-van/geldig-tot per waarde opnemen. +2. **Episode ↔ declaratietraject (besluit §5.1 #9)** — bevestigd door Nedap: klinische episode en zorgtraject/DBC/ZPM zijn gescheiden structuren die naar elkaar verwijzen. Open vraag voor de rapportage-ronde: Nedap koppelt verslagen many-to-many aan episodes (`episodeIds[]`). +3. **Verwijzer (besluiten §5.1 #2-#3)** — Nedaps `Referral` (referrerType, referrerCategory, agbCode, institution, specialism, documentId) valideert de gekozen decompositie; niets gevonden dat ertegen pleit. +4. **Wachtlijst (besluit §5.3)** — geen leverancier heeft dit publiek beter opgelost dan het discovery-besluit; Nedap noemt zijn eigen oplossing letterlijk tijdelijk. Beëindigingsreden ("closingReason... assumed use: BI tooling") als configureerbare waardelijst wordt door de praktijk bevestigd. +5. **Diagnose (besluiten §5.4)** — twee status-assen (certainty + klinische status) en mappingtabellen met ICD-9/ICD-10-kolommen per diagnosecode zijn bij Nedap staande praktijk; `immutable` na vaststelling is het equivalent van "definitief vastgesteld". GAF-scores op de classificatie zijn DSM-IV-erfgoed — niet overnemen, wel een waarschuwing over hoe classificatie-historie in een model versteent. +6. **Behandelplan (§5.5, open vragen)** — Nedap beantwoordt twee open discovery-vragen vanuit de praktijk: statusmachine minimaal (`DRAFT`/`ACTIVE`/`OLD`, één actief plan, versie = nieuw record) en **cliëntakkoord als los feit** (`CarePlanAgreement`) met een aparte besprekingsstatus (`discussed`/`discussed_later`/`no_discussion`/`no_agreement`) — dat laatste dekt de WGBO-realiteit dat een plan besproken-maar-niet-akkoord kan zijn. +7. **Rapportage (latere ronde)** — Nedap-patronen om mee te nemen: metingen als generiek entry-mechanisme i.p.v. tabel-per-meetsoort; vertrouwelijkheid/autorisatie per verslag-onderdeel (SOEP-deel) en zelfs per episode; auteurstype als enum (medewerker/naaste/extern — bij Triqura komt daar AI bij, spelregel 3.1 #2 gaat verder); verbergen met reden i.p.v. verwijderen. +8. **FHIR-positie (§4 discovery)** — geen enkele onderzochte leverancier gebruikt FHIR als intern datamodel. Nedap: proprietary REST + FHIR/zibs als rand. Medicore/PinkRoccade/Adapcare: FHIR genoemd als koppelstandaard. De discovery-koers (eigen model, FHIR als adapter) is marktconform. +9. **Openheid als onderscheid** — alleen Nedap publiceert zijn API volledig. Een publiek gedocumenteerde, semantisch rijke API (uit FCO-IM-feitzinnen afleidbaar) zou voor Triqura een reëel concurrentievoordeel zijn in een markt waar documentatie achter partnercontracten zit. + +## 8. Expliciet: wat NIET publiek vindbaar is + +- Interne datamodellen/ERD's van álle leveranciers (ook Nedap publiceert alleen het API-vlak, niet het databaseschema). +- Medicore: elke resource-/entiteitenlijst, statussen, waardelijsten; het bestaan van een FHIR-API is aannemelijk maar niet op resource-niveau verifieerbaar. +- PinkRoccade mijnCaress: elke technische API-documentatie; de "steeds meer open"-pagina is inmiddels offline (404). +- Adapcare: alles achter de "Care Connector"; geen spec, geen resources. +- USER/SDB: alles achter de "Connect Service"; ook GGZ-instroomfunctionaliteit (aanmelding/wachtlijst) is publiek onbeschreven. +- Wachtlijst-/aanmeldstatussen en instroomworkflows: bij geen enkele leverancier publiek gedocumenteerd behalve Nedaps wachtlijst-resource. +- Dit onderzoek heeft geen toegang gehad tot partnerportalen, NDA-documentatie of demo-omgevingen; alles hierboven komt uit openbare bronnen d.d. 18-07-2026. + +## 9. Bronnen + +**Nedap Ons (primair, hoge betrouwbaarheid):** +- [ons-api.nl — Wat is Ons API?](https://ons-api.nl/) · [API-overzicht](https://ons-api.nl/techniek/apis/APIS.html) · [API-eigenschappen](https://ons-api.nl/techniek/apis/API_eigenschappen.html) · [ZIBs en FHIR](https://ons-api.nl/techniek/FHIR%20en%20ZIBS.html) +- [OpenAPI-specificatie (json, geanalyseerd 18-07-2026)](https://ons-api.nl/assets/openapi.json) +- [Nedap Ons support — What is Ons API?](https://support.nedap-ons.nl/support/solutions/articles/103000371509-what-is-ons-api-) + +**Medicore:** +- [API-platform](https://medicore.nl/medicore-producten/api-platform/) · [Mint-oplossingen](https://medicore.nl/oplossingen/api-platform/) · [EPD](https://medicore.nl/medicore-producten/epd/) · [Medicore UP / standaarden](https://medicore.nl/kennis/de-virtuele-ecd-epd-assistent-die-je-tijd-en-inzichten-teruggeeft/) + +**PinkRoccade mijnCaress:** +- [mijnCaress Platform](https://mijncaress.nl/oplossingen/mijncaress-platform/) · [mijnCaress VVT](https://www.mijncaress.nl/vvt/) · [ZorgDomein-koppeling via FHIR (ICT&health)](https://www.icthealth.nl/nieuws/koppeling-zorgdomein-mijncaress-beperkt-administratielast/) · [FMT Gezondheidszorg](https://fmtgezondheidszorg.nl/koppeling-zorgdomein-en-mijncaress-verbetert-samenwerking-in-keten/) + +**Adapcare:** +- [Pluriform Zorg GGZ](https://www.adapcare.nl/pluriform-zorg/ggz/) · [Adapcare](https://www.adapcare.nl/) + +**USER / ZilliZ / Praktijkdata:** +- [USER EPD (SDB Zorgt)](https://sdbzorgt.nl/user/) · [SDB EPD GGZ](https://sdbzorgt.nl/oplossingen/epd-geestelijke-gezondheidszorg/) · [ZilliZ](https://zilliz.nl/) · [Brixxs — API-koppeling ZilliZ](https://brixxs.com/faq/hoe-kan-ik-een-api-koppeling-maken-met-zilliz/) · [Praktijkdata ↔ SnelStart](https://www.snelstart.nl/koppelingen/praktijkdata) · [BergOp EPD-koppelingen](https://www.bergop.info/aanbod/functies/koppelingen-epd) · [MyMindspace ↔ Praktijkdata](https://www.mymindspace.nl/praktijkdata-epd-koppelt-met-mymindspace) + +**Koppeltaal (context):** +- [Koppeltaal 2.0 package (Simplifier)](https://simplifier.net/packages/koppeltaalv2.00/0.7.2) · [Koppeltaal 2.0 Dev Guide](https://vzvz.gitbook.io/koppeltaal-2.0-dev-guide/technische-howto/resources-managen) diff --git a/docs/datamodel/onderzoek/onderzoek-rapportage.md b/docs/datamodel/onderzoek/onderzoek-rapportage.md new file mode 100644 index 0000000..2a685e2 --- /dev/null +++ b/docs/datamodel/onderzoek/onderzoek-rapportage.md @@ -0,0 +1,241 @@ +# Onderzoek: voortgangsrapportage & dossiervoering in de Nederlandse GGZ + +**Status:** onderzoeksinput voor datamodel-deelgebied voortgangsrapportage (ronde "rapportage & overdracht") +**Datum:** 18 juli 2026 +**Auteur:** onderzoeksagent "rapportage" (webresearch + prototype-analyse) +**Kader:** dit rapport draait geen besluiten uit `../deelmodellen/datamodel-discovery.md` terug; het levert feiten, bronnen en kandidaat-feitzinnen. Waar iets schuurt met een genomen besluit staat dat expliciet als open vraag gemarkeerd. + +--- + +## 1. Wettelijk kader: wat móet er in het dossier + +### 1.1 WGBO — dossierplicht (BW 7:454) + +- De hulpverlener is verplicht een dossier in te richten en daarin aantekening te houden van "gegevens omtrent de gezondheid van de patiënt en de te diens aanzien uitgevoerde verrichtingen", voor zover dit voor een goede hulpverlening noodzakelijk is. Er is geen limitatieve wettelijke lijst; de norm is *noodzakelijk voor goede zorg* ([KNMG — richtlijn Omgaan met medische gegevens](https://www.knmg.nl/richtlijn-omgaan-met-medische-gegevens/), [Ondernemersplein — medisch dossier bijhouden](https://ondernemersplein.overheid.nl/wetten-en-regels/medisch-dossier-bijhouden-en-delen/)). +- **Bewaartermijn: 20 jaar**, gerekend vanaf de laatste wijziging in het dossier (WGBO-wijziging per 1-1-2020, was 15 jaar vanaf vastlegging) ([LVVP](https://lvvp.info/nieuwsbrief/wijziging-wgbo-waaronder-verlenging-bewaartermijn-15-naar-20-jaar/), [Rijksoverheid](https://www.rijksoverheid.nl/onderwerpen/rechten-van-patient-en-privacy/uw-medisch-dossier/bewaren-medisch-dossier), [KNMG — wijzigingen WGBO](https://www.knmg.nl/advies-richtlijnen/dossiers/behandelingsovereenkomst-wgbo/wijzigingen-wgbo.htm)). Langer bewaren kan (belang van derden, bijv. erfelijke gegevens); bij gedwongen opname geldt aanvullend minimaal 5 jaar na ontslag ([Autoriteit Persoonsgegevens](https://www.autoriteitpersoonsgegevens.nl/en/themes/health/health-data-in-a-file/retention-of-the-health-data-file)). +- **Patiëntrechten op het dossier** ([AP — rechten bij het dossier](https://www.autoriteitpersoonsgegevens.nl/en/themes/health/health-data-in-a-file/rights-regarding-the-health-data-file), [VvAA](https://www.vvaa.nl/nieuws-en-kennis/nieuws-en-artikelen/kopie-wijziging-of-vernietiging-van-medisch-dossier), [KNMG praktijkdilemma vernietiging](https://www.knmg.nl/ik-ben-arts/praktijkdilemmas-1/praktijkdilemma/heeft-mijn-patient-recht-op-vernietiging-van-zijn-medisch-dossier)): + - **Inzage en afschrift** — altijd. + - **Correctie** — alleen voor feitelijke, objectief vaststelbare onjuistheden; indrukken/conclusies van de behandelaar vallen er níet onder. + - **Aanvulling** — de cliënt mag altijd een eigen (afwijkende) verklaring aan het dossier laten toevoegen; de hulpverlener móet die opnemen, ook bij inhoudelijk oneens zijn. + - **Vernietiging** — op verzoek, tenzij een aanmerkelijk belang van een ander zich verzet of een wettelijke bepaling vernietiging verbiedt. (Sinds de herziening 2024 van de KNMG-richtlijn: het vernietigingsverzoek zelf bewaren ná vernietiging.) + +### 1.2 Wabvpz — elektronische inzage en logging + +Per 1 juli 2020 (art. 15d/15e Wabvpz): cliënt heeft recht op **kosteloze elektronische inzage en elektronisch afschrift** van het dossier, en recht op een afschrift van de **logging** (wie keek wanneer in het dossier); logging moet voldoen aan **NEN 7513** ([Eldermans & Geerts](https://www.eldermans-geerts.nl/per-1-juli-2020-het-recht-op-elektronische-inzage-in-en-afschrift-van-het-medisch-dossier/), [Dirkzwager](https://www.dirkzwager.nl/kennis/artikelen/per-1-juli-2020-nieuwe-rechten-en-verplichtingen-met-betrekking-tot-het-elektronisch-patientendossier), [VGN](https://www.vgn.nl/nieuws/elektronische-inzage-afschrift-en-logging-1-juli-2020)). + +**Gevolg voor rapportage:** elke voortgangsrapportage is in beginsel cliënt-leesbaar. Dit is geen portaal-feature-keuze maar een wettelijk recht — een "vertrouwelijk"-vlag in het ECD kan zichtbaarheid in een portaal uitstellen, maar heft het inzagerecht niet op. + +### 1.3 Wkkgz — incidenten in het dossier (art. 10 lid 3) + +- Incidenten met **merkbare gevolgen voor de cliënt** (of die deze in de toekomst kunnen hebben) moeten aan de cliënt worden gemeld én in het **cliëntendossier** worden aangetekend: **aard, toedracht en tijdstip** van het incident plus de **namen van direct betrokkenen**. De cliënt kan hiervan afschrift vragen ([Holla — Wkkgz-meldingen](https://www.holla.nl/nieuws/wkkgz-meldingen), [Dirkzwager — mededeling van een incident](https://www.dirkzwager.nl/kennis/artikelen/inzage-in-het-medisch-dossier-van-een-overleden-pati%C3%ABnt-2-mededeling-van-een-incident)). +- Dit staat **los van** de interne VIM-melding (zie §6): VIM registreert élk incident in een apart register; het dossier krijgt alleen een aantekening bij merkbare gevolgen ([zzp-erindezorg — incident vs calamiteit](https://www.zzp-erindezorg.nl/blog/eisen-uit-de-wkkgz-verschil-tussen-incident-en-calamiteit/)). + +### 1.4 Landelijk Kwaliteitsstatuut GGZ (v4.0, jan 2025) + +- De **regiebehandelaar** (indicerend/coördinerend) is verantwoordelijk voor probleemanalyse, diagnose en behandelplan op hoofdlijnen, en voor **dossiervoering, verslaglegging en het eindverslag**; minimaal jaarlijks een planbespreking met alle betrokken disciplines, vastgelegd in het dossier ([Landelijk Kwaliteitsstatuut GGZ 4.0, Zorginzicht](https://www.zorginzicht.nl/binaries/content/assets/zorginzicht/kwaliteitsinstrumenten/landelijk-kwaliteitsstatuut-ggz-4.0.pdf), [Zorginstituut](https://www.zorginstituutnederland.nl/publicaties/publicatie/2020/12/15/landelijk-kwaliteitsstatuut-ggz)). +- MDO-conclusies en -afspraken horen direct in het dossier (multidisciplinair behandelplan) ([Verenso — handreiking MDO](https://www.verenso.nl/kwaliteit/richtlijnen-en-praktijkvoering/richtlijnendatabase/multidisciplinair-overleg-mdo), [normenkaderzorg — MDO-registratie-eis](https://www.normenkaderzorg.nl/index.php?title=DBC_zonder_geldig_MDO_%28N2220%29)). + +### 1.5 Zorgprestatiemodel — registratie naast rapportage + +Het ZPM registreert **consulten** (declaratie) via "planning = realisatie": afwijking > 15 minuten van de geplande tijd moet worden bijgesteld; tijdschrijven is afgeschaft; de agenda + periodieke controle is de verantwoordingsbron ([Spelregels correct registreren en declareren, zorgprestatiemodel.nl](https://www.zorgprestatiemodel.nl/content/uploads/2023/07/20230628-Spelregels-correct-registreren-en-declareren_definitief.pdf), [Dirkzwager — ZPM registratie](https://www.dirkzwager.nl/kennis/artikelen/het-zorgprestatiemodel-registratie), [NZa V&A](https://www.nza.nl/zorgsectoren/geestelijke-gezondheidszorg-ggz-en-forensische-zorg-fz/vraag-en-antwoord/vraag-en-antwoord-zorgprestatiemodel)). + +**Gevolg voor het model:** *consultregistratie* (declarabel feit, agenda-gedreven) en *inhoudelijke rapportage* (klinisch feit) zijn twee verschillende feiten die naar elkaar kunnen verwijzen, maar niet één record zijn. Uitzonderingssituaties moeten wel "uit het dossier blijken". + +--- + +## 2. Professionele richtlijnen dossiervoering + +### 2.1 KNMG — Omgaan met medische gegevens (herzien jan 2024) + +Hoofdstuk 2 behandelt dossierplicht, bewaartermijn en patiëntrechten; er is bewust géén uitputtende lijst wat per situatie in het dossier moet — de noodzakelijkheidsnorm geldt ([KNMG-richtlijn 2024, pdf](https://www.knmg.nl/download/knmg-richtlijn-omgaan-met-medische-gegevens-2), [overzichtspagina](https://www.knmg.nl/actueel/publicaties/publicaties-per-onderwerp/omgaan-met-medische-gegevens)). + +### 2.2 V&VN — Richtlijn Verslaglegging (2022) + +Richtlijn voor verpleegkundigen/verzorgenden, ook toepasbaar in de GGZ ([Kennisplatform V&VN — verslaglegging](https://kennisplatform.venvn.nl/onderwerp/verslaglegging/), [richtlijn-pdf](https://www.venvn.nl/media/w02buck0/v-vn-richtlijn-verslaglegging.pdf), [Nursing](https://www.nursing.nl/praktijk/organisatie-van-zorg/nieuwe-richtlijn-moet-verslaglegging-en-overdracht-verbeteren/)): + +- Doel: continuïteit en kwaliteit van zorg, samenwerking tussen zorgverleners. +- Vastleggen in **alle fasen van het verpleegkundig proces**: gegevensverzameling, zorgproblemen/diagnoses, **gezamenlijk vastgestelde doelen**, uitgevoerde zorg, **voortgangsrapportage**, evaluatie, overdracht. +- **Eenduidige taal** die uitwisselbaar is tussen organisaties (eenheid van taal / zibs-terminologie). +- **Cliënt betrekken**: controleer of wat je rapporteert aansluit bij de wensen/behoeften van de zorgvrager. +- Bij overdracht tussen settingen: de informatiestandaard **eOverdracht** gebruiken. + +### 2.3 NIP — dossier vs. persoonlijke werkaantekeningen + +- Het cliëntdossier bevat alles wat relevant is voor kwaliteit en continuïteit van de professionele relatie: behandelplan, gespreksaantekeningen, observaties, testuitslagen, correspondentie ([NIP — dossiervorming](https://www.psynip.nl/uw-beroep/cotan/cotan-beoordelingssysteem-ast-en-beroepscode/algemene-standaard-testgebruik-nip-2017/2-2-onderzoeksprocedure/2-2-2-dossiervorming/)). +- **Persoonlijke werkaantekeningen** (indrukken, vermoedens, vragen als geheugensteun voor het eigen denkproces) horen **niet** in het dossier, zijn tijdelijk en moeten vernietigd worden zodra niet meer relevant. Let op: gespreksnotities en observaties over de cliënt zijn *géén* werkaantekeningen — die horen wél in het dossier ([NIP — persoonlijke werkaantekeningen](https://www.psynip.nl/uw-beroep/cotan/cotan-beoordelingssysteem-ast-en-beroepscode/algemene-standaard-testgebruik-nip-2017/2-2-onderzoeksprocedure/2-2-4-persoonlijke-werkaantekeningen/)). +- **Gevolg voor het model:** werkaantekeningen zijn geen dossier-entiteit. Als het ECD ze faciliteert, dan strikt gescheiden van de rapportage-entiteit, buiten inzage/afschrift, met een vernietigings-levenscyclus. Simpeler: niet faciliteren. + +### 2.4 NVvP / psychiatrie + +Geen aparte NVvP-dossierrichtlijn gevonden naast WGBO/KNMG; dossiervoering is onderdeel van de kwaliteitsvisitatie en elk diagnostisch verslag is een medisch document dat tuchtrechtelijk toetsbaar is ([NVvP — wet- en regelgeving](https://www.psychiatrie.nl/alle-themas/wet-en-regelgeving/), [Richtlijn psychiatrische diagnostiek — wettelijk kader](https://richtlijnendatabase.nl/richtlijn/psychiatrische_diagnostiek/wettelijke_kader_psychiatrische_diagnostiek.html), [NVvP normenkader kwaliteitsvisitatie](https://www.psychiatrie.nl/app/uploads/2024/10/Normenkader-NVvP-Kwaliteitsvisitatie.pdf)). + +--- + +## 3. Rapportagemethodieken + +| Methodiek | Structuur | Gebruik | +|---|---|---| +| **SOEP / SOAP** | Subjectief · Objectief · Evaluatie/Analyse · Plan | Breedst gebruikt; huisartsen (NHG-referentiemodel), verpleging, ook GGZ | +| **DAP** | Data · Assessment · Plan | Internationaal dé behandelnotitie-vorm in mental/behavioral health; S+O samengevoegd tot één Data-sectie | +| **TIP** | Thema · Interventie · Plan | GGZ-specifiek alternatief, cliëntvriendelijker | +| **SBAR(R)** | Situation · Background · Assessment · Recommendation (· Repeat) | Mondelinge/acute overdracht, niet primair dossiernotitie | +| **SMART** | doelformulering, geen notitievorm | doelen in behandelplan | +| **Vrije tekst / decursus** | chronologisch journaal | klassiek medisch EPD-patroon | + +- **SOEP**: S = wat de cliënt zelf zegt/beleeft; O = observaties en meetbare vaststellingen incl. gedane interventie en reactie; E = interpretatie/conclusie uit S+O; P = vervolgplan ([Verslagleggen in de GGZ — SOEP-methode](https://www.verslagleggenindeggz.nl/blog/de-soep-methode-hoe-pas-je-die-toe-in-een-voortgangsrapportages/), [CME-Online — SOEP vs SOAP](https://www.cme-online.nl/blog/wat-is-soep-rapporteren/), [NHG HIS-referentiemodel — SOEP-verslag](https://referentiemodel.nhg.org/node/19)). Nedap Ons ondersteunt SOEP als gestructureerd rapportageformulier ([Nedap Ons — SOEP-rapportage](https://support.nedap-ons.nl/support/solutions/articles/103000267329-rapporteren-volgens-de-soep-methode-in-ons-dossier)). +- **DAP**: Data (uitspraken cliënt + observaties + interventies), Assessment (klinische indruk, voortgang op doelen, risico-overwegingen), Plan (vervolg, huiswerk, verwijzing). Typisch drie alinea's ([Headway — DAP notes](https://headway.co/resources/dap-note), [SimplePractice](https://www.simplepractice.com/resource/how-to-write-dap-notes/), [ICANotes](https://www.icanotes.com/2022/10/11/how-to-write-dap-notes/)). +- **TIP en kritiek op SOEP in de GGZ**: het E/A-veld verleidt tot het vastleggen van niet met de cliënt afgestemde aannames — pijnlijk nu cliënten hun dossier online meelezen (Wabvpz). TIP (Thema-Interventie-Plan) wordt in de GGZ aanbevolen als eenvoudiger en herkenbaarder voor de cliënt ([Verslagleggen in de GGZ — SOEP, SOAP of TIP](https://www.verslagleggenindeggz.nl/blog/soep-soap-of-tip-wat-is-een-handige-opbouw-voor-clientrapportages/)). +- **SBAR/I-PASS**: overdrachtscommunicatie; 66% van psychiatrisch verpleegkundigen is ontevreden over de overdracht en voor de klinische GGZ bestaan geen specifieke richtlijnen ([HBO Kennisbank — SBAR-onderzoeken](https://hbo-kennisbank.nl/details/sharekit_av:oai:surfsharekit.nl:270e77bd-1d30-4d7f-b42d-85d5cdd52b74), [eNurse — SBAR-methode](https://enurse.nl/2019/02/24/sbar-methode/), [Nursing — I-PASS](https://www.nursing.nl/student-corner-gebruik-de-i-pass-voor-een-goede-mondelinge-overdracht-2/)). + +**Modelles:** er is geen landelijk verplichte notitievorm. Instellingen kiezen (en wisselen). De methodiek is dus **configuratie** (template per rapportagetype/sessietype), geen schema-hardcoding — exact spelregel 3.1 #1. De gestructureerde secties (S/O/E/P of D/A/P) zijn wel waardevolle *optionele* structuur naast de vrije tekst (past op AI-voorbereiding §3.2: structuur + tekst in hetzelfde record). + +--- + +## 4. Relatie rapportage ↔ behandelplan (rapporteren op doelen) + +- Het behandelplan is in de GGZ "het hart van het dossier"; elk sessieverslag is een bijdrage aan dat plan: ligt de behandeling op koers, vragen doelen bijstelling, werkt de aanpak ([Kuno — behandelverslag koppelen aan behandelplan](https://www.heykuno.com/nl/blog/ggz-behandelverslag/), [Carecheck — verslaglegging in de GGZ](https://carecheck.nl/verslaglegging-in-ggz/)). +- ECD-praktijk: **rapporteren op een doel** — bij het actuele zorgplan per doel een voortgangsrapportage met een *mate van voortgang* (schaal) plus een notitie ([Nedap Ons — rapporteren op doelen en voortgang](https://support.nedap-ons.nl/support/solutions/articles/103000266084-rapporteren-op-doelen-en-voortgang-in-het-zorgplan-in-ons-dossier), [ZilliZ — rapporteren op zorgdoelen](https://zilliz.nl/kennis/blog/rapportegen-op-zorgdoelen-met-zilliz/)). +- V&VN: evaluatie moet aantoonbaar plaatsvinden — verifieer dat doelen periodiek beoordeeld worden ([Kennisplatform V&VN](https://kennisplatform.venvn.nl/onderwerp/verslaglegging/)). + +**Modelles:** een rapportage moet 0..n **behandeldoelen** kunnen refereren (niet verplicht: verpleegkundige dagrapportage of een incident staat vaak los van een doel). Optioneel per doelkoppeling een voortgangsindicatie uit een waardelijst. Evaluatieverslagen (het geplande evaluatiemoment uit §5.5 van de discovery) zijn een rapportagetype dat aan het behandelplan zelf refereert. + +--- + +## 5. Overdracht tussen diensten en teams + +- **Dienstoverdracht (klinisch/verpleegkundig):** rapportages worden per dienst/dagdeel gelezen; gestandaardiseerde mondelinge vormen zijn SBAR/I-PASS (§3). Het "include in handover"-patroon (rapportage markeren voor de overdracht) is gangbaar in ECD's en zit ook in het prototype. +- **eOverdracht (Nictiz):** landelijke informatiestandaard voor de verpleegkundige overdracht túsen instellingen, gebaseerd op zibs (publicatie 2017), uitwisseling via HL7 FHIR; kandidaat voor verplichting onder de Wegiz ([Nictiz — eOverdracht](https://www.nictiz.nl/informatiestandaarden/eoverdracht/), [functioneel ontwerp v3.1](https://informatiestandaarden.nictiz.nl/wiki/vpk:V3.1_Ontwerp_eOverdracht), [ICT&health — Wegiz](https://www.icthealth.nl/magazine/editie-01-2023/gegevensuitwisseling-vpo-bijna-klaar-voor-verplichting-onder-wegiz)). Conform discovery §4 is dit een **koppelvlak**, geen modelregel — maar de zib-begrippen zijn een gratis checklist voor wat een overdracht inhoudelijk draagt. +- **MDO:** gepland overleg van meerdere disciplines; conclusies en afspraken direct in het dossier vastleggen als (bijstelling van het) multidisciplinair behandelplan; de regiebehandelaar bewaakt voortgang én verslaglegging ([Verenso — handreiking MDO](https://www.verenso.nl/assets/Praktijkvoering_handreikingen/VER00331-HandrMultidoverl-DEF.pdf), [NZa — MDO-vergoeding ggz](https://www.nza.nl/vraag-en-antwoord/geestelijke-gezondheidszorg-ggz-en-forensische-zorg-fz/hoe-wordt-het-mdo-vergoed)). MDO-verslag = rapportagetype op episodeniveau (niet per se één cliëntcontact). + +--- + +## 6. Incidentmelding: twee gescheiden sporen + +Dit is de belangrijkste structurele bevinding voor het incident-rapportagetype: + +| Spoor | Wat | Waar | Wie leest | +|---|---|---|---| +| **VIM-melding** (Wkkgz art. 9) | élk incident, ook zonder gevolgen; doel: leren/verbeteren | intern VIM-register, **buiten het cliëntdossier** | VIM-commissie; beschermd, in beginsel niet voor cliënt/IGJ | +| **Dossieraantekening** (Wkkgz art. 10 lid 3) | alleen incidenten met (mogelijk) merkbare gevolgen: aard, toedracht, tijdstip, namen betrokkenen | **cliëntendossier** | cliënt (inzagerecht, afschrift) | +| **IGJ-melding** | calamiteit (dood of ernstig schadelijk gevolg), geweld in de zorgrelatie; binnen 3 werkdagen | IGJ-portaal | IGJ | + +- Elke zorgaanbieder moet een interne VIM-procedure hebben: signaleren, registreren, analyseren, maatregelen, terugkoppeling ([Erisietsmisgegaan — VIM](https://erisietsmisgegaan.nl/bijna-alles-dat-je-moet-weten-over-veilig-incidenten-melden/), [TPSC — VIM & Wkkgz](https://www.patientsafety.com/nl/blog/vim-wkkgz), [NHG — procedure VIM](https://www.nhg.org/praktijkvoering/patient/patientveiligheid/procedure-veilig-incident-melden/)). +- Calamiteit = onbedoelde/onverwachte gebeurtenis met dood of ernstig schadelijk gevolg; melden bij IGJ binnen 3 werkdagen; in de GGZ vallen daaronder ook suïcide(pogingen) bij tekortschietende zorg en geweld in de zorgrelatie ([IGJ — calamiteit melden](https://www.igj.nl/onderwerpen/klachten-en-melden/calamiteiten/melding-doen-van-een-calamiteit), [IGJ — suïcide(poging) melden ggz](https://www.igj.nl/onderwerpen/klachten-en-melden/suicidepoging)). +- Gangbare GGZ-incidentcategorieën: agressie/geweld, suïcide(poging)/automutilatie, medicatie-incident, vallen, vermissing/onttrekking, seksueel grensoverschrijdend gedrag ([IGJ — cijfers meldingen ggz](https://www.igj.nl/over-ons/igj-in-cijfers/cijfers-over-meldingen/cijfers-meldingen-geestelijke-gezondheidszorg)). + +**Modelles:** het prototype-type `incident` vermengt beide sporen. In het nieuwe model: (a) de **dossieraantekening incident** is een rapportagetype in het cliëntdossier (met de art.-10-velden aard/toedracht/tijdstip/betrokkenen); (b) de **VIM-melding** is een *aparte entiteit buiten het cliëntdossier* met eigen autorisatie, eigen levenscyclus (gemeld → in analyse → afgerond) en eigen categorie-waardelijst, met optionele verwijzing naar cliënt en rapportage. VIM valt mogelijk buiten scope van het ECD-dossierdeel — maar het besluit "waar leeft VIM" moet expliciet genomen worden. + +--- + +## 7. Hoe bestaande ECD's/EPD's rapportage structureren + +### 7.1 Nedap Ons (referentie-ECD in care/GGZ) + +([Cliëntrapportages toegelicht](https://support.nedap-ons.nl/support/solutions/articles/103000266173-cli%C3%ABntrapportages-in-ons-dossier-toegelicht), [rapportages configureren](https://support.nedap-ons.nl/support/solutions/articles/103000265596-rapportages-voor-ons-dossier-configureren), [autorisaties rondom rapportages](https://support.nedap-ons.nl/support/solutions/articles/103000265496-autorisaties-rondom-rapportages-in-ons-dossier)) + +- **Rapporttypen zijn beheer-configuratie**: Beheer → Dossierinstellingen → Rapporttypen; alleen geactiveerde typen zijn bruikbaar. Voorbeelden: *Rapportage* (vrije tekst), *Medische rapportage* (medisch kenmerk, andere autorisatie), *Familie-communicatie* (zichtbaar in cliëntportaal Caren), *Ketencommunicatie* (voor externen, bijv. huisarts/Zorgmail). +- Rapportagevormen: vrije tekst, voortgang op doel (met voortgangsmaat), meetwaarden, gestructureerde formulieren (o.a. SOEP). +- Rapportages koppelen aan zorgplan(doel), medische episode of maatregel. +- **Vertrouwelijkheid per rapportage**: alleen zichtbaar voor bepaalde deskundigheidsprofielen ("groen slotje"), en dan niet zichtbaar voor de cliënt in Caren. +- Moment van rapporteren (achteraf rapporteren toestaan of niet) is instelbaar. + +### 7.2 GGZ-specifieke EPD's (Medicore, Carecheck e.a.) + +- Rapportagetypen die een GGZ-EPD onderscheidt: intake-/behandelverslagen, voortgangsrapportages, evaluatieverslagen, diagnostische rapportages, MDO-verslagen, online sessierapporten, indirecte-tijdregistraties; per **sessietype** een rapportagetemplate en de keuze of het verslag met de cliënt wordt gedeeld via het portaal ([Carecheck — verslaglegging in de GGZ](https://carecheck.nl/verslaglegging-in-ggz/)). +- Signalering op **ontbrekende verslaglegging** (consult gehad, geen verslag) is een gangbare EPD-functie — bij Triqura is dat een natuurlijke Nudge-Engine-regel, geen schema-eis. +- Klassiek medisch patroon: het chronologische journaal heet **decursus** ("beloop") ([Studeersnel — EPD-opbouw en afkortingen](https://www.studeersnel.nl/nl/document/universiteit-twente/vcpg-2/20230719-opbouw-en-afkortingen-in-het-epd/109078787), [NHG — adequate dossiervorming met het EPD](https://www.nhg.org/praktijkvoering/informatisering/richtlijn-adequate-dossiervorming-epd/)). +- Zorgplannen kennen een concept → definitief-cyclus met ondertekening/akkoord van de cliënt ([Nedap Ons — conceptzorgplannen ondertekenen](https://support.nedap-ons.nl/support/solutions/articles/103000265569-conceptzorgplannen-ondertekenen-of-akkoord-laten-verklaren-in-ons-dossier)); voor losse rapportages is accorderen minder universeel, maar concept/definitief + naderhand alleen aanvullen (niet stilzwijgend herschrijven) volgt uit de correctie-systematiek van §1.1. + +--- + +## 8. Prototype-analyse (`app/api/reports`, `lib/types/report.ts`) + +### 8.1 Wat er nu is + +- Eén `reports`-tabel voor **10 typen**: `voortgang`, `observatie`, `incident`, `medicatie`, `contact`, `crisis`, `intake`, `behandeladvies`, `vrije_notitie`, `verpleegkundig` — hardcoded als TypeScript-const, niet als waardelijst in de database. +- **Twee validatieschema's op één tabel**: standaardrapportage (20–5000 tekens, optioneel `encounter_id`/`intake_id`) vs. `verpleegkundig` (1–500 tekens, verplichte `category` in `structured_data`, `include_in_handover`). De subset `VERPLEEG_REPORT_TYPES` (verpleegkundig, observatie, incident, medicatie, crisis) bepaalt wat het verpleegkundig overzicht toont — een derde impliciete typologie in code. +- `structured_data.category` (medicatie/adl/gedrag/incident/observatie) is een **JSON-veld zonder constraint**; categorielabels, iconen en kleuren leven in de UI-code (`CATEGORY_CONFIG`). +- **Shift-logica**: `calculateShiftDate` — vóór 07:00 telt de rapportage bij de dienst van de vorige dag; `shift_date` wordt alleen gezet voor verpleegkundige notities, bij andere typen is hij leeg maar wordt er wél op gefilterd. +- AI-provenance beperkt: `ai_confidence` + `ai_reasoning` (van de classifier), geen model/bronverwijzingen/content-hash zoals spelregel 3.1 #2 eist. +- `created_by` verwijst naar practitioner maar mag `null` zijn; soft delete via `deleted_at` is aanwezig; geen concept/definitief-status, geen versies, updates overschrijven de tekst zonder zichtbaar spoor. +- Rapportage hangt alleen aan `patient_id` (plus losse optionele `intake_id`/`encounter_id`) — geen koppeling aan zorgepisode, behandelplan of doel. + +### 8.2 Gebreken t.o.v. spelregels en wetgeving + +1. Typen/categorieën hardcoded in code → schendt spelregel 3.1 #1 (waardelijsten op één plek). +2. Twee (eigenlijk drie) typologieën door elkaar: rapporttype, verpleegkundige categorie, "telt mee in verpleegoverzicht" — de dimensies *wie schreef het (discipline)*, *wat voor soort inhoud* en *in welke weergave hoort het* zijn niet gescheiden. +3. `intake` en `behandeladvies` als rapporttype dupliceren entiteiten die in de discovery al een eigen plek hebben (intake, screeningsbesluit/behandelplan) — een verslag *over* een intake is iets anders dan de intake zelf. +4. Geen mutatiehistorie: een gewijzigde rapportage is juridisch een gewijzigd dossier; WGBO-correctiesystematiek en de append-only-auditregel (3.1 #3) vragen om versies of een expliciet wijzigingsspoor. +5. `shift_date` half toegepast (alleen verpleegkundig) en de 07:00-grens is hardcoded. +6. Incident-type dekt noch de art.-10-velden (aard/toedracht/tijdstip/betrokkenen), noch het VIM-spoor. +7. Geen cliëntzichtbaarheids-dimensie, terwijl Wabvpz-inzage en portaaldeling per type gangbaar zijn. + +--- + +## 9. Implicaties voor het datamodel (voorstel, geen besluit) + +### 9.1 Kern-entiteit RAPPORTAGE + +- Hangt aan **CLIENT** én (vrijwel altijd) aan **ZORGEPISODE** (consistent met besluiten §5.1 #9, §5.4 #2). Open: mag een rapportage zonder episode bestaan (bijv. tijdens screening/aanmeldfase)? +- **Rapportagetype uit een waardelijst** (referentietabel), met per type configuratie: structuurtemplate (vrij/SOEP/DAP/...), min/max-lengte, standaard-cliëntzichtbaarheid, telt-mee-in-overdracht, toegestane disciplines. Dit vervangt de drie hardcoded lijsten van het prototype en volgt het Nedap-patroon "rapporttypen zijn beheerconfiguratie". +- **Tekst + structuur in één record** (§3.2): verplichte vrije tekst; optionele gestructureerde secties per methodiek als aparte velden of een getypeerde sectietabel (sectiecode uit waardelijst: S/O/E/P, D/A/P, T/I/P) — zodat methodiekwissel configuratie is, geen migratie. +- **Auteur en accordering**: auteur verplicht (mens of AI met provenance conform 3.1 #2); status `concept → definitief`; na definitief geen stille edits — correctie = nieuwe versie of aanvulling, cliëntaanvulling (WGBO) als eigen record/vlag. +- **Referenties**: 0..1 contactmoment/consult (relatie met ZPM-registratie, §1.5), 0..n behandeldoelen (met optionele voortgangsindicatie per doel, §4), 0..1 behandelplan (voor evaluatieverslagen), 0..1 intake (verslag-over). + +### 9.2 Overdracht en diensten + +- `include_in_handover`-markering behouden als vlag óf afleiden via typeconfiguratie. +- **Dienst/dagdeel** (nacht/ochtend/middag/avond) als waardelijst; de shift-toewijzingsgrens (07:00) als instellingsconfiguratie, niet hardcoded. Shift-datum consequent voor alle rapportages die in dienstweergaven meedoen. +- De **overdracht zelf** (al dan niet AI-samengevat) is een afgeleid product; als hij wordt vastgelegd, dan met provenance en bronverwijzingen naar de onderliggende rapportages (spelregel 3.1 #2 + TIP-snapshotafspraak §3.3). +- MDO-verslag: rapportagetype op episodeniveau, meerdere betrokken disciplines vastlegbaar. + +### 9.3 Incident + +- Dossieraantekening incident = rapportagetype met gestructureerde art.-10-velden: aard, toedracht, tijdstip, betrokkenen; incidentcategorie uit waardelijst (agressie, medicatie, val, suïcide(poging), vermissing, seksueel grensoverschrijdend, overig). +- VIM-melding = aparte entiteit buiten het cliëntdossier (eigen autorisatie en levenscyclus), optioneel gelinkt aan de dossieraantekening. Calamiteit/IGJ-melding als status/escalatie op de VIM-melding. **Open besluit: hoort VIM in het ECD-domein of erbuiten?** +- Nudge-kansen: "incident met merkbare gevolgen zonder dossieraantekening", "calamiteit → IGJ-termijn 3 werkdagen". + +### 9.4 Zichtbaarheid en rechten + +- Per rapportage: zichtbaarheidsvlag voor cliëntportaal (default per type), wetende dat het Wabvpz-inzagerecht altijd blijft bestaan — de vlag stuurt portaalweergave, geen juridische afscherming. +- Vertrouwelijkheid per deskundigheid (Nedap-patroon "groen slotje") → rollenronde. +- Persoonlijke werkaantekeningen: **geen dossier-entiteit** (NIP); bewust buiten scope houden of als expliciet niet-dossier-domein modelleren. +- Bewaartermijn: 20 jaar vanaf laatste dossierwijziging → het batchjob-criterium van spelregel 3.1 #4 moet op *dossierniveau* rekenen, niet per record. + +### 9.5 Kandidaat-feitzinnen (startlijst voor de rapportage-ronde) + +1. *Voor de zorgepisode van Jan de Vries is op 2 augustus 2026 een rapportage van type "voortgang" vastgelegd door psycholoog M. de Boer.* +2. *De rapportage van 2 augustus is geschreven volgens structuur "SOEP".* / *...heeft als vrije tekst "..." en als sectie "Plan" de tekst "...".* +3. *De rapportage van 2 augustus hoort bij het contactmoment (consult) van 2 augustus 2026.* +4. *De rapportage van 2 augustus rapporteert op behandeldoel "weer drie nachten doorslapen" met voortgang "licht verbeterd".* +5. *De rapportage van 2 augustus heeft status "definitief" sinds 2 augustus 2026, 17:04.* +6. *Verpleegkundige K. Jansen heeft op 3 augustus 2026 om 06:30 een verpleegkundige notitie vastgelegd; deze telt bij de dienst van 2 augustus (nachtdienst).* +7. *De verpleegkundige notitie is gemarkeerd voor de overdracht.* +8. *De rapportage van 2 augustus is zichtbaar voor de cliënt in het portaal.* +9. *Rapportage Y is gegenereerd door AI-model Z op basis van bronnen A en B, en op 3 augustus bevestigd door M. de Boer.* (= feitzin 9 uit discovery §5.2) +10. *Op 5 augustus 2026 om 21:15 heeft zich een incident van categorie "medicatie-incident" voorgedaan rond cliënt Jan de Vries; aard, toedracht en betrokkenen zijn in het dossier aangetekend.* +11. *Voor het incident van 5 augustus is een VIM-melding gedaan; de VIM-melding heeft status "in analyse".* +12. *Cliënt Jan de Vries heeft op 6 augustus 2026 een eigen verklaring aan zijn dossier laten toevoegen.* +13. *Het MDO van 1 september 2026 over de zorgepisode van Jan de Vries is vastgelegd in een MDO-verslag, met betrokken disciplines psychiatrie en verpleegkunde.* + +### 9.6 Open vragen voor de sessie met Colin + +1. Rapportage zonder zorgepisode toestaan (screeningsfase)? Of hangt vroege verslaglegging aan de AANMELDING? +2. Welke rapportagetypen in de startwaardelijst — en vervallen `intake`/`behandeladvies` als rapporttype ten gunste van de eigen entiteiten? +3. Structuurmethodiek: alleen vrije tekst + optionele secties, of SOEP/DAP/TIP als eersteklas sectiemodel? +4. Versiebeheer rapportage: nieuw record per versie of muteren-met-audittrail (zelfde vraag als behandelplan §5.5)? +5. VIM binnen of buiten het ECD? Zo binnen: autorisatiemodel dat het uit het cliëntdossier houdt. +6. Is "definitief maken" (accorderen) een verplichte stap, en zit daar een termijn-nudge op ("verslag nog concept na X dagen")? +7. Dienstgrenzen (07:00 e.d.) per instelling of per afdeling configureerbaar? + +--- + +## Bronnen (samengevat) + +**Wet & toezicht:** [KNMG-richtlijn Omgaan met medische gegevens 2024](https://www.knmg.nl/download/knmg-richtlijn-omgaan-met-medische-gegevens-2) · [KNMG wijzigingen WGBO](https://www.knmg.nl/advies-richtlijnen/dossiers/behandelingsovereenkomst-wgbo/wijzigingen-wgbo.htm) · [Rijksoverheid bewaartermijn](https://www.rijksoverheid.nl/onderwerpen/rechten-van-patient-en-privacy/uw-medisch-dossier/bewaren-medisch-dossier) · [AP rechten dossier](https://www.autoriteitpersoonsgegevens.nl/en/themes/health/health-data-in-a-file/rights-regarding-the-health-data-file) · [Eldermans & Geerts Wabvpz](https://www.eldermans-geerts.nl/per-1-juli-2020-het-recht-op-elektronische-inzage-in-en-afschrift-van-het-medisch-dossier/) · [Holla Wkkgz-meldingen](https://www.holla.nl/nieuws/wkkgz-meldingen) · [IGJ calamiteiten](https://www.igj.nl/onderwerpen/klachten-en-melden/calamiteiten/melding-doen-van-een-calamiteit) · [IGJ suïcidemeldingen ggz](https://www.igj.nl/onderwerpen/klachten-en-melden/suicidepoging) · [Landelijk Kwaliteitsstatuut GGZ 4.0](https://www.zorginzicht.nl/binaries/content/assets/zorginzicht/kwaliteitsinstrumenten/landelijk-kwaliteitsstatuut-ggz-4.0.pdf) · [ZPM spelregels registreren/declareren](https://www.zorgprestatiemodel.nl/content/uploads/2023/07/20230628-Spelregels-correct-registreren-en-declareren_definitief.pdf) + +**Beroepsgroepen:** [V&VN Richtlijn Verslaglegging](https://kennisplatform.venvn.nl/onderwerp/verslaglegging/) · [NIP dossiervorming](https://www.psynip.nl/uw-beroep/cotan/cotan-beoordelingssysteem-ast-en-beroepscode/algemene-standaard-testgebruik-nip-2017/2-2-onderzoeksprocedure/2-2-2-dossiervorming/) · [NIP persoonlijke werkaantekeningen](https://www.psynip.nl/uw-beroep/cotan/cotan-beoordelingssysteem-ast-en-beroepscode/algemene-standaard-testgebruik-nip-2017/2-2-onderzoeksprocedure/2-2-4-persoonlijke-werkaantekeningen/) · [NVvP wet- en regelgeving](https://www.psychiatrie.nl/alle-themas/wet-en-regelgeving/) · [Verenso handreiking MDO](https://www.verenso.nl/kwaliteit/richtlijnen-en-praktijkvoering/richtlijnendatabase/multidisciplinair-overleg-mdo) + +**Methodieken:** [Verslagleggen in de GGZ — SOEP](https://www.verslagleggenindeggz.nl/blog/de-soep-methode-hoe-pas-je-die-toe-in-een-voortgangsrapportages/) · [Verslagleggen in de GGZ — SOEP/SOAP/TIP](https://www.verslagleggenindeggz.nl/blog/soep-soap-of-tip-wat-is-een-handige-opbouw-voor-clientrapportages/) · [NHG SOEP-verslag](https://referentiemodel.nhg.org/node/19) · [Headway DAP notes](https://headway.co/resources/dap-note) · [SimplePractice DAP](https://www.simplepractice.com/resource/how-to-write-dap-notes/) · [eNurse SBAR](https://enurse.nl/2019/02/24/sbar-methode/) + +**ECD-praktijk:** [Nedap Ons cliëntrapportages](https://support.nedap-ons.nl/support/solutions/articles/103000266173-cli%C3%ABntrapportages-in-ons-dossier-toegelicht) · [Nedap Ons rapporttypen configureren](https://support.nedap-ons.nl/support/solutions/articles/103000265596-rapportages-voor-ons-dossier-configureren) · [Nedap Ons rapporteren op doelen](https://support.nedap-ons.nl/support/solutions/articles/103000266084-rapporteren-op-doelen-en-voortgang-in-het-zorgplan-in-ons-dossier) · [Carecheck verslaglegging GGZ](https://carecheck.nl/verslaglegging-in-ggz/) · [Kuno behandelverslag](https://www.heykuno.com/nl/blog/ggz-behandelverslag/) · [Nictiz eOverdracht](https://www.nictiz.nl/informatiestandaarden/eoverdracht/) · [Medicore ECD](https://medicore.nl/medicore-producten/ecd/) diff --git a/docs/datamodel/onderzoek/onderzoek-standaarden.md b/docs/datamodel/onderzoek/onderzoek-standaarden.md new file mode 100644 index 0000000..f391e62 --- /dev/null +++ b/docs/datamodel/onderzoek/onderzoek-standaarden.md @@ -0,0 +1,214 @@ +# Onderzoek NL-zorgstandaarden als checklist voor het ECD-datamodel + +**Status:** onderzoeksrapport (agent "standaarden") +**Datum:** 18 juli 2026 +**Kader:** conform discovery §4 — standaarden zijn *koppelvlak- en uitwisselingszaken*, geen datamodel-regels. Doel per standaard: welke velden/structuren moet ons eigen model minimaal kunnen uitdrukken om later goedkoop te koppelen. Geen enkele standaard wordt als model overgenomen. + +--- + +## 1. Leeswijzer en conclusie vooraf + +Per standaard beantwoordt dit rapport drie vragen: (a) wat is het en wie beheert het, (b) welke structuren/velden zijn relevant voor ons domein, (c) wat betekent dat concreet als *checklist* voor het eigen model. Hoofdconclusies: + +1. **zibs zijn de goedkoopste checklist** voor klinische entiteiten — maar let op: de zib-wereld is in beweging (publicatie 2024 verving o.a. de zib Probleem door Diagnose/Symptoom/AandoeningOfGesteldheid), terwijl de FHIR-implementatiewereld (nl-core) nog op zib-release **2020** zit. Semantisch aansluiten bij zibs ≈ automatisch goedkoop richting FHIR. +2. **Er bestaat géén zib "Verwijzing".** Verwijzing is in NL geregeld via NZa-regels + veldafspraken (verwijstypen 01–07) en de NHG-richtlijn informatie-uitwisseling huisarts-ggz. Onze VERWIJZER/AANMELDING-entiteiten moeten die administratieve werkelijkheid kunnen uitdrukken, niet een zib. +3. **Het Zorgprestatiemodel stelt de hardste eisen** aan wat het model straks moet kunnen: zorgtrajectnummer, prestatiecode-bepalende velden per consult (beroep, type, duur, setting), verwijstype, zorglabels en zorgvraagtypering (HoNOS+). Dit valideert het besluit ZORGEPISODE als eigen entiteit (§5.1 #9) — mits de episode later een ZPM-zorgtraject kan dragen. +4. **DSM-5-TR → ICD-10**: de officiële afleiding bestaat als beheerde codelijst (WHO-FIC Collaborating Centre NL / RIVM). Het discovery-besluit "DSM als bronregistratie, ICD-10 afgeleid via mappingtabel" (§5.4 #1) is exact hoe de keten het bedoeld heeft. Twee risico's: licentie (DSM is auteursrechtelijk van APA/Boom) en versiegeldigheid van de codelijst. +5. **Koppeltaal 2.0** raakt het ECD-model nauwelijks in de kern — het is een FHIR R4-berichtendomein voor eHealth-taken. Vereist wel: stabiele ID's voor cliënt/behandelaar en het kunnen uitdrukken van "toegewezen eHealth-activiteit" (raakt de behandelplan-interventies). +6. **iWmo/iJw** (gemeente-gefinancierde zorg, o.a. jeugd-GGZ) valideert het besluit "wettelijk kader hoort bij de aanmelding" (§5.0 #4): per kader gelden totaal andere administratieve objecten (beschikking/toewijzing/305-307-berichten i.p.v. verwijsbrief/zorgtraject). + +--- + +## 2. Zibs / Nictiz + +### 2.1 Wat en wie + +Zorginformatiebouwstenen (zibs) zijn semantische definities van klinische begrippen (datamodel + waardelijsten + terminologiebindingen), beheerd door **Nictiz**. Vastgesteld als nationale standaard (Informatieberaad Zorg, 2018). Publicaties per jaar; de actuele grote release is **zib-publicatie 2024**; de meeste implementaties (FHIR nl-core, MedMij) zijn nog gebaseerd op **release 2020** (en Basisgegevens GGZ zelfs op 2017). +Bronnen: [Nictiz — wat is een zib](https://www.nictiz.nl/wat-we-doen/activiteiten/zibs/wat-is-een-zib/) · [zibs.nl publicatie 2024](https://www.zibs.nl/wiki/ZIB_Publicatie_2024(NL)) · [Registratie aan de bron](https://www.registratieaandebron.nl/zorginformatiebouwstenen) + +**Belangrijke wijziging in publicatie 2024** ([bron](https://www.zibs.nl/wiki/ZIB_Publicatie_2024(NL))): de zib **Probleem is vervallen** en vervangen door drie nieuwe zibs: **AandoeningOfGesteldheid v1.1**, **Diagnose v2.0** en **Symptoom v2.0**. Ook AllergieIntolerantie en MedicatieContraIndicatie zijn vervangen. Relevante versies 2024: Patient v4.3, Contactpersoon v5.0, Behandeldoel v4.0, BehandelAanwijzing2 v2.1, TekstUitslag v4.4, Zorgverlener v4.0.1, Zorgaanbieder v3.6, Contact v7.0. + +### 2.2 Relevante zibs per onderwerp (checklist) + +**zib Patient v4.3** ([zibs.nl](https://zibs.nl/wiki/Patient-v4.3(2024NL))) — kernelementen: Identificatienummer 0..* (BSN via OID 2.16.840.1.113883.2.4.6.3), Naamgegevens 1 (achternaam, voorvoegsels, voornamen, initialen, roepnaam), Adresgegevens 0..*, Contactgegevens 0..1 (telefoon/e-mail), Geboortedatum 1, Geslacht 1 (codelijst), **Genderidentiteit 0..1** (nieuw t.o.v. oudere versies), MeerlingIndicator, OverlijdensIndicator + DatumOverlijden. +→ *Checklist PERSOON*: BSN als herhaalbaar identificatienummer-patroon (wij: één BSN, versleuteld — voldoende), gestructureerde naam (niet één naamveld!), meerdere adressen mogelijk, geslacht als code uit waardelijst, overlijden als expliciet feit. Genderidentiteit apart van geslacht is voor GGZ relevant — overwegen als optioneel veld. + +**zib Contactpersoon v5.0** ([2024-publicatie](https://www.zibs.nl/wiki/ZIB_Publicatie_2024(NL)); v4.1-pagina: [zibs.nl](https://www.zibs.nl/wiki/Contactpersoon-v4.1(2024NL))) — kern: naam/adres/contactgegevens + **twee gescheiden assen**: *Relatie* (echtgenoot, ouder, kind, …) en *Rol* (eerste contactpersoon, wettelijk vertegenwoordiger, mantelzorger, …), beide uit waardelijsten. +→ *Checklist*: het discovery-besluit PERSOON/rollen (§5.1 #1) past hier perfect: contactpersoon wordt straks een rol op PERSOON met twee attributen (relatie + rol) uit waardelijsten — niet één "type"-veld. Wettelijk vertegenwoordiger is in de GGZ (Wvggz, WGBO, jeugd) een must-have rolwaarde. + +**Verwijzing — géén zib.** In geen enkele zib-publicatie bestaat een bouwsteen "Verwijzing". De inhoudseisen aan een verwijzing komen uit de **NHG-richtlijn Informatie-uitwisseling huisarts-ggz** en de **verwijsafspraken ggz** (veldafspraak zorgprestatiemodel): AGB-code verwijzer, (vermoeden van) DSM-benoemde psychische stoornis, echelon (basis-ggz vs gespecialiseerde ggz), en of het een heraanmelding betreft; geldigheid **9 maanden (275 dagen) tot aanmelddatum** bij de GGZ-aanbieder. Bij onvolledige verwijzing: inspanningsverplichting om info op te halen, aantoonbaar in dossier, behandeling mag wel starten. +Bronnen: [LVVP — eisen verwijsbrief](https://lvvp.info/nieuws/welke-eisen-worden-gesteld-aan-de-verwijsbrief/) · [Verwijsafspraken GGZ (zorgprestatiemodel, nov 2021)](https://www.zorgprestatiemodel.nl/content/uploads/2021/11/Verwijsafspraken-GGZ_november-2021.pdf) · [Zilveren Kruis — verwijzing naar GGZ](https://www.zilverenkruis.nl/zorgaanbieders/zorgsoort/ggz/declareren/verwijzing-naar-ggz) +→ *Checklist AANMELDING/VERWIJZER*: verwijsdatum én aanmelddatum apart (geldigheidstoets), AGB verwijzer + AGB praktijk (besluit §5.1 #3 klopt), echelon-indicatie, heraanmelding-vlag, vermoeden-DSM-stoornis (vrije tekst of code), en het ZPM-**verwijstype** (zie §6.3) als af te leiden/registreerbaar gegeven. + +**Probleem → Diagnose v2.0 (2024)** ([zibs.nl](https://www.zibs.nl/wiki/Diagnose-v2.0(2024NL))) — kern: DiagnoseNaam 1..* (codelijst: SNOMED CT, ICD-10-nl, DHD-thesaurus, ICF, ICPC-1, **DSM-IV/DSM-5** worden expliciet genoemd als toegestane codesystemen), DiagnoseStatus 1 (codelijst), DiagnoseDatum 1, DiagnoseSteller 1 (verwijzing Zorgverlener), WijzeVanVaststellen 1, Toelichting 0..1, Aanleiding 0..*, AandoeningOfGesteldheid 0..1 (verwijzing). De oude zib Probleem (2020, gebruikt in Basisgegevens GGZ) kende daarnaast ProbleemType, ProbleemStatus (actueel/niet-actueel) en verificatiestatus-achtige noties die in FHIR als `clinicalStatus`/`verificationStatus` terugkomen. +→ *Checklist DIAGNOSE*: code + codesysteem + weergavenaam als drieluik (nooit alleen een code-string), datum gesteld, steller, status als aparte as, toelichting. Het discovery-model (§5.4: verificatiestatus + klinische status als twee assen) is uitdrukbaar in zowel zib 2020 (Probleem) als FHIR Condition — houden zo. DSM-5 is een erkend codesysteem binnen de zib — DSM-bronregistratie botst dus niet met de standaardenwereld. + +**zib Behandeldoel v4.0** ([zibs.nl](https://www.zibs.nl/wiki/Behandeldoel-v4.0(2024NL))) — kern: **GewenstZorgresultaat** (tekstuele weergave van het doel), GewensteGezondheidstoestand 0..1 (verwijzing FunctioneleOfMentaleStatus), en een RedenBehandeling-container die verwijst naar Diagnose / Symptoom / VerpleegkundigeDiagnose / OvergevoeligheidIntolerantie. Oudere versie (2020, in FHIR nl-core) kent ook streefdatum en evaluatie-relatie. +→ *Checklist BEHANDELDOEL*: doel als tekst + expliciete **relatie doel → diagnose/probleem** (discovery-feitzin §5.5 #3 "adresseert diagnose F32.1" — dit moet een echte FK-relatie worden, geen tekst), streefdatum/termijn, en evalueerbaarheid. Onze extra's (cliëntversie B1, prioriteit, leefgebied) zijn eigen verrijkingen bovenop het zib-minimum — prima, exporteerbaar als extensie. + +**zib BehandelAanwijzing2 v2.1** ([zibs.nl](https://www.zibs.nl/wiki/BehandelAanwijzing2-v2.1(2024NL))) — kern: Behandeling (codelijst: reanimatie, beademing, opname, …), BehandelBesluit (wel uitvoeren / niet uitvoeren / anders + SpecificatieAnders), MeestRecenteBespreekdatum, DatumBeeindigd + RedenBeeindigd, AfspraakPartij (patiënt / vertegenwoordiger / zorgverlener). Hangt samen met zib Wilsverklaring. +→ *Checklist*: behandelaanwijzingen/wilsverklaringen zitten nog niet in discovery-ronde 1 — voor een GGZ-ECD (crisis, Wvggz-context) op de roadmap zetten. Minimum om te kunnen uitdrukken: welke behandeling, wel/niet-besluit, met wie afgesproken, laatst besproken, beëindigd + reden. Let op patroon: **"anders"-optie met specificatieveld** i.p.v. dichte enum — nuttig ontwerppatroon voor onze waardelijsten. + +**zib TekstUitslag v4.4** ([zibs.nl](https://www.zibs.nl/wiki/TekstUitslag-v4.4(2024NL))) — kern: TekstResultaat 1 (het verslag), TekstUitslagType 1 (code), TekstUitslagDatumTijd 0..1, TekstUitslagStatus 0..1 (pending/preliminary/**final**/corrected), Verrichting 0..1, VisueelResultaat 0..*. +→ *Checklist VERSLAG/RAPPORTAGE* (latere ronde): elk verslag heeft type (code), datum, status met minimaal concept/definitief/gecorrigeerd, en gestructureerde koppeling aan de aanleiding. Sluit naadloos aan op discovery-spelregel 3.1 #2 (provenance) en de AI-voorbereiding (tekst + structuur in één record). + +**GGZ-specifiek relevant, buiten de gevraagde lijst:** de **Basisgegevens GGZ** (MedMij/PGO-informatiestandaard, [functioneel ontwerp](https://informatiestandaarden.nictiz.nl/wiki/MedMij:V2020.01/OntwerpGGZ), [inhoud](https://informatiestandaarden.nictiz.nl/wiki/MedMij:V2019.01_InhoudGGZ)) is een selectie van ~24 zibs (release 2017) die de sector zelf als kern voor GGZ-uitwisseling heeft aangemerkt: Patient, Burgerlijke Staat, Betaler, **BehandelAanwijzing, Wilsverklaring, JuridischeStatus** (Wvggz!), Contactpersoon, FunctioneleOfMentaleStatus, Probleem, **Drugsgebruik, Alcoholgebruik, Tabakgebruik**, Woonsituatie, Gezinssituatie(Kind), Taalvaardigheid, ParticipatieInMaatschappij, HulpVanAnderen, LaboratoriumUitslag, AlgemeneMeting, Verrichting, TekstUitslag, Zorgverlener, Zorgaanbieder. Dit is de facto de checklist "wat verwacht een PGO/keten van een GGZ-dossier". +→ Voor ons model: **JuridischeStatus** (rechterlijke machtiging/Wvggz-maatregel) en de leefstijl-/sociale-context-zibs zijn kandidaten voor latere rondes; nu alleen borgen dat het model ze als aparte entiteiten kán toevoegen (episodisch, met begin/eind en bron). + +--- + +## 3. FHIR NL: zib-profielen en nl-core (R4) + +**Wat en wie:** Nictiz publiceert twee R4-packagelijnen op basis van zib-release 2020, gevet door HL7 Nederland ([GitHub Nictiz-R4-zib2020](https://github.com/Nictiz/Nictiz-R4-zib2020)): + +- `nictiz.fhir.nl.r4.zib2020` — 1-op-1 representatie van de zibs, **abstract**, niet voor implementatie ([Simplifier](https://simplifier.net/packages/nictiz.fhir.nl.r4.zib2020/0.11.0-beta.1)). +- `nictiz.fhir.nl.r4.nl-core` — de **implementatieprofielen** (afgeleid van de zib-profielen, verrijkt met o.a. expliciete Patient-referenties), dit is wat je in een adapter gebruikt ([Simplifier](https://simplifier.net/packages/nictiz.fhir.nl.r4.nl-core/) · voorbeeld [nl-core-Patient](https://hl7nl.github.io/Nictiz-R4-zib2020-IG/StructureDefinition-nl-core-Patient.html)). + +Relevante mapping onderwerpen → FHIR-resources: Patient→`Patient` (nl-core-Patient), Contactpersoon→`Patient.contact`/`RelatedPerson`, Probleem/Diagnose→`Condition` (met `clinicalStatus` + `verificationStatus` — exact onze twee status-assen), Behandeldoel→`Goal`, BehandelAanwijzing→`Consent`/ACP-profielen, TekstUitslag→`DiagnosticReport`/`DocumentReference`, Verwijzing→`ServiceRequest` (geen zib, wel een FHIR-resource), Zorgepisode→`EpisodeOfCare`, Behandelplan→`CarePlan`. + +→ *Checklist (adapter-goedkoopte, geen modeleis):* +- Overal **stabiele UUID's** (spelregel 3.1 #5) — FHIR-resources hebben een logical id nodig; onze UUID's kunnen 1-op-1 mee. +- Codes altijd als **(code, systeem, display)** opslaan, nooit alleen een label — FHIR `CodeableConcept` is dan een triviale projectie. Dit is dezelfde les als spelregel 3.1 #1 (waardelijsten als data): geef elke waardelijstrij optioneel een externe code + codesysteem-URI kolom. +- Status-assen gescheiden houden (klinisch vs verificatie op diagnose; plan-status vs cliëntakkoord op behandelplan — FHIR CarePlan kent `status` en separaat `Consent`). +- Versies: nl-core R4 is formeel nog beta (0.x); niets in beton gieten, alleen semantisch niet nodeloos afdrijven (discovery §4, aandachtspunt). + +--- + +## 4. Koppeltaal 2.0 (GGZ-specifiek) + +**Wat en wie:** Koppeltaal is de GGZ-standaard + infrastructuur (beheer: **VZVZ**, eigenaar: Stichting Koppeltaal) om **EPD/ECD, ROM-vragenlijsten en eHealth-modules** binnen een "domein" van een zorgaanbieder te verbinden. Koppeltaal 2.0 is **FHIR R4**-gebaseerd; profielen staan op Simplifier ([project koppeltaalv2.0](https://simplifier.net/koppeltaalv2.0)), documentatie in de [Koppeltaal 2.0 Dev Guide](https://vzvz.gitbook.io/koppeltaal-2.0-dev-guide) en op [koppeltaal.nl](https://www.koppeltaal.nl/koppeltaal/koppeltaal-20). + +**Kernresources/-profielen** (KT2-profielen, o.a. KT2Patient, KT2Practitioner, KT2CareTeam, KT2Task, [KT2ActivityDefinition](https://simplifier.net/Koppeltaal/KoppeltaalActivityDefinition), Endpoint, Device, Subscription; de server dwingt profielconformiteit af, bijv. op [KT2Task](https://vzvz.gitbook.io/koppeltaal-2.0-dev-guide/hackathon-use-cases/crud-task/use-case-1-opvoeren-taak)): + +- **ActivityDefinition** — een aanbieder publiceert een eHealth-activiteit (module, vragenlijst, game) domeinbreed. +- **Task** — toewijzing van zo'n activiteit aan een specifieke cliënt, altijd gebaseerd op een ActivityDefinition; aangemaakt vanuit het behandelaarportaal/ECD. Status-lifecycle: `completed|cancelled|failed|rejected`; resources worden **niet verwijderd** maar end-of-life gemarkeerd (ActivityDefinition `retired`) — zelfde filosofie als onze soft delete ([resources managen](https://vzvz.gitbook.io/koppeltaal-2.0-dev-guide/technische-howto/resources-managen)). +- **Patient/Practitioner/RelatedPerson/CareTeam** — identiteiten binnen het domein; **Endpoint/Device** — aangesloten applicaties; **Subscription** — notificaties bij wijzigingen; launch via **HTI** (Health Tools Interoperability, JWT-gebaseerd). + +→ *Checklist ECD-model:* +1. CLIENT en BEHANDELAAR moeten met stabiele ID's naar buiten te refereren zijn (hebben we: UUID's) en Koppeltaal-traceerbaarheid vergt geen extra ECD-velden. +2. Het behandelplan-deel "interventies" (discovery §5.5) moet een interventie kunnen typeren als **eHealth-activiteit met externe referentie** (welke module, bij welke aanbieder, welke taak-status). Minimaal: een optioneel extern-referentieveld (systeem + id) op interventie/activiteit — dan is een Koppeltaal-adapter later een dunne laag. +3. Taak-/opdrachtstatus (toegewezen → gestart → afgerond/geannuleerd) is proces-state: kandidaat-**TIP-terrein** (grensvlak §3.3), niet per se ECD. De klinische uitkomst (bijv. ROM-uitslag) is wél ECD. + +--- + +## 5. DSM-5-TR → ICD-10-mapping + +**Hoe de officiële mapping werkt en wie hem beheert:** + +- De **DSM-5-TR** zelf is van de American Psychiatric Association; de Nederlandse vertaling/uitgave ligt bij **Boom uitgevers** ([boom.nl](https://www.boom.nl/psychiatrie/101-4_DSM-5-TR)). Auteursrecht en licenties lopen via APA/Boom — de Nederlandse Staat had voor keten-gebruik een licentie ([whofic.nl](https://www.whofic.nl/dsm-5icd-10)). +- Het **WHO-FIC Collaborating Centre Nederland** (ondergebracht bij RIVM) beheert en publiceert de **"Codelijst DSM-5(-TR) met ICD-10 afleidingen"**: per DSM-classificatie de afgeleide ICD-10-code(s) ([whofic.nl — DSM-5/ICD-10](https://www.whofic.nl/dsm-5icd-10) · [codelijst-document](https://www.whofic.nl/documenten/codelijst-dsm-5-tr-met-icd-10-afleidingen)). De DSM-5-TR gebruikt in de VS ICD-10-CM-codes; de NL-codelijst leidt af naar ICD-10 zoals in NL gebruikt. De lijst is versioneerd met ingangs- en einddatum (bijv. publicatie 15-12-2023, geldig per 1-1-2024; die versie is niet meer geldig vanaf 1-1-2026 — er verschijnen dus periodiek opvolgers). +- Gebruiksbeperking: de codelijst mag **uitsluitend** worden gebruikt in "declaratie-, registratie- en informatiesystemen van de ggz en fz" ten behoeve van NZa-regelgeving; ander gebruik vereist afstemming met Boom ([whofic.nl](https://www.whofic.nl/documenten/codelijst-dsm-5-tr-met-icd-10-afleidingen)). +- Voor de relatie met NZa-diagnosehoofdgroepen bestaat aanvullend een afleiding DSM-hoofdgroep/ICD-10 → NZa-diagnosehoofdgroep ([beta.nl/ggzdiagnosen](https://beta.nl/ggzdiagnosen/)). + +**Registratieplicht in beweging (2024–2028):** de NZa-verplichting om DSM-gegevens op de factuur te zetten kreeg per 1-1-2025 een juridisch probleem (grondslag); sindsdien geldt: aanlevering DSM-hoofdgroep aan verzekeraars alleen met **getekende toestemmingsverklaring (opt-in)** van de patiënt, en VWS heeft per aparte ministeriële regeling ([Stcrt. 2025-11955](https://zoek.officielebekendmakingen.nl/stcrt-2025-11955.html)) de registratie-/aanleverplicht van DSM-hoofdgroep of basis-ggz-profiel op de factuur richting 2027/2028 geborgd. In het risicoverevenings-/bekostigingsdenken vervangt **zorgvraagtypering** op termijn de DSM-rol ([zorgprestatiemodel.nl — opt-in](https://www.zorgprestatiemodel.nl/nieuws/tijdelijke-toestemmingsverklaring-opt-in-voor-vermelden-van-gegevens-over-de-dsm-hoofdgroepdiagnose-of-het-basis-ggz-profiel-op-factuur/) · [zorgictzorgen.nl](https://zorgictzorgen.nl/grondslag-ggz-declaraties-per-2025-durft-nza-niet-te-wijzigen/)). + +→ *Checklist DIAGNOSE (bevestigt discovery-besluit §5.4 #1, met verscherping):* +1. **Bronregistratie in DSM-5-TR-termen; ICD-10 afgeleid via de WHO-FIC-codelijst** — als geïmporteerde, **versioneerde referentietabel** (kolommen minimaal: DSM-code, DSM-omschrijving, afgeleide ICD-10-code(s), ingangsdatum, einddatum, lijstversie). Nooit hardcoden. +2. Op het diagnoserecord vastleggen **met welke lijstversie** de afleiding is gedaan (reproduceerbaarheid bij herdeclaratie/controle). +3. Eén DSM-code kan naar **meerdere ICD-10-codes** leiden (en vice versa) — de mappingrelatie is m:n; het model moet dat aankunnen (aparte mappingtabel, geen kolom "icd10_code" met uniciteitsaanname). +4. **DSM-hoofdgroep** als afleidbaar gegeven (voor factuur/verzekeraar) + een plek voor de **opt-in-toestemmingsverklaring** van de cliënt (raakt privacy/toestemmings-entiteit — latere ronde, wel als open punt noteren). +5. **Open punt licentie (nieuw, geen teruggedraaid besluit):** commercieel gebruik van DSM-omschrijvingen in een ECD-product vereist vermoedelijk een licentie bij Boom/APA; de WHO-FIC-codelijst is alleen vrij voor NZa-doeleinden. Uitzoeken vóór de bouw van de diagnosemodule. + +--- + +## 6. Zorgprestatiemodel (ZPM) + +**Wat en wie:** bekostigingsmodel ggz/fz sinds 2022 (NZa + veldpartijen; programma [zorgprestatiemodel.nl](https://www.zorgprestatiemodel.nl)). Regelgeving: jaarlijkse NZa-regeling — voor 2026 is dat **NR-REG-2616b** met tariefbeschikking TB/REG-26628-05 en "Codetabel prestaties en tarieven v20260401" ([NZa — regels 2026](https://www.nza.nl/zorgsectoren/geestelijke-gezondheidszorg-ggz-en-forensische-zorg-fz/nieuwe-bekostiging-ggz/welke-regels-gelden-voor-de-ggz-en-fz-in-2026) · [Registreren en declareren ggz/fz](https://www.nza.nl/zorgsectoren/geestelijke-gezondheidszorg-ggz-en-forensische-zorg-fz/nieuwe-bekostiging-ggz) · [Veldafspraken 2025](https://www.zorgprestatiemodel.nl/content/uploads/2025/01/20250131-Veldafspraken-2025-.pdf)). + +### 6.1 Zorgtraject + +Het zorgtraject start zodra een patiënt zich met een psychische zorgvraag meldt; de aanbieder kent een **zorgtrajectnummer** toe en koppelt dat aan **alle** ggz-prestaties voor die patiënt tot einde behandeling. Bij terugval/recidive **binnen een jaar** na de laatste prestatie moet **hetzelfde zorgtrajectnummer** hergebruikt worden ([NZa](https://www.nza.nl/zorgsectoren/geestelijke-gezondheidszorg-ggz-en-forensische-zorg-fz/nieuwe-bekostiging-ggz)). Prestaties zijn dag-gebonden (losse consulten/verblijfsdagen), niet 365-dagen-trajecten zoals de oude DBC's ([Informatiekaart ZPM](https://puc.overheid.nl/nza/doc/PUC_646523_22/)). + +→ *Checklist ZORGEPISODE*: de episode moet een **zorgtrajectnummer** kunnen dragen (uniek per cliënt-aanbieder-zorgvraag), en de "terugval binnen 1 jaar → zelfde nummer"-regel betekent: episode-einde is geen harde afsluiting van het trajectnummer. Modelmatig: zorgtraject (ZPM/declaratie-object) en zorgepisode (klinisch object) zijn **verwant maar niet identiek** — discovery §5.1 #9 zegt al "sluit straks aan op het ZPM-zorgtraject (declaratie-ronde)"; dit onderzoek bevestigt: maak het t.z.t. een aparte, gekoppelde entiteit, forceer geen 1-op-1. + +### 6.2 Prestatiecodes + +De prestatiecode + het tarief van een consult wordt bepaald door **vier assen**: (1) **beroepscategorie** van de uitvoerder (NZa-beroepenlijst), (2) **type consult** (diagnostiek of behandeling), (3) **duur** (tijdsklassen "vanaf x minuten"; registratie op basis van *geplande* tijd, "planning = realisatie"-principe), (4) **setting** (o.a. ambulant kwaliteitsstatuut sectie II/III, outreachend, klinisch — met varianten hoog/laag). Daarnaast bestaan aparte prestaties voor verblijfsdagen, toeslagen (o.a. tolk) e.d. Codetabellen worden door de NZa gepubliceerd ([Toelichting tabellen ggz/fz](https://www.zorgprestatiemodel.nl/shared/content/uploads/2021/09/20210927-Toelichting-tabellen.pdf) · [Codetabel prestaties en tarieven](https://puc.overheid.nl/nza/doc/PUC_641029_22/4/) · [beroepenlijst-wijzigingen](https://www.zorgprestatiemodel.nl/nieuws/aanpassingen-in-beroepenlijst/)). + +→ *Checklist* (grotendeels agenda/declaratie-rondes, maar het fundament ligt nu): +- Elk contactmoment/consult moet kunnen vastleggen: **uitvoerder + diens beroep** (referentie naar NZa-beroepenlijst als geïmporteerde tabel), **type** (diagnostiek/behandeling), **geplande duur**, **setting** (waardelijst per instelling-locatie). Discovery-contactmomenten (§5.2) hebben al type+uitvoerder; beroep-van-uitvoerder komt via de rollenronde — borgen dat MEDEWERKER straks een beroepscode uit een NZa-lijst kan dragen. +- Prestatie- en tariefcodetabellen zijn **jaarlijks wisselende referentiedata** met geldigheidsperioden → zelfde import-patroon als de DSM-codelijst (versie + ingangs-/einddatum). Nooit hardcoden (spelregel 3.1 #1). + +### 6.3 Verwijstypen en zorglabels + +De ZPM-veldafspraken definiëren **verwijstypen** (registratie naast de AGB-code van de verwijzer) ([Verwijstypen en zorglabels, jan 2022](https://www.zorgprestatiemodel.nl/content/uploads/2022/01/20220111-Verwijstypen-en-zorglabels.pdf)): + +| Nr | Verwijstype | +|---|---| +| 01 | Verwijzing aanwezig (AGB initiële verwijzer) | +| 02 | Doorverwijzing (AGB verwijzende regiebehandelaar) | +| 03 | Geen verwijzing — uitzondering, verlate correspondentie (huisarts binnen 60 dagen informeren) | +| 04 | Geen verwijzing — patiënt staat correspondentie met huisarts niet toe | +| 05 | Geen verwijzing (factuur mag niet naar zorgverzekeraar) | +| 06 | Geen verwijzing, andere rechtmatigheidsgrond (alleen fz) | +| 07 | Verwijzing aanwezig, maar verwijzer heeft geen AGB-code (bijv. buitenlands) | + +Daarnaast **zorglabels** op prestaties voor uitzonderingssituaties: publiek (N01–N04, NZa-regelgeving: o.a. overgang jeugd-ggz → Zvw, tolk-toeslag), generiek privaat (G01–G04, veldafspraken: o.a. uitzonderingen "minimale betrokkenheid regiebehandelaar", Ketenveldnorm levensloopfunctie, acute ggz buiten budget) en specifiek privaat (S01–S03, contractueel: o.a. digitale zorg). + +→ *Checklist*: het discovery-verwijzertype (huisarts/specialist/gemeente/zelfaanmelding/… §5.0 #3) is een **ander begrip** dan het ZPM-verwijstype (01–07, een declaratiestatus van de verwijzing). Beide nodig: ons verwijzertype beschrijft *wie* verwijst; het ZPM-verwijstype is afleidbaar uit (aanwezigheid verwijzing × AGB × toestemmingsvlaggen) maar moet ook overschrijfbaar geregistreerd kunnen worden. Verwijstype 03/04 tonen bovendien twee feiten die het model moet kennen: "huisarts geïnformeerd op datum X" en "cliënt staat correspondentie met huisarts niet toe" (toestemmingsfeit!). Zorglabels: prestatie-niveau, declaratie-ronde — alleen borgen dat prestaties later 0..n labels uit een NZa-tabel kunnen dragen. + +### 6.4 Zorgvraagtypering (HoNOS+) + +Bij de start van een behandeling (en bij herijking) wordt een **HoNOS+**-vragenlijst afgenomen: **19 items, score 0–4** (items 1–12 = klassieke HoNOS, plus 13, A–E en Q). De NZa-**zorgvraagtyperingstool** (een door de NZa gepubliceerd algoritme) adviseert op basis daarvan zorgvraagtypes; de **behandelaar kiest** uiteindelijk zelf het zorgvraagtype (advies is niet bindend). Het zorgvraagtype staat verplicht op de factuur; sinds **1 juli 2023** worden zorgvraagtyperingsgegevens ook verplicht aan de NZa aangeleverd volgens de "Standaard voor gegevensaanlevering zorgvraagtypering" (na een AP-discussie over de rechtmatigheid, met privacyverklaring-optie voor de patiënt) ([Embloom — FAQ HoNOS+](https://www.embloom.nl/veelgestelde-vragen-over-de-honos/) · [NZa zorgvraagtyperingstool](https://www.zorgprestatiemodel.nl/nieuws/zorgvraagtyperingstool-nza-online/) · [algoritmeregister — zorgvraagtypering](https://algoritmes.overheid.nl/nl/algoritme/zb000158/97912239/zorgvraagtypering) · [NZa Q&A aanlevering](https://www.nza.nl/zorgsectoren/geestelijke-gezondheidszorg-ggz-en-forensische-zorg-fz/vraag-en-antwoord/vraag-en-antwoord-zorgvraagtypering) · [Standaard gegevensaanlevering (PUC_705646_22)](https://puc.overheid.nl/PUC/Handlers/DownloadDocument.ashx?identifier=PUC_705646_22&versienummer=2&type=pdf&ValChk=89nf2n26dv_DAuBfeXxRzVtQuS-8DOi3kmeb1fKvw5g1)) + +→ *Checklist* (nieuwe entiteit voor een latere ronde, hangt aan ZORGEPISODE): +- **Meetinstrument-afname** als generiek patroon: instrument (HoNOS+), afnamedatum, afnemer, 19 itemscores (0–4) als gestructureerde data. +- **Zorgvraagtypering** als apart record: geadviseerd(e) type(s) door algoritme (+ tool-/algoritmeversie!), gekozen type door behandelaar, datum, relatie naar de afname. Advies ≠ keuze — twee velden, en dit is een mooi voorbeeld van het provenance-patroon (spelregel 3.1 #2: algoritme-advies = AI/algoritme-herkomst, keuze = mens). +- Aanlevering NZa + privacyverklaring: raakt dezelfde toestemmings-/opt-outstructuur als §5.4 — één toekomstige toestemmingsentiteit kan beide dragen. + +--- + +## 7. iStandaarden: iWmo en iJw (gemeente-gefinancierde zorg) + +**Wat en wie:** de **iStandaarden** worden beheerd door **Zorginstituut Nederland**; iWmo (Wmo 2015) en iJw (Jeugdwet) regelen het berichtenverkeer tussen zorgaanbieders en gemeenten. Actuele release: **iWmo 3.2 / iJw 3.2** (in productie sinds 3 april 2023, big bang; geen nieuwe major release in 2025) ([istandaarden.nl](https://www.istandaarden.nl/) · [release iWmo 3.2](https://www.istandaarden.nl/iwmo/releases/release-iwmo-3.2) · [informatiemodel iJw 3.2](https://informatiemodel.istandaarden.nl/informatiemodel/ijw/3.2/processen/start-melden/)). + +**Berichtenstructuur** (identiek patroon iWmo/iJw, verschil zit in wet, productcodes en enkele velden; [Heldy — iWmo uitgelegd](https://www.heldy.nl/kennisbank/wmo/iwmo-uitgelegd) · [iJw uitgelegd](https://www.heldy.nl/kennisbank/jeugdzorg/ijw-uitgelegd)): + +- **301/302 Toewijzing**: gemeente wijst zorg toe (toewijzingsnummer, productcategorie/productcode, volume/frequentie, begindatum, ev. einddatum). +- **305/306 Start zorg**: aanbieder meldt werkelijke startdatum. **307/308 Stop zorg**: melding einde + reden. +- **303/304 Declaratie**: periodiek, per toewijzing; regels met productcode, volume, tarief; gemeente keurt per regel goed/af. +- Daarnaast verzoek om toewijzing (315/316) en regieberichten ([analyse regieberichten](https://www.istandaarden.nl/binaries/content/assets/istandaarden/iwmo/iwmo-3.1.0/analyse-08---regieberichten.pdf)). +- **Uitvoeringsvarianten**: inspanningsgericht (p×q), outputgericht, taakgericht — bepalen welke berichten gebruikt worden ([handreiking uitvoeringsvarianten](https://www.istandaarden.nl/binaries/content/assets/istandaarden/iwmo/handreiking-uitvoeringsvarianten-iwmo-en-ijw.pdf)). + +→ *Checklist* (valideert discovery §5.0 #4: wettelijk kader op de aanmelding): +1. Bij kader **Jeugdwet/Wmo** is de "verwijzing" feitelijk een **beschikking/toewijzing van de gemeente** met eigen sleutelgegevens: gemeente(code), toewijzingsnummer, productcategorie + productcode, toegewezen volume/frequentie, begin-/einddatum. Discovery-besluit §5.1 #7 ("beschikking telt ook als verwijzing", waardelijst `verwijsdocument_type`) klopt — maar de toewijzing heeft méér structuur dan een document: op termijn een eigen entiteit TOEWIJZING onder de aanmelding/episode bij gemeentelijk kader. +2. Start-/stopdatum van zorg moeten als harde feiten uit het model af te leiden zijn (305/307 zijn direct te genereren uit episode-start en -einde + reden — beëindigingsredenen-waardelijst van de episode moet t.z.t. mappen op de iStandaarden-redenenlijst). +3. Productcodes zijn (net als ZPM-tabellen en DSM-codelijst) **externe, versioneerde referentiedata** per gemeente/regio — zelfde importpatroon. +4. Woonplaatsbeginsel (jeugd): de verantwoordelijke gemeente is een gegeven aan de aanmelding-/financieringskant, niet aan de persoon. + +--- + +## 8. Geconsolideerde checklist per discovery-entiteit + +| Entiteit (discovery) | Moet minimaal kunnen uitdrukken (uit standaarden) | Bron | +|---|---|---| +| PERSOON | gestructureerde naamdelen; meerdere adressen; geslacht (code) + evt. genderidentiteit; overlijden(sdatum); BSN als geïdentificeerd nummer | zib Patient | +| Rol CONTACTPERSOON (later) | relatie én rol als twee aparte waardelijsten; wettelijk vertegenwoordiger | zib Contactpersoon | +| VERWIJZER / PRAKTIJK | AGB persoonlijk + AGB praktijk (besloten); verwijzertype; "verwijzer zonder AGB" mogelijk | ZPM-verwijsafspraken | +| AANMELDING | verwijsdatum ≠ aanmelddatum (275-dagen-toets); echelon; heraanmelding-vlag; ZPM-verwijstype 01–07 (afleidbaar/overschrijfbaar); huisarts-geïnformeerd-feit + correspondentie-toestemming; bij Wmo/Jw: toewijzingsgegevens (gemeente, nummer, productcode, volume, periode) | ZPM, NHG, iWmo/iJw | +| ZORGEPISODE | koppelbaar aan ZPM-zorgtrajectnummer (hergebruik bij terugval <1 jaar → geen 1-op-1); start/stop + reden (iStd 305/307); draagt zorgvraagtypering | NZa-regeling, iStandaarden | +| DIAGNOSE | (code, systeem, display)-drieluik; DSM-bron + versioneerde m:n-mapping naar ICD-10 incl. lijstversie op het record; DSM-hoofdgroep afleidbaar; steller + datum + wijze van vaststellen; twee status-assen | zib Diagnose, WHO-FIC-codelijst, NZa | +| BEHANDELPLAN / DOEL | doel-tekst + FK naar diagnose; streefdatum/termijn; planstatus ≠ cliëntakkoord (apart feit); interventie met optionele externe eHealth-referentie (systeem+id) | zib Behandeldoel, FHIR CarePlan/Consent, Koppeltaal | +| CONTACTMOMENT (agenda-ronde) | uitvoerder + beroepscode (NZa-lijst); type diagnostiek/behandeling; geplande duur; setting; 0..n zorglabels | ZPM-codetabellen | +| Waardelijsten algemeen | elke rij optioneel externe code + codesysteem-URI + geldigheidsperiode; "anders"-optie met specificatieveld waar zinvol | zibs/FHIR-patroon | +| Referentiedata-import (nieuw patroon) | één generiek mechanisme voor versioneerde externe tabellen: DSM/ICD-10-codelijst, NZa-prestatie-/beroepen-/zorglabeltabellen, iStd-productcodes, AGB (al geparkeerd) — met versie, ingangs- en einddatum | alle | + +## 9. Open vragen en risico's (geen teruggedraaide besluiten) + +1. **DSM-licentie**: gebruik van DSM-5-TR-omschrijvingen in een commercieel ECD vereist vermoedelijk afspraken met Boom/APA; de WHO-FIC-codelijst is alleen vrijgegeven voor NZa-doeleinden. Uitzoeken vóór de diagnosemodule-bouw. +2. **Geldigheid codelijst DSM→ICD-10 in 2026**: de versie van 15-12-2023 is per 1-1-2026 vervallen; bevestigen welke opvolgerversie nu geldt (whofic.nl) en dat abonneren op updates onderdeel wordt van het referentiedata-importpatroon. +3. **zib 2020 vs 2024**: nl-core (FHIR) volgt zib 2020 (Probleem), de zib-wereld is naar 2024 (Diagnose). Ons model volgt geen van beide letterlijk; bij adapterbouw de dan geldende nl-core-versie kiezen. +4. **Zorgtraject ↔ zorgepisode**: hergebruikregel trajectnummer (<1 jaar terugval) botst mogelijk met een strikte episode-afsluiting — ontwerpbeslissing voor de declaratie-ronde, nu alleen niet-1-op-1 aannemen. +5. **Toestemmingen** duiken op drie plekken op (DSM-hoofdgroep op factuur/opt-in, huisarts-correspondentie bij verwijstype 04, privacyverklaring zorgvraagtypering): pleit voor één generieke TOESTEMMING-entiteit in een latere ronde. +6. **DSM-registratieplicht is politiek in beweging** (grondslag-discussie 2025, zorgvraagtypering als opvolger richting 2028): declaratie-gerelateerde DSM-velden flexibel houden, niets aan de factuurketen hardcoden. + +## 10. Bronnen (hoofdlijst) + +- Nictiz zibs: https://www.nictiz.nl/wat-we-doen/activiteiten/zibs/ · https://www.zibs.nl/wiki/ZIB_Publicatie_2024(NL) · https://zibs.nl/wiki/Patient-v4.3(2024NL) · https://www.zibs.nl/wiki/Diagnose-v2.0(2024NL) · https://www.zibs.nl/wiki/Behandeldoel-v4.0(2024NL) · https://www.zibs.nl/wiki/BehandelAanwijzing2-v2.1(2024NL) · https://www.zibs.nl/wiki/TekstUitslag-v4.4(2024NL) +- FHIR NL: https://github.com/Nictiz/Nictiz-R4-zib2020 · https://simplifier.net/packages/nictiz.fhir.nl.r4.nl-core/ · https://hl7nl.github.io/Nictiz-R4-zib2020-IG/StructureDefinition-nl-core-Patient.html +- Basisgegevens GGZ: https://informatiestandaarden.nictiz.nl/wiki/MedMij:V2020.01/OntwerpGGZ · https://informatiestandaarden.nictiz.nl/wiki/MedMij:V2019.01_InhoudGGZ +- Koppeltaal 2.0: https://www.koppeltaal.nl/koppeltaal/koppeltaal-20 · https://simplifier.net/koppeltaalv2.0 · https://simplifier.net/Koppeltaal/KoppeltaalActivityDefinition · https://vzvz.gitbook.io/koppeltaal-2.0-dev-guide +- DSM/ICD-10: https://www.whofic.nl/dsm-5icd-10 · https://www.whofic.nl/documenten/codelijst-dsm-5-tr-met-icd-10-afleidingen · https://beta.nl/ggzdiagnosen/ · https://zoek.officielebekendmakingen.nl/stcrt-2025-11955.html · https://www.zorgprestatiemodel.nl/nieuws/tijdelijke-toestemmingsverklaring-opt-in-voor-vermelden-van-gegevens-over-de-dsm-hoofdgroepdiagnose-of-het-basis-ggz-profiel-op-factuur/ +- Zorgprestatiemodel/NZa: https://www.nza.nl/zorgsectoren/geestelijke-gezondheidszorg-ggz-en-forensische-zorg-fz/nieuwe-bekostiging-ggz · https://www.nza.nl/zorgsectoren/geestelijke-gezondheidszorg-ggz-en-forensische-zorg-fz/nieuwe-bekostiging-ggz/welke-regels-gelden-voor-de-ggz-en-fz-in-2026 · https://www.zorgprestatiemodel.nl/content/uploads/2022/01/20220111-Verwijstypen-en-zorglabels.pdf · https://www.zorgprestatiemodel.nl/content/uploads/2021/11/Verwijsafspraken-GGZ_november-2021.pdf · https://www.zorgprestatiemodel.nl/shared/content/uploads/2021/09/20210927-Toelichting-tabellen.pdf · https://puc.overheid.nl/nza/doc/PUC_641029_22/4/ +- Zorgvraagtypering: https://www.embloom.nl/veelgestelde-vragen-over-de-honos/ · https://algoritmes.overheid.nl/nl/algoritme/zb000158/97912239/zorgvraagtypering · https://www.nza.nl/zorgsectoren/geestelijke-gezondheidszorg-ggz-en-forensische-zorg-fz/vraag-en-antwoord/vraag-en-antwoord-zorgvraagtypering +- iStandaarden: https://www.istandaarden.nl/ · https://www.istandaarden.nl/iwmo/releases/release-iwmo-3.2 · https://www.heldy.nl/kennisbank/wmo/iwmo-uitgelegd · https://www.heldy.nl/kennisbank/jeugdzorg/ijw-uitgelegd · https://www.istandaarden.nl/binaries/content/assets/istandaarden/iwmo/handreiking-uitvoeringsvarianten-iwmo-en-ijw.pdf diff --git a/docs/datamodel/sessielogs/sessielog-2026-07-18.md b/docs/datamodel/sessielogs/sessielog-2026-07-18.md new file mode 100644 index 0000000..ce807d7 --- /dev/null +++ b/docs/datamodel/sessielogs/sessielog-2026-07-18.md @@ -0,0 +1,432 @@ +# Sessielog datamodel — 18 juli 2026 + +**Status:** chronologisch werkverslag +**Doel:** context, redenering, correcties en voortgang van de sessie bewaren +**Autoriteit:** dit sessielog is niet normatief; `../besluiten/besluitenlog-datamodel-2026-07-18.md` blijft leidend voor genomen besluiten +**Vervolg:** eerste FCO-IM-feitenronde voor referral/instroom + +## 1. Aanleiding + +De sessie begon met een beoordeling van de bestaande map `docs/datamodel`. De map bleek een FCO-IM-geïnspireerde discovery en modelleursronde voor het GGZ-ECD te bevatten, van aanmelding tot behandelplan. + +De eerste analyse identificeerde: + +- een bruikbare scheiding tussen ECD-feiten en TIP-procesbewaking; +- een voorgestelde ZORGEPISODE als klinisch anker; +- uitgewerkte modellen voor aanmelding, screening, intake, wachtlijst, diagnose, behandelplan en rapportage; +- ontbrekende of nog niet uitgewerkte rollen, autorisatie, agenda en declaratie; +- een achterlopende HTML-entiteitenkaart; +- een ontbrekend `onderzoek-proces.md`; +- onderlinge spanningen rond episodevorming, toestemming, wachtlijst en behandelplanversies. + +Afgesproken werd de open beslispunten eerst integraal te verzamelen en daarna één voor één inhoudelijk te bespreken. + +## 2. Aanmelding, acceptatie en zorgepisode + +### 2.1 Financiële en zorginhoudelijke start + +Colin corrigeerde het aanvankelijke voorstel dat een zorgepisode eenvoudig bij de uitkomst `intake` ontstaat. + +Belangrijke praktijkinzichten: + +- de financiële start en zorginhoudelijke start zijn niet hetzelfde; +- een aanmeldfase kan tot behandeling leiden, maar hoeft dat niet; +- eerdere aanmeldingen kunnen voor latere zorg relevant zijn; +- de zorgpraktijk is minder lineair dan regels en datamodellen vaak suggereren; +- bewaarplicht en bewaardoel verschillen naar gelang daadwerkelijk zorg tot stand komt. + +Hieruit volgde de scheiding tussen: + +1. aanmelding en institutionele beoordeling; +2. zorginhoudelijke episode; +3. financieel traject. + +De zorgepisode ontstaat bij een formeel acceptatiebesluit. Daarbij is expliciet vastgelegd dat acceptatie alleen toegang tot het zorgproces betekent en niet automatisch een behandelovereenkomst, zorgplicht, toegewezen behandelaar of organisatorische verantwoordelijkheid bewijst. + +Na afsluiting leidt terugkeer tot een nieuwe, gerelateerde zorgepisode. Een historische relatie geeft nooit automatisch inzage. + +### 2.2 Instellingbrede episode + +Colin verduidelijkte dat één zorgepisode binnen een instelling meerdere zorgprogramma’s en organisatorische eenheden kan omvatten. + +Daarom: + +- opent een interne overdracht niet automatisch een nieuwe episode; +- worden programma- en organisatiebetrokkenheid tijdgebonden relaties; +- zijn meerdere gelijktijdige episodes technisch mogelijk, maar binnen één instelling een gemotiveerde uitzondering; +- worden autorisatie en historische episodekoppeling afzonderlijk gemodelleerd. + +## 3. Binnenkomsten en aanmeldingstrajecten + +De mogelijkheid van meerdere aanmeldingen bleek een terminologisch en conceptueel probleem. Een cliënt kan informatie vanuit meerdere stromen of instanties ontvangen, maar iedere binnenkomst hoeft geen nieuw institutioneel traject te openen. + +Daarom werd onderscheid gemaakt tussen: + +- **aanmeldsignaal:** iedere afzonderlijke binnenkomst; +- **aanmeldingstraject:** de institutionele beoordeling van één samenhangende zorgvraag. + +De UX moet bij overlap ondersteunen: + +- koppelen aan een bestaand traject; +- een parallel traject starten met reden; +- als duplicaat registreren; +- logisch samenvoegen. + +Samenvoegen blijft niet-destructief. Alle oorspronkelijke bronnen en auditinformatie blijven behouden. AI mag matches voorstellen, maar nooit zelfstandig samenvoegen. + +Later in de sessie is de Engelse terminologie voor deze begrippen opnieuw onderzocht; zie §10. + +## 4. Intake + +De intake werd aangescherpt tot één dynamisch onderzoekstraject per samenhangende zorgvraag. + +Een intake kan bestaan uit: + +- meerdere gesprekken en contacten; +- meerdere onderzoeken; +- meerdere disciplines; +- meerdere bevindingen en bijdragen. + +Een intern afdelingscontact of organisatorische overgang is geen nieuwe intake. Parallelle intakes zijn alleen zinvol voor aantoonbaar verschillende zorgvragen. + +Open blijft of de intake altijd na formele acceptatie begint of deels vóór acceptatie kan plaatsvinden. Dat bepaalt of intake primair aan het aanmeldingstraject, de zorgepisode of een overgang tussen beide hangt. + +## 5. Wachtlijst + +De aanvankelijke modellering van wachtlijstplaatsing met status en opschortingsperioden bleek te eenvoudig voor de GGZ-wachtlijstproblematiek. + +Vastgelegde uitgangspunten: + +- de volledige tijdlijn blijft behouden; +- oorspronkelijke aanmeld- en wachtdatum worden niet overschreven; +- bruto wachttijd blijft altijd zichtbaar; +- netto of gecorrigeerde wachttijd is een versiegebonden afleiding; +- verplaatsing tussen teams of programma’s mag wachttijd niet ongemerkt resetten; +- capaciteitstekort en cliëntgebonden uitstel moeten afzonderlijk herkenbaar zijn. + +Nog te onderzoeken: + +- regulatoire wachttijd versus operationele wachtrij; +- teldatums; +- aftrekbare perioden; +- aanbodmomenten; +- prioritering; +- beëindigingsredenen; +- actuele NZa-regels en Treeknormrapportage; +- cardinaliteiten van wachtlijstplaatsingen. + +Conclusie: er is genoeg basis om het overkoepelende model voort te zetten, maar het huidige wachtlijstmodel is niet bouwrijp. + +## 6. Behandelplan + +Het behandelplan kreeg tijdens de sessie een fundamenteel andere opzet dan in het eerdere modelvoorstel. + +### 6.1 Eén logisch plan + +Colins praktijkervaring is dat deelplannen snel leiden tot wildgroei, afdelingsspecifieke documenten en verlies van gezamenlijk overzicht. + +Daarom: + +- bestaat per zorgepisode één logisch behandelplan; +- dragen alle betrokken disciplines aan hetzelfde plan bij; +- ontstaan geen afzonderlijke afdelings- of disciplineplannen; +- worden verschillende behoeften opgelost met scopes, filters en weergaven; +- blijft specialistische documentatie koppelbaar zonder concurrerend intern plan. + +### 6.2 Domeinmodel, geen formulier + +Het behandelplan wordt geen plat formulier. De stabiele kern bestaat uit: + +- aandachtgebieden; +- doelen; +- interventies en afspraken; +- betrokkenen en verantwoordelijkheden; +- evaluaties; +- cliëntperspectief en verschillen van inzicht. + +Een aandachtgebied kan meerdere perspectieven hebben: + +- klacht of diagnose; +- leefgebied; +- kracht of beschermende factor; +- risico; +- herstel; +- preventie. + +Dit voorkomt dat één instellingsvisie als rigide formulier op alle afdelingen wordt geprojecteerd. + +### 6.3 Preventie + +Colin benoemde preventie als een onderscheidend thema voor een EPD, juist omdat financieringsmodellen slecht aansluiten op onderwerpen als eenzaamheid, systeem, omgeving en maatschappij. + +Preventie wordt daarom een volwaardig perspectief binnen het behandelplan, bijvoorbeeld voor: + +- eenzaamheid en sociale verbinding; +- beschermende factoren; +- netwerkversterking; +- vroegsignalering; +- terugvalpreventie; +- bewezen helpende strategieën. + +Dataminimalisatie blijft een randvoorwaarde: het EPD wordt geen onbeperkt sociaal dossier. + +### 6.4 Levend plan en snapshots + +Het behandelplan houdt één identiteit tijdens de episode. + +- Het werkplan blijft dynamisch. +- Iedere wijziging krijgt auteurschap en audit. +- Niet iedere wijziging maakt een nieuw plan. +- Formele vaststelling, evaluatie en cliëntbespreking leveren onveranderlijke snapshots op. +- Eerdere snapshots blijven reproduceerbaar. + +Dit vervangt het eerdere voorstel waarin iedere planversie een volledig nieuw BEHANDELPLAN-record was. + +### 6.5 Procespositie + +De hoofdroute is: + +```text +intakebevindingen +→ conceptplan +→ MDO +→ bespreking met cliënt +→ vastgestelde snapshot +→ uitvoering en periodiek MDO +→ formele evaluatie +→ bijgesteld plan en nieuwe snapshot +→ afsluiting +``` + +Een halfjaarlijkse evaluatie wordt een configureerbare protocolregel. TIP bewaakt de termijn; het ECD bewaart het feit en de uitkomst. + +### 6.6 Cliëntakkoord + +Planakkoord en behandeltoestemming zijn afzonderlijke concepten. + +Per plansnapshot kan worden vastgelegd: + +- akkoord; +- gedeeltelijk akkoord; +- niet akkoord; +- akkoord niet verkregen; +- toelichting en bezwaren; +- vertegenwoordiging indien van toepassing. + +Geen akkoord met het behandelplan blokkeert niet automatisch iedere zorg. Behandeling vereist echter wel toestemming of een expliciete geldige juridische grondslag. Een institutioneel besluit is geen generieke bypass. + +## 7. MDO, evaluatie en bevoegdheid + +Colin verduidelijkte dat MDO en evaluatie dynamische processen zijn. + +Een MDO omvat: + +- casusinbreng; +- vraagstelling; +- triage en agendering; +- voorbereiding; +- sessie en panel; +- feitelijke deelnemers; +- gedeelde analyse; +- advies, besluit of nader onderzoek; +- actiepunten; +- planwijzigingsvoorstellen. + +Een formele evaluatie is een vergelijkbaar cliëntgericht proces en kan een MDO-bespreking bevatten, maar is niet hetzelfde als een MDO. + +Het MDO is niet altijd besluitvormend. Bevoegdheid wordt daarom gekoppeld aan: + +- het besluittype; +- de actuele teamrol; +- professionele kwalificaties; +- eventuele consultatie- of quorumeisen. + +Professie, teamrol en bevoegdheid blijven afzonderlijke begrippen. Een psychiater of psycholoog is niet uitsluitend door de professie automatisch regiebehandelaar. + +## 8. Longitudinale cliëntinformatie en rapportage + +Een voorgeschiedenis maakte duidelijk dat sommige informatie episode-overstijgend relevant is. + +Vastgelegd is: + +- het behandelplan blijft episodegebonden; +- geselecteerde informatie kan longitudinaal worden; +- ieder longitudinaal gegeven heeft bron, bronepisode, geldigheid, actualiteit, reviewdatum en eigen autorisatie; +- er komt geen generieke `CLIENT_GEGEVEN`-vergaarbak; +- concrete klinische concepten krijgen later eigen definities. + +Voor rapportage geldt: + +- iedere klinische rapportage heeft één primaire zorgepisode; +- verwijzingen naar eerdere episodes of longitudinale informatie zijn mogelijk; +- een verwijzing verleent geen toegang; +- pre-acceptatieregistraties horen bij instroom/screening en vragen een eigen bewaarbeleid; +- een MDO-verslag vervangt de MDO-workflow niet. + +## 9. Configureerbare behandelvisie + +Visie en taal worden configureerbaar zonder het klinische schema per instelling of afdeling te laten variëren. + +De configuratielaag bevat: + +- versiegebonden behandelvisieprofielen; +- terminologie; +- perspectieven en waardelijsten; +- aanbevolen en verplichte onderdelen; +- evaluatieprotocollen; +- instellings- en afdelingsscope; +- geldigheid en publicatiestatus. + +De klinische kern bewaart stabiele concepten. Plansnapshots verwijzen naar de gebruikte profielversie. Afdelingen mogen accenten toevoegen, maar maken geen eigen behandelplanmodel. + +## 10. Modellering, taal en terminologieonderzoek + +### 10.1 Methode + +Besloten is FCO-IM/NIAM-principes te behouden voor de conceptuele laag: + +- elementaire feitzinnen; +- natuurlijke verbalisatie; +- uniciteit en optionaliteit; +- temporele geldigheid; +- expliciete constraints. + +Dit wordt aangevuld met: + +- procesmodellen; +- statecharts; +- eventcatalogi; +- beslissingstabellen; +- logisch relationeel model; +- traceerbaarheid van besluit naar constraint en test. + +Volledig formele ORM2-diagrammen zijn niet het doel; de bruikbare fact-oriented discipline staat centraal. + +### 10.2 Engelstalig technisch model + +Het uiteindelijke technische datamodel wordt Engelstalig: + +- entiteiten; +- attributen; +- relaties; +- events; +- API-velden; +- constraints. + +Nederlandse UI-labels blijven configureerbaar. Officiële Nederlandse zorg- en wettelijke begrippen blijven met brondefinitie beschikbaar in een tweetalige begrippenlijst. + +### 10.3 Eerste begrippenlijst + +`../begrippenlijst-kernmodel.md` is opgesteld als versie 0.1. De eerste werktermen voor instroom waren: + +- `Inbound Care Signal`; +- `Access Assessment Case`; +- `Presenting Care Need`; +- `Care Acceptance Decision`; +- `Clinical Care Episode`. + +Colin gaf terecht aan dat de eerste twee termen onvoldoende duidend waren. + +### 10.4 Onderzoek Amerikaanse en Engelstalige EHR’s + +Onderzocht zijn: + +- Epic; +- Oracle Health; +- athenahealth; +- Netsmart; +- NextGen; +- Qualifacts; +- HL7 FHIR; +- ONC/360X; +- NHS-dataterminologie. + +Belangrijkste bevindingen: + +- generieke en behavioral-health EHR’s gebruiken vrijwel overal `Referral` als beheerd lifecycle-object; +- `Referral Order` is vaak een provider-authored verzoek en te smal voor generieke instroom; +- `ServiceRequest`, `Task`, `EpisodeOfCare` en `Encounter` hebben verschillende betekenissen; +- `signal` is geen herkenbare EHR-term voor een ontvangen aanmelding; +- `access assessment case` is begrijpelijk maar niet idiomatisch; +- behavioral-healthsystemen spreken over incoming referrals, referral packets, referral intake en pre-admit; +- de meeste GGZ-zorg is ambulant, waardoor `admission` en `pre-admission` ongeschikt zijn als generieke kernbegrippen. + +Het onderzoeksrapport staat in `../onderzoek/onderzoek-engelstalige-epd-terminologie.md`. + +### 10.5 Huidige voorkeursrichting + +De huidige, nog niet volledig in de begrippenlijst verwerkte voorkeursrichting is: + +```text +referral_submission — iedere afzonderlijke binnenkomst +referral — het beheerde aanmeldingstraject +acceptance_decision — formele acceptatie +clinical_care_episode — geaccepteerde zorgperiode +clinical_intake — klinische intake +``` + +Een mogelijke `referral_request` blijft open. Eerst moet worden vastgesteld of de Nederlandse VERWIJZING een zelfstandig inhoudelijk verzoek is naast de ontvangen submission en de beheerde referral. + +De zorgsetting wordt geen onderdeel van de episodenaam, maar een afzonderlijk tijdgebonden kenmerk, bijvoorbeeld ambulant, outreach, dagbehandeling, residentieel of klinisch. + +## 11. Vastgelegde artefacten + +Tijdens deze sessie zijn toegevoegd: + +- `../besluiten/besluitenlog-datamodel-2026-07-18.md`; +- `../begrippenlijst-kernmodel.md`; +- `../onderzoek/onderzoek-engelstalige-epd-terminologie.md`; +- `../entiteitenkaarten/ehr-referral-terminology.html`; +- canvas `EHR-referral-terminology.canvas.tsx`. + +De volgende documenten kregen een waarschuwing dat ze geheel of gedeeltelijk zijn achterhaald: + +- `../deelmodellen/datamodel-discovery.md`; +- `../deelmodellen/model-aanmelding.md`; +- `../deelmodellen/model-screening.md`; +- `../deelmodellen/model-intake.md`; +- `../deelmodellen/model-wachtlijst.md`; +- `../deelmodellen/model-behandelplan.md`; +- `../deelmodellen/model-rapportage.md`. + +De HTML-entiteitenkaart is nog niet bijgewerkt. Die wordt pas opnieuw gegenereerd nadat de Markdown-deelmodellen zijn herzien. + +## 12. Open punten + +### Instroom + +- Is een inhoudelijk Referral Request een zelfstandig object? +- Valt de Nederlandse VERWIJZING volledig samen met Referral Request? +- Kan één request via meerdere submissions binnenkomen? +- Kan één submission meerdere requests bevatten? +- Welke intakehandelingen mogen vóór formele acceptatie plaatsvinden? +- Wat zijn de statusmachine en bevoegdheden van het acceptatiebesluit? + +### Behandelplan en organisatie + +- Welke wijzigingen vereisen een formele snapshot? +- Hoe wordt cliëntreactie bij gedeeltelijk akkoord precies gescopeerd? +- Welke longitudinale informatietypen worden als eerste gemodelleerd? +- Hoe werkt profielmigratie tijdens een lopende episode? +- Welke bevoegdheidsmatrix geldt per besluittype? + +### Onderzoek + +- GGZ-wachtlijst en actuele NZa-definities; +- bewaarbeleid vóór acceptatie en bij afwijzing; +- juridische betekenis van acceptatie; +- autorisatie op historische episodeverwijzingen; +- externe terminologie voor longitudinale en behandelplanconcepten. + +## 13. Afgesproken volgende stap + +De volgende stap is een eerste FCO-IM-feitenronde voor referral/instroom. + +Doel: + +1. de voorkeursbegrippen verwerken; +2. elementaire feiten voor request, submission, referral en acceptatie formuleren; +3. uniciteit, optionaliteit, tijd en bevoegdheid per feit bepalen; +4. scenario’s testen met huisartsverwijzing, zelfaanmelding, aanvullende informatie en dubbele instroom; +5. op basis daarvan besluiten of `Referral Request` een eigen identiteit nodig heeft; +6. pas daarna entiteiten, cardinaliteiten en een logisch ER-model afleiden. diff --git a/docs/datamodel/sessielogs/sessielog-2026-07-19.md b/docs/datamodel/sessielogs/sessielog-2026-07-19.md new file mode 100644 index 0000000..63ab660 --- /dev/null +++ b/docs/datamodel/sessielogs/sessielog-2026-07-19.md @@ -0,0 +1,306 @@ +# Sessielog datamodel — 19 juli 2026 + +**Status:** chronologisch werkverslag +**Doel:** context, redenering, correcties en voortgang van de sessie bewaren +**Autoriteit:** dit sessielog is niet normatief; bevestigde besluiten moeten nog worden verwerkt in `../besluiten/besluitenlog-datamodel-2026-07-18.md` +**Vervolg:** feitenmodel instroom synchroniseren en de uitkomsten van het behandeladvies bepalen + +## 1. Aanleiding + +De sessie vervolgde de op 18 juli afgesproken FCO-IM-feitenronde voor +referral en instroom. Als werkdocument is `../feitenmodellen/feitenmodel-instroom.md` +opgesteld. Het doel is eerst de elementaire domeinfeiten te bevestigen en pas +daarna entiteiten, cardinaliteiten, statusmachines en SQL af te leiden. + +De ronde richtte zich op: + +- het inhoudelijke zorgverzoek; +- afzonderlijke binnenkomsten; +- het institutionele aanmeldingstraject; +- de formele Nederlandse verwijzing; +- screening, acceptatie en capaciteit; +- de overgang naar zorginhoudelijke intake. + +## 2. Referral Request, Submission en Case + +### 2.1 Meerdere samenhangende zorgvragen + +Bevestigd is dat één `Referral Request` meerdere samenhangende geuite +zorgvragen mag bevatten. Splitsing in afzonderlijke requests is pas nodig +wanneer de zorgvragen afzonderlijk moeten worden beoordeeld. + +Daarmee is een request niet beperkt tot één klacht of één tekstveld. Het +representeert één inhoudelijk verzoek om zorg of beoordeling met een +samenhangende scope. + +### 2.2 Eén submission kan meerdere requests bevatten + +Eén `Referral Submission` kan meerdere inhoudelijke requests bevatten of +representeren, bijvoorbeeld wanneer één ontvangen brief twee onafhankelijk +te beoordelen verzoeken bevat. + +De relaties tussen submission en requests worden expliciet vastgelegd. De +oorspronkelijke submission wordt niet gekopieerd of administratief gesplitst, +omdat bron, ontvangstmoment en documentcontext behouden moeten blijven. + +### 2.3 Eén request kan uitzonderlijk in meerdere cases lopen + +Eén request kan bij uitzondering in meerdere `Referral Cases` worden +behandeld wanneer verschillende proces- of wettelijke kaders dat vereisen. +Iedere parallelle behandeling vereist: + +- een expliciete reden; +- tijdgebonden historie; +- behoud van de oorspronkelijke request- en case-identiteiten; +- volledige audit. + +Dit is een uitzondering en geen standaardroute. + +## 3. Formele verwijzing is een zelfstandig object + +De formele Nederlandse **VERWIJZING** valt niet samen met het generieke +zorgverzoek. + +Bevestigd is: + +- `Referral Request` representeert het inhoudelijke verzoek om zorg of + beoordeling; +- `Professional Referral` representeert de formele verwijzing met onder meer + verwijzer, verwijsdatum, geldigheid en verwijsspecifieke gegevens; +- de formele verwijzing heeft een eigen identiteit; +- de formele verwijzing wordt gekoppeld aan het request waarop zij betrekking + heeft; +- een zelfaanmelding kan een request vormen zonder formele verwijzing. + +Deze scheiding sluit aan op de GGZ-praktijk bij spoed en crisis. Zorg kan +soms al worden geleverd voordat de formele verwijzing is ontvangen. Een +later ontvangen verwijzing vult de formele en financiële onderbouwing aan, +maar herschrijft de identiteit of herkomst van het oorspronkelijke request +niet. + +De vraag hoe correcties en vervangende verwijzingen logisch worden +gemodelleerd is geparkeerd. Append-only audit voorkomt ondertussen +informatieverlies. De keuze tussen meerdere verwijzingsidentiteiten en +versies van één verwijzing hoort bij het logische model. + +## 4. Screening in de GGZ-praktijk + +Colin beschreef de screeningsfase als de fase waarin de instelling beoordeelt: + +- of er een match is met de zorgvraag; +- of de benodigde inhoudelijke expertise aanwezig is; +- of er plek beschikbaar is; +- of er voldoende capaciteit is. + +Tijdens screening is meestal beperkt contact met de cliënt, verwijzer of +andere bron. Dit contact is doorgaans niet gefinancierd, maar kan dat bij +uitzondering wel zijn. + +Na een positieve screening volgt de zorginhoudelijke intake. Contacten binnen +de intake zijn meestal wel gefinancierd. Uit de intake volgt vervolgens een +behandeladvies. + +De financiering van een contact mag daarom niet uitsluitend uit de procesfase +worden afgeleid. Per concreet contact moet kunnen worden vastgelegd of en op +welke grond het gefinancierd of declarabel is. + +## 5. Screeningsbesluit is het acceptatiebesluit + +Bevestigd is dat het screeningsbesluit geen vrijblijvend advies is waarna nog +een zelfstandig acceptatiebesluit volgt. Het screeningsbesluit **is** het +acceptatiebesluit. + +Daarmee wordt de eerder veronderstelde scheiding tussen +`Screening Recommendation` en `Care Acceptance Decision` voor deze flow +herzien. De onderliggende beoordelingen blijven wel afzonderlijke feiten: + +1. inhoudelijke match; +2. beschikbare plek; +3. capaciteit; +4. formeel besluit; +5. vervolgroute; +6. onderbouwing. + +Het besluit en de onderliggende beoordelingen mogen niet in één statusveld +worden samengevoegd. + +## 6. Capaciteit bepaalt acceptatie niet automatisch + +Besproken is dat een inhoudelijke match zonder beschikbare capaciteit in de +praktijk tot twee geldige routes kan leiden. + +### Route A — accepteren en wachten + +- inhoudelijke match: ja; +- capaciteit: nee; +- acceptatiebesluit: geaccepteerd; +- vervolgroute: intakewachtlijst; +- gevolg: er ontstaat een zorgepisode. + +### Route B — afwijzen wegens capaciteit + +- inhoudelijke match: ja; +- capaciteit: nee; +- acceptatiebesluit: afgewezen; +- onderbouwing: geen capaciteit; +- eventuele vervolgroute: doorverwijzen; +- gevolg: er ontstaat geen zorgepisode. + +Capaciteit is dus invoer voor het besluit, maar bepaalt de besluituitkomst +niet automatisch. Het acceptatiebesluit bepaalt of een zorgepisode ontstaat. + +Deze scheiding ondersteunt ook andere combinaties: + +- inhoudelijke match en capaciteit beschikbaar → geaccepteerd → intake + plannen; +- geen inhoudelijke match → afgewezen met inhoudelijke onderbouwing; +- onvoldoende informatie → nog geen definitief besluit en aanvullende + informatie opvragen. + +## 7. Besluituitkomst en vervolgroute + +De volgende voorlopige scheiding is ontstaan. + +### Besluituitkomst + +- geaccepteerd; +- afgewezen. + +Mogelijk is `meer_informatie_nodig` geen besluituitkomst maar een open +processtatus, omdat er dan nog geen definitief acceptatiebesluit bestaat. + +### Vervolgroute + +- intake direct plannen; +- op intakewachtlijst plaatsen; +- bij spoed direct doorzetten; +- doorverwijzen; +- case afsluiten. + +`Planning` en `wachtlijst` zijn daarmee geen inhoudelijke besluituitkomsten, +maar operationele vervolgroutes na of naast het besluit. + +### Onderbouwing + +Bij afwijzing is een concrete onderbouwing nodig, bijvoorbeeld: + +- onvoldoende inhoudelijke match; +- benodigde expertise ontbreekt; +- geen capaciteit; +- zorgvraag valt buiten het aanbod of de toelatingscriteria. + +Een onderbouwing als “geen acceptatie” is niet informatief, omdat die alleen +de besluituitkomst herhaalt. + +## 8. Gevolg voor episodevorming + +Het huidige inhoudelijke besluit blijft: de zorgepisode ontstaat bij +acceptatie. + +Nu het screeningsbesluit het acceptatiebesluit blijkt te zijn, kan de episode +al vóór de eerste zorginhoudelijke intake ontstaan. Ook bij acceptatie gevolgd +door een intakewachtlijst bestaat dan al een zorgepisode. + +Dit vervangt het oudere voorstel in `../deelmodellen/model-aanmelding.md` waarin de episode +pas ontstond bij uitkomst “intake” of bij de feitelijke start van de intake. +Het feitenmodel en de oudere deelmodellen zijn op dit punt nog niet volledig +gesynchroniseerd. + +## 9. Huidige conceptuele flow + +```text +request en één of meer submissions +→ referral case +→ screening van zorgvraagmatch, expertise, plek en capaciteit +→ screeningsbesluit = acceptatiebesluit + ├─ geaccepteerd + │ ├─ intake direct plannen + │ ├─ intakewachtlijst + │ └─ spoedroute + │ + └─ afgewezen + ├─ inhoudelijke onderbouwing + ├─ capaciteitsonderbouwing + └─ eventueel doorverwijzen + +geaccepteerd +→ zorgepisode +→ zorginhoudelijke intake +→ behandeladvies +``` + +De formele verwijzing kan vóór of na het acceptatiebesluit worden ontvangen +en blijft een zelfstandig object naast het request. + +## 10. Gewijzigde en toegevoegde artefacten + +Tijdens deze feitenronde is toegevoegd: + +- `../feitenmodellen/feitenmodel-instroom.md`. + +Dat document bevat: + +- werkdefinities; +- elementaire feitzinnen; +- voorlopige uniqueness- en optionaliteitsregels; +- scenariotoetsen voor huisartsverwijzing, zelfaanmelding, spoed/crisis, + aanvullende informatie en dubbele instroom; +- een voorlopige conclusie over de identiteit van Referral Request. + +Het document bevat nog formuleringen die met de besluiten uit deze sessie +moeten worden gesynchroniseerd, met name: + +- screeningsadvies en acceptatie zijn nog deels als afzonderlijke besluiten + beschreven; +- de besluituitkomsten en vervolgroutes moeten worden gescheiden; +- episodevorming bij acceptatie vóór intake moet expliciet worden verwerkt. + +## 11. Open punten + +### Instroom en acceptatie + +- Welke open processtatussen gelden vóór een definitief screeningsbesluit? +- Welke gegevens en bevoegdheid zijn minimaal vereist om het + screenings-/acceptatiebesluit vast te leggen? +- Kan een geaccepteerde case later vóór de intake alsnog worden ingetrokken, + en zo ja, wat gebeurt dan met de zorgepisode? +- Hoe worden intakewachtlijst en regulatoire aanmeldwachttijd precies + gekoppeld nu de episode vóór intake kan ontstaan? + +### Behandeladvies + +- Welke uitkomsten kan het behandeladvies na de intake hebben? +- Is het behandeladvies uitsluitend adviserend, of bevat het ook een bevoegd + besluit over de start of vorm van behandeling? +- Hoe verhoudt het behandeladvies zich tot behandelplan, doorverwijzing en + afsluiting van de episode? + +### Logische uitwerking + +- Hoe worden gecorrigeerde of vervangende formele verwijzingen + gehistoriseerd? +- Welke cardinaliteiten volgen definitief tussen request, submission, case, + verwijzing en acceptatiebesluit? +- Welke statusmachine hoort bij Referral Case wanneer aanvullende informatie, + heroverweging of intrekking mogelijk is? + +## 12. Afgesproken volgende stap + +De eerstvolgende stap is het synchroniseren van +`../feitenmodellen/feitenmodel-instroom.md` met de besluiten uit deze sessie: + +1. screeningsbesluit en acceptatiebesluit samenvoegen; +2. beoordelingen, besluit, vervolgroute en onderbouwing als afzonderlijke + feiten formuleren; +3. beide routes bij ontbrekende capaciteit opnemen; +4. episodevorming vóór intake corrigeren; +5. de mogelijke uitkomsten van het behandeladvies met Colin vaststellen. + +Daarna volgen: + +1. definitieve uniqueness- en optionaliteitsconstraints; +2. de statusmachines van Referral Case en het acceptatiebesluit; +3. afleiding van entiteiten en cardinaliteiten; +4. verwerking in het besluitenlog; +5. synchronisatie van `../begrippenlijst-kernmodel.md` en + `../deelmodellen/model-aanmelding.md`. diff --git a/docs/datamodel/sessielogs/sessielog-2026-07-19b.md b/docs/datamodel/sessielogs/sessielog-2026-07-19b.md new file mode 100644 index 0000000..1429179 --- /dev/null +++ b/docs/datamodel/sessielogs/sessielog-2026-07-19b.md @@ -0,0 +1,266 @@ +# Sessielog datamodel — 19 juli 2026 (middag/avond) + +**Status:** chronologisch werkverslag +**Doel:** context, redenering, correcties en voortgang van de sessie bewaren +**Autoriteit:** dit sessielog is niet normatief; bevestigde besluiten staan al +verwerkt in `../besluiten/besluitenlog-datamodel-2026-07-18.md` (B21-B34) +**Vervolg:** bevoegdheidsronde (B16), statusmachines, definitieve +uniqueness-/optionaliteitsconstraints + +## 1. Aanleiding + +Vervolg op `sessielog-2026-07-19.md` (ochtendronde). Deze sessie bestaat uit +twee delen: + +1. afronding van de instroom-feitenronde: entiteiten/cardinaliteiten + afleiden, formaliseren in het besluitenlog, en de begrippenlijst en + `model-aanmelding.md` synchroniseren; +2. een nieuwe, aparte feitenronde voor zorginhoudelijke intake en + behandeladvies, die ook vraag 2 uit `feitenmodel-instroom.md` §7 + (behandeladvies-uitkomsten, sinds de ochtendronde geparkeerd) afsluit. + +Kort na de ochtendronde zijn eerst nog de resterende open vragen 3, 4 en 5 +uit `feitenmodel-instroom.md` §7 afgehandeld (zie §2). Daarna volgde een +langere pauze; het werk aan entiteiten en de nieuwe feitenronde is +'s middags/'s avonds gedaan. + +## 2. Afronding resterende instroomvragen (vraag 3, 4, 5) + +Direct aansluitend op de ochtendronde zijn drie openstaande punten uit +`feitenmodel-instroom.md` §7 beantwoord: + +- **Aanvullende informatie nodig** wordt een gebeurtenisfeit, geen + besluituitkomst en geen statusfase — er bestaat op dat moment nog geen + besluit. +- **Heroverweging na afwijzing** krijgt een expliciete "vervangt"-relatie + tussen acceptatiebesluiten, met verplichte reden en dezelfde + bevoegdheidseis als het origineel. +- **Intrekking vóór start intake** krijgt twee routes: cliënt-intrekking + zonder bevoegdheidseis, en institutionele correctie met dezelfde + bevoegdheid als het origineel. In beide gevallen wordt een al ontstane + zorgepisode altijd afgesloten, nooit verwijderd. + +Referral Case krijgt daarmee een tijdlijn-gebaseerde stand in plaats van een +vaste lineaire statusmachine. Alleen vraag 2 (behandeladvies-uitkomsten) +bleef op dit moment nog open. + +## 3. Entiteiten en cardinaliteiten instroom + +`../deelmodellen/model-instroom.md` is toegevoegd: de eerste +entiteiten-/cardinaliteitenafleiding uit `feitenmodel-instroom.md`. 20 +entiteiten, Engelstalig conform B20, met een vervangt-patroon voor +herzieningen en een tijdlijn in plaats van een statusmachine voor Referral +Case. + +Vier reviewrondes zijn verwerkt vóór het model als afgerond werd beschouwd: + +1. **Technische en GGZ-domeinreview** — vertakkende vervangt-keten gefixed + (uniek per vervangen exemplaar, geen boomstructuur), timestamp-precisie + gecorrigeerd, ontbrekende ZPM-velden hersteld. +2. **Tweede ronde met datamodel-, GGZ-domein- en UX-expert-agents** — lege + screening-entiteit vervallen verklaard (screening is activiteit, geen + besluitobject — consistent met B21), naamgeving aangescherpt, + cardinaliteitsfout gecorrigeerd, een read-model-eis toegevoegd. +3. **GGZ-wetgeving-onderzoek** (Wvggz, Wmo/Jeugdwet, 275-dagentoets, + 365-dagenregel, crisisdocumentatie) vertaald naar nieuwe entiteiten: + `municipal_care_assignment`, `legal_mandate`, `crisis_encounter_note`. +4. **Domeinreview eigenaarschap/urgentie met Colin** — een herhaalbare + urgentiebeoordeling (`case_urgency_assessment`, geen vast veld) en een + bewust minimale hook voor teambetrokkenheid + (`episode_team_involvement`); het volledige zorgteam-model blijft een + aparte, nog te plannen ronde (bevestigt B16). + +Ook is in `feitenmodel-instroom.md` de vraag "verzoek zonder bekende +persoon" (crisisaanmelding) beantwoord: geen placeholder-persoon, de +koppeling aan een persoon wordt een eigen, gedateerd feit dat pas ontstaat +zodra de identiteit bekend is. + +## 4. Formalisering in het besluitenlog: B21-B30 + +De uitkomst van de instroom-feitenronde en het entiteitenmodel is +vastgelegd als B21-B30 in +`../besluiten/besluitenlog-datamodel-2026-07-18.md`: + +| # | Kern | +|---|------| +| B21 | Screeningsbesluit = acceptatiebesluit, geen apart voorafgaand advies | +| B22 | Besluituitkomst (`geaccepteerd`/`afgewezen`), vervolgroute en onderbouwing zijn losse feiten; elk positief besluit laat een episode ontstaan, ook bij vervolgroute intakewachtlijst | +| B23 | Herziening via expliciete, unieke vervangt-relatie met verplichte reden en gelijke bevoegdheidseis | +| B24 | Cliënt-intrekking vereist geen beslisbevoegdheid; een ontstane episode wordt altijd afgesloten, nooit verwijderd | +| B25 | Geen statusmachine — actuele stand afgeleid uit een gebeurtenistijdlijn (aansluitend bij B7) | +| B26 | Zorgverzoek kan zonder bekende persoon bestaan; koppeling is een eigen, gedateerd feit | +| B27 | Wmo/Jeugdwet-toewijzing en Wvggz-mandaat zijn eigen instroomroutes naast de professionele verwijzing | +| B28 | Gestructureerde crisisdocumentatie kan vóór identificatie al vastgelegd worden | +| B29 | Urgentie is een herhaalbare beoordeling, geen vast veld | +| B30 | Minimale hook voor teambetrokkenheid; het volledige zorgteam-model blijft een aparte ronde | + +Ook §10 van het besluitenlog is bijgewerkt: afgeronde punten gemarkeerd, +de juridische verificatie- en behandeladvies-onderzoekspunten toegevoegd, +en genoteerd dat het AANMELDING/VERWIJZING/ZORGEPISODE-deel van +`model-aanmelding.md` is vervangen door `model-instroom.md` +(synchronisatie als eerstvolgende stap). + +## 5. Synchronisatie begrippenlijst en model-aanmelding + +`../begrippenlijst-kernmodel.md` §3 vervangt de voorlopige werktermen +(Inbound Care Signal, Access Assessment Case, Presenting Care Need, +Screening Recommendation) door de bevestigde instroom-begrippen: Referral +Request/Submission/Case, Professional/Municipal Referral, Legal Mandate, +Case Information Request/Withdrawal, Crisis Encounter Note, Case Urgency +Assessment. §10 bijgewerkt: opgeloste terminologievragen gemarkeerd, +Wvggz/Wmo-Jeugdwet-aannames toegevoegd als nieuw juridisch te bevestigen +punt. + +`../deelmodellen/model-aanmelding.md`: AANMELDING/VERWIJZING/ +VERWIJSDOCUMENT/TOEWIJZING/ZORGEPISODE (§2.10-§2.13, §2.15) +niet-destructief gemarkeerd als vervallen met verwijzing naar de +vervangende entiteit in `model-instroom.md` — de tekst blijft als +historisch werkdocument staan, consistent met het niet-destructieve +patroon dat elders in het datamodel wordt toegepast. +PERSOON/CLIENT/CLIENTRELATIE/VERWIJZER/PRAKTIJK_INSTELLING/ +CLIENT_HUISARTS/VERZEKERING/TOESTEMMING/CLIENTPORTAAL_ACCOUNT +(§2.1-§2.9, §2.14, §2.16) blijven ongewijzigd en behouden hun +sectienummers, waar `model-instroom.md` extern naar verwijst. + +Ook zijn twee foutieve padverwijzingen in `model-instroom.md` §4 +gecorrigeerd (waardelijsten verwezen naar de verkeerde sectie in +`model-aanmelding.md`). + +## 6. Nieuwe feitenronde: intake en behandeladvies + +Na afronding van de instroomronde is direct doorgewerkt aan een nieuwe +FCO-IM-feitenronde voor de zorginhoudelijke intake. Als werkdocument is +`../feitenmodellen/feitenmodel-intake-behandeladvies.md` opgesteld, met +feitzinnen, scenariotoetsen en domeinreview voor Clinical Intake +Assessment, Intake Contact, Child Safety Check en Treatment Advice. + +### 6.1 Episode-timing gecorrigeerd + +De intake hangt aan de zorgepisode, niet aan het aanmeldingstraject, en +start op enig moment ná het acceptatiebesluit dat de episode liet +ontstaan — mogelijk pas na een periode op de intakewachtlijst. Dit trekt +de consequentie door van B22 (episode ontstaat al bij acceptatie, niet bij +een aanmelding-uitkomst "intake" die niet meer bestaat) voor de intake +zelf. + +### 6.2 Behandeladvies als eigen object + +Net als bij het acceptatiebesluit (B21) bestaat er geen apart, voorafgaand +behandeladvies naast een later formeel besluit: het behandeladvies zelf +draagt de uitkomst en de vervolgrichting. Het krijgt wel een eigen +identiteit los van de intake (Treatment Advice), naar het patroon van Care +Acceptance Decision. Een behandeladvies kan een eerder advies van dezelfde +intake vervangen bij heroverweging, met dezelfde vervangt-constructie als +bij herziening van het acceptatiebesluit (B23). + +Dit sluit `feitenmodel-instroom.md` §7 vraag 2 af, die sinds de +ochtendronde geparkeerd stond. + +### 6.3 Vier behandeladvies-uitkomsten + +Op basis van volledige LKS 4.0-teksttoetsing (niet alleen samenvattingen): + +- Vier uitkomsten: `in_zorg`, `terugverwijzing`, `doorverwijzing`, + `extra_diagnostiek`. +- `Doorverwijzing` en `terugverwijzing` zijn volgens LKS 4.0 (patient + journey fase 2-3, §3.6.2) een **volgtijdelijk paar** — eerst een + inspanningsverplichting tot doorverwijzing, pas daarna terugverwijzing — + geen twee gelijkwaardige, onafhankelijke uitkomsten. De volgorde wordt + gedekt door het vervangt-mechanisme uit §6.2, niet door een aparte + sequentie-regel. +- `Extra_diagnostiek` is praktijkgefundeerd, nergens in LKS 4.0 als + zodanig benoemd — bewust behouden, maar expliciet gemarkeerd als + praktijkkeuze, geen landelijke norm. +- Gedeeld/onduidelijk advies bij twijfel tussen behandelaren krijgt geen + eigen uitkomstwaarde: LKS 4.0 §3.6.2 lost dit institutioneel op. +- Een cliënt die afziet van het geadviseerde vervolg is een later, apart + feit, geen behandeladvies-uitkomst (analoog aan planakkoord, B13). +- Wachtlijst voor behandelcapaciteit is een vervolgroute na `in_zorg`, + zelfde precedent als bij het acceptatiebesluit. + +### 6.4 Bevoegdheid en MDT-bespreking + +LKS 4.0 maakt onderscheid tussen wie de intake verricht en wie de uitkomst +vaststelt (regiebehandelaar, indicerende rol). Voor settings 3-8 is +MDT-bespreking een **dwingende landelijke norm** (LKS 4.0 Tabel 2), niet +instellingsbeleid; voor setting 2 optioneel; voor vrijgevestigden niet van +toepassing. Verwerkt als rol-attribuut plus een optionele MDT-hook +(`intake_mdt_review`) — de precieze verplichtingsregel per setting hoort +bij de bredere bevoegdheidsronde (B16) en vereist een setting-registratie +die nu nog niet bestaat. + +### 6.5 Kindcheck-structuur + +De bestaande prototypestructuur (drie ja/nee-vlaggen met verplichte +toelichting bij "ja") blijft de kern, met vlag 1 verbreed van +"thuiswonende kinderen" naar "verantwoordelijk voor minderjarigen" +(KNMG-meldcode: ook co-ouderschap en andere zorgrelaties tellen). Een +vierde vlag ("zwangerschap van cliënt of partner") is toegevoegd — de +KNMG-kindcheck rekent het ongeboren kind expliciet mee. Deze vierde vlag +is op basis van algemene domeinkennis toegevoegd, niet uit eerder +verzamelde onderzoeksrapporten, en moet nog tegen de actuele +meldcode-tekst geverifieerd worden. + +## 7. Nieuwe entiteiten en besluiten B31-B34 + +`../deelmodellen/model-intake-behandeladvies.md` is toegevoegd: +entiteiten-/cardinaliteitenafleiding voor Clinical Intake Assessment, +Intake Contact, Child Safety Check en Treatment Advice. + +In het besluitenlog vastgelegd: + +- **B31** — Intake hangt aan de zorgepisode, start ná het + acceptatiebesluit (§6.1). +- **B32** — Behandeladvies is een eigen object dat de intake afrondt, geen + apart voorafgaand advies (§6.2). +- **B33** — Vier behandeladvies-uitkomsten, deels LKS-genormeerd (§6.3), + met de bevoegdheidsmatrix-vraag doorverwezen naar B16 (§6.4). +- **B34** — Kindcheck-structuur bevestigd, vierde vlag toegevoegd (§6.5). + +`../begrippenlijst-kernmodel.md` §4 is gesynchroniseerd (Treatment Advice +en Child Safety Check zijn nieuwe begrippen; Clinical Intake Assessment en +Intake Contact bestonden al als werkterm). Het oudere +`../deelmodellen/model-intake.md` is niet-destructief gemarkeerd als +vervangen, consistent met hoe `model-aanmelding.md` eerder is behandeld. + +## 8. Open punten + +### Instroom + +Vrijwel volledig afgerond via B21-B30; het enige resterende punt is de +bevoegdheidsmatrix (zie hieronder, gedeeld met intake/behandeladvies). + +### Intake en behandeladvies + +1. Exacte bevoegdheidsmatrix per setting (welke settings precies wanneer + MDT-bespreking vereisen, en de rolinvulling daarvan) — deel van de + bredere bevoegdheidsronde (B16); het principe (rol + optioneel + MDT-feit) staat al vast, de detailinvulling niet. +2. Is `extra_diagnostiek` altijd een volledig nieuw, vervangend Treatment + Advice, of kan de bestaande intake simpelweg langer "bezig" blijven + zonder tussentijdse afronding? Beide patronen zijn nu toegestaan, welke + de praktijk het vaakst gebruikt is nog niet getoetst. +3. Vierde kindcheck-vlag (zwangerschap) nog te verifiëren tegen de actuele + meldcode-tekst. +4. Contactmoment-generalisatie naar een bredere consult-/afspraakstructuur + blijft uitdrukkelijk een latere (agenda-)ronde. + +### Nog niet opgepakt (aansluitend, uit eerdere sessies) + +- Het volledige zorgteam-model (leden, rollen, regiebehandelaarschap, + bevoegdheid, mogelijk toekomstige cliëntautorisatie) — bevestigd als + aparte ronde in B30. +- Definitieve statusmachines waar die nog ontbreken. + +## 9. Afgesproken volgende stap + +1. Definitieve uniqueness- en optionaliteitsconstraints voor zowel + instroom als intake/behandeladvies. +2. Bevoegdheidsronde (B16): setting-registratie en de volledige + bevoegdheidsmatrix (MDT-bespreking, regiebehandelaarschap, indicerende + rol). +3. Verificatie van de vierde kindcheck-vlag tegen de actuele + meldcode-tekst. +4. Beslissen of `extra_diagnostiek` altijd een vervangend Treatment Advice + vereist, op basis van praktijktoetsing. +5. Zorgteam-model als aparte ronde plannen (B30). diff --git a/docs/intent/samenspel-tip-ecd.html b/docs/intent/samenspel-tip-ecd.html new file mode 100644 index 0000000..2a8130a --- /dev/null +++ b/docs/intent/samenspel-tip-ecd.html @@ -0,0 +1,663 @@ + + + + + +Samenspel TIP en GGZ-ECD + + + + +
+ +
+
Discussiestuk · Triqura
+

Het samenspel tussen
TIP en het GGZ‑ECD

+
+ Voor — werksessie Colin & Joshua + Datum — 14 juli 2026 + Status — ter bespreking, geen besluit +
+
+ +
+
1

Constatering

+

We bouwen aan twee systemen die samen één geheel moeten vormen: het GGZ‑ECD als eerste toepassing, TIP als het intent‑platform eronder. We hebben allebei dezelfde vraag op tafel — welke API’s hebben we nodig? — maar die is pas te beantwoorden als we samen de rolverdeling kiezen. Er leven nu twee verschillende beelden van hoe de systemen samenwerken, en die leiden tot verschillende API’s, verschillende datamodellen en een andere taakverdeling tussen de teams.

+
+

De kernvraag van dit stuk: hoe verdelen ECD en TIP het werk — TIP als beslisplatform dat het ECD voedt met gegevens, of TIP als intent‑laag die met het ECD meekijkt? Die keuze bepaalt óók het verhaal van het platform richting volgende domeinen. Een gezamenlijke productkeuze dus, geen techniekvraag van één van beide kanten.

+
+
+ +
+
2

Aanleiding

+

Er komen drie sporen samen:

+

Het ECD wordt opnieuw opgebouwd. Het huidige prototype leunt op Supabase en FHIR‑conventies die eruit gaan. Er komt een eigen PostgreSQL‑datamodel onder, met voorbereiding op pgvector (semantisch zoeken) en Neo4j (ZPM‑relaties). Het moment om de architectuur goed te snijden is nú.

+

Het intent‑systeem komt uit TIP. De oorspronkelijke Cortex‑architectuur ging uit van een intent-motor als TypeScript‑bibliotheek ín de ECD‑app (lib/cortex/). TIP bestaat inmiddels als zelfstandig platform (Python‑services, eigen runtime) en is domein‑agnostisch opgezet: het GGZ‑ECD is de eerste toepassing, andere domeinen moeten volgen. Het ECD bewijst het platform; het platform draagt het ECD.

+

De gedeelde vraag: “welke API’s hebben we eigenlijk nodig?” Die vraag speelt aan beide kanten en is niet te beantwoorden zonder eerst samen de rolverdeling te kiezen. Bij het naast elkaar leggen van beide codebases bleek dat de twee beelden van die rolverdeling (§3) nu nog uiteenlopen — dat is geen probleem, maar wel iets om expliciet te maken vóór we contracten vastleggen.

+
+ +
+
3

Twee mentale modellen

+

Legenda: ECD klinische applicatie & dossier  ·  TIP intent-platform.

+ +
+
+
Model A — het beeld
+
TIP als laag bovenop het ECD
+
TIPvertaalt taal → intent, geeft suggesties
+
+ tekst / spraak ↑ + ↓ intent + suggestie +
+
ECDdraait volledig zelfstandig +
UIlogicaregelsdatabase
+
+

TIP heeft geen eigen administratie. Trek je TIP eruit, dan werkt het ECD nog steeds — alleen zonder taalinterface. Dit is het model uit het oorspronkelijke Cortex‑architectuurdocument (vgl. “AI‑laag boven Chipsoft/Nexus”).

+
+ +
+
Model B — hoe TIP gebouwd is
+
Het ECD aangesloten op TIP
+
ECDregistratie & klinische feiten +
UIklinisch dossier
+
+
+ artifacts & events ↓ + ↑ intents & taken +
+
TIPeigen database, eigen dossier-state +
dossiersworkspacesIE’sintentregelsscenario’s
+
+

TIP werkt pas als het gevoed wordt: documenten, gebeurtenissen en gegevens moeten naar TIP gepusht worden. TIP houdt zelf bij waar elke casus staat. Trek je de datafeed eruit, dan is TIP blind.

+
+
+ +

Beide paden in TIP — het gestructureerde pad (intentregels over Information Elements) en het ongestructureerde pad (vrije tekst via /detect) — leveren dezelfde intent op. Maar alleen het ongestructureerde pad werkt zonder datafeed. De kracht van TIP — regels, scenario’s, zorgpaden — zit in het gestructureerde pad, en dat vereist Model B.

+ +
+

Deze opzet is een logische ontwerpkeuze. Elke beslislaag moet de data kunnen zien waarover hij beslist. De Information‑Element‑laag is precies wat TIP domein‑agnostisch maakt: het platform hoeft geen enkel bronsysteem te kennen, zolang bronsystemen hun gegevens als IE’s aanleveren. Er hoort alleen een consequentie bij die we samen moeten inplannen: een toepassing op TIP aansluiten betekent altijd een datafeed naar TIP bouwen — integratiewerk aan beide kanten, geen laag die je er los oplegt.

+
+
+ +
+
4

Voorbeeld: de verwijsbrief-flow

+

Hoe Model B er in de praktijk uitziet, stap voor stap. Let op waar de state terechtkomt.

+ +
+
+
1
+
+ ECD +

Een verwijsbrief komt binnen (ZorgDomein, e-mail, upload). Het ECD slaat de brief op in het klinisch dossier en stuurt hem als artifact door naar TIP.

+
+
+
+
2
+
+ TIP +

TIP maakt in zijn eigen database een workspace referral aan en extraheert Information Elements: verwijzer, verwijsreden, urgentie.

+ State in TIP: dossier X · workspace “referral” · 3 IE’s gevuld +
+
+
+
3
+
+ TIP +

Een intentregel vuurt: “urgentie hoog én geen intake gepland → intent: intake inplannen”. Het scenario zet een menselijke taak klaar — TIP voert nooit zelf uit.

+ State in TIP: intent gevuurd · taak open · wacht op behandelaar +
+
+
+
4
+
+ ECD +

Het ECD toont de taak. De behandelaar bevestigt (human‑in‑the‑loop) en de intake‑afspraak wordt in het ECD vastgelegd — het klinische feit leeft in het ECD.

+
+
+
+
5
+
+ ECD +

Het ECD meldt de voltooiing terug aan TIP als event, zodat TIP weet dat de taak is afgerond en de procesfase verschuift.

+ State in TIP: fase verwijzing afgerond · intake gepland +
+
+
+ +

Het gevolg: twee administraties over dezelfde patiënt

+
+ + + + + + + + + + + + + + + + +
ECD (klinische database)TIP (platform-database)
BevatKlinische feiten: rapportages, afspraken, diagnoses, de brief zelfProces-state: fase, workspaces, IE’s, gevuurde intents, open taken
Bron van waarheid voorHet medisch dossier“Waar staat het proces en wat moet er gebeuren”
+
+

Zolang die scheiding scherp is — ECD = klinische feiten, TIP = proces- en beslislogica — is dit gezond. Het risico ontstaat waar hetzelfde feit op twee plekken leeft. “Intake gepland op 14 juli” bestaat als afspraak in het ECD én als IE in TIP. Wordt de afspraak in het ECD verzet, dan moet er een event naar TIP — anders beslist TIP op verouderde informatie. Per gegeven moet dus vastliggen wie eigenaar is; de ander kent het alleen als afgeleide kopie.

+
+ +
+
5

Drie routes

+ +
+
+
Route 1

Alleen /detect — TIP als echte laag

+

Het oorspronkelijke beeld: tekst in, intent uit, verder niets.

+
+
Wat het betekent

Het ECD roept alleen de intent‑detectieservice aan. Alle state, regels, nudges en procesbewaking worden in het ECD zelf gebouwd (zoals in het oorspronkelijke Cortex‑ontwerp).

+
Voordeel

Dun, stateless contract. Geen synchronisatievraagstuk. ECD blijft volledig autonoom; TIP‑uitval betekent alleen “geen taalinterface”.

+
Consequentie

We gebruiken ~10% van het platform en bouwen intentregels, scenario’s en taken dubbel in het ECD. Twee regelmotoren in twee talen die uit elkaar groeien.

+
Wat het van beide kanten vraagt

TIP-kant vrijwel niets — /detect bestaat al. ECD-kant het meeste werk: alle beslislogica zelf bouwen. En de platformbelofte blijft onbeproefd.

+
+
+ +
+
Route 2

TIP als procesbrein — volledig Model B

+

Het ECD wordt registratie + UI; TIP doet alle beslislogica.

+
+
Wat het betekent

Elk relevant document en elke gebeurtenis gaat naar TIP. TIP bewaakt fases, evalueert regels, zet taken klaar. Het ECD toont en registreert.

+
Voordeel

TIP’s volle kracht: zorgpaden, intentregels, audit‑chain. Regels wijzigen zonder ECD‑deployment. Sterkste bewijs voor TIP’s platformbelofte.

+
Consequentie

ECD‑datamodel en TIP’s dossiermodel moeten sámen ontworpen worden — geen twee projecten meer, maar één systeem in twee repo’s. Eigenaarschap per gegeven en de eventfeed moeten vanaf dag één kloppen. Grote wederzijdse afhankelijkheid, nog vóór we het samenspel in het klein hebben beproefd.

+
Wat het van beide kanten vraagt

ECD-kant: een volledige datafeed (artifacts, events) en taak-UI. TIP-kant: stabiele push‑API, GGZ‑workspace‑typen en beschikbaarheidsafspraken — het ECD leunt in dit model op het platform.

+
+
+ + +
+ +

De snijlijn die in alle routes overeind blijft: TIP kent geen patiëntnamen en geen schermen; het ECD kent geen intentregels en geen scenario’s. Entity resolution (“jan” → patiënt #427) vereist toegang tot het patiëntenbestand en hoort daarmee aan de ECD‑kant — of het ECD stuurt kandidaten mee in de request.

+
+ +
+
6

Het samenspel per flow: wie levert wat

+

Afgeleid uit de bestaande gebruikersflows van het ECD, gemapt op de huidige TIP‑API. Dit is de feitelijke basis onder onze gedeelde API‑vraag — per regel: wat de flow nodig heeft en waar dat vandaag staat.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ECD-flowNodig van TIPStatus in TIP
Commando typen/inspreken → intent + prefillPOST /detect✓ bestaat
Verwijsbrief/document binnen → gegevens eruitArtifact-push + IE-extractie in workspace✓ bestaat (data layer)
Na actie → vervolgsuggestie (nudge)Event-push → intentregels → taak terug△ model bestaat; push-API vanuit het ECD moet in het contract
Bevestiging behandelaar (human-in-the-loop)/human-gates, taken ophalen & afmelden✓ bestaat
Beheer: intents, regels, scenario’s configureren/intents, /rules, /contexts, /scenarios✓ bestaat
Audit-inzage/audit/events, /audit/chain✓ bestaat
Chat (streaming, conversationeel)✗ nog nergens belegd — samen kiezen: ECD-kant of platform-roadmap (ai-engine)
Entity resolution (“jan” → patiënt #427)✗ hoort ECD-kant: vereist patiëntendata
Kennisbank / RAG (bronverwijzing bij suggestie)✗ nog niet zichtbaar in TIP
+
+
+ +
+
7

Beslisvragen voor het gesprek

+
+
+ Welke rolverdeling kiezen we — platform of laag? +

Sluiten toepassingen aan op TIP (Model B), of legt TIP zich als laag op toepassingen (Model A)? Deze keuze maken we samen: hij bepaalt het verhaal richting álle toekomstige domeinen, niet alleen dit ECD.

+
+
+ Welke route kiezen we voor het GGZ-ECD? +

Voorstel in dit stuk: route 3 — /detect plus de instroomflow als proef. Akkoord, of zien we het anders?

+
+
+ Wie is eigenaar van welk gegeven? +

Per gegeven vastleggen: leidend in het ECD of in TIP, en hoe de ander de kopie actueel houdt (events). Startpunt: de gegevens uit de instroomflow.

+
+
+ Hoe ziet de push-API eruit — artifacts en events? +

Het ontbrekende stuk contract voor route 3: hoe levert het ECD documenten en gebeurtenissen aan, en hoe haalt het taken op en meldt het ze af?

+
+
+ Waar beleggen we chat, entity resolution en RAG? +

Drie behoeftes die nu nergens belegd zijn. Bewust ECD-kant houden, of op de platform-roadmap (bijv. de ai-engine-service)? Per stuk samen besluiten.

+
+
+
+ + + +
+ + diff --git a/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.02.png b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.02.png new file mode 100644 index 0000000..09a07c3 Binary files /dev/null and b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.02.png differ diff --git a/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.20.png b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.20.png new file mode 100644 index 0000000..57a37da Binary files /dev/null and b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.20.png differ diff --git a/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.31.png b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.31.png new file mode 100644 index 0000000..7ce83c2 Binary files /dev/null and b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.31.png differ diff --git a/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.38.png b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.38.png new file mode 100644 index 0000000..f15497d Binary files /dev/null and b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.38.png differ diff --git a/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.47.png b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.47.png new file mode 100644 index 0000000..5fdf9c9 Binary files /dev/null and b/docs/releasenotes/Scherm­afbeelding 2026-03-09 om 12.06.47.png differ diff --git a/docs/sessions/2026-07-14-sessielog.md b/docs/sessions/2026-07-14-sessielog.md new file mode 100644 index 0000000..f33dada --- /dev/null +++ b/docs/sessions/2026-07-14-sessielog.md @@ -0,0 +1,63 @@ +# Sessielog — 14 juli 2026 + +**Deelnemers:** Colin (PO) + Claude Code +**Onderwerp:** TIP↔ECD-samenspel, module-reviews, opschoning codebase (fase 0 + 0b) +**Branch:** `swift-cortex` + +--- + +## 1. Besluiten + +| # | Besluit | Toelichting | +|---|---|---| +| 1 | **ECD wordt opnieuw opgebouwd op een eigen datamodel** | Prototype leunt op Supabase + FHIR-conventies met structurele gebreken. Geen quick fixes; eerst strippen, dan van scratch opbouwen met goede logica en module-interacties. | +| 2 | **Geen quick fixes op de gevonden bugs** | De reviewbevindingen (zie §3) zijn symptomen van het ontbrekende datamodel; structureel oplossen in de rebuild. | +| 3 | **Kapotte intake-tabs en behandelplan-module verwijderd** | Anamnese/Onderzoeken/ROM konden nooit opslaan; behandelplan-AI negeerde diagnose en ernst. Beide komen terug bij de rebuild. | +| 4 | **FHIR en Cortex blijven voorlopig staan** | `/api/fhir/Patient` is de facto de patiënten-API (dossier, agenda, Cortex). Vervangen is fase 3. Cortex-flows raken de TIP-koppeling — niet twee keer bouwen. | +| 5 | **Instroom als eerste rebuild-module (voorstel, nog te bevestigen)** | Verwijzing → screening → intake is ook de TIP-proefflow; bewijst datamodel én TIP-samenspel in één beweging. | + +## 2. TIP↔ECD-verkenning + +- TIP-repo (`../intent-platform/tip`) verkend: Dossier → Workspace → Artifact → Information Elements → Intentregels → Intent → Scenario. Twee paden naar een intent: gestructureerd (regels over IE's) en ongestructureerd (`POST /detect`). +- **Kerninzicht:** TIP is een platform waar je een systeem *op aansluit* (datafeed vereist), geen laag die je *op een systeem legt*. Dat wijkt af van het beeld waarmee het ECD is ontworpen — en van het Cortex-architectuurdocument v0.1, dat de intent-motor als in-app bibliotheek (`lib/cortex/`) beschrijft en TIP nog niet kent. +- Drie routes uitgewerkt: (1) alleen `/detect`, (2) TIP als volledig procesbrein, (3) **groeipad** — `/detect` + één gestructureerde flow (verwijsbrief → intake) als proef. Aanbeveling: route 3. +- Open behoeftes die TIP nu niet dekt: streaming chat, entity resolution (hoort ECD-kant: vereist patiëntendata), RAG/kennisbank. +- **Discussiestuk voor de werksessie Colin & Joshua:** `docs/intent/samenspel-tip-ecd.html` (ook als artifact gepubliceerd). Bevat de twee modellen, verwijsbrief-flow, drie routes, API-mapping en vijf beslisvragen. + +## 3. Module-reviews (drie parallelle agents) + +**Intake** — Anamnese, Onderzoeken en ROM functioneel volledig kapot: UI stuurt weergave-labels ("Psychiatrische anamnese") waar de database systeemcodes eist (`psychiatrisch`). Alle verwijderknoppen doen stil niets (DELETE RLS-policies ontbreken op alle intake-tabellen). Intake-header "Bewerken" is een lege placeholder. Kindcheck met default-antwoorden slaat leeg object op → voortgangsring blijft op "ontbreekt". + +**Diagnoses** — Verwijderen is een no-op (zelfde RLS-oorzaak, live geverifieerd). Behandelplan-AI kreeg "DSM-categorie: primary-diagnosis" in de prompt i.p.v. de diagnose; ernst viel altijd terug op "middel". DSM-5-veld werd verzameld maar nooit opgeslagen. UI kent 4 van de 6 statussen. ICD-10 hardcoded (55 F-codes). Twee parallelle diagnosemodules op dezelfde tabel. `conditions`-tabel staat in geen enkele migration (schema niet reproduceerbaar). + +**Agenda** — Drie schrijfpaden (EPD-modal, Cortex server actions, Cortex API-routes) met elk eigen conventies. Modal-afspraken verschuiven +2 uur per opslag (naïeve tijdstring → timestamptz). `practitioner_id` leeg → Cortex cancel/reschedule altijd 403. Cortex annuleer/verzet-flow doodlopend (parser levert string, UI verwacht object). Dagweergave filtert vanaf kloktijd i.p.v. middernacht. Patiëntzoeker case-sensitive op voornaam. + +## 4. Uitgevoerd: opschoning (7 commits, −24.097 regels) + +Van 394 naar 298 ts/tsx-bestanden. Build en lint groen na elke stap. + +| Commit | Inhoud | +|---|---| +| `10c994b` | Fase 0: marketing-site, blog, leads-API, `_archive`, sitemap; root → `/login`; robots op disallow-all | +| `5e910c3` | Dubbele diagnosemodule (patiëntniveau) weg | +| `a8b02c1` | Kapotte intake-tabs weg, incl. server actions, voortgangsring en Cortex-navigatiedoelen | +| `96848ec` | Behandelplan-module weg (ook uit patiëntdashboard, dashboard-API, Cortex-dashboardblock) | +| `38dd7c1` | Placeholder-pagina's dashboard/reports weg; login-tests → `/epd/agenda` | +| `caeba77` | `/epd/clients` redirect-laag weg; alles landt op `/epd/patients` | +| `b00bfa5` | `next-mdx-remote`, `gray-matter`, laatste marketing-restanten weg | + +Sidebar toont nu alleen werkende modules: Cortex, Cliënten, Verpleegrapportage, Agenda. + +**Nog open uit de verificatie:** e2e-suites en browser-smoke-check (vereisen draaiende dev-server). Commits zijn lokaal — nog niet gepusht. + +## 5. Vervolgstappen + +1. **Werksessie Colin & Joshua** over het TIP↔ECD-samenspel — discussiestuk als agenda (vijf beslisvragen, o.a. platform-of-laag en eigenaarschap per gegeven). +2. **Ontwerpdocument rebuild:** moduleset + flows + datamodel-voorstel (centrale waardelijsten, herkomst-velden mens/AI, stabiele ID's, pgvector-klaar, Neo4j ontworpen-niet-aangezet). Review met Colin vóór er een migration wordt geschreven. +3. Daarna: verse schema-baseline, `lib/dat/`, modules één voor één terugbouwen — **instroom eerst**. + +## Referenties + +- Discussiestuk: `docs/intent/samenspel-tip-ecd.html` +- Platform-architectuur: `../../docs/knowledge/technische-architectuur.md` (v0.1 — behoeft v0.2 met TIP-ADR) +- Opruimplan (goedgekeurd): zes stappen fase 0b, uitgevoerd zoals hierboven diff --git a/docs/sessions/2026-07-17-sessielog.md b/docs/sessions/2026-07-17-sessielog.md new file mode 100644 index 0000000..8d63cbe --- /dev/null +++ b/docs/sessions/2026-07-17-sessielog.md @@ -0,0 +1,64 @@ +# Sessielog — 17 juli 2026 + +**Deelnemers:** Colin (PO) + Claude Code +**Onderwerp:** Datamodel-discovery ronde 1 — feitzinnen en entiteitenkaart van aanmelding t/m behandelplan +**Branch:** `swift-cortex` + +--- + +## 1. Wat er ligt + +- **`docs/datamodel/deelmodellen/datamodel-discovery.md`** — feitzinnen, besluiten en open vragen per flow (§5.1 t/m §5.5), FCO-IM-werkwijze +- **`docs/datamodel/entiteitenkaarten/entiteitenkaart-instroom.html`** — samenhang-diagram + drie ER-diagrammen + waardelijsten, gepubliceerd als artifact ter review + +De scope van ronde 1 is deze sessie verbreed: van alleen instroom naar **aanmelding t/m behandelplan** (incl. wachtlijstbeheer, diagnose, behandelplan-kern). Agenda en rapportage & overdracht blijven latere rondes. + +## 2. Besluiten + +| # | Besluit | Toelichting | +|---|---|---| +| 1 | **PERSOON en CLIENT gesplitst** | PERSOON draagt identiteit (naam, geboortedatum, BSN); CLIENT is een rol erop (cliëntnummer, sinds-datum). Zelfde persoon kan later ook contactpersoon, vertegenwoordiger of medewerker zijn. **BSN optioneel op PERSOON** — de verplichting hoort bij de rol (cliënt: Wabvpz), niet bij de persoon. | +| 2 | **ZORGEPISODE als eigen entiteit** | Aanmelding = binnenkomst-gebeurtenis (verwijzer, screening, besluit, aanmeldwachttijd); zorgepisode = periode van zorg die bij uitkomst "intake" start (intakes, diagnoses, behandelplan, behandelwachttijd). Sluit straks aan op het ZPM-zorgtraject. | +| 3 | **VERWIJZER + PRAKTIJK_INSTELLING als losse entiteiten** | AGB-verplichting hangt als attribuut aan de waardelijst `verwijzertype` (huisarts ja, gemeente nee). Geen AGB-validatie tegen Vecozo nu — declaratie-ronde. | +| 4 | **Aanmelding-status flexibel** | `nieuw → in screening → besloten`, vrij terug te bewegen. Uitkomst bij besloten: `intake` / `afgewezen` / `doorverwezen` / `wachtlijst`. | +| 5 | **Verwijsbrief is een nudge, geen harde eis** | Protocolregel met severity per financieringtype; beschikking telt ook als verwijzing (waardelijst `verwijsdocument_type`). Zelfde patroon voor screening-vóór-intake. | +| 6 | **Intake hangt aan de zorgepisode, meerdere per episode** | Interne intakes (bijv. afdelingsovergang) bestaan; `aanleiding`-waardelijst (regulier/intern/crisis). | +| 7 | **AFDELING en ZORGPROGRAMMA gescheiden waardelijsten** | FACT is een programma, geen afdeling. Lost de drie inconsistente hardcoded lijsten uit het prototype op. | +| 8 | **Screeningsactiviteiten getypeerd** | Type uit waardelijst + vrije toelichting (prototype had alleen vrije tekst). | +| 9 | **WACHTLIJSTPLAATSING als entiteit, twee momenten** | Aanmeldwachttijd (aan aanmelding) én behandelwachttijd (aan episode) — volgt de Treeknormen. Prioriteit, telt-vanaf, beëindigingsreden. | +| 10 | **DSM-5-TR primair, ICD-10 via mapping** | Behandelaren registreren DSM; ICD-10 afgeleid via mappingtabel. Prototype deed het omgekeerd (ICD-10 + DSM als vrije tekst). | +| 11 | **Diagnose aan de episode, intake als herkomst** | Diagnose overleeft de intake; twee status-assen (klinisch + verificatie werkdiagnose/definitief). | +| 12 | **Behandelplan: kern eerst** | Doelen (B1-cliëntversie), interventies, evaluatiemomenten, versies, cliëntakkoord. Sessieplanning → agenda-ronde; leefgebieden en veiligheidsplan → later. | +| 13 | **CLIENTPORTAAL_ACCOUNT als entiteit** (0..1 op CLIENT) | Alleen de relatie; auth-details in de auth/ADM-ronde. Verwijzersportaal genoteerd als idee, geen besluit. | + +## 3. Bevindingen prototype (verkenningen) + +- **Afdelingen:** drie onderling inconsistente hardcoded lijsten (screening 6, intake 3 met andere labels, behandeladvies 4 + 4 zorgprogramma's). +- **Diagnose-module:** heet DSM-5 maar registreert feitelijk ICD-10 F-codes; `verification_status` zit in de tabel maar de UI gebruikt het niet. +- **Kindcheck:** geen enkele uitkomstwaarde maar drie ja/nee-vlaggen + tekst — zo overgenomen in het model. +- **Wachtlijst:** bestaat nergens in code/DB, alleen als roadmap-idee — vanaf nul ontworpen. +- **Behandelplan:** gestript maar typemodel teruggehaald uit git-historie; statussen waren inconsistent tussen TS- en DB-laag. +- **Intake afronden:** liep via behandeladvies-tab met uitkomst `in_zorg`/`doorverwijzing`/`extra_diagnostiek` — als startlijst overgenomen. + +## 4. Open vragen (belangrijkste) + +1. Wanneer ontstaat de ZORGEPISODE precies — bij uitkomst "intake" of al bij "wachtlijst"? +2. Verhouding screeningsbesluit ↔ aanmelding-uitkomst: één feit of twee? +3. Behandelplan: statusmachine (cliëntakkoord als status of feit, WGBO), versiebeheer-vorm, verplicht-vóór-behandeling als nudge of eis. +4. "Max één hoofddiagnose" — per episode of per moment in de tijd? +5. DSM-ICD-mappingtabel: bron en onderhoud (APA-licentie) — vóór de schema-baseline. +6. Startlijsten bevestigen: afdelingen, zorgprogramma's, AGB-verplichting per verwijzertype, beëindigingsredenen wachtlijst, intake-uitkomsten. +7. Wie mag statussen zetten / diagnose stellen / intake afronden → rollen- en disciplinesronde. + +## 5. Vervolgstappen + +1. Entiteitenkaart reviewen (Colin) — met name kardinaliteiten en de aannames in de "Openstaand"-blokken. +2. Statussen + eigenaarschap ECD/TIP per entiteit vastleggen (§6 stap 3). +3. Latere rondes: agenda, rapportage & overdracht, rollen/disciplines, declaratie. +4. Pas daarna: verse schema-baseline (migrations) + `lib/dat/`. + +## Referenties + +- Discovery: `docs/datamodel/deelmodellen/datamodel-discovery.md` +- Entiteitenkaart: `docs/datamodel/entiteitenkaarten/entiteitenkaart-instroom.html` (artifact: claude.ai/code/artifact/e2340972-f677-4f0d-af95-b40e8dcac241) +- Vorige sessie: `docs/sessions/2026-07-14-sessielog.md` diff --git a/lib/ai/behandelplan-prompt.ts b/lib/ai/behandelplan-prompt.ts deleted file mode 100644 index c2eb3a6..0000000 --- a/lib/ai/behandelplan-prompt.ts +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Behandelplan AI Prompts - * - * System prompt en user prompt templates voor AI-generatie van behandelplannen - */ - -import { type LifeDomainScore, LIFE_DOMAIN_META } from '@/lib/types/leefgebieden'; -import { type Severity, getInterventionsForCategory, getRecommendedSessionCount, getRecommendedFrequency, getRecommendedFormat } from './intervention-mapping'; - -/** - * Context voor behandelplan generatie - */ -export interface PlanContext { - patientId: string; - intakeNotes: string; - dsmCategory: string; - severity: Severity; - lifeDomains: LifeDomainScore[]; - extraInstructions?: string; -} - -/** - * System prompt voor Claude - * Instructies voor het genereren van een behandelplan - */ -export const BEHANDELPLAN_SYSTEM_PROMPT = `Je bent een ervaren GGZ-behandelaar die behandelplannen opstelt voor cliënten in de ambulante GGZ. -Je maakt SMART doelen die recovery-gericht en evidence-based zijn. - -INSTRUCTIES: -1. Genereer 2-4 SMART doelen gebaseerd op de intake en diagnose -2. Focus op leefgebieden met prioriteit "hoog" -3. Verdeel doelen over minimaal 2 verschillende leefgebieden -4. Maak concrete, meetbare doelen (geen vage termen zoals "beter voelen") -5. Genereer voor elk doel een B1-taal versie (cliënt-vriendelijk, simpele woorden) -6. Kies evidence-based interventies passend bij de DSM-categorie -7. Plan sessies gebaseerd op severity niveau -8. Voeg een veiligheidsplan toe ALLEEN bij severity "hoog" - -SMART CRITERIA: -- Specifiek: Wat precies wil de cliënt bereiken? -- Meetbaar: Hoe meten we vooruitgang? (bijv. "3x per week", "score van 3 naar 4") -- Acceptabel: Past bij wensen en mogelijkheden cliënt -- Realistisch: Haalbaar binnen de behandelperiode -- Tijdgebonden: Binnen hoeveel weken? - -B1-TAAL RICHTLIJNEN (cliënt-versie): -- Korte zinnen (max 15 woorden) -- Alledaagse woorden (geen jargon) -- Actieve zinnen ("Ik ga..." niet "Er zal worden...") -- Directe aanspreking ("jij" of "je") - -OUTPUT FORMAT: -Retourneer ALLEEN valide JSON volgens dit exacte schema (geen extra tekst): -{ - "behandelstructuur": { - "duur": "string (bijv. '8 weken')", - "frequentie": "string (bijv. 'Wekelijks')", - "aantalSessies": number, - "vorm": "string (bijv. 'Individueel')" - }, - "doelen": [ - { - "id": "string (uuid)", - "title": "string (max 200 tekens, korte beschrijving)", - "description": "string (SMART uitwerking, 2-3 zinnen)", - "clientVersion": "string (B1-taal versie voor cliënt)", - "lifeDomain": "dlv|wonen|werk|sociaal|vrijetijd|financien|gezondheid", - "priority": "hoog|middel|laag", - "measurability": "string (hoe meten we vooruitgang?)", - "timelineWeeks": number, - "status": "niet_gestart", - "progress": 0 - } - ], - "interventies": [ - { - "id": "string (uuid)", - "name": "string (bijv. 'CGT', 'EMDR')", - "description": "string (uitleg interventie)", - "rationale": "string (waarom past dit bij deze cliënt?)", - "linkedGoalIds": ["string (id van gekoppeld doel)"] - } - ], - "sessiePlanning": [ - { - "id": "string (uuid)", - "nummer": number, - "focus": "string (waar gaat de sessie over?)", - "status": "gepland", - "gekoppeldeDoelIds": ["string"] - } - ], - "evaluatiemomenten": [ - { - "id": "string (uuid)", - "type": "tussentijds|eind", - "weekNumber": number, - "plannedDate": "string (ISO date, mag leeg)", - "status": "gepland" - } - ], - "veiligheidsplan": null of { - "waarschuwingssignalen": ["string (3-5 items)"], - "copingStrategieen": ["string (3-5 items)"], - "contacten": [ - { - "naam": "string", - "rol": "string", - "telefoon": "string" - } - ], - "restricties": ["string (optioneel)"] - } -}`; - -/** - * Bouw user prompt met context - */ -export function buildUserPrompt(context: PlanContext): string { - // Get high priority domains - const highPriorityDomains = context.lifeDomains.filter(d => d.priority === 'hoog'); - const lowScoreDomains = context.lifeDomains.filter(d => d.baseline <= 2); - - // Get intervention suggestions - const suggestedInterventions = getInterventionsForCategory(context.dsmCategory, context.severity); - const recommendedSessions = getRecommendedSessionCount(context.dsmCategory, context.severity); - const recommendedFrequency = getRecommendedFrequency(context.severity); - const recommendedFormat = getRecommendedFormat(context.severity); - - // Build life domains section - const lifeDomainLines = context.lifeDomains.map(d => { - const meta = LIFE_DOMAIN_META[d.domain]; - const priorityMarker = d.priority === 'hoog' ? ' [PRIORITEIT]' : ''; - const lowScoreMarker = d.baseline <= 2 ? ' [LAAG]' : ''; - return `- ${meta.label} (${d.domain}): ${d.baseline}/5 → doel ${d.target}/5${priorityMarker}${lowScoreMarker}${d.notes ? ` (${d.notes})` : ''}`; - }).join('\n'); - - // Build intervention suggestions - const interventionLines = suggestedInterventions.slice(0, 3).map(i => - `- ${i.name}: ${i.description} (~${i.recommendedSessions} sessies)` - ).join('\n'); - - return `CLIËNT CONTEXT: -================= -Intake notities: -${context.intakeNotes} - -DSM-categorie: ${context.dsmCategory} -Severity: ${context.severity} - -LEEFGEBIEDEN ASSESSMENT: -======================== -${lifeDomainLines} - -${highPriorityDomains.length > 0 ? ` -FOCUS GEBIEDEN (hoge prioriteit): -${highPriorityDomains.map(d => `- ${LIFE_DOMAIN_META[d.domain].label}`).join('\n')} -` : ''} -${lowScoreDomains.length > 0 ? ` -AANDACHTSPUNTEN (lage scores): -${lowScoreDomains.map(d => `- ${LIFE_DOMAIN_META[d.domain].label} (score: ${d.baseline}/5)`).join('\n')} -` : ''} - -AANBEVOLEN INTERVENTIES (evidence-based): -========================================= -${interventionLines} - -AANBEVOLEN BEHANDELSTRUCTUUR: -============================= -- Aantal sessies: ~${recommendedSessions} -- Frequentie: ${recommendedFrequency} -- Vorm: ${recommendedFormat} - -${context.extraInstructions ? ` -EXTRA INSTRUCTIES VAN BEHANDELAAR: -================================== -${context.extraInstructions} -` : ''} - -Genereer nu een compleet behandelplan in JSON formaat. -Zorg dat elk doel: -1. Gekoppeld is aan een leefgebied met hoge prioriteit of lage score -2. Een meetbare indicator heeft -3. Een B1-taal versie heeft voor de cliënt -4. Realistisch is voor de behandelperiode - -${context.severity === 'hoog' ? 'BELANGRIJK: Voeg ook een veiligheidsplan toe met waarschuwingssignalen, copingstrategieën en contactpersonen.' : ''}`; -} - -/** - * Valideer of de context voldoende informatie bevat - */ -export function validatePlanContext(context: PlanContext): { valid: boolean; errors: string[] } { - const errors: string[] = []; - - if (!context.intakeNotes || context.intakeNotes.length < 50) { - errors.push('Intake notities zijn te kort (minimaal 50 tekens)'); - } - - if (!context.dsmCategory) { - errors.push('DSM-categorie ontbreekt'); - } - - if (!context.severity) { - errors.push('Severity niveau ontbreekt'); - } - - if (!context.lifeDomains || context.lifeDomains.length !== 7) { - errors.push('Leefgebieden assessment is incompleet (7 domeinen vereist)'); - } - - const hasHighPriority = context.lifeDomains?.some(d => d.priority === 'hoog'); - if (!hasHighPriority) { - errors.push('Minimaal 1 leefgebied moet prioriteit "hoog" hebben'); - } - - return { - valid: errors.length === 0, - errors, - }; -} diff --git a/lib/ai/intervention-mapping.ts b/lib/ai/intervention-mapping.ts deleted file mode 100644 index b2736f8..0000000 --- a/lib/ai/intervention-mapping.ts +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Evidence-Based Intervention Mapping - * - * Mapping van DSM-categorieën naar evidence-based interventies - * met sessie-aantallen per severity level - */ - -export interface InterventionSuggestion { - name: string; - description: string; - sessions: { - laag: number; - middel: number; - hoog: number; - }; -} - -export type Severity = 'laag' | 'middel' | 'hoog'; - -/** - * DSM categorieën naar interventie mapping - * Gebaseerd op richtlijnen GGZ Standaarden - */ -export const INTERVENTION_MAPPING: Record = { - angststoornissen: [ - { - name: 'CGT', - description: 'Cognitieve Gedragstherapie gericht op angstreductie door blootstelling en cognitieve herstructurering', - sessions: { laag: 8, middel: 10, hoog: 14 }, - }, - { - name: 'Exposure therapie', - description: 'Systematische blootstelling aan angstopwekkende situaties met responspreventie', - sessions: { laag: 6, middel: 8, hoog: 12 }, - }, - { - name: 'ACT', - description: 'Acceptance and Commitment Therapy voor psychologische flexibiliteit', - sessions: { laag: 8, middel: 10, hoog: 12 }, - }, - ], - stemmingsklachten: [ - { - name: 'CGT', - description: 'Cognitieve Gedragstherapie gericht op negatieve denkpatronen en gedragsactivatie', - sessions: { laag: 8, middel: 10, hoog: 14 }, - }, - { - name: 'IPT', - description: 'Interpersoonlijke Therapie gericht op relationele patronen en sociale steun', - sessions: { laag: 8, middel: 12, hoog: 16 }, - }, - { - name: 'Gedragsactivatie', - description: 'Gestructureerde toename van plezierige en betekenisvolle activiteiten', - sessions: { laag: 6, middel: 8, hoog: 10 }, - }, - ], - trauma_ptss: [ - { - name: 'EMDR', - description: 'Eye Movement Desensitization and Reprocessing voor traumaverwerking', - sessions: { laag: 6, middel: 10, hoog: 16 }, - }, - { - name: 'Narratieve therapie', - description: 'Verwerking door het opbouwen van een coherent traumaverhaal', - sessions: { laag: 8, middel: 12, hoog: 16 }, - }, - { - name: 'CGT trauma-focus', - description: 'Trauma-gefocuste CGT met exposure en cognitieve verwerking', - sessions: { laag: 8, middel: 12, hoog: 16 }, - }, - ], - persoonlijkheid: [ - { - name: 'Schematherapie', - description: 'Langdurige therapie gericht op disfunctionele schemas en copingstijlen', - sessions: { laag: 16, middel: 24, hoog: 40 }, - }, - { - name: 'MBT', - description: 'Mentalization-Based Treatment voor emotieregulatie en relaties', - sessions: { laag: 16, middel: 24, hoog: 40 }, - }, - { - name: 'DGT', - description: 'Dialectische Gedragstherapie voor emotieregulatie en crisisvaardigheden', - sessions: { laag: 16, middel: 24, hoog: 40 }, - }, - ], - verslaving: [ - { - name: 'Motiverende gespreksvoering', - description: 'Versterken van intrinsieke motivatie voor gedragsverandering', - sessions: { laag: 4, middel: 6, hoog: 8 }, - }, - { - name: 'CGT verslaving', - description: 'Cognitieve Gedragstherapie gericht op craving en terugvalpreventie', - sessions: { laag: 8, middel: 12, hoog: 16 }, - }, - { - name: 'Terugvalpreventie', - description: 'Identificeren en managen van risicosituaties en triggers', - sessions: { laag: 6, middel: 8, hoog: 12 }, - }, - ], - adhd: [ - { - name: 'Psycho-educatie', - description: 'Educatie over ADHD en praktische copingstrategieën', - sessions: { laag: 4, middel: 6, hoog: 8 }, - }, - { - name: 'Coaching/planning', - description: 'Structuur en planningsvaardigheden ontwikkelen', - sessions: { laag: 6, middel: 8, hoog: 12 }, - }, - { - name: 'CGT ADHD', - description: 'Gedragstherapie gericht op impulscontrole en executieve functies', - sessions: { laag: 8, middel: 10, hoog: 14 }, - }, - ], - autisme: [ - { - name: 'Psycho-educatie', - description: 'Educatie over autisme en zelfacceptatie', - sessions: { laag: 4, middel: 6, hoog: 8 }, - }, - { - name: 'Sociale vaardigheden', - description: 'Training in sociale interactie en communicatie', - sessions: { laag: 8, middel: 12, hoog: 16 }, - }, - { - name: 'Stressmanagement', - description: 'Omgaan met prikkels en sensorische overbelasting', - sessions: { laag: 6, middel: 8, hoog: 12 }, - }, - ], - overig: [ - { - name: 'Ondersteunende gesprekken', - description: 'Steunende begeleiding en reflectie', - sessions: { laag: 4, middel: 6, hoog: 8 }, - }, - { - name: 'Psycho-educatie', - description: 'Educatie over klachten en copingstrategieën', - sessions: { laag: 4, middel: 6, hoog: 8 }, - }, - ], -}; - -/** - * Haal interventies op voor een DSM categorie - */ -export function getInterventionsForCategory( - dsmCategory: string, - severity: Severity -): { name: string; description: string; recommendedSessions: number }[] { - // Normalize category - const normalizedCategory = dsmCategory - .toLowerCase() - .replace(/[^a-z]/g, '_') - .replace(/_+/g, '_'); - - // Find matching category - const interventions = - INTERVENTION_MAPPING[normalizedCategory] || - INTERVENTION_MAPPING['overig']; - - return interventions.map((intervention) => ({ - name: intervention.name, - description: intervention.description, - recommendedSessions: intervention.sessions[severity], - })); -} - -/** - * Bereken aanbevolen aantal sessies - */ -export function getRecommendedSessionCount( - dsmCategory: string, - severity: Severity -): number { - const interventions = getInterventionsForCategory(dsmCategory, severity); - if (interventions.length === 0) return 8; - - // Neem gemiddelde van eerste 2 interventies - const topInterventions = interventions.slice(0, 2); - const avg = - topInterventions.reduce((sum, i) => sum + i.recommendedSessions, 0) / - topInterventions.length; - - return Math.round(avg); -} - -/** - * Bepaal behandelvorm op basis van severity - */ -export function getRecommendedFormat(severity: Severity): string { - switch (severity) { - case 'laag': - return 'Individueel'; - case 'middel': - return 'Individueel'; - case 'hoog': - return 'Individueel + groep (optioneel)'; - default: - return 'Individueel'; - } -} - -/** - * Bepaal behandelfrequentie op basis van severity - */ -export function getRecommendedFrequency(severity: Severity): string { - switch (severity) { - case 'laag': - return 'Wekelijks tot tweewekelijks'; - case 'middel': - return 'Wekelijks'; - case 'hoog': - return 'Wekelijks tot 2x per week'; - default: - return 'Wekelijks'; - } -} diff --git a/lib/content/loader.ts b/lib/content/loader.ts deleted file mode 100644 index 424b980..0000000 --- a/lib/content/loader.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Content Loader Utility - * - * Loads JSON content files from the content directory structure. - * Supports server-side loading with TypeScript type safety. - * Also supports loading and parsing markdown files. - * - * @example - * ```ts - * const manifesto = await getContent('nl', 'manifesto') - * const markdownSections = await getMarkdownContent('docs/manifesto.md') - * ``` - */ - -import { readFile } from 'fs/promises' -import { join } from 'path' -import { markdownToSections } from './markdown-parser' - -export async function getContent( - locale: string = 'nl', - file: string -): Promise { - try { - const content = await import(`@/content/${locale}/${file}.json`) - return content.default as T - } catch (error) { - console.error(`Failed to load content: ${locale}/${file}.json`, error) - throw new Error(`Content not found: ${locale}/${file}.json`) - } -} - -/** - * Load and parse markdown file to ManifestoSection format - * Useful for converting manifesto.md to React components - */ -export async function getMarkdownContent( - filePath: string -): Promise> { - try { - const fullPath = join(process.cwd(), filePath) - const markdown = await readFile(fullPath, 'utf-8') - return markdownToSections(markdown) - } catch (error) { - console.error(`Failed to load markdown: ${filePath}`, error) - throw new Error(`Markdown file not found: ${filePath}`) - } -} - -/** - * Type-safe content loader with fallback - * - * Returns default content if file is not found (useful for development) - */ -export async function getContentWithFallback( - locale: string = 'nl', - file: string, - fallback: T -): Promise { - try { - return await getContent(locale, file) - } catch (error) { - console.warn(`Using fallback content for: ${locale}/${file}.json`) - return fallback - } -} - diff --git a/lib/content/markdown-parser.ts b/lib/content/markdown-parser.ts deleted file mode 100644 index 3dd2af3..0000000 --- a/lib/content/markdown-parser.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Markdown Parser Utility - * - * Simple markdown parser for converting manifesto.md content - * to React components with proper typography. - * - * This is a lightweight parser for basic markdown features: - * - Paragraphs - * - Line breaks - * - Basic formatting (bold, italic) - */ - -export interface ParsedMarkdown { - paragraphs: string[] - metadata?: Record -} - -/** - * Parse markdown content into structured format - * Splits content by double line breaks into paragraphs - */ -export function parseMarkdown(markdown: string): ParsedMarkdown { - // Split by double line breaks or single line breaks followed by empty line - const paragraphs = markdown - .split(/\n\s*\n/) - .map((para) => para.trim()) - .filter((para) => para.length > 0) - - return { - paragraphs, - } -} - -/** - * Convert markdown paragraphs to ManifestoSection format - * This allows markdown content to be used with existing components - */ -export function markdownToSections(markdown: string): Array<{ - id: string - type: 'paragraph' - content: string -}> { - const parsed = parseMarkdown(markdown) - - return parsed.paragraphs.map((content, index) => ({ - id: `paragraph-${index + 1}`, - type: 'paragraph' as const, - content: content.trim(), - })) -} - -/** - * Simple markdown renderer for inline formatting - * Converts **bold** and *italic* to HTML - */ -export function renderMarkdownInline(text: string): string { - return text - .replace(/\*\*(.+?)\*\*/g, '$1') - .replace(/\*(.+?)\*/g, '$1') - .replace(/`(.+?)`/g, '$1') -} - diff --git a/lib/cortex/entity-extractor.ts b/lib/cortex/entity-extractor.ts index 6e607cb..5c8df2b 100644 --- a/lib/cortex/entity-extractor.ts +++ b/lib/cortex/entity-extractor.ts @@ -685,9 +685,6 @@ export type IntakeTab = | 'contacts' | 'kindcheck' | 'risk' - | 'anamnese' - | 'examination' - | 'rom' | 'diagnosis' | 'behandeladvies'; @@ -703,10 +700,6 @@ const INTAKE_TAB_KEYWORDS: Record = { 'diagnose': 'diagnosis', 'diagnoses': 'diagnosis', 'dsm': 'diagnosis', - // Anamnese - 'anamnese': 'anamnese', - 'voorgeschiedenis': 'anamnese', - 'geschiedenis': 'anamnese', // Contacts 'contact': 'contacts', 'contacten': 'contacts', @@ -714,14 +707,6 @@ const INTAKE_TAB_KEYWORDS: Record = { // Kindcheck 'kindcheck': 'kindcheck', 'kinderen': 'kindcheck', - // Examination - 'onderzoek': 'examination', - 'psychiatrisch': 'examination', - // ROM - 'rom': 'rom', - 'meetinstrumenten': 'rom', - 'vragenlijst': 'rom', - 'vragenlijsten': 'rom', // Behandeladvies 'behandeladvies': 'behandeladvies', 'advies': 'behandeladvies', diff --git a/lib/cortex/intent-classifier.ts b/lib/cortex/intent-classifier.ts index 8580a2f..ac4b98f 100644 --- a/lib/cortex/intent-classifier.ts +++ b/lib/cortex/intent-classifier.ts @@ -170,17 +170,15 @@ const INTENT_PATTERNS: Record, PatternConfig[]> // Navigation to specific intake tabs/sections { pattern: /^ga\s+naar\s+(de\s+)?(risico|risicotaxatie)/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?diagnose[ns]?/i, weight: 1.0 }, - { pattern: /^ga\s+naar\s+(de\s+)?anamnese/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?behandelgeschiedenis/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?medicatie/i, weight: 1.0 }, - { pattern: /^ga\s+naar\s+(de\s+)?meetinstrumenten/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(het\s+)?netwerk/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?doelen/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?samenvatting/i, weight: 1.0 }, // Open patterns - { pattern: /^open\s+(de\s+)?(risico|diagnose|anamnese|medicatie|netwerk|doelen|samenvatting)/i, weight: 0.95 }, + { pattern: /^open\s+(de\s+)?(risico|diagnose|medicatie|netwerk|doelen|samenvatting)/i, weight: 0.95 }, // Show patterns - { pattern: /^(toon|laat\s+zien)\s+(de\s+)?(risico|diagnose|anamnese|medicatie|netwerk|doelen|samenvatting)/i, weight: 0.9 }, + { pattern: /^(toon|laat\s+zien)\s+(de\s+)?(risico|diagnose|medicatie|netwerk|doelen|samenvatting)/i, weight: 0.9 }, ], risico_query: [ diff --git a/lib/cortex/reflex-classifier.ts b/lib/cortex/reflex-classifier.ts index 7ee22e1..3c2eb6c 100644 --- a/lib/cortex/reflex-classifier.ts +++ b/lib/cortex/reflex-classifier.ts @@ -179,18 +179,15 @@ const INTENT_PATTERNS: Record, PatternConfig[]> { pattern: /^ga\s+naar\s+(de\s+)?(risico|risicotaxatie)/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?diagnose/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?kindcheck/i, weight: 1.0 }, - { pattern: /^ga\s+naar\s+(de\s+)?anamnese/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?behandeladvies/i, weight: 1.0 }, - { pattern: /^ga\s+naar\s+(de\s+)?(rom|vragenlijst)/i, weight: 1.0 }, - { pattern: /^ga\s+naar\s+(de\s+)?onderzoek/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?contact/i, weight: 1.0 }, { pattern: /^ga\s+naar\s+(de\s+)?algemeen/i, weight: 1.0 }, // "Open [sectie]" patterns - { pattern: /^open\s+(de\s+)?(risico|diagnose|kindcheck|anamnese)/i, weight: 0.95 }, + { pattern: /^open\s+(de\s+)?(risico|diagnose|kindcheck)/i, weight: 0.95 }, // "Naar [sectie]" patterns (shorter) - { pattern: /^naar\s+(risico|diagnose|kindcheck|anamnese|behandeladvies)/i, weight: 0.9 }, + { pattern: /^naar\s+(risico|diagnose|kindcheck|behandeladvies)/i, weight: 0.9 }, ], risico_query: [ diff --git a/lib/cortex/types.ts b/lib/cortex/types.ts index 9068236..99c9317 100644 --- a/lib/cortex/types.ts +++ b/lib/cortex/types.ts @@ -92,7 +92,7 @@ export interface ExtractedEntities { time?: string; // Intake navigation (MVP) - navigationTarget?: 'contacts' | 'kindcheck' | 'risk' | 'anamnese' | 'examination' | 'rom' | 'diagnosis' | 'behandeladvies'; + navigationTarget?: 'contacts' | 'kindcheck' | 'risk' | 'diagnosis' | 'behandeladvies'; } // Block sizes diff --git a/lib/mdx/blog.ts b/lib/mdx/blog.ts deleted file mode 100644 index 612c0b9..0000000 --- a/lib/mdx/blog.ts +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Blog MDX Utilities - * - * Functions for loading and parsing blog MDX files with series support - * Based on lib/mdx/documentatie.ts - */ - -import fs from 'fs' -import path from 'path' -import matter from 'gray-matter' - -const BLOG_DIR = path.join(process.cwd(), 'content/nl/blog') - -// ============================================================================ -// Types -// ============================================================================ - -export interface BlogSeries { - id: string - title: string - description: string - color: 'teal' | 'amber' | 'slate' - order: number - status: 'active' | 'completed' | 'planned' -} - -export interface BlogFrontmatter { - title: string - description: string - date: string - published?: boolean - seriesOrder: number - tags?: string[] - image?: string // Optional OG image path (relative to /public, e.g. "/images/blog/my-post.png") -} - -export interface BlogPost { - slug: string - seriesId: string - frontmatter: BlogFrontmatter - content: string - readingTime: number -} - -export interface SeriesNavigation { - previous: { slug: string; title: string } | null - next: { slug: string; title: string } | null - current: number - total: number -} - -// ============================================================================ -// Helper Functions -// ============================================================================ - -function calculateReadingTime(content: string): number { - const wordsPerMinute = 200 - const wordCount = content.split(/\s+/).length - return Math.ceil(wordCount / wordsPerMinute) -} - -// ============================================================================ -// Series Functions -// ============================================================================ - -/** - * Get all series metadata - */ -export async function getAllSeries(): Promise { - try { - const seriesPath = path.join(BLOG_DIR, '_series.json') - - if (!fs.existsSync(seriesPath)) { - console.warn('_series.json not found') - return [] - } - - const content = fs.readFileSync(seriesPath, 'utf-8') - const data = JSON.parse(content) - - return (data.series || []).sort( - (a: BlogSeries, b: BlogSeries) => a.order - b.order - ) - } catch (error) { - console.error('Error loading series:', error) - return [] - } -} - -/** - * Get a single series by ID - */ -export async function getSeries(seriesId: string): Promise { - const allSeries = await getAllSeries() - return allSeries.find((s) => s.id === seriesId) || null -} - -// ============================================================================ -// Post Functions -// ============================================================================ - -/** - * Get all blog posts from all series - */ -export async function getAllPosts(): Promise { - if (!fs.existsSync(BLOG_DIR)) return [] - - const series = await getAllSeries() - const allPosts: BlogPost[] = [] - - for (const serie of series) { - const serieDir = path.join(BLOG_DIR, serie.id) - - if (!fs.existsSync(serieDir)) continue - - const files = fs.readdirSync(serieDir).filter((f) => f.endsWith('.mdx')) - - for (const file of files) { - const slug = file.replace('.mdx', '') - const filePath = path.join(serieDir, file) - const fileContent = fs.readFileSync(filePath, 'utf-8') - const { data, content } = matter(fileContent) - const frontmatter = data as BlogFrontmatter - - // Skip unpublished in production - if ( - process.env.NODE_ENV !== 'development' && - frontmatter.published === false - ) { - continue - } - - allPosts.push({ - slug, - seriesId: serie.id, - frontmatter, - content, - readingTime: calculateReadingTime(content), - }) - } - } - - // Sort by date, newest first - return allPosts.sort( - (a, b) => - new Date(b.frontmatter.date).getTime() - - new Date(a.frontmatter.date).getTime() - ) -} - -/** - * Get all posts for a specific series - */ -export async function getPostsBySeries(seriesId: string): Promise { - const serieDir = path.join(BLOG_DIR, seriesId) - - if (!fs.existsSync(serieDir)) return [] - - const files = fs.readdirSync(serieDir).filter((f) => f.endsWith('.mdx')) - const posts: BlogPost[] = [] - - for (const file of files) { - const slug = file.replace('.mdx', '') - const filePath = path.join(serieDir, file) - const fileContent = fs.readFileSync(filePath, 'utf-8') - const { data, content } = matter(fileContent) - const frontmatter = data as BlogFrontmatter - - // Skip unpublished in production - if ( - process.env.NODE_ENV !== 'development' && - frontmatter.published === false - ) { - continue - } - - posts.push({ - slug, - seriesId, - frontmatter, - content, - readingTime: calculateReadingTime(content), - }) - } - - // Sort by seriesOrder - return posts.sort( - (a, b) => a.frontmatter.seriesOrder - b.frontmatter.seriesOrder - ) -} - -/** - * Get a single post - */ -export async function getPost( - seriesId: string, - slug: string -): Promise { - const filePath = path.join(BLOG_DIR, seriesId, `${slug}.mdx`) - - if (!fs.existsSync(filePath)) return null - - const fileContent = fs.readFileSync(filePath, 'utf-8') - const { data, content } = matter(fileContent) - const frontmatter = data as BlogFrontmatter - - return { - slug, - seriesId, - frontmatter, - content, - readingTime: calculateReadingTime(content), - } -} - -/** - * Get navigation for a post within its series - */ -export async function getSeriesNavigation( - seriesId: string, - slug: string -): Promise { - const posts = await getPostsBySeries(seriesId) - const currentIndex = posts.findIndex((p) => p.slug === slug) - - return { - previous: - currentIndex > 0 - ? { slug: posts[currentIndex - 1].slug, title: posts[currentIndex - 1].frontmatter.title } - : null, - next: - currentIndex < posts.length - 1 - ? { slug: posts[currentIndex + 1].slug, title: posts[currentIndex + 1].frontmatter.title } - : null, - current: currentIndex + 1, - total: posts.length, - } -} - -/** - * Get series with post counts - */ -export async function getSeriesWithCounts(): Promise< - (BlogSeries & { postCount: number })[] -> { - const series = await getAllSeries() - const result = [] - - for (const serie of series) { - const posts = await getPostsBySeries(serie.id) - result.push({ - ...serie, - postCount: posts.length, - }) - } - - return result -} - diff --git a/lib/mdx/documentatie.ts b/lib/mdx/documentatie.ts deleted file mode 100644 index 16fa8c9..0000000 --- a/lib/mdx/documentatie.ts +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Release Notes MDX Utilities - * - * Functions for loading and parsing release note MDX files - */ - -import fs from 'fs' -import path from 'path' -import matter from 'gray-matter' - -const RELEASES_DIR = path.join(process.cwd(), 'content/nl/documentatie') - -export interface ReleaseFrontmatter { - title: string - category: string - group: 'foundation' | 'features' | 'infrastructure' | 'bugs' - version: string - releaseDate: string - status: 'completed' | 'in_progress' | 'planned' - description: string -} - -export interface ReleaseNote { - slug: string - frontmatter: ReleaseFrontmatter - content: string -} - -/** - * Get all release note files - */ -export async function getAllReleases(): Promise { - const files = fs.readdirSync(RELEASES_DIR) - - const releases = files - .filter(file => file.endsWith('.mdx') && !file.startsWith('_')) - .map(file => { - const slug = file.replace('.mdx', '') - const filePath = path.join(RELEASES_DIR, file) - const fileContent = fs.readFileSync(filePath, 'utf-8') - const { data, content } = matter(fileContent) - - return { - slug, - frontmatter: data as ReleaseFrontmatter, - content, - } - }) - - // Sort by group order, then by status (completed first), then alphabetically - return releases.sort((a, b) => { - const groupOrder = { foundation: 1, features: 2, infrastructure: 3, bugs: 4 } - const statusOrder = { completed: 1, in_progress: 2, planned: 3 } - - // 1. Sort by group - const aGroupOrder = groupOrder[a.frontmatter.group] - const bGroupOrder = groupOrder[b.frontmatter.group] - if (aGroupOrder !== bGroupOrder) return aGroupOrder - bGroupOrder - - // 2. Sort by status (completed first, then in_progress, then planned) - const aStatusOrder = statusOrder[a.frontmatter.status] ?? 4 - const bStatusOrder = statusOrder[b.frontmatter.status] ?? 4 - if (aStatusOrder !== bStatusOrder) return aStatusOrder - bStatusOrder - - // 3. Sort alphabetically within same status - return a.frontmatter.category.localeCompare(b.frontmatter.category) - }) -} - -/** - * Get a single release by slug - */ -export async function getRelease(slug: string): Promise { - const filePath = path.join(RELEASES_DIR, `${slug}.mdx`) - - if (!fs.existsSync(filePath)) { - return null - } - - const fileContent = fs.readFileSync(filePath, 'utf-8') - const { data, content } = matter(fileContent) - - return { - slug, - frontmatter: data as ReleaseFrontmatter, - content, - } -} - -/** - * Get releases grouped by their group (foundation, features, infrastructure, bugs) - */ -export async function getReleasesGrouped() { - const releases = await getAllReleases() - - return { - foundation: releases.filter(r => r.frontmatter.group === 'foundation'), - features: releases.filter(r => r.frontmatter.group === 'features'), - infrastructure: releases.filter(r => r.frontmatter.group === 'infrastructure'), - bugs: releases.filter(r => r.frontmatter.group === 'bugs'), - } -} - -/** - * Get category metadata from index file - */ -export interface CategoryMetadata { - slug: string - title: string - group: string - description: string - order: number -} - -export interface GroupMetadata { - id: string - title: string - description: string - order: number -} - -interface IndexData { - groups: GroupMetadata[] - categories: CategoryMetadata[] -} - -export async function getCategoryMetadata(): Promise { - try { - const indexPath = path.join(RELEASES_DIR, '_index.json') - - if (!fs.existsSync(indexPath)) { - console.warn('_index.json not found, returning empty metadata') - return { - groups: [], - categories: [] - } - } - - const indexContent = fs.readFileSync(indexPath, 'utf-8') - return JSON.parse(indexContent) - } catch (error) { - console.error('Error loading category metadata:', error) - return { - groups: [], - categories: [] - } - } -} - -/** - * Generate slug from heading text (must match the slugify function in mdx-components.tsx) - */ -function slugify(text: string): string { - return text - .toString() - .toLowerCase() - .trim() - .replace(/\s+/g, '-') - .replace(/[^\w\-]+/g, '') - .replace(/\-\-+/g, '-') -} - -/** - * Table of Contents item - */ -export interface TocItem { - id: string - text: string - level: number -} - -/** - * Extract headings from MDX content for Table of Contents - * Extracts h1, h2, and h3 headings, filtered for main sections - */ -export function extractHeadings(content: string): TocItem[] { - const headingRegex = /^(#{1,3})\s+(.+)$/gm - const headings: TocItem[] = [] - let match - - // Main sections to include in TOC (h2 level) - const includedH2Sections = [ - 'overview', - 'standaard-compliance', - 'geimplementeerde-resources', - 'relaties-tussen-resources', - 'privacy-beveiliging', - 'roadmap', - 'patient-api', - 'practitioner-api', - 'encounter-api', - 'condition-api', - 'observation-api', - 'careplan-api', - 'authenticatie-autorisatie', - 'error-handling', - ] - - // Resource subsections to include (h3 level) - const includedH3Sections = [ - '1-practitioners-behandelaren', - '2-organizations-instellingen', - '3-patients-patientenclienten', - '4-encounters-contactmomenten', - '5-conditions-diagnoses', - '6-observations-metingen-en-observaties', - '7-careplans-behandelplannen', - ] - - while ((match = headingRegex.exec(content)) !== null) { - const level = match[1].length - const text = match[2] - .replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1') // Remove markdown links - .replace(/`([^`]+)`/g, '$1') // Remove inline code - .replace(/\*\*([^*]+)\*\*/g, '$1') // Remove bold - .replace(/\*([^*]+)\*/g, '$1') // Remove italic - .trim() - - const id = slugify(text) - - // Include h2 headings from the main sections list - if (level === 2 && includedH2Sections.includes(id)) { - headings.push({ - id, - text, - level, - }) - } - // Include h3 headings from the resource sections list - else if (level === 3 && includedH3Sections.includes(id)) { - headings.push({ - id, - text, - level, - }) - } - } - - return headings -} diff --git a/lib/types/behandelplan.ts b/lib/types/behandelplan.ts deleted file mode 100644 index 579450c..0000000 --- a/lib/types/behandelplan.ts +++ /dev/null @@ -1,620 +0,0 @@ -/** - * Behandelplan (Treatment Plan) Types - * - * Types voor AI-gegenereerde behandelplannen - * Gebaseerd op FHIR CarePlan met GGZ-specifieke uitbreidingen - */ - -import { z } from 'zod'; -import { type LifeDomain, type LifeDomainScore, LIFE_DOMAINS, PRIORITIES } from './leefgebieden'; - -// ============================================================================= -// STATUS TYPES -// ============================================================================= - -/** - * Status van een behandelplan - */ -export const PLAN_STATUSES = ['concept', 'actief', 'in_evaluatie', 'afgerond', 'gearchiveerd'] as const; -export type PlanStatus = typeof PLAN_STATUSES[number]; - -/** - * Status van een doel - */ -export const GOAL_STATUSES = ['niet_gestart', 'bezig', 'gehaald', 'bijgesteld'] as const; -export type GoalStatus = typeof GOAL_STATUSES[number]; - -/** - * Type evaluatiemoment - */ -export const EVALUATION_TYPES = ['tussentijds', 'eind', 'crisis'] as const; -export type EvaluationType = typeof EVALUATION_TYPES[number]; - -/** - * Status evaluatiemoment - */ -export const EVALUATION_STATUSES = ['gepland', 'afgerond', 'overgeslagen'] as const; -export type EvaluationStatus = typeof EVALUATION_STATUSES[number]; - -// ============================================================================= -// CORE TYPES -// ============================================================================= - -/** - * Behandelstructuur - algemene parameters van het plan - */ -export interface Behandelstructuur { - duur: string; // bijv. "8 weken" - frequentie: string; // bijv. "Wekelijks" - aantalSessies: number; // bijv. 8 - vorm: string; // bijv. "Individueel" -} - -/** - * SMART Doel - */ -export interface SmartGoal { - id: string; - title: string; // Korte beschrijving (1 zin) - description: string; // SMART-uitwerking (2-3 zinnen) - clientVersion: string; // B1-taal versie voor cliënt - lifeDomain: LifeDomain; // Gekoppeld leefgebied - priority: 'hoog' | 'middel' | 'laag'; - measurability: string; // Hoe meten we vooruitgang? - timelineWeeks: number; // Binnen X weken - status: GoalStatus; - progress: number; // 0-100 -} - -/** - * Evidence-based Interventie - */ -export interface Intervention { - id: string; - name: string; // bijv. "CGT", "EMDR", "ACT" - description: string; // Uitleg van de interventie - rationale: string; // Waarom past dit bij deze cliënt? - linkedGoalIds: string[]; // Welke doelen worden hiermee benaderd? -} - -/** - * Evaluatiemoment - */ -export interface Evaluatiemoment { - id: string; - type: EvaluationType; - weekNumber: number; - plannedDate: string; // ISO date string - actualDate?: string; // Ingevuld na uitvoering - status: EvaluationStatus; - outcome?: string; // Vrije tekst resultaat - lifeDomainUpdates?: LifeDomainScore[]; // Nieuwe scores -} - -/** - * Veiligheidsplan (alleen bij severity "Hoog") - */ -export interface Veiligheidsplan { - waarschuwingssignalen: string[]; // 3-5 items - copingStrategieen: string[]; // 3-5 items - contacten: { - naam: string; - rol: string; - telefoon: string; - }[]; - restricties?: string[]; // bijv. "Geen alcohol tijdens behandeling" -} - -/** - * Sessie in de planning - */ -export interface Sessie { - id: string; - nummer: number; - focus: string; - datum?: string; // ISO date string - status: 'gepland' | 'afgerond' | 'no_show' | 'verzet' | 'geannuleerd'; - gekoppeldeDoelIds: string[]; - notities?: string; -} - -// ============================================================================= -// GENERATED PLAN (AI OUTPUT) -// ============================================================================= - -/** - * Volledig door AI gegenereerd behandelplan - */ -export interface GeneratedPlan { - behandelstructuur: Behandelstructuur; - doelen: SmartGoal[]; - interventies: Intervention[]; - sessiePlanning: Sessie[]; - evaluatiemomenten: Evaluatiemoment[]; - veiligheidsplan?: Veiligheidsplan; // Alleen bij severity "Hoog" -} - -// ============================================================================= -// API INPUT/OUTPUT TYPES -// ============================================================================= - -/** - * Input voor behandelplan generatie - */ -export interface GenerateBehandelplanInput { - patientId: string; - intakeId: string; - conditionId?: string; // Optioneel, haalt anders laatste op - extraInstructions?: string; // Optionele aanvullende instructies -} - -/** - * Input voor micro-regeneratie - */ -export interface RegenerateSectionInput { - patientId: string; - carePlanId: string; - sectionType: 'goal' | 'intervention'; - sectionId: string; - instruction?: string; // Extra instructie voor AI - currentPlan: GeneratedPlan; // Context van huidige plan -} - -/** - * Output van micro-regeneratie - */ -export interface RegeneratedSection { - type: 'goal' | 'intervention'; - original: SmartGoal | Intervention; - regenerated: SmartGoal | Intervention; -} - -// ============================================================================= -// ZOD SCHEMAS -// ============================================================================= - -export const BehandelstructuurSchema = z.object({ - duur: z.string(), - frequentie: z.string(), - aantalSessies: z.number().min(1).max(52), - vorm: z.string(), -}); - -export const SmartGoalSchema = z.object({ - id: z.string(), - title: z.string().min(5).max(200), - description: z.string().min(10).max(500), - clientVersion: z.string().min(5).max(300), - lifeDomain: z.enum(LIFE_DOMAINS), - priority: z.enum(PRIORITIES), - measurability: z.string().min(5).max(200), - timelineWeeks: z.number().min(1).max(52), - status: z.enum(GOAL_STATUSES), - progress: z.number().min(0).max(100), -}); - -export const InterventionSchema = z.object({ - id: z.string(), - name: z.string().min(2).max(100), - description: z.string().min(10).max(500), - rationale: z.string().min(10).max(500), - linkedGoalIds: z.array(z.string()), -}); - -export const EvaluatiemomentSchema = z.object({ - id: z.string(), - type: z.enum(EVALUATION_TYPES), - weekNumber: z.number().min(1).max(52), - plannedDate: z.string(), - actualDate: z.string().optional(), - status: z.enum(EVALUATION_STATUSES), - outcome: z.string().optional(), - lifeDomainUpdates: z.array(z.any()).optional(), // Simplified for now -}); - -export const VeiligheidsplanSchema = z.object({ - waarschuwingssignalen: z.array(z.string()).min(1).max(10), - copingStrategieen: z.array(z.string()).min(1).max(10), - contacten: z.array(z.object({ - naam: z.string(), - rol: z.string(), - telefoon: z.string(), - })), - restricties: z.array(z.string()).optional(), -}); - -export const SessieSchema = z.object({ - id: z.string(), - nummer: z.number().min(1), - focus: z.string(), - datum: z.string().optional(), - status: z.enum(['gepland', 'afgerond', 'no_show', 'verzet', 'geannuleerd']), - gekoppeldeDoelIds: z.array(z.string()), - notities: z.string().optional(), -}); - -export const GeneratedPlanSchema = z.object({ - behandelstructuur: BehandelstructuurSchema, - doelen: z.array(SmartGoalSchema).min(1).max(6), - interventies: z.array(InterventionSchema).min(1).max(5), - sessiePlanning: z.array(SessieSchema), - evaluatiemomenten: z.array(EvaluatiemomentSchema).min(1), - veiligheidsplan: VeiligheidsplanSchema.optional(), -}); - -export const GenerateBehandelplanInputSchema = z.object({ - patientId: z.string().uuid(), - intakeId: z.string().uuid(), - conditionId: z.string().uuid().optional(), - extraInstructions: z.string().max(500).optional(), -}); - -export const RegenerateSectionInputSchema = z.object({ - patientId: z.string().uuid(), - carePlanId: z.string().uuid(), - sectionType: z.enum(['goal', 'intervention']), - sectionId: z.string(), - instruction: z.string().max(200).optional(), - currentPlan: GeneratedPlanSchema, -}); - -// ============================================================================= -// HELPER FUNCTIONS -// ============================================================================= - -/** - * Genereer een nieuw UUID-achtig ID - */ -export function generateId(): string { - return crypto.randomUUID(); -} - -/** - * Maak een nieuw leeg SMART doel - */ -export function createEmptyGoal(lifeDomain: LifeDomain = 'dlv'): SmartGoal { - return { - id: generateId(), - title: '', - description: '', - clientVersion: '', - lifeDomain, - priority: 'middel', - measurability: '', - timelineWeeks: 8, - status: 'niet_gestart', - progress: 0, - }; -} - -/** - * Maak een nieuwe lege interventie - */ -export function createEmptyIntervention(): Intervention { - return { - id: generateId(), - name: '', - description: '', - rationale: '', - linkedGoalIds: [], - }; -} - -/** - * Bereken totale voortgang van alle doelen - */ -export function calculateTotalProgress(goals: SmartGoal[]): number { - if (goals.length === 0) return 0; - const sum = goals.reduce((acc, goal) => acc + goal.progress, 0); - return Math.round(sum / goals.length); -} - -/** - * Krijg doelen per leefgebied - */ -export function getGoalsByDomain(goals: SmartGoal[]): Record { - const result = {} as Record; - for (const domain of LIFE_DOMAINS) { - result[domain] = goals.filter((g) => g.lifeDomain === domain); - } - return result; -} - -/** - * Check of plan klaar is voor publicatie - */ -export function canPublish(plan: GeneratedPlan): { valid: boolean; errors: string[] } { - const errors: string[] = []; - - if (plan.doelen.length === 0) { - errors.push('Minimaal 1 doel is vereist'); - } - - if (plan.interventies.length === 0) { - errors.push('Minimaal 1 interventie is vereist'); - } - - if (!plan.behandelstructuur.duur || !plan.behandelstructuur.frequentie) { - errors.push('Behandelstructuur moet compleet zijn'); - } - - if (plan.evaluatiemomenten.length < 2) { - errors.push('Minimaal 2 evaluatiemomenten zijn vereist'); - } - - return { - valid: errors.length === 0, - errors, - }; -} - -/** - * Status label voor UI (oude Nederlandse keys - deprecated) - */ -export const PLAN_STATUS_LABELS: Record = { - concept: { label: 'Concept', color: '#60a5fa' }, // blauw - actief: { label: 'Actief', color: '#10b981' }, // groen - in_evaluatie: { label: 'In evaluatie', color: '#f59e0b' }, // oranje - afgerond: { label: 'Afgerond', color: '#6b7280' }, // grijs - gearchiveerd: { label: 'Gearchiveerd', color: '#9ca3af' }, // lichtgrijs -}; - -/** - * FHIR status naar Nederlandse UI labels - * Database gebruikt FHIR statussen, UI toont Nederlands - */ -export type FhirCarePlanStatus = 'draft' | 'active' | 'on-hold' | 'completed' | 'revoked' | 'entered-in-error' | 'unknown'; - -export const FHIR_STATUS_LABELS: Record = { - draft: { label: 'Concept', color: '#60a5fa' }, // blauw - active: { label: 'Actueel', color: '#10b981' }, // groen - 'on-hold': { label: 'Gepauzeerd', color: '#f59e0b' }, // oranje - completed: { label: 'Definitief', color: '#6b7280' }, // grijs - revoked: { label: 'Archief', color: '#9ca3af' }, // lichtgrijs - 'entered-in-error': { label: 'Verwijderd', color: '#ef4444' }, // rood - unknown: { label: 'Onbekend', color: '#9ca3af' }, // lichtgrijs -}; - -/** - * Goal status label voor UI - */ -export const GOAL_STATUS_LABELS: Record = { - niet_gestart: { label: 'Niet gestart', color: '#9ca3af' }, - bezig: { label: 'Bezig', color: '#3b82f6' }, - gehaald: { label: 'Gehaald', color: '#10b981' }, - bijgesteld: { label: 'Bijgesteld', color: '#f59e0b' }, -}; - -// ============================================================================= -// FLAT BEHANDELPLAN TYPES (Nieuwe platte structuur) -// ============================================================================= - -/** - * Embedded interventie binnen een behandeldoel - * Simpelere versie zonder linkedGoalIds (want embedded in doel) - */ -export interface EmbeddedInterventie { - id: string; - name: string; // bijv. "CGT", "EMDR", "ACT" - description: string; // Korte beschrijving van de aanpak -} - -/** - * Behandeldoel met embedded interventies - * Kerntype voor de "platte" behandelplan structuur - */ -export interface Behandeldoel { - id: string; - - // Doel informatie - title: string; // Professionele formulering - clientVersion: string; // B1-taal versie voor cliënt - - // Classificatie - lifeDomain: LifeDomain; // Gekoppeld leefgebied - - // Embedded interventies (KERNVERANDERING - niet meer apart) - interventies: EmbeddedInterventie[]; - - // Timeline - startWeek: number; // Start week (1-52) - endWeek: number; // Eind week (1-52) - - // Status & voortgang - status: GoalStatus; - progress: number; // 0-100 -} - -// ============================================================================= -// FLAT BEHANDELPLAN ZOD SCHEMAS -// ============================================================================= - -export const EmbeddedInterventieSchema = z.object({ - id: z.string(), - name: z.string().min(1).max(100), - description: z.string().max(500), -}); - -export const BehandeldoelSchema = z.object({ - id: z.string(), - title: z.string().min(5).max(200), - clientVersion: z.string().min(5).max(300), - lifeDomain: z.enum(LIFE_DOMAINS), - interventies: z.array(EmbeddedInterventieSchema), - startWeek: z.number().min(1).max(52), - endWeek: z.number().min(1).max(52), - status: z.enum(GOAL_STATUSES), - progress: z.number().min(0).max(100), -}); - -/** - * Schema voor AI-gegenereerd flat behandelplan - */ -export const GeneratedPlanFlatSchema = z.object({ - behandelstructuur: BehandelstructuurSchema, - behandeldoelen: z.array(BehandeldoelSchema).min(1).max(6), - evaluatiemomenten: z.array(EvaluatiemomentSchema).min(1), - veiligheidsplan: VeiligheidsplanSchema.optional(), -}); - -export type GeneratedPlanFlat = z.infer; - -// ============================================================================= -// TRANSFORMATIE FUNCTIES (Oude <-> Nieuwe structuur) -// ============================================================================= - -/** - * Transformeer oude structuur (goals + interventions apart) naar flat (behandeldoelen) - * Koppelt interventies aan doelen op basis van linkedGoalIds - */ -export function transformToFlat( - goals: SmartGoal[], - interventions: Intervention[] -): Behandeldoel[] { - return goals.map((goal) => { - // Vind interventies die aan dit doel gekoppeld zijn - const linkedInterventions = interventions - .filter((int) => int.linkedGoalIds.includes(goal.id)) - .map((int) => ({ - id: int.id, - name: int.name, - description: int.description, - })); - - return { - id: goal.id, - title: goal.title, - clientVersion: goal.clientVersion, - lifeDomain: goal.lifeDomain, - interventies: linkedInterventions, - startWeek: 1, // Default, kan later uit goal.timelineWeeks berekend worden - endWeek: goal.timelineWeeks, - status: goal.status, - progress: goal.progress, - }; - }); -} - -/** - * Transformeer flat structuur terug naar oude structuur (voor backwards compatibility) - * Zet embedded interventies om naar aparte array met linkedGoalIds - */ -export function transformFromFlat(behandeldoelen: Behandeldoel[]): { - goals: SmartGoal[]; - interventions: Intervention[]; -} { - const goals: SmartGoal[] = []; - const interventionMap = new Map(); - - for (const doel of behandeldoelen) { - // Maak SmartGoal van Behandeldoel - goals.push({ - id: doel.id, - title: doel.title, - description: '', // Niet meer gebruikt in flat structuur - clientVersion: doel.clientVersion, - lifeDomain: doel.lifeDomain, - priority: 'middel', // Default - measurability: '', // Niet meer gebruikt in flat structuur - timelineWeeks: doel.endWeek, - status: doel.status, - progress: doel.progress, - }); - - // Verzamel interventies en koppel aan doel - for (const int of doel.interventies) { - const existing = interventionMap.get(int.id); - if (existing) { - // Interventie bestaat al, voeg dit doel toe aan linkedGoalIds - existing.linkedGoalIds.push(doel.id); - } else { - // Nieuwe interventie - interventionMap.set(int.id, { - id: int.id, - name: int.name, - description: int.description, - rationale: '', // Niet meer gebruikt in flat structuur - linkedGoalIds: [doel.id], - }); - } - } - } - - return { - goals, - interventions: Array.from(interventionMap.values()), - }; -} - -/** - * Maak een nieuw leeg behandeldoel - */ -export function createEmptyBehandeldoel(lifeDomain: LifeDomain = 'dlv'): Behandeldoel { - return { - id: generateId(), - title: '', - clientVersion: '', - lifeDomain, - interventies: [], - startWeek: 1, - endWeek: 8, - status: 'niet_gestart', - progress: 0, - }; -} - -/** - * Maak een nieuwe lege embedded interventie - */ -export function createEmptyEmbeddedInterventie(): EmbeddedInterventie { - return { - id: generateId(), - name: '', - description: '', - }; -} - -/** - * Bereken totale voortgang van behandeldoelen - */ -export function calculateBehandeldoelenProgress(doelen: Behandeldoel[]): number { - if (doelen.length === 0) return 0; - const sum = doelen.reduce((acc, doel) => acc + doel.progress, 0); - return Math.round(sum / doelen.length); -} - -/** - * Check of flat plan klaar is voor publicatie - */ -export function canPublishFlat( - behandeldoelen: Behandeldoel[], - behandelstructuur: Behandelstructuur, - evaluatiemomenten: Evaluatiemoment[] -): { valid: boolean; errors: string[] } { - const errors: string[] = []; - - if (behandeldoelen.length === 0) { - errors.push('Minimaal 1 behandeldoel is vereist'); - } - - // Check of elk doel minimaal 1 interventie heeft - const doelenZonderInterventie = behandeldoelen.filter( - (d) => d.interventies.length === 0 - ); - if (doelenZonderInterventie.length > 0) { - errors.push('Elk behandeldoel moet minimaal 1 interventie hebben'); - } - - if (!behandelstructuur.duur || !behandelstructuur.frequentie) { - errors.push('Behandelstructuur moet compleet zijn'); - } - - if (evaluatiemomenten.length < 2) { - errors.push('Minimaal 2 evaluatiemomenten zijn vereist'); - } - - return { - valid: errors.length === 0, - errors, - }; -} diff --git a/middleware.ts b/middleware.ts index fc5abcf..0fca4d4 100644 --- a/middleware.ts +++ b/middleware.ts @@ -49,12 +49,7 @@ export async function middleware(request: NextRequest) { '/reset-password', '/update-password', '/auth/callback', - '/contact', - '/blog', - '/documentatie', - '/api/leads', '/robots.txt', - '/sitemap.xml', ] // API routes - let them handle auth themselves @@ -89,7 +84,7 @@ export async function middleware(request: NextRequest) { // Helper: get preferred interface redirect path const getPreferredPath = () => { const preference = user?.user_metadata?.preferred_interface - return preference === 'cortex' ? '/epd/cortex' : '/epd/clients' + return preference === 'cortex' ? '/epd/cortex' : '/epd/patients' } // Redirect to preferred interface if authenticated and trying to access login diff --git a/package.json b/package.json index ff6ae76..f6c9120 100644 --- a/package.json +++ b/package.json @@ -53,10 +53,8 @@ "cmdk": "^1.1.1", "date-fns": "^4.1.0", "framer-motion": "^12.23.24", - "gray-matter": "^4.0.3", "lucide-react": "^0.553.0", "next": "14.2.18", - "next-mdx-remote": "^5.0.0", "next-themes": "^0.4.6", "react": "18.3.1", "react-dom": "18.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 371e17f..5d96ee7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,18 +107,12 @@ importers: framer-motion: specifier: ^12.23.24 version: 12.23.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - gray-matter: - specifier: ^4.0.3 - version: 4.0.3 lucide-react: specifier: ^0.553.0 version: 0.553.0(react@18.3.1) next: specifier: 14.2.18 version: 14.2.18(@playwright/test@1.59.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - next-mdx-remote: - specifier: ^5.0.0 - version: 5.0.0(@types/react@18.3.27)(react@18.3.1) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -205,14 +199,6 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.28.4': resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} @@ -496,15 +482,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@mdx-js/mdx@3.1.1': - resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} - - '@mdx-js/react@3.1.1': - resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} - peerDependencies: - '@types/react': '>=16' - react: '>=16' - '@mediapipe/tasks-vision@0.10.17': resolution: {integrity: sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==} @@ -1402,21 +1379,9 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - '@types/draco3d@1.4.10': resolution: {integrity: sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==} - '@types/estree-jsx@1.0.5': - resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -1426,18 +1391,12 @@ packages: '@types/markdown-it@14.1.2': resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} '@types/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} @@ -1486,12 +1445,6 @@ packages: '@types/tmp@0.2.6': resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==} - '@types/unist@2.0.11': - resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/use-sync-external-store@0.0.6': resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} @@ -1718,9 +1671,6 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1774,10 +1724,6 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - astring@1.9.0: - resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} - hasBin: true - async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -1814,9 +1760,6 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} - bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1907,25 +1850,10 @@ packages: caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - - character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - - character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - - character-reference-invalid@2.0.1: - resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -1962,9 +1890,6 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc - collapse-white-space@2.1.0: - resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1983,9 +1908,6 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -2078,9 +2000,6 @@ packages: supports-color: optional: true - decode-named-character-reference@1.2.0: - resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2100,10 +2019,6 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - detect-gpu@5.0.70: resolution: {integrity: sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==} @@ -2114,9 +2029,6 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -2207,12 +2119,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esast-util-from-estree@2.0.0: - resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} - - esast-util-from-js@2.0.1: - resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} - esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} @@ -2322,11 +2228,6 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -2339,27 +2240,6 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - estree-util-attach-comments@3.0.0: - resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} - - estree-util-build-jsx@3.0.1: - resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} - - estree-util-is-identifier-name@3.0.0: - resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} - - estree-util-scope@1.0.0: - resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} - - estree-util-to-js@2.0.0: - resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} - - estree-util-visit@2.0.0: - resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -2382,10 +2262,6 @@ packages: resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} engines: {node: '>=4'} - extend-shallow@2.0.1: - resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} - engines: {node: '>=0.10.0'} - extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -2586,10 +2462,6 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - gray-matter@4.0.3: - resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} - engines: {node: '>=6.0'} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -2621,15 +2493,6 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hast-util-to-estree@3.1.3: - resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} - - hast-util-to-jsx-runtime@2.3.6: - resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} - - hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - hls.js@1.6.15: resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==} @@ -2674,19 +2537,10 @@ packages: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} engines: {node: '>=10'} - inline-style-parser@0.2.7: - resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} - internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - is-alphabetical@2.0.1: - resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} - - is-alphanumerical@2.0.1: - resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -2726,13 +2580,6 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} - is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - - is-extendable@0.1.1: - resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} - engines: {node: '>=0.10.0'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2757,9 +2604,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-installed-globally@0.4.0: resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} engines: {node: '>=10'} @@ -2784,10 +2628,6 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} @@ -2871,10 +2711,6 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} - hasBin: true - js-yaml@4.1.1: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true @@ -2915,10 +2751,6 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -3041,9 +2873,6 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -3065,10 +2894,6 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - markdown-extensions@2.0.0: - resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} - engines: {node: '>=16'} - markdown-it@14.1.0: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true @@ -3077,33 +2902,6 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdast-util-from-markdown@2.0.2: - resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} - - mdast-util-mdx-expression@2.0.1: - resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} - - mdast-util-mdx-jsx@3.2.0: - resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} - - mdast-util-mdx@3.0.0: - resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} - - mdast-util-mdxjs-esm@2.0.1: - resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} - - mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - - mdast-util-to-hast@13.2.0: - resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} - - mdast-util-to-markdown@2.1.2: - resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} - - mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -3122,90 +2920,6 @@ packages: meshoptimizer@0.22.0: resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==} - micromark-core-commonmark@2.0.3: - resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} - - micromark-extension-mdx-expression@3.0.1: - resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} - - micromark-extension-mdx-jsx@3.0.2: - resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} - - micromark-extension-mdx-md@2.0.0: - resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} - - micromark-extension-mdxjs-esm@3.0.0: - resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} - - micromark-extension-mdxjs@3.0.0: - resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} - - micromark-factory-destination@2.0.1: - resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} - - micromark-factory-label@2.0.1: - resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} - - micromark-factory-mdx-expression@2.0.3: - resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} - - micromark-factory-space@2.0.1: - resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} - - micromark-factory-title@2.0.1: - resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} - - micromark-factory-whitespace@2.0.1: - resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-chunked@2.0.1: - resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} - - micromark-util-classify-character@2.0.1: - resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} - - micromark-util-combine-extensions@2.0.1: - resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} - - micromark-util-decode-numeric-character-reference@2.0.2: - resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} - - micromark-util-decode-string@2.0.1: - resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-events-to-acorn@2.0.3: - resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} - - micromark-util-html-tag-name@2.0.1: - resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} - - micromark-util-normalize-identifier@2.0.1: - resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} - - micromark-util-resolve-all@2.0.1: - resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-subtokenize@2.1.0: - resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - micromark@4.0.2: - resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -3265,12 +2979,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next-mdx-remote@5.0.0: - resolution: {integrity: sha512-RNNbqRpK9/dcIFZs/esQhuLA8jANqlH694yqoDBK8hkVdJUndzzGmnPHa2nyi90N4Z9VmzuSWNRpr5ItT3M7xQ==} - engines: {node: '>=14', npm: '>=7'} - peerDependencies: - react: '>=16' - next-themes@0.4.6: resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} peerDependencies: @@ -3392,9 +3100,6 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-entities@4.0.2: - resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3528,9 +3233,6 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - property-information@7.1.0: - resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - prosemirror-changeset@2.3.1: resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==} @@ -3686,20 +3388,6 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - recma-build-jsx@1.0.0: - resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} - - recma-jsx@1.0.1: - resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - recma-parse@1.0.0: - resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} - - recma-stringify@1.0.0: - resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} - reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -3708,18 +3396,6 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - rehype-recma@1.0.0: - resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} - - remark-mdx@3.1.1: - resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} - - remark-parse@11.0.0: - resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - - remark-rehype@11.1.2: - resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} - request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} @@ -3789,10 +3465,6 @@ packages: scheduler@0.25.0: resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} - section-matter@1.0.0: - resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} - engines: {node: '>=4'} - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -3857,16 +3529,6 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - sshpk@1.18.0: resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} engines: {node: '>=0.10.0'} @@ -3931,9 +3593,6 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3942,10 +3601,6 @@ packages: resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} - strip-bom-string@1.0.0: - resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} - engines: {node: '>=0.10.0'} - strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -3958,12 +3613,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - style-to-js@1.1.21: - resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} - - style-to-object@1.0.14: - resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} - styled-jsx@5.1.1: resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -4081,9 +3730,6 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - troika-three-text@0.52.4: resolution: {integrity: sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==} peerDependencies: @@ -4097,9 +3743,6 @@ packages: troika-worker-utils@0.52.0: resolution: {integrity: sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==} - trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -4178,36 +3821,6 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - unified@11.0.5: - resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - - unist-util-is@5.2.1: - resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} - - unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - - unist-util-position-from-estree@2.0.0: - resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-remove@3.1.1: - resolution: {integrity: sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@5.1.3: - resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} - - unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - - unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -4269,15 +3882,6 @@ packages: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} - vfile-matter@5.0.1: - resolution: {integrity: sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw==} - - vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} @@ -4393,21 +3997,10 @@ packages: use-sync-external-store: optional: true - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - snapshots: '@alloc/quick-lru@5.2.0': {} - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/helper-validator-identifier@7.28.5': {} - '@babel/runtime@7.28.4': {} '@cypress/request@3.0.10': @@ -4659,42 +4252,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@mdx-js/mdx@3.1.1': - dependencies: - '@types/estree': 1.0.8 - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdx': 2.0.13 - acorn: 8.15.0 - collapse-white-space: 2.1.0 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - estree-util-scope: 1.0.0 - estree-walker: 3.0.3 - hast-util-to-jsx-runtime: 2.3.6 - markdown-extensions: 2.0.0 - recma-build-jsx: 1.0.0 - recma-jsx: 1.0.1(acorn@8.15.0) - recma-stringify: 1.0.0 - rehype-recma: 1.0.0 - remark-mdx: 3.1.1 - remark-parse: 11.0.0 - remark-rehype: 11.1.2 - source-map: 0.7.6 - unified: 11.0.5 - unist-util-position-from-estree: 2.0.0 - unist-util-stringify-position: 4.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@mdx-js/react@3.1.1(@types/react@18.3.27)(react@18.3.1)': - dependencies: - '@types/mdx': 2.0.13 - '@types/react': 18.3.27 - react: 18.3.1 - '@mediapipe/tasks-vision@0.10.17': {} '@monogrid/gainmap-js@3.2.0(three@0.181.2)': @@ -5611,22 +5168,8 @@ snapshots: tslib: 2.8.1 optional: true - '@types/debug@4.1.12': - dependencies: - '@types/ms': 2.1.0 - '@types/draco3d@1.4.10': {} - '@types/estree-jsx@1.0.5': - dependencies: - '@types/estree': 1.0.8 - - '@types/estree@1.0.8': {} - - '@types/hast@3.0.4': - dependencies: - '@types/unist': 3.0.3 - '@types/json5@0.0.29': {} '@types/linkify-it@5.0.0': {} @@ -5636,16 +5179,10 @@ snapshots: '@types/linkify-it': 5.0.0 '@types/mdurl': 2.0.0 - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - '@types/mdurl@2.0.0': {} '@types/mdx@2.0.13': {} - '@types/ms@2.1.0': {} - '@types/node@18.19.130': dependencies: undici-types: 5.26.5 @@ -5695,10 +5232,6 @@ snapshots: '@types/tmp@0.2.6': {} - '@types/unist@2.0.11': {} - - '@types/unist@3.0.3': {} - '@types/use-sync-external-store@0.0.6': {} '@types/webxr@0.5.24': {} @@ -5913,10 +5446,6 @@ snapshots: arg@5.0.2: {} - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - argparse@2.0.1: {} aria-hidden@1.2.6: @@ -6000,8 +5529,6 @@ snapshots: ast-types-flow@0.0.8: {} - astring@1.9.0: {} - async-function@1.0.0: {} asynckit@0.4.0: {} @@ -6030,8 +5557,6 @@ snapshots: axobject-query@4.1.0: {} - bail@2.0.2: {} - balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -6120,21 +5645,11 @@ snapshots: caseless@0.12.0: {} - ccount@2.0.1: {} - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - character-entities@2.0.2: {} - - character-reference-invalid@2.0.1: {} - chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -6184,8 +5699,6 @@ snapshots: - '@types/react' - '@types/react-dom' - collapse-white-space@2.1.0: {} - color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -6201,8 +5714,6 @@ snapshots: dependencies: delayed-stream: 1.0.0 - comma-separated-tokens@2.0.3: {} - commander@4.1.1: {} commander@6.2.1: {} @@ -6320,10 +5831,6 @@ snapshots: optionalDependencies: supports-color: 8.1.1 - decode-named-character-reference@1.2.0: - dependencies: - character-entities: 2.0.2 - deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -6342,8 +5849,6 @@ snapshots: delayed-stream@1.0.0: {} - dequal@2.0.3: {} - detect-gpu@5.0.70: dependencies: webgl-constants: 1.1.1 @@ -6352,10 +5857,6 @@ snapshots: detect-node-es@1.1.0: {} - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - didyoumean@1.2.2: {} dlv@1.1.3: {} @@ -6507,20 +6008,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esast-util-from-estree@2.0.0: - dependencies: - '@types/estree-jsx': 1.0.5 - devlop: 1.1.0 - estree-util-visit: 2.0.0 - unist-util-position-from-estree: 2.0.0 - - esast-util-from-js@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - acorn: 8.15.0 - esast-util-from-estree: 2.0.0 - vfile-message: 4.0.3 - esbuild@0.25.12: optionalDependencies: '@esbuild/aix-ppc64': 0.25.12 @@ -6740,8 +6227,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 3.4.3 - esprima@4.0.1: {} - esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -6752,39 +6237,6 @@ snapshots: estraverse@5.3.0: {} - estree-util-attach-comments@3.0.0: - dependencies: - '@types/estree': 1.0.8 - - estree-util-build-jsx@3.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - estree-walker: 3.0.3 - - estree-util-is-identifier-name@3.0.0: {} - - estree-util-scope@1.0.0: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - - estree-util-to-js@2.0.0: - dependencies: - '@types/estree-jsx': 1.0.5 - astring: 1.9.0 - source-map: 0.7.6 - - estree-util-visit@2.0.0: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/unist': 3.0.3 - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.8 - esutils@2.0.3: {} eventemitter2@6.4.7: {} @@ -6809,10 +6261,6 @@ snapshots: dependencies: pify: 2.3.0 - extend-shallow@2.0.1: - dependencies: - is-extendable: 0.1.1 - extend@3.0.2: {} extract-zip@2.0.1(supports-color@8.1.1): @@ -7024,13 +6472,6 @@ snapshots: graphemer@1.4.0: {} - gray-matter@4.0.3: - dependencies: - js-yaml: 3.14.2 - kind-of: 6.0.3 - section-matter: 1.0.0 - strip-bom-string: 1.0.0 - has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -7058,51 +6499,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hast-util-to-estree@3.1.3: - dependencies: - '@types/estree': 1.0.8 - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - estree-util-attach-comments: 3.0.0 - estree-util-is-identifier-name: 3.0.0 - hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - style-to-js: 1.1.21 - unist-util-position: 5.0.0 - zwitch: 2.0.4 - transitivePeerDependencies: - - supports-color - - hast-util-to-jsx-runtime@2.3.6: - dependencies: - '@types/estree': 1.0.8 - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - style-to-js: 1.1.21 - unist-util-position: 5.0.0 - vfile-message: 4.0.3 - transitivePeerDependencies: - - supports-color - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.4 - hls.js@1.6.15: {} http-signature@1.4.0: @@ -7137,21 +6533,12 @@ snapshots: ini@2.0.0: {} - inline-style-parser@0.2.7: {} - internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.1.0 - is-alphabetical@2.0.1: {} - - is-alphanumerical@2.0.1: - dependencies: - is-alphabetical: 2.0.1 - is-decimal: 2.0.1 - is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -7200,10 +6587,6 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-decimal@2.0.1: {} - - is-extendable@0.1.1: {} - is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -7228,8 +6611,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-hexadecimal@2.0.1: {} - is-installed-globally@0.4.0: dependencies: global-dirs: 3.0.1 @@ -7248,8 +6629,6 @@ snapshots: is-path-inside@3.0.3: {} - is-plain-obj@4.1.0: {} - is-promise@2.2.2: {} is-regex@1.2.1: @@ -7331,11 +6710,6 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@3.14.2: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - js-yaml@4.1.1: dependencies: argparse: 2.0.1 @@ -7380,8 +6754,6 @@ snapshots: dependencies: json-buffer: 3.0.1 - kind-of@6.0.3: {} - language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -7488,8 +6860,6 @@ snapshots: strip-ansi: 7.1.2 wrap-ansi: 9.0.2 - longest-streak@3.1.0: {} - loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -7509,8 +6879,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - markdown-extensions@2.0.0: {} - markdown-it@14.1.0: dependencies: argparse: 2.0.1 @@ -7522,105 +6890,6 @@ snapshots: math-intrinsics@1.1.0: {} - mdast-util-from-markdown@2.0.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.2.0 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.2 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-decode-string: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-expression@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-jsx@3.2.0: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - parse-entities: 4.0.2 - stringify-entities: 4.0.4 - unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.3 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx@3.0.0: - dependencies: - mdast-util-from-markdown: 2.0.2 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-mdxjs-esm@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-phrasing@4.1.0: - dependencies: - '@types/mdast': 4.0.4 - unist-util-is: 6.0.1 - - mdast-util-to-hast@13.2.0: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.3 - - mdast-util-to-markdown@2.1.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.1.0 - mdast-util-to-string: 4.0.0 - micromark-util-classify-character: 2.0.1 - micromark-util-decode-string: 2.0.1 - unist-util-visit: 5.0.0 - zwitch: 2.0.4 - - mdast-util-to-string@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdurl@2.0.0: {} merge-stream@2.0.0: {} @@ -7633,212 +6902,6 @@ snapshots: meshoptimizer@0.22.0: {} - micromark-core-commonmark@2.0.3: - dependencies: - decode-named-character-reference: 1.2.0 - devlop: 1.1.0 - micromark-factory-destination: 2.0.1 - micromark-factory-label: 2.0.1 - micromark-factory-space: 2.0.1 - micromark-factory-title: 2.0.1 - micromark-factory-whitespace: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-html-tag-name: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-mdx-expression@3.0.1: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - micromark-factory-mdx-expression: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-mdx-jsx@3.0.2: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - micromark-factory-mdx-expression: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - vfile-message: 4.0.3 - - micromark-extension-mdx-md@2.0.0: - dependencies: - micromark-util-types: 2.0.2 - - micromark-extension-mdxjs-esm@3.0.0: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.3 - - micromark-extension-mdxjs@3.0.0: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - micromark-extension-mdx-expression: 3.0.1 - micromark-extension-mdx-jsx: 3.0.2 - micromark-extension-mdx-md: 2.0.0 - micromark-extension-mdxjs-esm: 3.0.0 - micromark-util-combine-extensions: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-destination@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-label@2.0.1: - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-mdx-expression@2.0.3: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.3 - - micromark-factory-space@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-types: 2.0.2 - - micromark-factory-title@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-whitespace@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-chunked@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-classify-character@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-combine-extensions@2.0.1: - dependencies: - micromark-util-chunked: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-decode-numeric-character-reference@2.0.2: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-decode-string@2.0.1: - dependencies: - decode-named-character-reference: 1.2.0 - micromark-util-character: 2.1.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-symbol: 2.0.1 - - micromark-util-encode@2.0.1: {} - - micromark-util-events-to-acorn@2.0.3: - dependencies: - '@types/estree': 1.0.8 - '@types/unist': 3.0.3 - devlop: 1.1.0 - estree-util-visit: 2.0.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - vfile-message: 4.0.3 - - micromark-util-html-tag-name@2.0.1: {} - - micromark-util-normalize-identifier@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-resolve-all@2.0.1: - dependencies: - micromark-util-types: 2.0.2 - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-subtokenize@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - micromark@4.0.2: - dependencies: - '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@8.1.1) - decode-named-character-reference: 1.2.0 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-combine-extensions: 2.0.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-encode: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - transitivePeerDependencies: - - supports-color - micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -7886,19 +6949,6 @@ snapshots: natural-compare@1.4.0: {} - next-mdx-remote@5.0.0(@types/react@18.3.27)(react@18.3.1): - dependencies: - '@babel/code-frame': 7.27.1 - '@mdx-js/mdx': 3.1.1 - '@mdx-js/react': 3.1.1(@types/react@18.3.27)(react@18.3.1) - react: 18.3.1 - unist-util-remove: 3.1.1 - vfile: 6.0.3 - vfile-matter: 5.0.1 - transitivePeerDependencies: - - '@types/react' - - supports-color - next-themes@0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -8031,16 +7081,6 @@ snapshots: dependencies: callsites: 3.1.0 - parse-entities@4.0.2: - dependencies: - '@types/unist': 2.0.11 - character-entities-legacy: 3.0.0 - character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.2.0 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - is-hexadecimal: 2.0.1 - path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -8144,8 +7184,6 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - property-information@7.1.0: {} - prosemirror-changeset@2.3.1: dependencies: prosemirror-transform: 1.10.5 @@ -8333,35 +7371,6 @@ snapshots: dependencies: picomatch: 2.3.1 - recma-build-jsx@1.0.0: - dependencies: - '@types/estree': 1.0.8 - estree-util-build-jsx: 3.0.1 - vfile: 6.0.3 - - recma-jsx@1.0.1(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - estree-util-to-js: 2.0.0 - recma-parse: 1.0.0 - recma-stringify: 1.0.0 - unified: 11.0.5 - - recma-parse@1.0.0: - dependencies: - '@types/estree': 1.0.8 - esast-util-from-js: 2.0.1 - unified: 11.0.5 - vfile: 6.0.3 - - recma-stringify@1.0.0: - dependencies: - '@types/estree': 1.0.8 - estree-util-to-js: 2.0.0 - unified: 11.0.5 - vfile: 6.0.3 - reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -8382,38 +7391,6 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - rehype-recma@1.0.0: - dependencies: - '@types/estree': 1.0.8 - '@types/hast': 3.0.4 - hast-util-to-estree: 3.1.3 - transitivePeerDependencies: - - supports-color - - remark-mdx@3.1.1: - dependencies: - mdast-util-mdx: 3.0.0 - micromark-extension-mdxjs: 3.0.0 - transitivePeerDependencies: - - supports-color - - remark-parse@11.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.2 - micromark-util-types: 2.0.2 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-rehype@11.1.2: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - mdast-util-to-hast: 13.2.0 - unified: 11.0.5 - vfile: 6.0.3 - request-progress@3.0.0: dependencies: throttleit: 1.0.1 @@ -8484,11 +7461,6 @@ snapshots: scheduler@0.25.0: {} - section-matter@1.0.0: - dependencies: - extend-shallow: 2.0.1 - kind-of: 6.0.3 - semver@6.3.1: {} semver@7.7.3: {} @@ -8565,12 +7537,6 @@ snapshots: source-map-js@1.2.1: {} - source-map@0.7.6: {} - - space-separated-tokens@2.0.2: {} - - sprintf-js@1.0.3: {} - sshpk@1.18.0: dependencies: asn1: 0.2.6 @@ -8672,11 +7638,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -8685,22 +7646,12 @@ snapshots: dependencies: ansi-regex: 6.2.2 - strip-bom-string@1.0.0: {} - strip-bom@3.0.0: {} strip-final-newline@2.0.0: {} strip-json-comments@3.1.1: {} - style-to-js@1.1.21: - dependencies: - style-to-object: 1.0.14 - - style-to-object@1.0.14: - dependencies: - inline-style-parser: 0.2.7 - styled-jsx@5.1.1(react@18.3.1): dependencies: client-only: 0.0.1 @@ -8823,8 +7774,6 @@ snapshots: tree-kill@1.2.2: {} - trim-lines@3.0.1: {} - troika-three-text@0.52.4(three@0.181.2): dependencies: bidi-js: 1.0.3 @@ -8839,8 +7788,6 @@ snapshots: troika-worker-utils@0.52.0: {} - trough@2.2.0: {} - ts-api-utils@2.1.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -8935,58 +7882,6 @@ snapshots: undici-types@6.21.0: {} - unified@11.0.5: - dependencies: - '@types/unist': 3.0.3 - bail: 2.0.2 - devlop: 1.1.0 - extend: 3.0.2 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 6.0.3 - - unist-util-is@5.2.1: - dependencies: - '@types/unist': 2.0.11 - - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position-from-estree@2.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-remove@3.1.1: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 5.2.1 - unist-util-visit-parents: 5.1.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@5.1.3: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 5.2.1 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - universalify@2.0.1: {} unrs-resolver@1.11.1: @@ -9056,21 +7951,6 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vfile-matter@5.0.1: - dependencies: - vfile: 6.0.3 - yaml: 2.8.1 - - vfile-message@4.0.3: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.3 - w3c-keyname@2.2.8: {} webgl-constants@1.1.1: {} @@ -9153,7 +8033,8 @@ snapshots: ws@8.18.3: {} - yaml@2.8.1: {} + yaml@2.8.1: + optional: true yauzl@2.10.0: dependencies: @@ -9176,5 +8057,3 @@ snapshots: '@types/react': 18.3.27 react: 18.3.1 use-sync-external-store: 1.6.0(react@18.3.1) - - zwitch@2.0.4: {} diff --git a/supabase/migrations/20260720000001_create_reference_data.sql b/supabase/migrations/20260720000001_create_reference_data.sql new file mode 100644 index 0000000..fe10e7c --- /dev/null +++ b/supabase/migrations/20260720000001_create_reference_data.sql @@ -0,0 +1,293 @@ +-- ============================================================================ +-- REFERENCE DATA (WAARDELIJSTEN) — instroom / intake / behandeladvies +-- ============================================================================ +-- Created: 2026-07-20 +-- Purpose: shared reference-list table for the redesigned instroom/intake/ +-- behandeladvies domain (docs/datamodel/deelmodellen/model-instroom.md, +-- model-intake-behandeladvies.md, model-aanmelding.md §4). +-- +-- Design note: spelregel 3.1 #1 ("elke waardelijst is een referentietabel") +-- is honoured with ONE shared, generic table (list_name-discriminated) +-- instead of ~30 near-identical single-purpose tables. Each row still +-- carries code/label/external_code/code_system/geldig_van/geldig_tot/actief. +-- List-specific extra attributes (agb_verplicht, verwijsnudge_severity, ...) +-- live in `extra` (JSONB) rather than one-off columns. +-- +-- Scope note: this migration and the ones that follow (create_person_client_ +-- domain, create_referral_domain, create_intake_treatment_advice_domain) +-- implement the REDESIGNED target schema from the 19-20 juli 2026 FCO-IM +-- rounds. They are new, additive tables — the existing prototype tables +-- (patients, screenings, intakes, patients.is_john_doe, intakes.kindcheck_data) +-- are intentionally left untouched. Wiring the live app to this schema, and +-- migrating prototype data into it, is a separate follow-up decision. +-- ============================================================================ + +CREATE TABLE value_list_item ( + list_name TEXT NOT NULL, + code TEXT NOT NULL, + label TEXT NOT NULL, + external_code TEXT, + code_system TEXT, + extra JSONB NOT NULL DEFAULT '{}'::jsonb, + valid_from DATE, + valid_to DATE, + active BOOLEAN NOT NULL DEFAULT true, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + + PRIMARY KEY (list_name, code) +); + +CREATE INDEX idx_value_list_item_list_active ON value_list_item(list_name, active); + +COMMENT ON TABLE value_list_item IS 'Generieke waardelijst-tabel (spelregel 3.1 #1): één rij per (list_name, code). Startlijsten, geen definitieve besluiten (zie modeldocumenten).'; +COMMENT ON COLUMN value_list_item.extra IS 'List-specifieke attributen, bv. agb_verplicht (verwijzertype), verwijsnudge_severity (wettelijk_kader).'; + +ALTER TABLE value_list_item ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Enable read for authenticated users" ON value_list_item + FOR SELECT USING (auth.role() = 'authenticated'); + +CREATE TRIGGER update_value_list_item_updated_at + BEFORE UPDATE ON value_list_item + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- Seed data — startlijsten uit de modeldocumenten, geen definitieve besluiten +-- ============================================================================ + +INSERT INTO value_list_item (list_name, code, label, extra) VALUES +-- geslacht (model-aanmelding.md §4.1) — externe code: HL7 AdministrativeGender +('geslacht', 'man', 'Man', '{}'), +('geslacht', 'vrouw', 'Vrouw', '{}'), +('geslacht', 'anders', 'Anders', '{}'), +('geslacht', 'onbekend', 'Onbekend', '{}'), + +-- genderidentiteit (zib Patient v4.3) +('genderidentiteit', 'man', 'Man', '{}'), +('genderidentiteit', 'vrouw', 'Vrouw', '{}'), +('genderidentiteit', 'non_binair', 'Non-binair', '{}'), +('genderidentiteit', 'anders', 'Anders', '{}'), +('genderidentiteit', 'zegt_het_niet', 'Zegt het niet', '{}'), + +-- adrestype +('adrestype', 'woonadres', 'Woonadres', '{}'), +('adrestype', 'postadres', 'Postadres', '{}'), +('adrestype', 'tijdelijk_verblijf', 'Tijdelijk verblijf', '{}'), + +-- contactgegeven_type +('contactgegeven_type', 'telefoon', 'Telefoon', '{}'), +('contactgegeven_type', 'email', 'E-mail', '{}'), + +-- contactgegeven_soort +('contactgegeven_soort', 'mobiel_prive', 'Mobiel privé', '{}'), +('contactgegeven_soort', 'vast_prive', 'Vast privé', '{}'), +('contactgegeven_soort', 'werk', 'Werk', '{}'), +('contactgegeven_soort', 'overig', 'Overig', '{}'), + +-- relatie +('relatie', 'partner', 'Partner', '{}'), +('relatie', 'ouder', 'Ouder', '{}'), +('relatie', 'kind', 'Kind', '{}'), +('relatie', 'broer_zus', 'Broer/zus', '{}'), +('relatie', 'familielid_overig', 'Familielid overig', '{}'), +('relatie', 'vriend_kennis', 'Vriend/kennis', '{}'), +('relatie', 'professional', 'Professional', '{}'), +('relatie', 'overig', 'Overig', '{}'), + +-- relatierol (twee assen relatie x rol — zib Contactpersoon) +('relatierol', 'eerste_contactpersoon', 'Eerste contactpersoon', '{}'), +('relatierol', 'wettelijk_vertegenwoordiger', 'Wettelijk vertegenwoordiger', '{}'), +('relatierol', 'gezaghebbende_ouder', 'Gezaghebbende ouder', '{}'), +('relatierol', 'mantelzorger', 'Mantelzorger', '{}'), +('relatierol', 'naaste', 'Naaste', '{}'), + +-- vertegenwoordigingsgrond (WGBO-volgorde) +('vertegenwoordigingsgrond', 'curator', 'Curator', '{}'), +('vertegenwoordigingsgrond', 'mentor', 'Mentor', '{}'), +('vertegenwoordigingsgrond', 'schriftelijk_gemachtigde', 'Schriftelijk gemachtigde', '{}'), +('vertegenwoordigingsgrond', 'ouder_voogd', 'Ouder/voogd', '{}'), +('vertegenwoordigingsgrond', 'partner_familie', 'Partner/familie', '{}'), + +-- huisarts_situatie (voedt zpm_verwijstype 04/05) +('huisarts_situatie', 'bekend', 'Bekend', '{}'), +('huisarts_situatie', 'geen_huisarts', 'Geen huisarts', '{}'), +('huisarts_situatie', 'geen_toestemming_correspondentie', 'Geen toestemming correspondentie', '{}'), +('huisarts_situatie', 'onbekend', 'Onbekend', '{}'), + +-- verwijzertype (extra.agb_verplicht — discovery §5.0 besluit 3) +('verwijzertype', 'huisarts', 'Huisarts', '{"agb_verplicht": true}'), +('verwijzertype', 'medisch_specialist', 'Medisch specialist', '{"agb_verplicht": true}'), +('verwijzertype', 'straatdokter', 'Straatdokter', '{"agb_verplicht": true}'), +('verwijzertype', 'bedrijfsarts', 'Bedrijfsarts', '{"agb_verplicht": true}'), +('verwijzertype', 'regiebehandelaar', 'Regiebehandelaar', '{"agb_verplicht": true}'), +('verwijzertype', 'ggz_instelling', 'GGZ-instelling', '{"agb_verplicht": true}'), +('verwijzertype', 'gemeente', 'Gemeente', '{"agb_verplicht": false}'), +('verwijzertype', 'zelfaanmelding', 'Zelfaanmelding', '{"agb_verplicht": false}'), +('verwijzertype', 'crisis', 'Crisis', '{"agb_verplicht": false}'), + +-- praktijk_soort +('praktijk_soort', 'huisartsenpraktijk', 'Huisartsenpraktijk', '{}'), +('praktijk_soort', 'ziekenhuis', 'Ziekenhuis', '{}'), +('praktijk_soort', 'ggz_instelling', 'GGZ-instelling', '{}'), +('praktijk_soort', 'gemeente', 'Gemeente', '{}'), +('praktijk_soort', 'arbodienst', 'Arbodienst', '{}'), +('praktijk_soort', 'overig', 'Overig', '{}'), + +-- wettelijk_kader (extra.verwijsnudge_severity — te bevestigen in de nudge-/declaratieronde; wvggz toegevoegd bij B27) +('wettelijk_kader', 'zvw', 'Zorgverzekeringswet', '{"verwijsnudge_severity": "blokkade"}'), +('wettelijk_kader', 'jeugdwet', 'Jeugdwet', '{"verwijsnudge_severity": "signaal"}'), +('wettelijk_kader', 'wmo', 'Wmo', '{"verwijsnudge_severity": "signaal"}'), +('wettelijk_kader', 'wlz', 'Wlz', '{"verwijsnudge_severity": "waarschuwing"}'), +('wettelijk_kader', 'forensisch', 'Forensisch', '{"verwijsnudge_severity": "waarschuwing"}'), +('wettelijk_kader', 'wvggz', 'Wvggz', '{"verwijsnudge_severity": null}'), + +-- submission_channel (was aanmeldkanaal — model-instroom.md §4) +('submission_channel', 'zorgdomein', 'ZorgDomein', '{}'), +('submission_channel', 'e_mail', 'E-mail', '{}'), +('submission_channel', 'telefoon', 'Telefoon', '{}'), +('submission_channel', 'portaal', 'Cliëntportaal', '{}'), +('submission_channel', 'post', 'Post', '{}'), +('submission_channel', 'overig', 'Overig', '{}'), + +-- document_type (model-instroom.md §4) +('document_type', 'verwijsbrief', 'Verwijsbrief', '{}'), +('document_type', 'diagnostische_aanvulling', 'Diagnostische aanvulling', '{}'), +('document_type', 'beschikking', 'Beschikking', '{}'), +('document_type', 'overig', 'Overig', '{}'), + +-- screening_activity_type +('screening_activity_type', 'telefonisch_contact', 'Telefonisch contact', '{}'), +('screening_activity_type', 'dossieronderzoek', 'Dossieronderzoek', '{}'), +('screening_activity_type', 'vragenlijst', 'Vragenlijst', '{}'), +('screening_activity_type', 'overig', 'Overig', '{}'), + +-- decision_outcome (B22) +('decision_outcome', 'geaccepteerd', 'Geaccepteerd', '{}'), +('decision_outcome', 'afgewezen', 'Afgewezen', '{}'), + +-- follow_up_route +('follow_up_route', 'intake_direct', 'Intake direct plannen', '{}'), +('follow_up_route', 'intakewachtlijst', 'Intakewachtlijst', '{}'), +('follow_up_route', 'spoedroute', 'Spoedroute', '{}'), +('follow_up_route', 'doorverwijzen', 'Doorverwijzen', '{}'), +('follow_up_route', 'case_afsluiten', 'Case afsluiten', '{}'), + +-- episode_end_reason (model-aanmelding.md §4.24 + B24-aanvullingen) +('episode_end_reason', 'ingetrokken_door_client', 'Ingetrokken door cliënt', '{}'), +('episode_end_reason', 'acceptatiebesluit_herzien', 'Acceptatiebesluit herzien', '{}'), +('episode_end_reason', 'behandeling_afgerond', 'Behandeling afgerond', '{}'), +('episode_end_reason', 'doorverwezen', 'Doorverwezen', '{}'), +('episode_end_reason', 'client_beeindigt', 'Cliënt beëindigt', '{}'), +('episode_end_reason', 'geen_contact', 'Geen contact meer', '{}'), +('episode_end_reason', 'overleden', 'Overleden', '{}'), +('episode_end_reason', 'overig', 'Overig', '{}'), + +-- echelon +('echelon', 'gb_ggz', 'Generalistische basis-ggz', '{}'), +('echelon', 'g_ggz', 'Gespecialiseerde ggz', '{}'), +('echelon', 'onbekend', 'Onbekend', '{}'), + +-- zpm_verwijstype (Vektis/ZPM-code als external_code) +('zpm_verwijstype', '01', 'Verwijzing aanwezig', '{}'), +('zpm_verwijstype', '02', 'Doorverwijzing regiebehandelaar', '{}'), +('zpm_verwijstype', '03', 'Geen verwijzing, verlate correspondentie', '{}'), +('zpm_verwijstype', '04', 'Geen verwijzing, correspondentie niet toegestaan', '{}'), +('zpm_verwijstype', '05', 'Geen verwijzing, niet declarabel', '{}'), +('zpm_verwijstype', '06', 'Geen verwijzing, andere grond fz', '{}'), +('zpm_verwijstype', '07', 'Verwijzing zonder AGB', '{}'), + +-- municipal_legal_framework (B27) +('municipal_legal_framework', 'wmo', 'Wmo', '{}'), +('municipal_legal_framework', 'jeugdwet', 'Jeugdwet', '{}'), + +-- municipal_product_category / municipal_product_unit — minimale startwaarden (declaratie-ronde volgt) +('municipal_product_category', 'overig', 'Overig (nader te bepalen)', '{}'), +('municipal_product_unit', 'overig', 'Overig (nader te bepalen)', '{}'), + +-- mandate_type / mandate_decider_role (B27, Wvggz) +('mandate_type', 'crisismaatregel', 'Crisismaatregel', '{}'), +('mandate_type', 'zorgmachtiging', 'Zorgmachtiging', '{}'), +('mandate_decider_role', 'burgemeester', 'Burgemeester', '{}'), +('mandate_decider_role', 'rechter', 'Rechter', '{}'), + +-- crisis_fact_type +('crisis_fact_type', 'medicatie_toegediend', 'Medicatie toegediend', '{}'), +('crisis_fact_type', 'risico_inschatting', 'Risico-inschatting', '{}'), +('crisis_fact_type', 'vitale_functie', 'Vitale functie', '{}'), +('crisis_fact_type', 'dwangmaatregel', 'Dwangmaatregel', '{}'), +('crisis_fact_type', 'overig', 'Overig', '{}'), + +-- urgency_assessor_role / urgency_level +('urgency_assessor_role', 'screener', 'Screener', '{}'), +('urgency_assessor_role', 'triagist', 'Triagist', '{}'), +('urgency_assessor_role', 'intake_team', 'Intake-team', '{}'), +('urgency_assessor_role', 'mdo', 'MDO', '{}'), +('urgency_level', 'laag', 'Laag', '{}'), +('urgency_level', 'normaal', 'Normaal', '{}'), +('urgency_level', 'hoog', 'Hoog', '{}'), +('urgency_level', 'spoed', 'Spoed', '{}'), + +-- initiator_type +('initiator_type', 'professional', 'Professional', '{}'), +('initiator_type', 'organisatie', 'Organisatie', '{}'), +('initiator_type', 'client', 'Cliënt', '{}'), +('initiator_type', 'vertegenwoordiger', 'Vertegenwoordiger', '{}'), + +-- intake_initiation_reason +('intake_initiation_reason', 'regulier', 'Regulier', '{}'), +('intake_initiation_reason', 'intern', 'Intern', '{}'), +('intake_initiation_reason', 'crisis', 'Crisis', '{}'), + +-- intake_status +('intake_status', 'gepland', 'Gepland', '{}'), +('intake_status', 'bezig', 'Bezig', '{}'), +('intake_status', 'afgerond', 'Afgerond', '{}'), +('intake_status', 'afgebroken', 'Afgebroken', '{}'), + +-- intake_abort_reason +('intake_abort_reason', 'client_trekt_terug', 'Cliënt trekt zich terug', '{}'), +('intake_abort_reason', 'geen_contact_meer', 'Geen contact meer', '{}'), +('intake_abort_reason', 'overleden', 'Overleden', '{}'), +('intake_abort_reason', 'overig', 'Overig', '{}'), + +-- intake_contact_type +('intake_contact_type', 'intakegesprek', 'Intakegesprek', '{}'), +('intake_contact_type', 'aanvullend_onderzoek', 'Aanvullend onderzoek', '{}'), +('intake_contact_type', 'telefonisch_contact', 'Telefonisch contact', '{}'), +('intake_contact_type', 'huisbezoek', 'Huisbezoek', '{}'), +('intake_contact_type', 'beeldcontact', 'Beeldcontact', '{}'), +('intake_contact_type', 'overig', 'Overig', '{}'), + +-- treatment_advice_outcome (B33 — doorverwijzing/terugverwijzing volgtijdelijk, extra_diagnostiek praktijkgefundeerd) +('treatment_advice_outcome', 'in_zorg', 'In zorg', '{}'), +('treatment_advice_outcome', 'terugverwijzing', 'Terugverwijzing', '{"lks_genormeerd": true, "volgt_op": "doorverwijzing"}'), +('treatment_advice_outcome', 'doorverwijzing', 'Doorverwijzing', '{"lks_genormeerd": true}'), +('treatment_advice_outcome', 'extra_diagnostiek', 'Extra diagnostiek', '{"lks_genormeerd": false}'), + +-- department / care_program — instellingsconfigureerbaar, minimale startwaarden +('department', 'volwassenen', 'Volwassenen', '{}'), +('department', 'jeugd', 'Jeugd', '{}'), +('department', 'ouderen', 'Ouderen', '{}'), +('department', 'forensisch', 'Forensisch', '{}'), +('department', 'overig', 'Overig', '{}'), +('care_program', 'algemeen_ggz', 'Algemeen GGZ', '{}'), +('care_program', 'stemming', 'Stemming', '{}'), +('care_program', 'trauma', 'Trauma', '{}'), +('care_program', 'verslaving', 'Verslaving', '{}'), +('care_program', 'overig', 'Overig', '{}'), + +-- toestemming_type (extra.grondslag) +('toestemming_type', 'correspondentie_huisarts_verwijzer', 'Correspondentie huisarts/verwijzer', '{"grondslag": "WGBO/verwijsafspraken"}'), +('toestemming_type', 'dsm_hoofdgroep_op_factuur', 'DSM-hoofdgroep op factuur', '{"grondslag": "Stcrt. 2025-11955, opt-in"}'), +('toestemming_type', 'privacyverklaring_zorgvraagtypering', 'Privacyverklaring zorgvraagtypering', '{"grondslag": "NZa"}'), +('toestemming_type', 'gegevensdeling_derden', 'Gegevensdeling derden', '{"grondslag": "AVG"}'), +('toestemming_type', 'inzage_naasten', 'Inzage naasten', '{"grondslag": "WGBO"}'), + +-- toestemming_status / toestemming_wijze +('toestemming_status', 'verleend', 'Verleend', '{}'), +('toestemming_status', 'geweigerd', 'Geweigerd', '{}'), +('toestemming_status', 'ingetrokken', 'Ingetrokken', '{}'), +('toestemming_wijze', 'mondeling', 'Mondeling', '{}'), +('toestemming_wijze', 'schriftelijk', 'Schriftelijk', '{}'), +('toestemming_wijze', 'portaal', 'Portaal', '{}'); diff --git a/supabase/migrations/20260720000002_create_person_client_domain.sql b/supabase/migrations/20260720000002_create_person_client_domain.sql new file mode 100644 index 0000000..ba74133 --- /dev/null +++ b/supabase/migrations/20260720000002_create_person_client_domain.sql @@ -0,0 +1,349 @@ +-- ============================================================================ +-- PERSON / CLIENT DOMAIN +-- ============================================================================ +-- Created: 2026-07-20 +-- Source: docs/datamodel/deelmodellen/model-aanmelding.md §2.1-2.9, §2.14, §2.16 +-- (PERSOON, ADRES, CONTACTGEGEVEN, CLIENT, CLIENTRELATIE, VERWIJZER, +-- PRAKTIJK_INSTELLING, CLIENT_HUISARTS, VERZEKERING, TOESTEMMING, +-- CLIENTPORTAAL_ACCOUNT) — the AANMELDING/VERWIJZING/ZORGEPISODE part of +-- that document is superseded by the referral/intake domains that follow +-- this migration, not translated here. +-- +-- Naming: English identifiers per besluit B20. Dutch field names in the +-- source document are translated 1:1 in meaning; nothing renamed to a +-- different concept. +-- +-- Privacy: raw BSN is never stored, only a SHA-256 hash (bsn_hash) — this +-- follows the platform-wide privacy rule (root CLAUDE.md), which is a +-- stricter requirement than the source document's "versleuteld (pgcrypto)" +-- wording. Reversible BSN storage for declaratie/Vecozo purposes, if ever +-- needed, is a separate, explicitly-scoped decision — not implemented here. +-- ============================================================================ + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +-- ============================================================================ +-- person +-- ============================================================================ +CREATE TABLE person ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + family_name TEXT NOT NULL, + name_prefix TEXT, + given_names TEXT, + initials TEXT, + preferred_name TEXT, + birth_date DATE NOT NULL, + gender TEXT NOT NULL, -- waardelijst geslacht + gender_identity TEXT, -- waardelijst genderidentiteit + bsn_hash TEXT, -- SHA-256, optioneel op PERSOON (rolgebonden eis via nudge) + deceased BOOLEAN NOT NULL DEFAULT false, + deceased_at DATE, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT person_family_name_not_empty CHECK (length(trim(family_name)) > 0), + CONSTRAINT person_deceased_at_requires_flag CHECK (deceased_at IS NULL OR deceased) +); + +CREATE UNIQUE INDEX idx_person_bsn_hash ON person(bsn_hash) WHERE bsn_hash IS NOT NULL AND deleted_at IS NULL; +CREATE INDEX idx_person_name ON person(family_name, given_names) WHERE deleted_at IS NULL; + +COMMENT ON TABLE person IS 'Natuurlijke persoon, onafhankelijk van rollen (cliënt, contactpersoon, vertegenwoordiger, medewerker). model-aanmelding.md §2.1.'; +COMMENT ON COLUMN person.bsn_hash IS 'SHA-256 hash van het BSN; geen raw BSN opgeslagen (platform-privacyregel). Naam+geboortedatum-duplicaten zijn een nudge, geen constraint.'; + +ALTER TABLE person ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON person FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_person_updated_at BEFORE UPDATE ON person FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- address +-- ============================================================================ +CREATE TABLE address ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + person_id UUID NOT NULL REFERENCES person(id) ON DELETE CASCADE, + address_type TEXT NOT NULL, -- waardelijst adrestype + street TEXT NOT NULL, + house_number TEXT NOT NULL, + house_number_addition TEXT, + postal_code TEXT NOT NULL, + city TEXT NOT NULL, + country TEXT NOT NULL DEFAULT 'NL', + valid_from DATE, + valid_to DATE, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +-- max één actueel (valid_to leeg) adres per persoon per adrestype +CREATE UNIQUE INDEX idx_address_current_per_type ON address(person_id, address_type) + WHERE valid_to IS NULL AND deleted_at IS NULL; +CREATE INDEX idx_address_person ON address(person_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE address IS 'model-aanmelding.md §2.2 — 0..n adressen per persoon (zib Patient).'; + +ALTER TABLE address ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON address FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_address_updated_at BEFORE UPDATE ON address FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- contact_detail +-- ============================================================================ +CREATE TABLE contact_detail ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + person_id UUID NOT NULL REFERENCES person(id) ON DELETE CASCADE, + contact_type TEXT NOT NULL, -- waardelijst contactgegeven_type (telefoon/e-mail) + contact_subtype TEXT, -- waardelijst contactgegeven_soort + value TEXT NOT NULL, + is_preferred BOOLEAN NOT NULL DEFAULT false, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +-- max één voorkeursgegeven per persoon per contacttype +CREATE UNIQUE INDEX idx_contact_detail_preferred ON contact_detail(person_id, contact_type) + WHERE is_preferred AND deleted_at IS NULL; +CREATE INDEX idx_contact_detail_person ON contact_detail(person_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE contact_detail IS 'model-aanmelding.md §2.3 — telefoonnummers en e-mailadressen per persoon.'; + +ALTER TABLE contact_detail ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON contact_detail FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_contact_detail_updated_at BEFORE UPDATE ON contact_detail FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- client (rol op person) +-- ============================================================================ +CREATE TABLE client ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + person_id UUID NOT NULL REFERENCES person(id) ON DELETE RESTRICT, + client_number BIGINT NOT NULL, + client_since DATE NOT NULL, + gp_situation TEXT NOT NULL DEFAULT 'onbekend', -- waardelijst huisarts_situatie + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +-- max één CLIENT-rol per PERSOON +CREATE UNIQUE INDEX idx_client_person ON client(person_id) WHERE deleted_at IS NULL; +CREATE UNIQUE INDEX idx_client_number ON client(client_number) WHERE deleted_at IS NULL; + +COMMENT ON TABLE client IS 'model-aanmelding.md §2.4 — instellingsgebonden rol op PERSOON. Nudge (geen constraint): cliënt zonder bsn_hash (Wabvpz).'; + +ALTER TABLE client ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON client FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_client_updated_at BEFORE UPDATE ON client FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- client_relation (contactpersoon / wettelijk vertegenwoordiger) +-- ============================================================================ +CREATE TABLE client_relation ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + client_id UUID NOT NULL REFERENCES client(id) ON DELETE CASCADE, + person_id UUID NOT NULL REFERENCES person(id) ON DELETE RESTRICT, + relation_type TEXT, -- waardelijst relatie (partner, ouder, kind, ...) + relation_role TEXT NOT NULL, -- waardelijst relatierol + representation_basis TEXT, -- waardelijst vertegenwoordigingsgrond (alleen bij rol wettelijk_vertegenwoordiger) + valid_from DATE NOT NULL, + valid_to DATE, + notes TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +-- max één actuele rij per (client, persoon, rol) +CREATE UNIQUE INDEX idx_client_relation_current ON client_relation(client_id, person_id, relation_role) + WHERE valid_to IS NULL AND deleted_at IS NULL; +CREATE INDEX idx_client_relation_client ON client_relation(client_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE client_relation IS 'model-aanmelding.md §2.5 — contactpersonen/vertegenwoordigers, twee assen relatie x rol (zib Contactpersoon). Leeftijdsafhankelijke rechten (12/16 WGBO) zijn autorisatie, geen schema.'; + +ALTER TABLE client_relation ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON client_relation FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_client_relation_updated_at BEFORE UPDATE ON client_relation FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- Zelfrelatie (persoon = cliënt-persoon) niet toegestaan. CHECK-constraints +-- kunnen geen subquery bevatten in Postgres, dus dit is een trigger i.p.v. +-- een CHECK — functioneel identiek aan wat model-aanmelding.md §2.5 vraagt. +CREATE OR REPLACE FUNCTION prevent_client_relation_self_reference() +RETURNS TRIGGER AS $$ +BEGIN + IF NEW.person_id = (SELECT person_id FROM client WHERE client.id = NEW.client_id) THEN + RAISE EXCEPTION 'client_relation: person_id mag niet gelijk zijn aan de cliënt-persoon zelf'; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER client_relation_no_self_reference + BEFORE INSERT OR UPDATE ON client_relation + FOR EACH ROW EXECUTE FUNCTION prevent_client_relation_self_reference(); + +-- ============================================================================ +-- practice_organization (moet vóór referrer bestaan i.v.m. FK) +-- ============================================================================ +CREATE TABLE practice_organization ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name TEXT NOT NULL, + practice_type TEXT, -- waardelijst praktijk_soort + agb_code TEXT, + address_text TEXT, + phone TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE UNIQUE INDEX idx_practice_organization_agb ON practice_organization(agb_code) WHERE agb_code IS NOT NULL AND deleted_at IS NULL; + +COMMENT ON TABLE practice_organization IS 'model-aanmelding.md §2.7 — organisatie waaraan verwijzers/huisartsen verbonden zijn.'; + +ALTER TABLE practice_organization ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON practice_organization FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_practice_organization_updated_at BEFORE UPDATE ON practice_organization FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- referrer +-- ============================================================================ +CREATE TABLE referrer ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name TEXT NOT NULL, + referrer_type TEXT NOT NULL, -- waardelijst verwijzertype (extra.agb_verplicht bepaalt nudge) + agb_code TEXT, + practice_organization_id UUID REFERENCES practice_organization(id) ON DELETE SET NULL, + phone TEXT, + email TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE UNIQUE INDEX idx_referrer_agb ON referrer(agb_code) WHERE agb_code IS NOT NULL AND deleted_at IS NULL; +CREATE INDEX idx_referrer_practice ON referrer(practice_organization_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE referrer IS 'model-aanmelding.md §2.6 — persoon/functionaris die verwijst, los van practice_organization. Kan zonder praktijk bestaan (gemeente-ambtenaar). Ook referent voor de vaste huisarts.'; + +ALTER TABLE referrer ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON referrer FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_referrer_updated_at BEFORE UPDATE ON referrer FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- client_general_practitioner (vaste huisarts) +-- ============================================================================ +CREATE TABLE client_general_practitioner ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + client_id UUID NOT NULL REFERENCES client(id) ON DELETE CASCADE, + referrer_id UUID REFERENCES referrer(id) ON DELETE SET NULL, + practice_organization_id UUID REFERENCES practice_organization(id) ON DELETE SET NULL, + valid_from DATE NOT NULL, + valid_to DATE, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT client_gp_at_least_one_ref CHECK (referrer_id IS NOT NULL OR practice_organization_id IS NOT NULL) +); + +-- max één actuele registratie per cliënt +CREATE UNIQUE INDEX idx_client_gp_current ON client_general_practitioner(client_id) + WHERE valid_to IS NULL AND deleted_at IS NULL; + +COMMENT ON TABLE client_general_practitioner IS 'model-aanmelding.md §2.8 — vaste huisarts, los van de incidentele verwijzer. "Geen huisarts"/"geen toestemming" staat op client.gp_situation, niet hier.'; + +ALTER TABLE client_general_practitioner ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON client_general_practitioner FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_client_gp_updated_at BEFORE UPDATE ON client_general_practitioner FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- insurance +-- ============================================================================ +CREATE TABLE insurance ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + client_id UUID NOT NULL REFERENCES client(id) ON DELETE CASCADE, + uzovi_code TEXT NOT NULL, + insurer_name TEXT, + policy_number TEXT, + valid_from DATE NOT NULL, + valid_to DATE, + cov_checked_at DATE, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +-- max één actuele verzekering per cliënt +CREATE UNIQUE INDEX idx_insurance_current ON insurance(client_id) WHERE valid_to IS NULL AND deleted_at IS NULL; + +COMMENT ON TABLE insurance IS 'model-aanmelding.md §2.9 — Zvw-verzekeringsgegevens. Nudge: Zvw-aanmelding zonder actuele COV-controle. Wlz/forensisch: declaratie-ronde.'; + +ALTER TABLE insurance ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON insurance FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_insurance_updated_at BEFORE UPDATE ON insurance FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- consent +-- ============================================================================ +-- referral_case_id/clinical_care_episode_id FKs are added by later migrations +-- (create_referral_domain, create_intake_treatment_advice_domain) once those +-- tables exist — see ALTER TABLE at the bottom of create_referral_domain. +CREATE TABLE consent ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + client_id UUID NOT NULL REFERENCES client(id) ON DELETE CASCADE, + consent_type TEXT NOT NULL, -- waardelijst toestemming_type (extra.grondslag) + status TEXT NOT NULL, -- waardelijst toestemming_status + consent_date DATE NOT NULL, + method TEXT, -- waardelijst toestemming_wijze + recorded_by UUID REFERENCES practitioners(id), + valid_to DATE, + revoked_at DATE, + scope_referral_case_id UUID, -- optioneel; default cliëntbreed + notes TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +-- max één actuele (niet-ingetrokken) rij per (client, type, scope) +CREATE UNIQUE INDEX idx_consent_current ON consent(client_id, consent_type, COALESCE(scope_referral_case_id, '00000000-0000-0000-0000-000000000000')) + WHERE status <> 'ingetrokken' AND deleted_at IS NULL; +CREATE INDEX idx_consent_client ON consent(client_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE consent IS 'model-aanmelding.md §2.14 — generieke WGBO/AVG-toestemming. Cliëntakkoord op het behandelplan is een apart feit bij het behandelplan, geen consent-rij. scope_referral_case_id FK volgt in create_referral_domain.sql.'; + +ALTER TABLE consent ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON consent FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_consent_updated_at BEFORE UPDATE ON consent FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- client_portal_account +-- ============================================================================ +CREATE TABLE client_portal_account ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + client_id UUID NOT NULL REFERENCES client(id) ON DELETE CASCADE, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE UNIQUE INDEX idx_client_portal_account_client ON client_portal_account(client_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE client_portal_account IS 'model-aanmelding.md §2.16 — relatie-placeholder (Wabvpz elektronische inzage). Authenticatiedetails: auth/ADM-ronde.'; + +ALTER TABLE client_portal_account ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON client_portal_account FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_client_portal_account_updated_at BEFORE UPDATE ON client_portal_account FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); diff --git a/supabase/migrations/20260720000003_create_referral_domain.sql b/supabase/migrations/20260720000003_create_referral_domain.sql new file mode 100644 index 0000000..5f4dfb7 --- /dev/null +++ b/supabase/migrations/20260720000003_create_referral_domain.sql @@ -0,0 +1,567 @@ +-- ============================================================================ +-- REFERRAL / INSTROOM DOMAIN +-- ============================================================================ +-- Created: 2026-07-20 +-- Source: docs/datamodel/deelmodellen/model-instroom.md (20 entiteiten), +-- docs/datamodel/feitenmodellen/feitenmodel-instroom.md, besluiten B21-B30. +-- +-- Design principles carried over from the model documents: +-- - referral_case has NO status column — the current state is derived from +-- the event timeline (signal_case_assignment/screening_activity/ +-- case_information_request/care_acceptance_decision/case_withdrawal/ +-- case_consolidation). See model-instroom.md §5. Application code (or a +-- read-model view, per the UX review) computes "actuele stand", not this +-- schema. +-- - "vervangt"-relations (care_acceptance_decision, professional_referral, +-- municipal_care_assignment) are enforced unique-if-set (partial unique +-- index) so the chain can never fork. +-- - `medewerker`/beslisser references point at the existing `practitioners` +-- table (already used by the prototype screening/intake schema). +-- - `decision_authority_id` on care_acceptance_decision is left nullable +-- for now: the target table for decision authority does not exist yet +-- (bevoegdheidsronde, B16/B33). The model calls it "verplicht"; enforcing +-- NOT NULL today would make every insert impossible. Tighten once that +-- ronde delivers a real authority table. +-- ============================================================================ + +-- ============================================================================ +-- referral_request +-- ============================================================================ +CREATE TABLE referral_request ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + person_id UUID REFERENCES person(id) ON DELETE RESTRICT, + person_identified_at TIMESTAMPTZ, + person_identified_by UUID REFERENCES practitioners(id), + initiated_at TIMESTAMPTZ NOT NULL, + initiator_type TEXT NOT NULL, -- waardelijst initiator_type + referrer_id UUID REFERENCES referrer(id), + requested_scope TEXT NOT NULL, + presenting_need_text TEXT NOT NULL, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT referral_request_person_identification CHECK ( + (person_id IS NULL AND person_identified_at IS NULL AND person_identified_by IS NULL) + OR (person_id IS NOT NULL AND person_identified_at IS NOT NULL AND person_identified_by IS NOT NULL) + ) +); + +COMMENT ON TABLE referral_request IS 'model-instroom.md §2.1. person_id optioneel: crisisaanmelding met onbekende identiteit (B26) — geen placeholder-persoon, de koppeling is een eigen feit.'; + +ALTER TABLE referral_request ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON referral_request FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_referral_request_updated_at BEFORE UPDATE ON referral_request FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- referral_submission +-- ============================================================================ +CREATE TABLE referral_submission ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + received_at TIMESTAMPTZ NOT NULL, + channel TEXT NOT NULL, -- waardelijst submission_channel + source_reference TEXT, -- vrije tekst of naam verwijzer; "onbekend" toegestaan + recorded_by UUID REFERENCES practitioners(id), + raw_note TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +COMMENT ON TABLE referral_submission IS 'model-instroom.md §2.2. Nooit overschreven door een latere aanvulling — een aanvulling is een nieuwe submission.'; + +ALTER TABLE referral_submission ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON referral_submission FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_referral_submission_updated_at BEFORE UPDATE ON referral_submission FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- submission_document +-- ============================================================================ +CREATE TABLE submission_document ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + submission_id UUID NOT NULL REFERENCES referral_submission(id) ON DELETE CASCADE, + document_type TEXT NOT NULL, -- waardelijst document_type + document_reference UUID NOT NULL, -- documentopslag + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE INDEX idx_submission_document_submission ON submission_document(submission_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE submission_document IS 'model-instroom.md §2.3.'; + +ALTER TABLE submission_document ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON submission_document FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_submission_document_updated_at BEFORE UPDATE ON submission_document FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- submission_request_link +-- ============================================================================ +CREATE TABLE submission_request_link ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + submission_id UUID NOT NULL REFERENCES referral_submission(id) ON DELETE CASCADE, + referral_request_id UUID NOT NULL REFERENCES referral_request(id) ON DELETE CASCADE, + established_at TIMESTAMPTZ NOT NULL, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + UNIQUE (submission_id, referral_request_id) +); + +CREATE INDEX idx_submission_request_link_request ON submission_request_link(referral_request_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE submission_request_link IS 'model-instroom.md §2.4. "representeert" vs "vult_aan" is niet opgeslagen — af te leiden als vroegste established_at per referral_request_id.'; + +ALTER TABLE submission_request_link ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON submission_request_link FOR ALL USING (auth.role() = 'authenticated'); + +-- ============================================================================ +-- submission_duplicate_assessment +-- ============================================================================ +CREATE TABLE submission_duplicate_assessment ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + submission_id UUID NOT NULL REFERENCES referral_submission(id) ON DELETE CASCADE, + duplicate_of_submission_id UUID NOT NULL REFERENCES referral_submission(id) ON DELETE CASCADE, + assessed_by UUID NOT NULL REFERENCES practitioners(id), + assessed_at TIMESTAMPTZ NOT NULL, + reason TEXT NOT NULL, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT submission_duplicate_not_self CHECK (submission_id <> duplicate_of_submission_id) +); + +COMMENT ON TABLE submission_duplicate_assessment IS 'model-instroom.md §2.5. Niet-destructief: beide submissions blijven bestaan.'; + +ALTER TABLE submission_duplicate_assessment ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON submission_duplicate_assessment FOR ALL USING (auth.role() = 'authenticated'); + +-- ============================================================================ +-- referral_case +-- ============================================================================ +CREATE TABLE referral_case ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + person_id UUID REFERENCES person(id) ON DELETE RESTRICT, + person_identified_at TIMESTAMPTZ, + person_identified_by UUID REFERENCES practitioners(id), + opened_at TIMESTAMPTZ NOT NULL, + presenting_need_summary TEXT NOT NULL, + legal_framework TEXT NOT NULL, -- waardelijst wettelijk_kader + zpm_referral_type TEXT, -- waardelijst zpm_verwijstype + gp_informed_at DATE, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT referral_case_person_identification CHECK ( + (person_id IS NULL AND person_identified_at IS NULL AND person_identified_by IS NULL) + OR (person_id IS NOT NULL AND person_identified_at IS NOT NULL AND person_identified_by IS NOT NULL) + ) +); + +COMMENT ON TABLE referral_case IS 'model-instroom.md §2.6. Geen statusveld — zie §5 aldaar en de opmerking bovenaan dit bestand. legal_framework/zpm_referral_type/gp_informed_at hersteld van het oudere AANMELDING (B21-B27).'; + +ALTER TABLE referral_case ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON referral_case FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_referral_case_updated_at BEFORE UPDATE ON referral_case FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- Now that referral_case exists: wire the consent scope FK from the person/client migration +ALTER TABLE consent + ADD CONSTRAINT consent_scope_referral_case_fk FOREIGN KEY (scope_referral_case_id) REFERENCES referral_case(id) ON DELETE SET NULL; + +-- ============================================================================ +-- submission_case_assignment +-- ============================================================================ +CREATE TABLE submission_case_assignment ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + submission_id UUID NOT NULL REFERENCES referral_submission(id) ON DELETE CASCADE, + referral_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE CASCADE, + assigned_at TIMESTAMPTZ NOT NULL, + assigned_by UUID NOT NULL REFERENCES practitioners(id), + reason TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + UNIQUE (submission_id, referral_case_id) +); + +CREATE INDEX idx_submission_case_assignment_case ON submission_case_assignment(referral_case_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE submission_case_assignment IS 'model-instroom.md §2.7 (hernoemd van signal_case_assignment). Toewijzing is een gebeurtenis, geen overschrijfbaar veld.'; + +ALTER TABLE submission_case_assignment ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON submission_case_assignment FOR ALL USING (auth.role() = 'authenticated'); + +-- ============================================================================ +-- request_case_link +-- ============================================================================ +CREATE TABLE request_case_link ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_request_id UUID NOT NULL REFERENCES referral_request(id) ON DELETE CASCADE, + referral_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE CASCADE, + since_date DATE NOT NULL, + reason TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + UNIQUE (referral_request_id, referral_case_id) +); + +COMMENT ON TABLE request_case_link IS 'model-instroom.md §2.8. reason verplicht (applicatieniveau) zodra een request aan >1 case gekoppeld is.'; + +ALTER TABLE request_case_link ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON request_case_link FOR ALL USING (auth.role() = 'authenticated'); + +-- ============================================================================ +-- case_consolidation +-- ============================================================================ +CREATE TABLE case_consolidation ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + primary_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE RESTRICT, + related_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE RESTRICT, + overlap_assessed_at TIMESTAMPTZ, + designated_at TIMESTAMPTZ NOT NULL, + designated_by UUID NOT NULL REFERENCES practitioners(id), + reason TEXT NOT NULL, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT case_consolidation_no_self CHECK (primary_case_id <> related_case_id) +); + +-- een case kan hoogstens één keer de niet-leidende rol vervullen +CREATE UNIQUE INDEX idx_case_consolidation_related_unique ON case_consolidation(related_case_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE case_consolidation IS 'model-instroom.md §2.9 (hernoemd van access_case_consolidation). Geen speciale decision_authority vereist (administratieve correctie). Transitieve cykels niet uitgesloten op schemaniveau — applicatieverantwoordelijkheid.'; + +ALTER TABLE case_consolidation ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON case_consolidation FOR ALL USING (auth.role() = 'authenticated'); + +-- ============================================================================ +-- screening_activity +-- ============================================================================ +CREATE TABLE screening_activity ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE CASCADE, + performed_at TIMESTAMPTZ NOT NULL, + activity_type TEXT NOT NULL, -- waardelijst screening_activity_type + performed_by UUID NOT NULL REFERENCES practitioners(id), + notes TEXT, + funded BOOLEAN NOT NULL DEFAULT false, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE INDEX idx_screening_activity_case ON screening_activity(referral_case_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE screening_activity IS 'model-instroom.md §2.10. Directe koppeling aan referral_case — de aparte "screening"-groeperingsentiteit is vervallen (reviewronde 19 juli).'; + +ALTER TABLE screening_activity ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON screening_activity FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_screening_activity_updated_at BEFORE UPDATE ON screening_activity FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- case_information_request +-- ============================================================================ +CREATE TABLE case_information_request ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE CASCADE, + requested_at TIMESTAMPTZ NOT NULL, + requested_by UUID NOT NULL REFERENCES practitioners(id), + requested_information TEXT NOT NULL, + resolved_by_submission_id UUID REFERENCES referral_submission(id), + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE UNIQUE INDEX idx_case_information_request_resolved_submission ON case_information_request(resolved_by_submission_id) WHERE resolved_by_submission_id IS NOT NULL AND deleted_at IS NULL; +CREATE INDEX idx_case_information_request_case ON case_information_request(referral_case_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE case_information_request IS 'model-instroom.md §2.11. Geen besluituitkomst en geen statusfase (B21/vraag 5) — een gedateerd gebeurtenisfeit; kan zich ook ná een eerder besluit voordoen tijdens heroverweging.'; + +ALTER TABLE case_information_request ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON case_information_request FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_case_information_request_updated_at BEFORE UPDATE ON case_information_request FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- care_acceptance_decision +-- ============================================================================ +CREATE TABLE care_acceptance_decision ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE RESTRICT, + decided_at TIMESTAMPTZ NOT NULL, + decided_by UUID NOT NULL REFERENCES practitioners(id), + decision_authority_id UUID, -- ref decision_authority — tabel volgt uit bevoegdheidsronde (B16); tijdelijk geen FK/NOT NULL + content_match BOOLEAN NOT NULL, + capacity_available BOOLEAN, + program_context TEXT, -- waardelijst care_program of vrije tekst + decision_outcome TEXT NOT NULL, -- waardelijst decision_outcome + follow_up_route TEXT NOT NULL, -- waardelijst follow_up_route + rationale TEXT, + replaces_decision_id UUID REFERENCES care_acceptance_decision(id), + replacement_reason TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT care_acceptance_decision_outcome_check CHECK (decision_outcome IN ('geaccepteerd', 'afgewezen')), + CONSTRAINT care_acceptance_decision_rationale_required CHECK ( + decision_outcome <> 'afgewezen' OR (rationale IS NOT NULL AND length(trim(rationale)) > 0) + ), + CONSTRAINT care_acceptance_decision_replacement_reason_required CHECK ( + replaces_decision_id IS NULL OR (replacement_reason IS NOT NULL AND length(trim(replacement_reason)) > 0) + ), + CONSTRAINT care_acceptance_decision_no_self_replace CHECK (replaces_decision_id <> id) +); + +-- vervangt-keten kan nooit vertakken (B23 / reviewronde 19 juli, kritieke bevinding) +CREATE UNIQUE INDEX idx_care_acceptance_decision_replaces_unique ON care_acceptance_decision(replaces_decision_id) WHERE replaces_decision_id IS NOT NULL AND deleted_at IS NULL; +CREATE INDEX idx_care_acceptance_decision_case ON care_acceptance_decision(referral_case_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE care_acceptance_decision IS 'model-instroom.md §2.12. Geen apart voorafgaand screeningsadvies (B21). Concurrency bij gelijktijdige heroverwegingen (SELECT ... FOR UPDATE op de keten-tail) is applicatieverantwoordelijkheid.'; + +ALTER TABLE care_acceptance_decision ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON care_acceptance_decision FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_care_acceptance_decision_updated_at BEFORE UPDATE ON care_acceptance_decision FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- case_withdrawal +-- ============================================================================ +CREATE TABLE case_withdrawal ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE CASCADE, + withdrawn_at TIMESTAMPTZ NOT NULL, + recorded_by UUID NOT NULL REFERENCES practitioners(id), + note TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE INDEX idx_case_withdrawal_case ON case_withdrawal(referral_case_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE case_withdrawal IS 'model-instroom.md §2.13. Geen beslisbevoegdheid vereist; kan op elk moment, ook ná een positief besluit (B24).'; + +ALTER TABLE case_withdrawal ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON case_withdrawal FOR ALL USING (auth.role() = 'authenticated'); + +-- ============================================================================ +-- professional_referral +-- ============================================================================ +CREATE TABLE professional_referral ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_request_id UUID NOT NULL REFERENCES referral_request(id) ON DELETE RESTRICT, + referrer_id UUID NOT NULL REFERENCES referrer(id), + referral_date DATE NOT NULL, + echelon TEXT, -- waardelijst echelon + suspected_condition TEXT, + is_readmission BOOLEAN NOT NULL DEFAULT false, + replaces_referral_id UUID REFERENCES professional_referral(id), + replacement_reason TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT professional_referral_replacement_reason_required CHECK ( + replaces_referral_id IS NULL OR (replacement_reason IS NOT NULL AND length(trim(replacement_reason)) > 0) + ), + CONSTRAINT professional_referral_no_self_replace CHECK (replaces_referral_id <> id) +); + +CREATE UNIQUE INDEX idx_professional_referral_replaces_unique ON professional_referral(replaces_referral_id) WHERE replaces_referral_id IS NOT NULL AND deleted_at IS NULL; +CREATE INDEX idx_professional_referral_request ON professional_referral(referral_request_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE professional_referral IS 'model-instroom.md §2.14. Correctie/aanvulling = nieuw, vervangend record, geen nieuw request (was open punt 1, opgelost).'; + +ALTER TABLE professional_referral ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON professional_referral FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_professional_referral_updated_at BEFORE UPDATE ON professional_referral FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- municipal_care_assignment +-- ============================================================================ +CREATE TABLE municipal_care_assignment ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_request_id UUID NOT NULL REFERENCES referral_request(id) ON DELETE RESTRICT, + legal_framework TEXT NOT NULL, -- waardelijst municipal_legal_framework + municipality_code TEXT NOT NULL, + assignment_number TEXT NOT NULL, + product_category TEXT NOT NULL, -- waardelijst municipal_product_category + product_code TEXT NOT NULL, + volume NUMERIC NOT NULL, + unit TEXT NOT NULL, -- waardelijst municipal_product_unit + frequency TEXT NOT NULL, + start_date DATE NOT NULL, + end_date DATE, + issued_at TIMESTAMPTZ NOT NULL, + replaces_assignment_id UUID REFERENCES municipal_care_assignment(id), + replacement_reason TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT municipal_care_assignment_replacement_reason_required CHECK ( + replaces_assignment_id IS NULL OR (replacement_reason IS NOT NULL AND length(trim(replacement_reason)) > 0) + ), + CONSTRAINT municipal_care_assignment_no_self_replace CHECK (replaces_assignment_id <> id) +); + +CREATE UNIQUE INDEX idx_municipal_care_assignment_replaces_unique ON municipal_care_assignment(replaces_assignment_id) WHERE replaces_assignment_id IS NOT NULL AND deleted_at IS NULL; +CREATE UNIQUE INDEX idx_municipal_care_assignment_number ON municipal_care_assignment(municipality_code, assignment_number) WHERE deleted_at IS NULL; + +COMMENT ON TABLE municipal_care_assignment IS 'model-instroom.md §2.15 (voorstel, juridisch onderzoek 19 juli). Naast, niet in plaats van professional_referral (Jeugdwet kent een eigen professionele verwijsroute).'; + +ALTER TABLE municipal_care_assignment ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON municipal_care_assignment FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_municipal_care_assignment_updated_at BEFORE UPDATE ON municipal_care_assignment FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- legal_mandate +-- ============================================================================ +CREATE TABLE legal_mandate ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE RESTRICT, + mandate_type TEXT NOT NULL, -- waardelijst mandate_type + decision_reference TEXT NOT NULL, + decided_by_role TEXT NOT NULL, -- waardelijst mandate_decider_role + decided_at TIMESTAMPTZ NOT NULL, + medical_statement_reference UUID, -- documentopslag + effectuated_at TIMESTAMPTZ NOT NULL, + valid_from TIMESTAMPTZ NOT NULL, + valid_until TIMESTAMPTZ, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT legal_mandate_type_check CHECK (mandate_type IN ('crisismaatregel', 'zorgmachtiging')), + CONSTRAINT legal_mandate_medical_statement_required CHECK ( + mandate_type <> 'crisismaatregel' OR medical_statement_reference IS NOT NULL + ) +); + +CREATE INDEX idx_legal_mandate_case ON legal_mandate(referral_case_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE legal_mandate IS 'model-instroom.md §2.16 (voorstel, juridisch onderzoek 19 juli). Geen vervangt-relatie: een crisismaatregel die overgaat in een zorgmachtiging is een nieuw mandaat.'; + +ALTER TABLE legal_mandate ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON legal_mandate FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_legal_mandate_updated_at BEFORE UPDATE ON legal_mandate FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- crisis_encounter_note +-- ============================================================================ +CREATE TABLE crisis_encounter_note ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE CASCADE, + occurred_at TIMESTAMPTZ NOT NULL, + recorded_by UUID NOT NULL REFERENCES practitioners(id), + fact_type TEXT NOT NULL, -- waardelijst crisis_fact_type + description TEXT NOT NULL, + structured_value JSONB, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE INDEX idx_crisis_encounter_note_case ON crisis_encounter_note(referral_case_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE crisis_encounter_note IS 'model-instroom.md §2.17 (voorstel). Werkt al zonder geïdentificeerde persoon. 14-dagen-identificatienudge hangt aan referral_request.person_identified_at IS NULL, geen constraint hier.'; + +ALTER TABLE crisis_encounter_note ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON crisis_encounter_note FOR ALL USING (auth.role() = 'authenticated'); + +-- ============================================================================ +-- clinical_care_episode +-- ============================================================================ +CREATE TABLE clinical_care_episode ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + originating_decision_id UUID REFERENCES care_acceptance_decision(id) ON DELETE RESTRICT, + originating_mandate_id UUID REFERENCES legal_mandate(id) ON DELETE RESTRICT, + started_at TIMESTAMPTZ NOT NULL, + ended_at TIMESTAMPTZ, + end_reason TEXT, -- waardelijst episode_end_reason + possible_continuation_of_episode_id UUID REFERENCES clinical_care_episode(id), + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT clinical_care_episode_origin_xor CHECK ( + (originating_decision_id IS NOT NULL AND originating_mandate_id IS NULL) + OR (originating_decision_id IS NULL AND originating_mandate_id IS NOT NULL) + ), + CONSTRAINT clinical_care_episode_end_reason_required CHECK ( + ended_at IS NULL OR end_reason IS NOT NULL + ), + CONSTRAINT clinical_care_episode_no_self_continuation CHECK (possible_continuation_of_episode_id <> id) +); + +CREATE UNIQUE INDEX idx_clinical_care_episode_decision ON clinical_care_episode(originating_decision_id) WHERE originating_decision_id IS NOT NULL AND deleted_at IS NULL; +CREATE UNIQUE INDEX idx_clinical_care_episode_mandate ON clinical_care_episode(originating_mandate_id) WHERE originating_mandate_id IS NOT NULL AND deleted_at IS NULL; + +COMMENT ON TABLE clinical_care_episode IS 'model-instroom.md §2.18 (alleen ontstaan/einde — volledig episodemodel is een ander deelgebied). XOR: ontstaat óf uit een acceptatiebesluit óf uit een mandaat. possible_continuation_of_episode_id is een zorginhoudelijke aanwijzing, GEEN NZa-trajectnummertoets.'; + +ALTER TABLE clinical_care_episode ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON clinical_care_episode FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_clinical_care_episode_updated_at BEFORE UPDATE ON clinical_care_episode FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- case_urgency_assessment +-- ============================================================================ +CREATE TABLE case_urgency_assessment ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + referral_case_id UUID NOT NULL REFERENCES referral_case(id) ON DELETE CASCADE, + assessed_at TIMESTAMPTZ NOT NULL, + assessed_by UUID NOT NULL REFERENCES practitioners(id), + assessed_in_role TEXT NOT NULL, -- waardelijst urgency_assessor_role + urgency_level TEXT NOT NULL, -- waardelijst urgency_level + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE INDEX idx_case_urgency_assessment_case ON case_urgency_assessment(referral_case_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE case_urgency_assessment IS 'model-instroom.md §2.19. Herhaalbaar, laatste assessed_at geldt; geen vervangt-relatie (lagere inzet dan een acceptatiebesluit).'; + +ALTER TABLE case_urgency_assessment ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON case_urgency_assessment FOR ALL USING (auth.role() = 'authenticated'); + +-- ============================================================================ +-- episode_team_involvement (minimale, voorlopige hook — zie besluitenlog §10 punt 3) +-- ============================================================================ +CREATE TABLE episode_team_involvement ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + clinical_care_episode_id UUID NOT NULL REFERENCES clinical_care_episode(id) ON DELETE CASCADE, + team_reference TEXT NOT NULL, -- vrije tekst; geen Team-entiteit vóór de zorgteam-ronde + involved_since TIMESTAMPTZ NOT NULL, + recorded_by UUID NOT NULL REFERENCES practitioners(id), + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +CREATE INDEX idx_episode_team_involvement_episode ON episode_team_involvement(clinical_care_episode_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE episode_team_involvement IS 'model-instroom.md §2.20. Bewust minimaal — geen rollen, geen bevoegdheid, geen individueel lidmaatschap. Wordt vervangen/geabsorbeerd door het volledige zorgteam-model (besluitenlog §10 punt 3).'; + +ALTER TABLE episode_team_involvement ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON episode_team_involvement FOR ALL USING (auth.role() = 'authenticated'); diff --git a/supabase/migrations/20260720000004_create_intake_treatment_advice_domain.sql b/supabase/migrations/20260720000004_create_intake_treatment_advice_domain.sql new file mode 100644 index 0000000..43f4659 --- /dev/null +++ b/supabase/migrations/20260720000004_create_intake_treatment_advice_domain.sql @@ -0,0 +1,178 @@ +-- ============================================================================ +-- INTAKE / TREATMENT ADVICE DOMAIN +-- ============================================================================ +-- Created: 2026-07-20 +-- Source: docs/datamodel/deelmodellen/model-intake-behandeladvies.md (5 +-- entiteiten), docs/datamodel/feitenmodellen/feitenmodel-intake-behandeladvies.md, +-- besluiten B31-B34. +-- +-- clinical_intake_assessment hangs off clinical_care_episode (not off +-- referral_case) — it starts at some point after the acceptance decision, +-- possibly after a wait-list period (status = 'gepland' covers that). +-- ============================================================================ + +-- ============================================================================ +-- clinical_intake_assessment +-- ============================================================================ +CREATE TABLE clinical_intake_assessment ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + clinical_care_episode_id UUID NOT NULL REFERENCES clinical_care_episode(id) ON DELETE RESTRICT, + initiation_reason TEXT NOT NULL, -- waardelijst intake_initiation_reason + department TEXT NOT NULL, -- waardelijst department + status TEXT NOT NULL DEFAULT 'gepland', -- waardelijst intake_status + planned_at TIMESTAMPTZ NOT NULL, + started_at TIMESTAMPTZ, + ended_at TIMESTAMPTZ, + abort_reason TEXT, -- waardelijst intake_abort_reason + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT clinical_intake_assessment_status_check CHECK (status IN ('gepland', 'bezig', 'afgerond', 'afgebroken')), + CONSTRAINT clinical_intake_assessment_started_required CHECK ( + status NOT IN ('bezig', 'afgerond', 'afgebroken') OR started_at IS NOT NULL + ), + CONSTRAINT clinical_intake_assessment_ended_required CHECK ( + status NOT IN ('afgerond', 'afgebroken') OR ended_at IS NOT NULL + ), + CONSTRAINT clinical_intake_assessment_abort_reason_required CHECK ( + status <> 'afgebroken' OR abort_reason IS NOT NULL + ) +); + +CREATE INDEX idx_clinical_intake_assessment_episode ON clinical_intake_assessment(clinical_care_episode_id) WHERE deleted_at IS NULL; +CREATE INDEX idx_clinical_intake_assessment_status ON clinical_intake_assessment(status) WHERE deleted_at IS NULL; + +COMMENT ON TABLE clinical_intake_assessment IS 'model-intake-behandeladvies.md §1.1. Start ná het acceptatiebesluit (B31), niet gelijktijdig met episode-ontstaan — planned_at dekt een eventuele intakewachtlijst-periode. "Maximaal één lopende intake per episode" is een aan/uit-zetbare bedrijfsregel, geen schemabeperking.'; + +ALTER TABLE clinical_intake_assessment ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON clinical_intake_assessment FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_clinical_intake_assessment_updated_at BEFORE UPDATE ON clinical_intake_assessment FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- intake_contact +-- ============================================================================ +CREATE TABLE intake_contact ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + clinical_intake_assessment_id UUID NOT NULL REFERENCES clinical_intake_assessment(id) ON DELETE CASCADE, + occurred_at TIMESTAMPTZ NOT NULL, + contact_type TEXT NOT NULL, -- waardelijst intake_contact_type + performed_by UUID NOT NULL REFERENCES practitioners(id), + notes TEXT, + planned_duration_minutes INTEGER, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT intake_contact_duration_non_negative CHECK (planned_duration_minutes IS NULL OR planned_duration_minutes >= 0) +); + +CREATE INDEX idx_intake_contact_assessment ON intake_contact(clinical_intake_assessment_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE intake_contact IS 'model-intake-behandeladvies.md §1.2. Exclusief aan één clinical_intake_assessment; generalisatie naar een bredere consult-/afspraakstructuur is de agenda-ronde.'; + +ALTER TABLE intake_contact ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON intake_contact FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_intake_contact_updated_at BEFORE UPDATE ON intake_contact FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- child_safety_check +-- ============================================================================ +CREATE TABLE child_safety_check ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + clinical_intake_assessment_id UUID NOT NULL REFERENCES clinical_intake_assessment(id) ON DELETE CASCADE, + performed_at TIMESTAMPTZ NOT NULL, + performed_by UUID NOT NULL REFERENCES practitioners(id), + responsible_for_minors BOOLEAN NOT NULL, + minor_count INTEGER, + minor_ages TEXT, -- vrije tekst, bewust geen kindrecords (dataminimalisatie) + safety_concern BOOLEAN NOT NULL, + safety_concern_notes TEXT, + action_taken BOOLEAN NOT NULL, + action_taken_notes TEXT, + pregnancy BOOLEAN NOT NULL, -- vierde vlag, te verifiëren tegen KNMG-meldcode (B34) + notes TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT child_safety_check_safety_notes_required CHECK (NOT safety_concern OR safety_concern_notes IS NOT NULL), + CONSTRAINT child_safety_check_action_notes_required CHECK (NOT action_taken OR action_taken_notes IS NOT NULL) +); + +-- maximaal één per intake +CREATE UNIQUE INDEX idx_child_safety_check_assessment ON child_safety_check(clinical_intake_assessment_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE child_safety_check IS 'model-intake-behandeladvies.md §1.3 (B34). Wet verplichte meldcode / KNMG-kindcheck.'; + +ALTER TABLE child_safety_check ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON child_safety_check FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_child_safety_check_updated_at BEFORE UPDATE ON child_safety_check FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- intake_mdt_review (minimale, voorlopige hook — zie §6 aldaar) +-- ============================================================================ +CREATE TABLE intake_mdt_review ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + occurred_at TIMESTAMPTZ NOT NULL, + participants TEXT NOT NULL, -- vrije tekst; structurering wacht op de MDO-ronde (B14-B15) + notes TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ +); + +COMMENT ON TABLE intake_mdt_review IS 'model-intake-behandeladvies.md §1.4. Geen eigen FK naar clinical_intake_assessment — gekoppeld via treatment_advice.mdt_review_id, telt pas zodra hij aan een vastgesteld advies hangt. LKS 4.0: dwingend voor settings 3-8, optioneel setting 2, n.v.t. vrijgevestigden.'; + +ALTER TABLE intake_mdt_review ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON intake_mdt_review FOR ALL USING (auth.role() = 'authenticated'); + +-- ============================================================================ +-- treatment_advice +-- ============================================================================ +CREATE TABLE treatment_advice ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + clinical_intake_assessment_id UUID NOT NULL REFERENCES clinical_intake_assessment(id) ON DELETE RESTRICT, + decided_at TIMESTAMPTZ NOT NULL, + decided_by UUID NOT NULL REFERENCES practitioners(id), -- indicerende regiebehandelaar + outcome TEXT NOT NULL, -- waardelijst treatment_advice_outcome + recommended_care_program TEXT, -- waardelijst care_program + rationale TEXT, + mdt_review_id UUID REFERENCES intake_mdt_review(id), + replaces_advice_id UUID REFERENCES treatment_advice(id), + replacement_reason TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deleted_at TIMESTAMPTZ, + + CONSTRAINT treatment_advice_outcome_check CHECK (outcome IN ('in_zorg', 'terugverwijzing', 'doorverwijzing', 'extra_diagnostiek')), + CONSTRAINT treatment_advice_care_program_required CHECK ( + outcome <> 'in_zorg' OR recommended_care_program IS NOT NULL + ), + CONSTRAINT treatment_advice_rationale_required CHECK ( + outcome = 'in_zorg' OR (rationale IS NOT NULL AND length(trim(rationale)) > 0) + ), + CONSTRAINT treatment_advice_replacement_reason_required CHECK ( + replaces_advice_id IS NULL OR (replacement_reason IS NOT NULL AND length(trim(replacement_reason)) > 0) + ), + CONSTRAINT treatment_advice_no_self_replace CHECK (replaces_advice_id <> id) +); + +-- vervangt-keten kan nooit vertakken, zelfde patroon als care_acceptance_decision +CREATE UNIQUE INDEX idx_treatment_advice_replaces_unique ON treatment_advice(replaces_advice_id) WHERE replaces_advice_id IS NOT NULL AND deleted_at IS NULL; +CREATE INDEX idx_treatment_advice_assessment ON treatment_advice(clinical_intake_assessment_id) WHERE deleted_at IS NULL; + +COMMENT ON TABLE treatment_advice IS 'model-intake-behandeladvies.md §1.5 (B32-B33). Geen apart voorafgaand advies — dit object zelf geeft de vervolgrichting. doorverwijzing/terugverwijzing zijn LKS-genormeerd volgtijdelijk (via replaces_advice_id); extra_diagnostiek is praktijkgefundeerd, niet LKS-genormeerd.'; + +ALTER TABLE treatment_advice ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable all for authenticated users" ON treatment_advice FOR ALL USING (auth.role() = 'authenticated'); +CREATE TRIGGER update_treatment_advice_updated_at BEFORE UPDATE ON treatment_advice FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- Een afgeronde intake vereist minimaal één (niet noodzakelijk het laatst +-- vervangen) treatment_advice — afgedwongen op applicatieniveau, niet als +-- schema-constraint (zou een circulaire FK-afhankelijkheid vereisen). +COMMENT ON COLUMN clinical_intake_assessment.status IS 'gepland/bezig/afgerond/afgebroken. Bij afgerond hoort minimaal één treatment_advice (applicatieniveau, geen FK-constraint i.v.m. circulaire afhankelijkheid).'; diff --git a/tests/README.md b/tests/README.md index 260213f..3ecee20 100644 --- a/tests/README.md +++ b/tests/README.md @@ -159,7 +159,7 @@ Beide tools testen exact dezelfde flows. De syntax verschilt iets: | Foutmelding bij verkeerde gegevens | Foutmelding verschijnt bij foute combinatie | | Demo login knop werkt | ⚡ knop logt direct in en stuurt door naar het EPD | | Redirect na succesvol inloggen | Na inloggen kom je op `/epd/` terecht | -| Beveiligde route stuurt door | Probeer `/epd/dashboard` zonder sessie → je gaat naar login | +| Beveiligde route stuurt door | Probeer `/epd/agenda` zonder sessie → je gaat naar login | --- diff --git a/tests/cypress/login.cy.ts b/tests/cypress/login.cy.ts index ebfd99e..57bfc7f 100644 --- a/tests/cypress/login.cy.ts +++ b/tests/cypress/login.cy.ts @@ -30,7 +30,7 @@ describe('Login flow', () => { }); it('beveiligde route stuurt door naar login', () => { - cy.visit('/epd/dashboard'); + cy.visit('/epd/agenda'); cy.url().should('include', '/login'); }); }); diff --git a/tests/playwright/login.spec.ts b/tests/playwright/login.spec.ts index 08ed036..d940eb5 100644 --- a/tests/playwright/login.spec.ts +++ b/tests/playwright/login.spec.ts @@ -48,7 +48,7 @@ test.describe('Login flow', () => { }); test('beveiligde route stuurt door naar login', async ({ page }) => { - await page.goto('/epd/dashboard'); + await page.goto('/epd/agenda'); await page.waitForURL('/login**', { timeout: 5_000 }); expect(page.url()).toContain('/login');