1import{j as e,aA as d,C as o,q as a,v as r,b as i,B as c}from"./index-BWYXbCDk.js";import{A as m,b as x,c as p,d as u}from"./iconMapping-B7V8kQdh.js";import{o as h}from"./odoo-logo-Bz8idgHT.js";import{F as g}from"./separator-BseUOeT3.js";import{m as j}from"./sheet-BkALcbyN.js";import{C as l}from"./circle-check-DuWgpWTO.js";import"./circle-oiTnu0S3.js";import"./search-D017GuSe.js";import"./arrow-left-D7uSaoui.js";import"./arrow-right-ppbF7pdL.js";import"./volume-x-XxgtBFAk.js";import"./cpu-DKUTSiM6.js";import"./chevron-right-C5Bwh-pi.js";import"./package-check-DTo_d_fV.js";import"./circle-x-C3cp_uUG.js";import"./clock-C2p1W8t_.js";import"./eye-DFTW9LgU.js";import"./history-DwxW46W2.js";import"./laptop-4MvXsn9a.js";import"./life-buoy-Dp8WZVPZ.js";import"./monitor-D3HAv5OI.js";import"./plug-zap-DBc2I2nl.js";import"./plus-BS97ruWX.js";import"./refresh-cw-uZCpQ3HL.js";import"./shield-alert-CbKkVKKv.js";import"./shopping-cart-BwnhpZLS.js";import"./tv-C9w1fruU.js";import"./wrench-ckOvx86t.js";const b=[{num:1,title:"Introductie & Context",desc:"Wie is NWB, waarom Odoo, doel document",content:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("p",{children:[e.jsx("strong",{children:"Nextwave Business (NWB)"})," levert zakelijke IT-diensten (connectiviteit, cloud, telecom, security) aan het MKB in de Benelux."]}),e.jsx("p",{children:"De huidige webapp fungeert als het centrale platform met drie onderdelen:"}),e.jsxs("ul",{className:"list-disc pl-6 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Publieke website"})," â marketing, product-pagina's, blog, configurator"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Admin Portaal"})," â volledig beheer van klanten, assets, offertes, facturatie, tickets, licenties, CMS en meer"]}),e.jsxs("li",{children:[e.jsx("strong",{children:'"Mijn Nextwave" Klantenportaal'})," â klanten bekijken facturen, tickets, licenties en bedrijfsgegevens"]})]}),e.jsxs("p",{children:["Odoo wordt geïntroduceerd als ",e.jsx("strong",{children:"master-ERP"})," voor financiële processen (facturatie, boekhouding, CRM) en als bron van waarheid voor klantnummers, facturen en abonnementen. Dit document beschrijft de volledige technische voorbereiding."]})]})},{num:2,title:"Platformoverzicht",desc:"Website, admin portaal, klantenportaal",content:e.jsxs("div",{className:"space-y-3",children:[e.jsx("h4",{className:"font-semibold",children:"Publieke Website"}),e.jsx("p",{children:"React SPA met dynamische CMS-pagina's, productcatalogus, configurator, blog en SEO-optimalisatie. Content wordt beheerd via het admin portaal met een TipTap rich-text editor."}),e.jsx("h4",{className:"font-semibold",children:"Admin Portaal"}),e.jsx("p",{children:"Volledig beheerdashboard met modules voor:"}),e.jsxs("ul",{className:"list-disc pl-6 space-y-1",children:[e.jsx("li",{children:"Klantbeheer (CRUD, klantnummers, contactpersonen)"}),e.jsx("li",{children:"Offertes & Orders (PDF-generatie, digitale ondertekening)"}),e.jsx("li",{children:"Asset Management (serienummers, IP-adressen, garantie)"}),e.jsx("li",{children:"Facturatie & Abonnementen"}),e.jsx("li",{children:"Ticketing (met SLA-tracking)"}),e.jsx("li",{children:"Licentie-provisioning"}),e.jsx("li",{children:"CMS, Blog, CTA-management"}),e.jsx("li",{children:"Odoo-integratie instellingen"})]}),e.jsx("h4",{className:"font-semibold",children:'Klantenportaal "Mijn Nextwave"'}),e.jsx("p",{children:"Klanten loggen in en zien hun facturen, openstaande tickets, actieve licenties en bedrijfsgegevens. Facturen worden opgehaald uit Odoo via de Edge Functions."})]})},{num:3,title:"Architectuur & Technische Stack",desc:"React, Supabase, Edge Functions, XML-RPC",content:e.jsxs("div",{className:"space-y-3",children:[e.jsx("table",{className:"w-full text-sm border-collapse",children:e.jsx("tbody",{children:[["Frontend","React 18 + Vite + TypeScript + Tailwind CSS + shadcn/ui"],["Backend","Supabase (PostgreSQL, Auth, Edge Functions, Storage, Realtime)"],["State Management","TanStack Query (React Query) + Zustand"],["Odoo Communicatie","XML-RPC via Deno Edge Functions (supabase/functions/)"],["Authenticatie","Supabase Auth met RBAC (user_roles tabel)"],["Hosting","Lovable Cloud (SPA) + Supabase Cloud (backend)"]].map(([s,t])=>e.jsxs("tr",{className:"border-b border-muted",children:[e.jsx("td",{className:"py-2 pr-4 font-medium whitespace-nowrap",children:s}),e.jsx("td",{className:"py-2",children:t})]},s))})}),e.jsxs("p",{children:["Alle Odoo-communicatie verloopt via ",e.jsx("strong",{children:"Supabase Edge Functions"})," die als proxy fungeren. De frontend roept nooit direct Odoo aan. Edge Functions gebruiken een gedeelde ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"OdooClient"})," klasse (",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"supabase/functions/_shared/odoo-client.ts"}),") die XML-RPC calls abstraheert."]})]})},{num:4,title:"Odoo als Master-systeem",desc:"Welk systeem is master voor welke data",content:e.jsx("div",{className:"space-y-3",children:e.jsxs("table",{className:"w-full text-sm border-collapse",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b-2 border-muted",children:[e.jsx("th",{className:"py-2 text-left",children:"Entiteit"}),e.jsx("th",{className:"py-2 text-left",children:"Master"}),e.jsx("th",{className:"py-2 text-left",children:"Toelichting"})]})}),e.jsx("tbody",{children:[["Klantnummer","Odoo","ref veld in res.partner, gesynchroniseerd naar customers.customer_number"],["Facturen","Odoo","account.move â altijd uit Odoo, nooit aangemaakt in webapp"],["Abonnementen","Odoo","sale.subscription â webapp leest, Odoo beheert"],["Klantgegevens","Webapp (CRUD)","Initieel aangemaakt in webapp, gesynchroniseerd naar Odoo res.partner"],["Assets","Webapp","Beheerd in webapp, optioneel gesynchroniseerd naar Odoo product.product"],["Offertes","Webapp","Aangemaakt in webapp, gesynchroniseerd als sale.order in Odoo"],["Tickets","Webapp","Helpdesk in webapp, gesynchroniseerd naar helpdesk.ticket"],["Leads/CRM","Bidirectioneel","Leads kunnen in beide systemen ontstaan"]].map(([s,t,n])=>e.jsxs("tr",{className:"border-b border-muted",children:[e.jsx("td",{className:"py-2 pr-3 font-medium",children:s}),e.jsx("td",{className:"py-2 pr-3",children:t}),e.jsx("td",{className:"py-2 text-muted-foreground",children:n})]},s))})]})})},{num:5,title:"Klantbeheer & Klantnummers",desc:"Klantnummers uit Odoo, ref veld, sync flow",content:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("p",{children:["Klantnummers worden door Odoo gegenereerd en zijn het ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"ref"})," veld op ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"res.partner"}),". De sync flow:"]}),e.jsxs("ol",{className:"list-decimal pl-6 space-y-1",children:[e.jsxs("li",{children:["Klant wordt aangemaakt in de webapp (",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"customers"})," tabel)"]}),e.jsxs("li",{children:["Edge Function ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"odoo-sync-customer"})," maakt een ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"res.partner"})," aan in Odoo"]}),e.jsxs("li",{children:["Odoo genereert een klantnummer en retourneert het ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"partner_id"})]}),e.jsxs("li",{children:["Webapp slaat ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"odoo_partner_id"})," en ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"customer_number"})," op in de customers tabel"]})]}),e.jsx("h4",{className:"font-semibold",children:"Relevante database kolommen"}),e.jsxs("ul",{className:"list-disc pl-6 space-y-1 text-sm",children:[e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"customers.odoo_partner_id"})," â integer, Odoo res.partner ID"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"customers.customer_number"})," â string, Odoo ref veld"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"customers.odoo_sync_status"})," â enum: pending, synced, error"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"customers.last_synced_to_odoo"})," â timestamp"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"customers.odoo_sync_error"})," â foutmelding bij falen"]})]})]})},{num:6,title:"Facturatie",desc:"Facturen altijd uit Odoo, PDF, payment_state",content:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("p",{children:["Facturen worden ",e.jsx("strong",{children:"altijd aangemaakt en beheerd in Odoo"})," (",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"account.move"}),"). De webapp leest deze via de Edge Functions en toont ze in zowel het admin portaal als het klantenportaal."]}),e.jsx("h4",{className:"font-semibold",children:"Dataflow"}),e.jsxs("ul",{className:"list-disc pl-6 space-y-1",children:[e.jsxs("li",{children:["Facturen worden opgehaald via XML-RPC ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"search_read"})," op ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"account.move"})]}),e.jsx("li",{children:"PDF wordt opgehaald via Odoo's report engine en beschikbaar gemaakt via download-proxy"}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"payment_state"})," wordt gesynchroniseerd voor betalingsstatus"]}),e.jsx("li",{children:'Klanten zien in "Mijn Nextwave" hun facturen met status, bedrag en download-l
1ink'})]}),e.jsx("h4",{className:"font-semibold",children:"Veldmapping account.move"}),e.jsxs("table",{className:"w-full text-sm border-collapse",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b-2 border-muted",children:[e.jsx("th",{className:"py-1 text-left",children:"Odoo veld"}),e.jsx("th",{className:"py-1 text-left",children:"Webapp"})]})}),e.jsx("tbody",{children:[["name","Factuurnummer"],["invoice_date","Factuurdatum"],["invoice_date_due","Vervaldatum"],["amount_total","Totaalbedrag"],["amount_residual","Openstaand bedrag"],["payment_state","Betalingsstatus"],["state","Factuurstatus (draft/posted/cancel)"]].map(([s,t])=>e.jsxs("tr",{className:"border-b border-muted",children:[e.jsx("td",{className:"py-1 pr-3 font-mono text-xs",children:s}),e.jsx("td",{className:"py-1",children:t})]},s))})]})]})},{num:7,title:'Klantenportaal "Mijn Nextwave"',desc:"Wat klanten zien: facturen, tickets, licenties",content:e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{children:"Het klantenportaal biedt ingelogde klanten inzicht in hun gegevens:"}),e.jsxs("ul",{className:"list-disc pl-6 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Facturen"})," â Overzicht van alle facturen uit Odoo, met PDF-download en betalingsstatus"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Tickets"})," â Openstaande en gesloten supporttickets, inclusief SLA-informatie"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Licenties"})," â Actieve softwarelicenties met verloopdatum en activatiestatus"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Bedrijfsgegevens"})," â Adres, contactpersonen, klantnummer"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Abonnementen"})," â Actieve diensten en contracten vanuit Odoo"]})]}),e.jsxs("p",{children:["Klantdata wordt gefilterd op basis van de ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"customer_id"})," koppeling in ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"company_users"}),". RLS-policies zorgen dat klanten alleen hun eigen data zien."]})]})},{num:8,title:"Edge Functions â API-laag",desc:"15 edge functions, richting, Odoo modellen",content:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("p",{children:["Alle Odoo-communicatie verloopt via Supabase Edge Functions in ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"supabase/functions/"}),". Overzicht:"]}),e.jsxs("table",{className:"w-full text-sm border-collapse",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b-2 border-muted",children:[e.jsx("th",{className:"py-1 text-left",children:"Edge Function"}),e.jsx("th",{className:"py-1 text-left",children:"Richting"}),e.jsx("th",{className:"py-1 text-left",children:"Odoo Model"})]})}),e.jsx("tbody",{children:[["odoo-sync-customer","Webapp â Odoo","res.partner"],["odoo-sync-quote","Webapp â Odoo","sale.order"],["odoo-sync-sale-order","Webapp â Odoo","sale.order"],["odoo-sync-subscription","Webapp â Odoo","sale.subscription"],["odoo-sync-asset","Webapp â Odoo","product.product"],["odoo-sync-repair","Webapp â Odoo","repair.order"],["odoo-sync-lead","Webapp â Odoo","crm.lead"],["odoo-sync-purchase-order","Webapp â Odoo","purchase.order"],["odoo-sync-worker","Bidirectioneel","Queue dispatcher"],["odoo-test-connection","Webapp â Odoo","res.partner (test)"],["odoo-webhook-handler","Odoo â Webapp","Inbound events"],["odoo-crm-pipeline","Odoo â Webapp","crm.lead"],["odoo-cta-data","Odoo â Webapp","Diverse"],["odoo-customer-revenue","Odoo â Webapp","account.move"],["odoo-storefront-products","Odoo â Webapp","product.template"]].map(([s,t,n])=>e.jsxs("tr",{className:"border-b border-muted",children:[e.jsx("td",{className:"py-1 pr-3 font-mono text-xs",children:s}),e.jsx("td",{className:"py-1 pr-3",children:t}),e.jsx("td",{className:"py-1 font-mono text-xs",children:n})]},s))})]}),e.jsxs("p",{className:"text-muted-foreground",children:["Alle functies gebruiken de gedeelde ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"OdooClient"})," klasse in ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"_shared/odoo-client.ts"})," voor XML-RPC communicatie."]})]})},{num:9,title:"Veldmappings per Module",desc:"8 modules met webappâOdoo veldmapping",content:e.jsxs("div",{className:"space-y-4",children:[e.jsx("p",{children:"Elke module heeft een specifieke veldmapping tussen de webapp en Odoo. Hieronder de belangrijkste:"}),e.jsx("h4",{className:"font-semibold",children:"Klanten (res.partner)"}),e.jsx("div",{className:"text-sm grid grid-cols-2 gap-x-4 gap-y-1",children:[["company_name","name"],["email","email"],["phone","phone"],["street + house_number","street"],["city","city"],["postal_code","zip"],["country","country_id"],["vat_number","vat"],["customer_number","ref"]].map(([s,t])=>e.jsxs(d.Fragment,{children:[e.jsx("span",{className:"font-mono text-xs",children:s}),e.jsxs("span",{className:"font-mono text-xs text-muted-foreground",children:["â ",t]})]},s))}),e.jsx("h4",{className:"font-semibold",children:"Offertes / Orders (sale.order)"}),e.jsx("div",{className:"text-sm grid grid-cols-2 gap-x-4 gap-y-1",children:[["quote_number","name"],["customer.odoo_partner_id","partner_id"],["valid_until","validity_date"],["total_amount","amount_total"],["items[]","order_line[]"]].map(([s,t])=>e.jsxs(d.Fragment,{children:[e.jsx("span",{className:"font-mono text-xs",children:s}),e.jsxs("span",{className:"font-mono text-xs text-muted-foreground",children:["â ",t]})]},s))}),e.jsx("h4",{className:"font-semibold",children:"Assets (product.product)"}),e.jsx("div",{className:"text-sm grid grid-cols-2 gap-x-4 gap-y-1",children:[["product_name","name"],["serial_number","serial_number (lot)"],["product_sku","default_code"],["mac_address","x_mac_address (custom)"],["
1ip_address","x_ip_address (custom)"]].map(([s,t])=>e.jsxs(d.Fragment,{children:[e.jsx("span",{className:"font-mono text-xs",children:s}),e.jsxs("span",{className:"font-mono text-xs text-muted-foreground",children:["â ",t]})]},s))}),e.jsx("p",{className:"text-muted-foreground text-sm",children:"Overige modules (leads, repairs, purchase orders, subscriptions) volgen hetzelfde patroon. Zie de individuele Edge Functions voor de volledige mapping."})]})},{num:10,title:"Database-infrastructuur",desc:"odoo_sync_queue, sync_log, odoo_* kolommen",content:e.jsxs("div",{className:"space-y-3",children:[e.jsx("h4",{className:"font-semibold",children:"Odoo-specifieke tabellen"}),e.jsxs("ul",{className:"list-disc pl-6 space-y-1 text-sm",children:[e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"odoo_sync_queue"})," â Wachtrij voor asynchrone synchronisatie-opdrachten"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"odoo_sync_log"})," â Logging van alle sync-operaties (entity_type, entity_id, odoo_id, status, request/response payloads)"]})]}),e.jsx("h4",{className:"font-semibold",children:"odoo_* kolommen op bestaande tabellen"}),e.jsx("p",{className:"text-sm",children:"De volgende tabellen hebben Odoo-gerelateerde kolommen:"}),e.jsxs("table",{className:"w-full text-sm border-collapse",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b-2 border-muted",children:[e.jsx("th",{className:"py-1 text-left",children:"Tabel"}),e.jsx("th",{className:"py-1 text-left",children:"Kolommen"})]})}),e.jsx("tbody",{children:[["customers","odoo_partner_id, customer_number, odoo_sync_status, odoo_sync_error, last_synced_to_odoo"],["assets","odoo_product_id, odoo_serial_number_id, odoo_repair_id, odoo_sync_status, odoo_sync_error"],["subscriptions","odoo_subscription_id, odoo_sync_status, last_synced_to_odoo, odoo_sync_error"],["quotes","odoo_sale_order_id, odoo_sync_status, last_synced_to_odoo, odoo_sync_error"],["tickets","odoo_ticket_id, odoo_sync_status"]].map(([s,t])=>e.jsxs("tr",{className:"border-b border-muted",children:[e.jsx("td",{className:"py-1 pr-3 font-mono text-xs font-medium",children:s}),e.jsx("td",{className:"py-1 font-mono text-xs text-muted-foreground",children:t})]},s))})]})]})},{num:11,title:"Synchronisatie-architectuur",desc:"Queue-based sync, retry, worker dispatch",content:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("p",{children:["De synchronisatie is ",e.jsx("strong",{children:"queue-based en asynchroon"}),":"]}),e.jsxs("ol",{className:"list-decimal pl-6 space-y-2 text-sm",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Trigger"})," â Bij een CRUD-operatie in de webapp wordt een record in ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"odoo_sync_queue"})," geplaatst"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Worker"})," â De ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"odoo-sync-worker"})," Edge Function pikt items op en dispatcht naar de juiste sync-functie"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Sync"})," â De specifieke Edge Function (bijv. ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"odoo-sync-customer"}),") voert de XML-RPC call uit"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Logging"})," â Resultaat wordt gelogd in ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"odoo_sync_log"})]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Update"})," â De bron-record wordt bijgewerkt met ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"odoo_sync_status"})," en eventuele Odoo IDs"]})]}),e.jsx("h4",{className:"font-semibold",children:"Retry-logica"}),e.jsxs("ul",{className:"list-disc pl-6 space-y-1 text-sm",children:[e.jsxs("li",{children:["Bij falen wordt ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"retry_count"})," verhoogd en ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"next_retry_at"})," berekend (exponential backoff)"]}),e.jsx("li",{children:"Maximaal 5 retries per item"}),e.jsxs("li",{children:["Na max retries wordt de status ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"failed"})," en verschijnt een melding in het admin dashboard"]})]})]})},{num:12,title:"Webhooks (Odoo â Webapp)",desc:"Subscription events, HMAC, endpoint",content:e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{children:"Odoo kan de webapp notificeren via webhooks. Het endpoint is:"}),e.jsx("code",{className:"block bg-muted px-3 py-2 rounded text-xs",children:"POST /functions/v1/odoo-webhook-handler"}),e.jsx("h4",{className:"font-semibold",children:"Ondersteunde events"}),e.jsxs("ul",{className:"list-disc pl-6 space-y-1 text-sm",children:[e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"subscription.created"})," â Nieuw abonnement aangemaakt"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"subscription.updated"})," â Abonnement gewijzigd"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"subscription.cancelled"})," â Abonnement opgezegd"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"invoice.created"})," â Factuur aangemaakt"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"invoice.paid"})," â Factuur betaald"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"partner.updated"})," â Klantgegevens gewijzigd"]})]}),e.jsx("h4",{className:"font-semibold",children:"Beveiliging"}),e.jsxs("p",{className:"text-sm",children:["Webhooks worden geverifieerd met HMAC-SHA256. De ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"X-Odoo-Signature"})," header wordt gevalideerd tegen een gedeeld webhook secret dat is opgeslagen in de Supabase Edge Function secrets."]})]})},{num:13,title:"Beveiliging & Credentials",desc:"Opslag credentials, wachtwoordbevestiging",content:e.jsxs("div",{className:"space-y-3",children:[e.jsx("h4",{className:"font-semibold",children:"Credential-opslag"}),e.jsxs("p",{className:"text-sm",children:["Odoo credentials (URL, database, username, API key) worden opgeslagen in de ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"site_configuration"})," tabel onder ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"api_config.odoo"}),". De API key is versleuteld en vereist wachtwoordbevestiging om te bekijken of te wijzigen."]}),e.jsx("h4",{className:"font-semibold",children:"Toegangscontrole"}),e.jsxs("ul",{className:"list-disc pl-6 space-y-1 text-sm",children:[e.jsxs("li",{children:["Alleen ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"admin"})," en ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"super_admin"})," rollen hebben toegang tot Odoo-instellingen"]}),e.jsx("li",{children:"Wachtwoordbevestiging vereist bij wijzigen van credentials"}),e.jsx("li",{children:"Test-verbinding knop verifieert connectiviteit zonder gevoelige data te tonen"}),e.jsx("li",{children:"Edge Functions lezen credentials server-side â nooit blootgesteld aan de frontend"})]}),e.jsx("h4",{className:"font-semibold",children:"Webhook Secret"}),e.jsxs("p",{className:"text-sm",children:["Het HMAC webhook secret wordt opgeslagen als Supabase Edge Function secret (",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"ODOO_WEBHOOK_SECRET"}),") en is niet toegankelijk vanuit de frontend."]})]})},{num:14,title:"Gevraagde Acties",desc:"Concrete acties voor implementatiepartner",content:e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{children:"De volgende acties worden verwacht van de Odoo implementatiepartner:"}),e.jsxs("ol",{className:"list-decimal pl-6 space-y-2 text-sm",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Odoo-instantie inrichten"})," â Installeer de benodigde modules: Sales, Subscriptions, Invoicing, Helpdesk, CRM, Inventory"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Klantnummer-sequentie configureren"})," â Stel het ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"ref"})," veld in op res.partner met een automatische sequentie (bijv. NWB-XXXX)"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Custom velden aanmaken"})," â ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"x_mac_address"}),", ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"x_ip_address"}),", ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"x_webapp_id"})," op relevante modellen"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"API-gebruiker aanmaken"})," â Dedicated gebruiker met API-key voor XML-RPC toegang met de juiste rechten"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Webhook-module installeren"})," â Configureer outbound webhooks naar ons endpoint met HMAC-ondertekening"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Productcatalogus synchroniseren"})," â Initiële import van product.template voor de webshop"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Testfase coördineren"}
1)," â Samen met NWB de sync-flows end-to-end testen per module"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Facturatieflow valideren"})," â Verifieer dat facturen correct worden opgehaald en getoond in het klantenportaal"]})]})]})}];function Q(){const s=()=>{window.open("/documents/NWB-Odoo-Technisch-Handover.docx","_blank")};return e.jsxs("div",{className:"space-y-6 max-w-4xl",children:[e.jsx("div",{className:"bg-gradient-to-r from-[#714B67] to-[#875A7B] rounded-xl p-6 text-white shadow-lg",children:e.jsx("div",{className:"flex items-center justify-between",children:e.jsxs("div",{className:"flex items-center gap-5",children:[e.jsx("div",{className:"bg-white rounded-xl p-2.5 shadow-md",children:e.jsx("img",{src:h,alt:"Odoo logo",className:"h-10 w-auto"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-semibold tracking-tight",children:"Odoo Technisch Handover"}),e.jsx("p",{className:"text-white/80 text-sm mt-0.5",children:"Document voor de Odoo implementatiepartner"})]})]})})}),e.jsxs(o,{className:"border-[#714B67]/20",children:[e.jsx(a,{className:"pb-3",children:e.jsxs(r,{className:"flex items-center gap-2 text-lg",children:[e.jsx(g,{className:"h-5 w-5 text-[#714B67]"}),"NWB-Odoo-Technisch-Handover.docx"]})}),e.jsxs(i,{className:"space-y-4",children:[e.jsx("p",{className:"text-sm text-muted-foreground",children:"Dit document beschrijft de volledige technische architectuur, voorbereide infrastructuur en verwachte integratiepunten voor de Odoo implementatie. Klik hieronder op een hoofdstuk om de inhoud direct te lezen, of download het volledige .docx bestand."}),e.jsxs("div",{className:"flex flex-wrap gap-3 text-xs text-muted-foreground",children:[e.jsx("span",{className:"bg-muted px-2 py-1 rounded",children:"14 hoofdstukken"}),e.jsx("span",{className:"bg-muted px-2 py-1 rounded",children:"12 pagina's"}),e.jsx("span",{className:"bg-muted px-2 py-1 rounded",children:"Versie 1.0"}),e.jsx("span",{className:"bg-muted px-2 py-1 rounded",children:"30 maart 2026"})]}),e.jsx("div",{className:"pt-2 flex gap-3",children:e.jsxs(c,{onClick:s,className:"bg-[#714B67] hover:bg-[#5a3c53]",children:[e.jsx(j,{className:"h-4 w-4 mr-2"}),"Download .docx"]})})]})]}),e.jsxs(o,{children:[e.jsxs(a,{className:"pb-3",children:[e.jsx(r,{className:"text-lg",children:"Inhoud van het document"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Klik op een hoofdstuk om de inhoud te lezen"})]}),e.jsx(i,{children:e.jsx(m,{type:"multiple",className:"w-full",children:b.map(t=>e.jsxs(x,{value:`ch-${t.num}`,className:"border-b border-muted",children:[e.jsx(p,{className:"hover:no-underline py-3 gap-3",children:e.jsxs("div",{className:"flex items-center gap-3 text-left",children:[e.jsx("div",{className:"h-7 w-7 rounded-lg bg-[#714B67]/10 flex items-center justify-center shrink-0",children:e.jsx("span",{className:"text-xs font-bold text-[#714B67]",children:t.num})}),e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium",children:t.title}),e.jsx("p",{className:"text-xs text-muted-foreground font-normal",children:t.desc})]})]})}),e.jsx(u,{children:e.jsx("div",{className:"pl-10 pr-2 text-sm leading-relaxed",children:t.content})})]},t.num))})})]}),e.jsxs(o,{className:"border-[#28A745]/20 bg-[#D4EDDA]/10",children:[e.jsx(a,{className:"pb-3",children:e.jsxs(r,{className:"text-lg flex items-center gap-2",children:[e.jsx(l,{className:"h-5 w-5 text-[#28A745]"}),"Wat is al voorbereid"]})}),e.jsx(i,{children:e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3 text-sm",children:["15 Edge Functions voor bidirectionele sync","Asynchrone sync queue met retry-logica","odoo_* kolommen op alle relevante tabellen","Webhook handler voor inbound Odoo events","8 modules met complete veldmappings","Beveiligde credentials met wachtwoordbevestiging","Test-verbinding knop in admin portaal","Sync status dashboard met monitoring"].map(t=>e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(l,{className:"h-4 w-4 text-[#28A745] mt-0.5 shrink-0"}),e.jsx("span",{children:t})]},t))})})]})]})}export{Q as OdooHandoverDocument};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.