Teknisk dokumentation

Datamodel

Penly CRM er bygget i PHP Laravel med én central MySQL-database (8.4.11): 35 tabeller i 10 moduler og 69 fremmednøgler. Siden er genereret direkte fra databasens skema, så den altid passer med systemet.

PK = primærnøgle · FK = fremmednøgle (klik for at gå til tabellen) · fed = påkrævet felt. Tabellerne er InnoDB med utf8mb4 og dansk sortering. Filer gemmes krypteret (AES-256-GCM) uden for databasen; document_versions peger på dem. Alle vigtige handlinger skrives i audit_log.

Brugere og adgang

erDiagram
  users ||--o{ api_tokens : "user_id"
  users ||--o{ crm_sessions : "user_id"

users

Medarbejdere med login. En bruger kan have flere roller.

PKidbigint
usernamevarchar(60) · unik
namevarchar(120)
emailvarchar(160)
phonevarchar(40)
titlevarchar(120)
teamvarchar(80)
rolesvarchar(160)kommasepareret: admin,management,advisor,sales,compliance,finance
pass_hashvarchar(255)
activetinyint(1)standard 1
ics_tokenvarchar(40) · unik
last_login_atdatetime
created_atdatetime

crm_sessions

Aktive login-sessioner. Kun en hash af cookien gemmes.

PKtokenvarchar(64)sha256 af cookie-værdien
FKuser_idbigint → users
created_atdatetime
expires_atdatetime

api_tokens

Nøgler til REST API for Penlys egne platforme. Kun hash gemmes.

PKidbigint
FKuser_idbigint → users
namevarchar(120)
token_hashvarchar(64) · unik
last_used_atdatetime
created_atdatetime

Kunder (CRM)

erDiagram
  companies ||--o{ activities : "company_id"
  customers ||--o{ activities : "customer_id"
  users ||--o{ activities : "user_id"
  users ||--o{ companies : "advisor_id"
  companies ||--o{ contacts : "company_id"
  customers ||--o{ customer_relations : "customer_id"
  customers ||--o{ customer_relations : "related_customer_id"
  users ||--o{ customers : "advisor_id"
  companies ||--o{ customers : "employer_company_id"

companies

Erhvervskunder, fx virksomheder med firmapensionsordning.

PKidbigint
namevarchar(160)
cvrvarchar(12)
industryvarchar(80)
employeesint
addressvarchar(160)
cityvarchar(80)
phonevarchar(40)
emailvarchar(160)
segmentvarchar(40)
FKadvisor_idbigint → users
statusvarchar(20)standard aktiv
notestext
created_atdatetime

customers

Privatkunder. Kan være ansat i en erhvervskunde.

PKidbigint
first_namevarchar(80)
last_namevarchar(80)
birth_datedate
cpr_last4varchar(4)kun de sidste 4 cifre gemmes i demoen
emailvarchar(160)
phonevarchar(40)
addressvarchar(160)
cityvarchar(80)
zipvarchar(10)
civil_statusvarchar(30)
occupationvarchar(80)
annual_incomeint
FKemployer_company_idbigint → companies
FKadvisor_idbigint → users
segmentvarchar(40)Privat, Privat+, Firmapension, Selvstændig
statusvarchar(20)aktiv, prospekt, inaktiv, slettet
sourcevarchar(40)
review_interval_monthstinyintstandard 12
next_review_datedate
created_atdatetime

contacts

Kontaktpersoner hos en erhvervskunde (HR, ejer, bogholder).

PKidbigint
FKcompany_idbigint → companies
namevarchar(120)
titlevarchar(80)
emailvarchar(160)
phonevarchar(40)
is_primarytinyint(1)standard 0
created_atdatetime

customer_relations

Relationer mellem kunder, fx ægtefælle.

PKidbigint
FKcustomer_idbigint → customers
FKrelated_customer_idbigint → customers
relationvarchar(30)ægtefælle, samlever, barn, forælder

activities

Al kommunikation og historik, som udgør kundens tidslinje.

PKidbigint
FKcustomer_idbigint → customers
FKcompany_idbigint → companies
FKuser_idbigint → users
typevarchar(20)note, opkald, møde, email, sms, system
subjectvarchar(255)
bodytext
atdatetime

Salg

erDiagram
  customers ||--o{ leads : "customer_id"
  users ||--o{ leads : "owner_id"
  companies ||--o{ meetings : "company_id"
  customers ||--o{ meetings : "customer_id"
  opportunities ||--o{ meetings : "opportunity_id"
  users ||--o{ meetings : "user_id"
  companies ||--o{ opportunities : "company_id"
  customers ||--o{ opportunities : "customer_id"
  leads ||--o{ opportunities : "lead_id"
  users ||--o{ opportunities : "owner_id"
  pipeline_stages ||--o{ opportunities : "stage_id"

leads

Henvendelser fra hjemmeside, beregner, henvisning m.m.

PKidbigint
namevarchar(120)
emailvarchar(160)
phonevarchar(40)
company_namevarchar(160)
sourcevarchar(40)hjemmeside, omkostningsberegner, henvisning, kampagne, event, telefon
interestvarchar(80)
statusvarchar(20)ny, kontaktet, kvalificeret, diskvalificeret, konverteret
scoretinyintstandard 0
FKowner_idbigint → users
FKcustomer_idbigint → customers
notestext
created_atdatetime
converted_atdatetime
Bruges af: opportunities

pipeline_stages

Konfigurerbare salgsfaser med sandsynlighed.

PKidbigint
namevarchar(60)
positionsmallint
probabilitytinyint
is_wontinyint(1)standard 0
is_losttinyint(1)standard 0
Bruges af: opportunities

opportunities

Salgsmuligheder med værdi, fase og forventet lukkedato.

PKidbigint
titlevarchar(200)
FKcustomer_idbigint → customers
FKcompany_idbigint → companies
FKlead_idbigint → leads
FKstage_idbigint → pipeline_stages
FKowner_idbigint → users
productvarchar(80)
valueintforventet årlig indbetaling / provisionsgrundlag i kr
expected_closedate
proposal_statusvarchar(20)ingen, udkast, sendt, accepteret, afvist
lost_reasonvarchar(160)
created_atdatetime
closed_atdatetime
Bruges af: commissions, meetings

meetings

Møder i kalenderen, koblet til kunde og salgsmulighed.

PKidbigint
titlevarchar(200)
FKcustomer_idbigint → customers
FKcompany_idbigint → companies
FKopportunity_idbigint → opportunities
FKuser_idbigint → users
start_atdatetime
end_atdatetime
locationvarchar(30)kontor, video, telefon, hos kunden
statusvarchar(20)planlagt, afholdt, aflyst
notestext
created_atdatetime

Pension

erDiagram
  providers ||--o{ pension_products : "provider_id"
  companies ||--o{ pension_schemes : "company_id"
  customers ||--o{ pension_schemes : "customer_id"
  pension_products ||--o{ pension_schemes : "product_id"
  providers ||--o{ pension_schemes : "provider_id"
  pension_schemes ||--o{ scheme_history : "scheme_id"
  users ||--o{ scheme_history : "user_id"

pension_products

Produkter hos udbyderne.

PKidbigint
FKprovider_idbigint → providers
namevarchar(120)
kindvarchar(30)ratepension, livrente, aldersopsparing, kapitalpension, forsikring, frie midler
Bruges af: pension_schemes

pension_schemes

Kundens pensionsordninger med depot, indbetaling og dækninger.

PKidbigint
FKcustomer_idbigint → customers
FKcompany_idbigint → companies
FKprovider_idbigint → providers
FKproduct_idbigint → pension_products
policy_novarchar(40)
kindvarchar(30)
scheme_typevarchar(10)privat, firma
statusvarchar(20)aktiv, hvilende, under flytning, ophørt
start_datedate
renewal_datedate
balancebigintstandard 0
monthly_contributionintstandard 0
employer_pctdouble
employee_pctdouble
annual_cost_pctdouble
investment_profilevarchar(40)
cover_deathint
cover_disabilityint
cover_critical_illnessint
sourcevarchar(20)manuel, pensionsinfo, udbyder-api, udtræk
updated_atdatetime
created_atdatetime

scheme_history

Historik over ændringer i en pensionsordning (felt for felt).

PKidbigint
FKscheme_idbigint → pension_schemes
FKuser_idbigint → users
fieldvarchar(40)
old_valuevarchar(255)
new_valuevarchar(255)
atdatetime

Rådgivning

erDiagram
  users ||--o{ advisory_cases : "advisor_id"
  users ||--o{ advisory_cases : "approved_by"
  customers ||--o{ advisory_cases : "customer_id"
  advisory_cases ||--o{ case_events : "case_id"
  users ||--o{ case_events : "user_id"

advisory_cases

Rådgivningssager med behovsanalyse, risikoprofil, anbefaling og godkendelse.

PKidbigint
FKcustomer_idbigint → customers
FKadvisor_idbigint → users
titlevarchar(200)
typevarchar(30)pensionstjek, samlet økonomi, firmapension, udbetaling, forsikring
statusvarchar(20)behovsanalyse, anbefaling, til godkendelse, godkendt, afvist, afsluttet
objectivestext
retirement_agetinyint
desired_income_pcttinyint
risk_profilevarchar(10)lav, middel, høj
risk_scoretinyint
horizon_yearstinyint
incomeint
assetsbigint
debtbigint
housingvarchar(30)
dependantstinyint
needs_jsonjsonbehovsanalyse: svar på spørgeskema
recommendationtext
rationaletext
FKapproved_bybigint → users
approved_atdatetime
approval_commenttext
follow_up_datedate
created_atdatetime
updated_atdatetime
closed_atdatetime

case_events

Statusforløb og kommentarer på en rådgivningssag.

PKidbigint
FKcase_idbigint → advisory_cases
FKuser_idbigint → users
eventvarchar(30)
commenttext
atdatetime

Dokumenter

erDiagram
  documents ||--o{ document_versions : "document_id"
  users ||--o{ document_versions : "uploaded_by"
  advisory_cases ||--o{ documents : "case_id"
  companies ||--o{ documents : "company_id"
  users ||--o{ documents : "created_by"
  customers ||--o{ documents : "customer_id"
  document_templates ||--o{ documents : "template_id"

document_templates

Skabeloner til aftaler, samtykker og rapporter.

PKidbigint
namevarchar(120)
categoryvarchar(30)
bodytexttekst med {{felter}}
versionsmallintstandard 1
updated_atdatetime
Bruges af: documents

documents

Kundedokumenter med godkendelses- og underskriftsstatus.

PKidbigint
FKcustomer_idbigint → customers
FKcompany_idbigint → companies
FKcase_idbigint → advisory_cases
FKtemplate_idbigint → document_templates
titlevarchar(200)
categoryvarchar(30)aftale, samtykke, rådgivningsrapport, pensionsoversigt, id, andet
confidentialtinyint(1)kun ansvarlig rådgiver, compliance og admin
current_versionsmallintstandard 1
approval_statusvarchar(20)ikke krævet, afventer, godkendt, afvist
signature_statusvarchar(20)ikke krævet, klar, sendt, underskrevet, afvist
signed_atdatetime
retention_untildate
extracted_jsonjson
FKcreated_bybigint → users
created_atdatetime

document_versions

Versioner af hvert dokument. Filen gemmes krypteret uden for webroden.

PKidbigint
FKdocument_idbigint → documents
versionsmallint
filenamevarchar(160)
mimevarchar(100)
sizeint
storage_keyvarchar(24)krypteret fil (AES-256-GCM) uden for webroden
sha256varchar(64)
FKuploaded_bybigint → users
atdatetime

Opgaver og automatisering

erDiagram
  users ||--o{ notifications : "user_id"
  users ||--o{ tasks : "assignee_id"
  companies ||--o{ tasks : "company_id"
  users ||--o{ tasks : "created_by"
  customers ||--o{ tasks : "customer_id"
  automation_rules ||--o{ tasks : "rule_id"

automation_rules

Regler der automatisk opretter opgaver ved hændelser.

PKidbigint
namevarchar(120)
eventvarchar(40)lead.oprettet, møde.afholdt, salg.vundet, sag.til_godkendelse, sag.godkendt, pension.fornyelse, dokument.mangler, kunde.gennemgang, samtykke.udløber
offset_dayssmallintstandard 0
task_titlevarchar(160)
assign_tovarchar(40)ejer, rådgiver, rolle:compliance ...
priorityvarchar(10)standard normal
activetinyint(1)standard 1
Bruges af: tasks

tasks

Opgaver til rådgivere og teams, manuelle eller automatiske.

PKidbigint
titlevarchar(255)
descriptiontext
FKcustomer_idbigint → customers
FKcompany_idbigint → companies
related_typevarchar(20)lead, opportunity, case, scheme, document, consent, meeting, customer
related_idbigint
FKassignee_idbigint → users
teamvarchar(80)
due_datedate
priorityvarchar(10)lav, normal, høj
statusvarchar(10)åben, færdig
recurring_monthstinyint
FKrule_idbigint → automation_rules
FKcreated_bybigint → users
created_atdatetime
completed_atdatetime

notifications

Påmindelser og varsler til brugerne.

PKidbigint
FKuser_idbigint → users
kindvarchar(20)påmindelse, forfalden, tildelt, godkendelse
textvarchar(255)
linkvarchar(160)
read_atdatetime
atdatetime

Compliance og GDPR

erDiagram
  users ||--o{ audit_log : "user_id"
  customers ||--o{ consents : "customer_id"
  documents ||--o{ consents : "document_id"
  users ||--o{ consents : "registered_by"
  customers ||--o{ deletion_requests : "customer_id"
  users ||--o{ deletion_requests : "handled_by"
  retention_policies { bigint id }

consents

Registrerede samtykker med metode, udløb og tilbagekaldelse.

PKidbigint
FKcustomer_idbigint → customers
typevarchar(40)rådgivning, pensionsinfo-indhentning, markedsføring, deling med udbyder
given_atdatetime
expires_atdate
withdrawn_atdatetime
methodvarchar(40)MitID, underskrift, mundtligt (noteret)
FKdocument_idbigint → documents
FKregistered_bybigint → users

retention_policies

Opbevaringsregler pr. datatype.

PKidbigint
data_typevarchar(80)
retention_yearstinyint
basisvarchar(160)lovhjemmel
actionvarchar(20)slet, anonymisér

deletion_requests

GDPR-anmodninger: sletning, indsigt og berigtigelse.

PKidbigint
FKcustomer_idbigint → customers
typevarchar(20)sletning, indsigt, berigtigelse
reasontext
statusvarchar(20)modtaget, under behandling, udført, afvist
requested_atdatetime
due_atdate
FKhandled_bybigint → users
handled_atdatetime
outcometext

audit_log

Revisionsspor over vigtige bruger- og datahandlinger.

PKidbigint
FKuser_idbigint → users
actionvarchar(30)login, vis, opret, ret, slet, eksport, download, godkend ...
entityvarchar(40)
entity_idbigint
detailstext
ipvarchar(45)
atdatetime

Provision

erDiagram
  providers ||--o{ commission_payments : "provider_id"
  users ||--o{ commission_payments : "registered_by"
  users ||--o{ commission_rules : "advisor_id"
  providers ||--o{ commission_rules : "provider_id"
  users ||--o{ commissions : "advisor_id"
  customers ||--o{ commissions : "customer_id"
  opportunities ||--o{ commissions : "opportunity_id"
  providers ||--o{ commissions : "provider_id"
  commission_rules ||--o{ commissions : "rule_id"
  pension_schemes ||--o{ commissions : "scheme_id"
  commissions ||--o{ payment_allocations : "commission_id"
  commission_payments ||--o{ payment_allocations : "payment_id"

commission_rules

Provisionssatser pr. udbyder, produkt og rådgiver.

PKidbigint
FKprovider_idbigint → providers
product_kindvarchar(30)null = alle produkter
FKadvisor_idbigint → users
basisvarchar(20)etablering_pct, løbende_pct, fast
ratedouble
advisor_share_pctdoublestandard 40
clawback_monthstinyintstandard 12
activetinyint(1)standard 1
Bruges af: commissions

commissions

Forventet og optjent provision pr. ordning og periode.

PKidbigint
FKcustomer_idbigint → customers
FKscheme_idbigint → pension_schemes
FKopportunity_idbigint → opportunities
FKprovider_idbigint → providers
FKadvisor_idbigint → users
FKrule_idbigint → commission_rules
periodchar(7)YYYY-MM
kindvarchar(20)etablering, løbende, justering, clawback
expected_amountint
received_amountintstandard 0
statusvarchar(20)forventet, optjent, delvist betalt, betalt, annulleret, clawback
notevarchar(255)
created_atdatetime

commission_payments

Modtagne provisionsbetalinger fra udbydere.

PKidbigint
FKprovider_idbigint → providers
referencevarchar(60)
received_atdate
amountint
statusvarchar(20)ikke afstemt, delvist afstemt, afstemt
FKregistered_bybigint → users

payment_allocations

Afstemning: hvilke provisioner en betaling dækker.

PKidbigint
FKpayment_idbigint → commission_payments
FKcommission_idbigint → commissions
amountint

Integrationer og AI

erDiagram
  customers ||--o{ ai_outputs : "customer_id"
  users ||--o{ ai_outputs : "reviewed_by"
  users ||--o{ ai_outputs : "user_id"
  users ||--o{ integration_log : "user_id"
  integrations { bigint id }

integrations

Status for MitID, e-signatur, kalender, PensionsInfo, økonomi m.m.

PKidbigint
keyvarchar(30) · unik
namevarchar(80)
categoryvarchar(40)
statusvarchar(20)forbundet, demo, ikke forbundet
descriptionvarchar(255)
last_sync_atdatetime

integration_log

Log over kald til integrationer.

PKidbigint
integration_keyvarchar(30)
FKuser_idbigint → users
actionvarchar(255)
resultvarchar(120)
atdatetime

ai_outputs

AI-genereret indhold. Skal godkendes af et menneske før brug.

PKidbigint
FKuser_idbigint → users
FKcustomer_idbigint → customers
kindvarchar(20)mødereferat, udtræk, mailudkast, næste skridt, søgning
inputtext
outputtext
statusvarchar(20)kræver gennemsyn, godkendt, afvist
FKreviewed_bybigint → users
reviewed_atdatetime
created_atdatetime