Skip to content

Datamodel

Het Werkbon datamodel bestaat uit drie Custom Post Types (file, job, contact), vier ACF veldgroepen, een custom database tabel voor adressen, en zes postmeta indexes.

┌──────────────┐ file-job ┌──────────────┐
│ FILE │ ──────────────→ │ JOB │
│ (werkbon) │ │ (melding) │
└──────┬───────┘ └──────┬───────┘
│ file-contact │ job-contact
└──────────┐ ┌──────────────────┘
▼ ▼
┌──────────────┐
│ CONTACT │ ←── moneybird_id ──→ Moneybird
└──────────────┘
┌────┘
┌──────────────┐
│ USER │ ←── file-employer / job-employer
│ (monteur) │
└──────────────┘
register_post_type('file', [
'label' => 'Werkbonnen',
'public' => true,
'publicly_queryable' => false,
'show_in_rest' => true,
'supports' => ['title', 'custom-fields'],
'has_archive' => false,
'exclude_from_search' => true,
]);
register_post_type('job', [
'label' => 'Meldingen',
'public' => true,
'publicly_queryable' => false,
'show_in_rest' => true,
'supports' => ['title', 'custom-fields'],
'taxonomies' => ['category'],
'delete_with_user' => false,
]);
register_post_type('contact', [
'label' => 'Contacten',
'public' => true,
'publicly_queryable' => false,
'show_in_rest' => true,
'supports' => ['title', 'custom-fields'],
'delete_with_user' => false,
]);
VeldnaamTypeFormat / OptiesToelichting
file-datedate_pickerDisplay: d/m/Y, Return: YmdDatum werkbon
file-firstnametextVoornaam klant
file-lastnametextAchternaam klant
file-companytextBedrijfsnaam
file-emailemailEmail klant
file-phonetextTelefoon (E.164 na normalisatie)
file-zipcodetextPostcode (1234AB)
file-streetnametextStraatnaam
file-numbertextHuisnummer
file-additiontextToevoeging
file-citytextStad
file-descriptiontextLeesbare werkzaamheden
file-description-jsontextareaJSON array werkzaamheden
file-typeselectmaasdelta, particulier, zakelijkKlanttype
file-employeruser (relationship)MultipleMonteur(s)
file-materialtextareaLeesbaar: “qty x label” per regel
file-material-jsontextareaJSON: [{id, label, qty}]
file-notetextareaInterne notitie
file-contactpost_object→ contact CPTGekoppeld contact
file-jobpost_object→ job CPTGekoppelde melding
VeldnaamTypeFormat / OptiesToelichting
job-statusselect0=Openstaand, 1=In behandeling, 2=AfgerondStatus
job-typeselectmaasdelta, particulier, zakelijkKlanttype
job-datedate_pickerDisplay: d/m/Y, Return: YmdPlanningsdatum (optioneel)
job-firstnametextVoornaam
job-lastnametextAchternaam
job-companytextBedrijfsnaam
job-emailemailEmail
job-phonetextTelefoon
job-zipcodetextPostcode
job-streetnametextStraatnaam
job-numbertextHuisnummer
job-additiontextToevoeging
job-citytextStad
job-descriptiontextareaBeschrijving
job-notetextareaInterne notitie
job-employeruser (relationship)MultipleMonteur(s)
job-contactpost_object→ contact CPTGekoppeld contact
job-address-customtrue/falseCustom adres vlag
VeldnaamTypeFormat / OptiesToelichting
contact-typeselectmaasdelta, particulier, zakelijkKlanttype
contact-firstnametextVoornaam
contact-lastnametextAchternaam
contact-companytextBedrijfsnaam
contact-emailemailEmail
contact-phonetextTelefoon
contact-zipcodetextPostcode
contact-streetnametextStraatnaam
contact-numbertextHuisnummer
contact-additiontextToevoeging
contact-citytextStad
contact-notetextareaNotitie
moneybird_idtextMoneybird koppeling ID
VeldnaamTypeFormat / OptiesToelichting
img-profileimageReturn: arrayProfielfoto
employertrue/falseMonteur markering
admintrue/falseAdmin rechten (delete meldingen)

Zie Werkbon Formulier voor details per veld.

KeyField IDACF Meta KeyType
job_id58file-jobhidden
date49file-datedate
streetname17file-streetnamehidden (synced)
number52file-numberhidden (synced)
addition19file-additionhidden (synced)
zipcode20file-zipcodehidden (synced)
city21file-cityhidden (synced)
description5file-descriptiontextarea
description_json53file-description-jsonhidden
material37file-materialtextarea
material_json55file-material-jsonhidden
company68file-companytext
firstname69file-firstnametext
lastname70file-lastnametext
email71file-emailemail
phone82file-phonetext
contact_id78file-contacthidden
type74file-typeselect
employer10file-employercheckbox
note28file-notetextarea

Zie Melding Formulier voor details per veld.

KeyField IDACF Meta KeyType
job_id77hidden (update detect)
date49job-datedate
streetname17job-streetnamehidden (synced)
number52job-numberhidden (synced)
addition19job-additionhidden (synced)
zipcode20job-zipcodehidden (synced)
city21job-cityhidden (synced)
description28job-descriptiontextarea
company70job-companytext
firstname67job-firstnametext
lastname68job-lastnametext
email69job-emailemail
phone83job-phonetext
contact_id79job-contacthidden
type76job-typeselect
status61job-statusselect
employer10job-employercheckbox
KolomTypeConstraintToelichting
idbigint(20)PK, AUTO_INCREMENT
streetnamevarchar(255)NOT NULLStraatnaam
house_numbervarchar(20)NOT NULLHuisnummer
additionvarchar(20)DEFAULT ”Toevoeging
zipcodevarchar(10)NOT NULLPostcode (1234AB)
cityvarchar(100)NOT NULLStad
is_maasdeltatinyint(1)DEFAULT 0Maasdelta woningcorporatie
NaamKolommenType
addr_unique(zipcode, house_number, addition)UNIQUE
idx_zipcode(zipcode)INDEX
idx_streetname(streetname(50))INDEX
idx_street_city_md(streetname(50), city(30), is_maasdelta)INDEX

Zie Adressysteem voor lookup functies.

Index NaamMeta KeyIndex Type
idx_moneybird_idmoneybird_id(meta_key, meta_value(100))
idx_contact_zipcodecontact-zipcode(meta_key, meta_value(100))
idx_contact_streetnamecontact-streetname(meta_key, meta_value(100))
idx_contact_emailcontact-email(meta_key, meta_value(100))
idx_contact_typecontact-type(meta_key, meta_value(100))
idx_contact_citycontact-city(meta_key, meta_value(100))

Beheerbaar via Admin > Tools > DB Optimalisatie. Zie Admin Interface.

Werkzaamheden JSON (file-description-json)

Section titled “Werkzaamheden JSON (file-description-json)”
[
{
"index": 0,
"main": "Riool ontstopt met hogedruk",
"date_display": "05-04-2026",
"extra_display": "1,5 uur",
"locations": ["Keuken", "Badkamer"],
"works": ["Hogedruk reinigen"],
"extra_time": "1.5",
"extra_date": "2026-04-05"
}
]
Keuken, Badkamer: Riool ontstopt met hogedruk (05-04-2026 - 1,5 uur)
[
{"id": "mat_001", "label": "PVC Buis 110mm", "qty": 2},
{"id": "mat_042", "label": "Ontstopperveer 16mm", "qty": 1}
]
2 x PVC Buis 110mm
1 x Ontstopperveer 16mm

Adresvelden in formulieren werken via een drielaags systeem:

Laag 1: Select2 dropdowns (zichtbaar voor gebruiker)
├─ Field 23: Straat select → AJAX search, min 2 chars
├─ Field 24: Nummer select → Gevuld na straat keuze
└─ Field 25: Toevoeging select → Gevuld na nummer keuze
Laag 2: Auto-filled readonly velden
├─ Field 26: Postcode → Auto na nummer/toevoeging
└─ Field 27: Stad → Auto na nummer/toevoeging
Laag 3: Hidden sync velden (opgeslagen naar ACF)
├─ Field 17 → file-streetname / job-streetname
├─ Field 52 → file-number / job-number
├─ Field 19 → file-addition / job-addition
├─ Field 20 → file-zipcode / job-zipcode
└─ Field 21 → file-city / job-city
Fallback: Custom address (field 22 toggle)
├─ Field 2 → Vrij straatveld
├─ Field 15 → Vrij nummerveld
├─ Field 14 → Vrij toevoegingveld
├─ Field 13 → Vrij postcodeveld
└─ Field 4 → Vrij stadsveld

De sync van Laag 1 → Laag 3 wordt afgehandeld door wpforms-werkbon.js. Zie Adressysteem voor de Select2 AJAX chain.

Beide formulieren genereren dezelfde titel structuur:

$date_str = wp_date('Ymd');
$address_parts = array_filter([$street, $number, $addition]);
$address = implode(' ', $address_parts);
if ($city) $address .= ', ' . $city;
$title = $address
? ($date_str . ': ' . $address) // "20260405: Hoofdstraat 42 A, Rotterdam"
: ($date_str . ': Werkbon'); // "20260405: Werkbon" (fallback)

Centrale functie die form velden naar ACF meta schrijft:

function beam_save_form_values_to_meta(
int $form_id,
int $post_id,
array $fields,
array $exclude = []
): void {
$map = beam_get_field_map($form_id);
foreach ($map as $key => $config) {
if (in_array($key, $exclude, true)) continue;
if ($config['meta_key'] === null) continue; // Geen meta key = niet opslaan
$value = beam_get_wpforms_field_value($fields, $config['field_id']);
if ($value === '') continue;
beam_update_post_meta($post_id, $config['meta_key'], $value);
}
}

Velden met meta_key: null (Select2 bronvelden, auto-filled velden, custom address velden) worden overgeslagen — alleen de hidden sync velden worden opgeslagen.

MechanismeDoelImplementatie
Contact upsert cacheVoorkomt dubbele contacten in 1 requestStatic variabele per form:entry
Webhook debounceVoorkomt dubbele Make.com callsPost meta timestamp (3 sec window)
Employer array parsingVoorkomt string i.p.v. arrayFilter op wpforms_post_submissions_process_meta
Placeholder filteringVoorkomt “Selecteer…” als waardeFilter op wpforms_process_filter (priority 5)
Postcode normalisatieConsistente opslagstrtoupper + spaties verwijderen
Email normalisatieConsistente matchingstrtolower + trim

In de nieuwe Beam stack worden alle CPTs en ACF velden vervangen door Supabase tabellen (contacts, jobs, work_orders, activity_log). Belangrijke verbeteringen:

  • Geen data duplicatie: contact/adres data alleen op contact, rest via FK
  • Contact overerving: werkbon erft contact van melding, kan optioneel overschrijven
  • Adres overerving: melding erft adres van contact, kan optioneel overschrijven
  • Activity log: elke wijziging wordt gelogd (wie, wanneer, wat)
  • Uitgebreid status model: 7 statussen i.p.v. 3
  • Auto-save draft: werkbonnen als concept opslaan

Zie Migratie naar Beam Stack voor het volledige schema, RLS policies, en de contact/adres overerving patronen.

BestandLocatieFunctie
posttypes.phpfunctions/CPT registratie
wpforms-field-mappings.phpfunctions/Centrale field ID ↔ ACF key mapping
wpforms-create-file-post.phpfunctions/FILE CPT aanmaken + save meta
wpforms-update-job-post-submission.phpfunctions/JOB CPT aanmaken/updaten
wpforms-add-or-update-contact.phpfunctions/Contact upsert + matching
db-addresses.phpfunctions/Address tabel management
db-optimize.phpfunctions/Postmeta indexes
group_5c7027cd8e1d8.jsonacf-json/Werkbonnen veldgroep
group_693b783dd764b.jsonacf-json/Meldingen veldgroep
group_694e8176243b2.jsonacf-json/Contact veldgroep
group_5e14d7eb790f3.jsonacf-json/Gebruiker veldgroep
CodeLabelKleurBeschrijving
0OpenstaandRoodNieuwe melding
1In behandelingOranjeToegewezen/in uitvoering
2AfgerondGroenWerkbon ingevuld
CodeLabelMoneybird SyncSpeciale Regels
maasdeltaMaasdeltaNeeAuto-bepaald op adres
particulierParticulierJaStandaard
zakelijkZakelijkJaCompany veld gevuld