Skip to content

Adressysteem

Het adressysteem koppelt CSV-bronbestanden via een database tabel aan een client-side Select2-keten. Elke stap in de keten valideert en verrijkt het adres totdat postcode en stad automatisch ingevuld zijn.

CSV bestanden (/assets/csv/addresses/)
| import (admin trigger)
v
wp_werkbon_addresses tabel
| queries
v
6 lookup functies (PHP)
|
v
AJAX endpoint (wp_ajax_myproject_address)
| JSON responses
v
Select2 chain (JavaScript)
|
v
Formulier velden (straat -> nummer -> toevoeging -> postcode + stad)

De lookup functies opereren op een geindexeerde MySQL-tabel en vervangen de eerdere in-memory CSV-parsing. Alle adressen die afkomstig zijn uit het Maasdelta-bestand worden gemarkeerd met is_maasdelta = 1, wat stroomafwaarts het contacttype en de sync-logica beinvloedt.


Bronbestand: functions/db-addresses.php

KolomTypeDefaultBeschrijving
idBIGINT UNSIGNEDAUTO_INCREMENTPrimaire sleutel
streetnameVARCHAR(255)''Straatnaam, exact zoals in CSV
house_numberVARCHAR(20)''Huisnummer als string (bijv. "8", "10")
additionVARCHAR(20)''Toevoeging, uppercase (bijv. "A", "B")
zipcodeVARCHAR(10)''Postcode zonder spatie (bijv. "3221AJ")
cityVARCHAR(100)''Plaatsnaam
is_maasdeltaTINYINT(1)01 als geimporteerd uit maasdelta.csv
NaamTypeKolommenDoel
PRIMARYPrimaryidRij-identificatie
address_uniqueUniquezipcode, house_number, additionVoorkomt duplicaten bij import
zipcode_idxIndexzipcodeSnelle lookup op postcode
streetname_idxIndexstreetname(50)LIKE-zoekopdrachten op straatnaam
street_city_idxIndexstreetname(50), city(30), is_maasdeltaGecombineerde query op straat + stad + bron
  • De address_unique index op (zipcode, house_number, addition) garandeert dat elk fysiek adres maximaal een keer voorkomt in de tabel.
  • Bij import wordt INSERT IGNORE gebruikt: als een adres al bestaat, wordt de rij overgeslagen.
  • Omdat maasdelta.csv als eerste geimporteerd wordt, krijgen Maasdelta-adressen prioriteit bij duplicaten met andere CSV-bestanden.

Alle CSV-bestanden bevinden zich in:

/assets/csv/addresses/

Op dit moment bevat de directory de volgende bestanden:

BestandBeschrijving
maasdelta.csvMaasdelta woningen (krijgt importprioriteit)
spijkenisse.csvSpijkenisse adressen
hellevoetsluis.csvHellevoetsluis adressen
brielle.csvBrielle adressen
rozenburg.csvRozenburg adressen
rockanje.csvRockanje adressen
Overige plaatsen in de regio

Kolommen zijn komma-gescheiden. De eerste rij is een header.

Straat,Huisnummer,Huisnummer toevoeging,Postcode,Plaats
Oostkade,8,,3221 AJ,Hellevoetsluis
Oostkade,8,A,3221 AJ,Hellevoetsluis
Oostkade,10,,3221 AJ,Hellevoetsluis
KolomPositieVoorbeeldNormalisatie bij import
Straatnaam0Oostkadetrim()
Huisnummer18trim()
Toevoeging2A of leegstrtoupper(trim())
Postcode33221 AJSpatie verwijderd, uppercase: 3221AJ
Plaats4Hellevoetsluistrim()

De delimiter wordt automatisch gedetecteerd: komma of puntkomma.

werkbon_addresses_import_csv(bool $truncate_first = true): array

Gedrag:

  1. Maakt de tabel aan als deze niet bestaat (werkbon_addresses_create_table())
  2. Leegt de tabel als $truncate_first = true (standaard)
  3. Sorteert bestanden zodat maasdelta.csv als eerste verwerkt wordt
  4. Schakelt autocommit uit voor betere performance
  5. Verwerkt rijen in batches van 500 met INSERT IGNORE
  6. Zet is_maasdelta = 1 voor rijen uit maasdelta.csv, 0 voor alle overige bestanden
  7. Commit alle transacties en herstelt autocommit

Return:

[
'imported' => int, // Aantal succesvol ingevoegde rijen
'skipped' => int, // Ongeldige rijen (lege postcode, nummer of straat)
'errors' => array, // Foutmeldingen (directory niet gevonden, etc.)
]

De import is beschikbaar via het WordPress admin-menu:

Hulpmiddelen > Adressen Database (tools.php?page=werkbon-addresses)

Deze pagina toont:

  • Status: of de tabel actief is en het aantal adressen
  • Tabel aanmaken: knop om de tabel te creeren (indien niet aanwezig)
  • CSV bestanden importeren: knop om alle CSV’s opnieuw te importeren (met bevestigingsdialoog)

De tabel wordt ook automatisch aangemaakt bij theme-activatie via de after_switch_theme hook.


Alle functies zijn gedefinieerd in functions/db-addresses.php.

Zoekt een adres op basis van postcode, huisnummer en toevoeging.

werkbon_addresses_lookup(
string $zipcode,
string $house_number,
string $addition = ''
): array
ParameterTypeBeschrijving
$zipcodestringPostcode (met of zonder spatie, wordt genormaliseerd)
$house_numberstringHuisnummer
$additionstringToevoeging (optioneel, wordt naar uppercase geconverteerd)

Query: exact match op de address_unique index (zipcode + house_number + addition).

Return:

// Gevonden:
['streetname' => 'Oostkade', 'is_maasdelta' => true]
// Niet gevonden:
[]

Gebruikt door: acs_job_api_lookup_address() in api-job.php voor adresnormalisatie bij het aanmaken van werkbonnen via de REST API.


Zoekt straatnamen op basis van een zoekterm (voor de Select2 dropdown).

werkbon_addresses_search_streets(
string $search,
int $limit = 50
): array
ParameterTypeBeschrijving
$searchstringZoekterm (minimaal 2 karakters)
$limitintMaximaal aantal resultaten (standaard 50)

Query: LIKE '%zoekterm%' op streetname, gegroepeerd op streetname + city + is_maasdelta. Resultaten worden gesorteerd met Maasdelta-straten bovenaan (is_maasdelta DESC).

Return:

[
[
'streetname' => 'Oostkade',
'city' => 'Hellevoetsluis',
'is_maasdelta' => '1',
'street_key' => 'Oostkade||maasdelta',
],
[
'streetname' => 'Oostkade',
'city' => 'Hellevoetsluis',
'is_maasdelta' => '0',
'street_key' => 'Oostkade||hellevoetsluis',
],
]

Deduplicatie: de GROUP BY clause groepeert op straat + stad + bron, zodat elke unieke combinatie slechts een keer voorkomt. Een straat kan meerdere keren voorkomen als deze zowel in Maasdelta als in een plaatsbestand staat.

streetKey formaat: "Straatnaam||source" waarbij source gelijk is aan 'maasdelta' of de plaatsnaam in lowercase (bijv. 'hellevoetsluis').


Zoekt straten op exacte naam (voor resolve bij prefill).

werkbon_addresses_resolve_street(string $streetname): array
ParameterTypeBeschrijving
$streetnamestringExacte straatnaam

Query: exact match op streetname, gegroepeerd op streetname + city + is_maasdelta. Gesorteerd met Maasdelta eerst (is_maasdelta DESC).

Return:

[
[
'streetname' => 'Oostkade',
'city' => 'Hellevoetsluis',
'is_maasdelta' => '1',
'street_key' => 'Oostkade||maasdelta',
],
// ... eventuele niet-Maasdelta matches
]

Voorkeur: als zowel een Maasdelta- als een niet-Maasdelta-variant bestaat, wordt de Maasdelta-variant als eerste geretourneerd. De AJAX handler selecteert standaard de eerste match (dus Maasdelta heeft voorrang).


Haalt alle huisnummers op voor een straat + stad combinatie.

werkbon_addresses_get_numbers(
string $streetname,
string $city,
bool $is_maasdelta
): array
ParameterTypeBeschrijving
$streetnamestringStraatnaam
$citystringPlaatsnaam
$is_maasdeltaboolMaasdelta-filter

Query: SELECT DISTINCT house_number met sortering op numerieke waarde (CAST(house_number AS UNSIGNED)).

Return: array van strings, bijv. ["1", "2", "3", "8", "10"].

Let op: de AJAX handler ontvangt een streetKey en parst deze eerst naar streetname, city en is_maasdelta via myproject_parse_street_key() en myproject_get_city_for_street().


Haalt alle toevoegingen op voor een straat + nummer combinatie.

werkbon_addresses_get_additions(
string $streetname,
string $city,
string $house_number,
bool $is_maasdelta
): array
ParameterTypeBeschrijving
$streetnamestringStraatnaam
$citystringPlaatsnaam
$house_numberstringHuisnummer
$is_maasdeltaboolMaasdelta-filter

Query: SELECT DISTINCT addition waar addition != '', gesorteerd op addition.

Return: array van strings, bijv. ["A", "B", "C"]. Lege toevoegingen worden uitgefilterd.


Haalt postcode en stad op voor een volledig adres (reverse lookup).

werkbon_addresses_get_zipcode(
string $streetname,
string $city,
string $house_number,
string $addition,
bool $is_maasdelta
): array
ParameterTypeBeschrijving
$streetnamestringStraatnaam
$citystringPlaatsnaam
$house_numberstringHuisnummer
$additionstringToevoeging
$is_maasdeltaboolMaasdelta-filter

Return:

// Gevonden:
['postcode' => '3221 AJ', 'city' => 'Hellevoetsluis']
// Niet gevonden:
[]

De postcode wordt geformateerd met spatie ("3221 AJ") als het compacte formaat 6 tekens bevat met het patroon \d{4}[A-Z]{2}.


EigenschapWaarde
Actionswp_ajax_myproject_address + wp_ajax_nopriv_myproject_address
Handlermyproject_ajax_address() in functions/form-addresses.php
Noncemyproject_address_nonce (gevalideerd via wp_verify_nonce())
MethodGET
ParametersVia query string ($_GET)
ParameterTypeBeschrijving
actionstringAltijd myproject_address
noncestringNonce token
modestringEen van de 6 modes hieronder
qstringZoekterm (bij streets en resolve_street)
streetKeystringStraat-identifier (formaat `“Straat
numberstringHuisnummer
additionstringToevoeging

De handler maakt gebruik van twee helper functies om een streetKey te verwerken:

myproject_parse_street_key(string $streetKey): array

Parst "Oostkade||maasdelta" naar:

[
'streetname' => 'Oostkade',
'source' => 'maasdelta',
'is_maasdelta' => true,
]

myproject_get_city_for_street(string $streetname, string $source, bool $is_maasdelta): string

Haalt de werkelijke city waarde op uit de database. Nodig omdat de streetKey bij niet-Maasdelta-adressen de lowercase plaatsnaam als source bevat, terwijl de database de originele casing bewaard.

ModeInputOutputGebruik
streetsq (min 2 chars)[{id, text, street, source, city, suffix}]Select2 zoekresultaten
resolve_streetq (exacte naam)streetKey (string)Prefill: straat naar key
resolve_fullstreet, number, addition{exists, streetKey, streetLabel, number, addition, numbers[], additions[], postcode, city}Contact search modal: volledig adres resolven
numbersstreetKey[string]Huisnummer-select vullen
additionsstreetKey, number[string]Toevoeging-select vullen
zipcitystreetKey, number, addition{postcode, city}Postcode en stad auto-fill

Zoekt straten op naam. Resultaat bevat een suffix veld voor weergave in de dropdown:

  • Maasdelta-adres met stad: "(Maasdelta - Hellevoetsluis)"
  • Maasdelta-adres zonder stad: "(Maasdelta)"
  • Niet-Maasdelta met stad: "(Hellevoetsluis)"
{
"success": true,
"data": [
{
"id": "Oostkade||maasdelta",
"text": "Oostkade",
"street": "Oostkade",
"source": "maasdelta",
"city": "Hellevoetsluis",
"suffix": "(Maasdelta - Hellevoetsluis)"
}
]
}

Probeert een volledig adres te resolven. Wordt gebruikt door de contactzoek-modal. Als het nummer niet gevonden wordt, retourneert de response exists: false met de beschikbare nummers. Als de toevoeging niet tot een postcode leidt, probeert de handler opnieuw zonder toevoeging.

{
"success": true,
"data": {
"exists": true,
"streetKey": "Oostkade||maasdelta",
"streetLabel": "Oostkade",
"number": "8",
"addition": "A",
"numbers": ["8", "10", "12"],
"additions": ["A", "B", "C", "D", "E"],
"postcode": "3221 AJ",
"city": "Hellevoetsluis"
}
}

Alle modes gebruiken het standaard WordPress AJAX-formaat:

{
"success": true,
"data": { ... }
}

Bij fouten:

{
"success": false,
"data": {
"message": "Invalid nonce"
}
}

Geinjecteerd via myproject_wpforms_address_script() op de wpforms_frontend_output_after hook (prioriteit 20). Actief op formulier ID 40 en 3493.

VeldIDTypeBeschrijving
Straat#23Select (Select2)Zoekbare straat-dropdown
Nummer#24SelectHuisnummer-dropdown
Toevoeging#25SelectToevoeging-dropdown
Postcode#26Text (readonly)Auto-ingevulde postcode
Stad#27Text (readonly)Auto-ingevulde stad
Custom adres#22CheckboxToggle voor vrije invoer

De veld-IDs worden server-side geconfigureerd en als JSON-object doorgegeven aan de JavaScript.

Het straatveld is een Select2-dropdown met AJAX-zoekmogelijkheid.

  • Minimale invoer: 2 karakters
  • Debounce: 200ms delay
  • Dropdown formaat: straatnaam + suffix (stad of “Maasdelta”)
  • Geselecteerde waarde: de streetKey (bijv. "Oostkade||maasdelta")
  • Weergave na selectie: alleen de straatnaam

De suffix wordt per streetKey gecached voor weergave-doeleinden.

Na selectie van een straat:

  1. AJAX request met mode=numbers en de geselecteerde streetKey
  2. Het nummer-select (#24) wordt gevuld met de resultaten
  3. Het toevoeging-select (#25) wordt geleegd
  4. De postcode- en stad-velden worden geleegd

Na selectie van een nummer:

  1. AJAX request met mode=additions en streetKey + number
  2. Het toevoeging-select (#25) wordt gevuld met de resultaten
  3. Tegelijkertijd: AJAX request met mode=zipcity voor postcode en stad
  4. De postcode (#26) en stad (#27) worden automatisch ingevuld (readonly)

Na wijziging van de toevoeging:

  1. AJAX request met mode=zipcity en de nieuwe streetKey + number + addition
  2. Postcode en stad worden bijgewerkt als de toevoeging een andere postcode oplevert

Zolang er geen straat geselecteerd is, zijn het nummer- en toevoeging-select disabled. Na selectie van een straat wordt het nummer-select geactiveerd. Het toevoeging-select wordt pas actief na selectie van een nummer.


Geimplementeerd in assets/js/modules/wpforms-werkbon.js.

Na elke adreswijziging worden de waarden gesynchroniseerd naar verborgen WPForms-velden die uiteindelijk opgeslagen worden als ACF meta op de werkbon.

+------------------------------+ +-----------------------------+
| Select2 / auto-fill velden | | Verborgen WP velden (ACF) |
|------------------------------| |-----------------------------|
| #23 straat (of #2 custom) | --> | #17 straat |
| #24 nummer (of #15 custom) | --> | #52 nummer |
| #25 toevoeging (of #14 cust) | --> | #19 toevoeging |
| #26 postcode (of #13 custom) | --> | #20 postcode |
| #27 stad (of #4 custom) | --> | #21 stad |
+------------------------------+ +-----------------------------+

Als de checkbox #22 is aangevinkt:

  • Alle Select2-adressen worden geleegd
  • Vrije tekstvelden worden getoond: #2 (straat), #15 (nummer), #14 (toevoeging), #13 (postcode), #4 (stad)
  • De sync leest uit de custom-velden in plaats van de select-velden

Als de checkbox wordt uitgevinkt:

  • De prefill-data wordt hersteld (indien beschikbaar)
  • De Select2-keten wordt weer getoond

Het prefill-systeem voorkomt onbedoelde wijzigingen wanneer een adres via contactselectie is ingevuld.

Wanneer een contact geselecteerd wordt via de zoekmodal (modal-search-contact.js):

  1. AJAX call naar mode=resolve_full met het contactadres (straat, nummer, toevoeging)
  2. Als het adres gevonden wordt: alle select-velden worden gevuld
  3. Display mode geactiveerd: readonly adres-divs worden getoond in plaats van selects
  4. window.BEAM_ADDR_DISPLAY_MODE[formId] = true
  5. Prefill-data wordt opgeslagen in window.BEAM_ADDR_PREFILL[formId]

Wanneer de gebruiker op “Bewerk” klikt om het adres aan te passen:

  1. window.BEAM_ADDR_INIT_SELECT2[formId]() wordt aangeroepen (lazy Select2 initialisatie)
  2. De opgeslagen prefill-data wordt teruggeladen in de select-velden
  3. De display-mode divs worden verwijderd
  4. window.BEAM_ADDR_UNLOCKED[formId] = true

Na het ontgrendelen van een geprefild adres geldt een lock op postcode en stad:

  • Als het nummer en de toevoeging ongewijzigd zijn ten opzichte van de prefill-data, blijven postcode en stad vergrendeld (geen nieuwe AJAX-call)
  • Als een van beide wijzigt, wordt de lock opgeheven en wordt een nieuwe zipcity request gedaan

De lock wordt intern bijgehouden via de variabelen zipCityLocked, originalPrefillNumber en originalPrefillAddition.

GlobalTypeBeschrijving
BEAM_ADDR_DISPLAY_MODE[formId]booleanDisplay mode actief (readonly divs)
BEAM_ADDR_PREFILL[formId]objectOpgeslagen prefill-data
BEAM_ADDR_UNLOCKED[formId]booleanAdres is ontgrendeld door “Bewerk” klik
BEAM_ADDR_INIT_SELECT2[formId]functionLazy Select2 initialisatie callback
MYPROJECT_ADDR_LOCK[formId]booleanAlgemene lock (voorkomt wijzigingen)

Adressen die afkomstig zijn uit maasdelta.csv worden door het gehele systeem herkend en behandeld als Maasdelta-woningen. Dit heeft gevolgen voor het contacttype en de externe sync.

LaagMechanisme
Databaseis_maasdelta = 1 op de rij in wp_werkbon_addresses
AJAXsource veld in streets-response bevat "maasdelta"
Client-sidewpforms-werkbon.js detecteert "maasdelta" in de street source
APIacs_job_api_lookup_address() retourneert is_maasdelta: true
  1. Contacttype geforceerd: als een adres als Maasdelta gedetecteerd wordt, wordt het contacttype van het gekoppelde contact ingesteld op 'maasdelta' via acs_job_api_force_contact_type_maasdelta()

  2. Sync blokkade: contacten met type 'maasdelta' worden uitgesloten van synchronisatie naar Make.com en Moneybird

  3. Formulier: in de Select2-dropdown worden Maasdelta-straten getoond met de suffix "(Maasdelta - Stad)", zodat de gebruiker de bron kan herkennen

POST /wp-json/acs/v1/jobs/create
|
v
acs_job_api_lookup_address($params)
| query: zipcode + number + addition
v
werkbon_addresses_lookup()
| return: {streetname, is_maasdelta}
v
if is_maasdelta:
$params['streetname'] = genormaliseerde straatnaam
$params['_acs_is_maasdelta_address'] = true
|
v
Na contact aanmaken/vinden:
acs_job_api_force_contact_type_maasdelta($contact_id)
| update_field('contact_type', 'maasdelta')
v
Werkbon aangemaakt met genormaliseerd adres

Gedefinieerd in functions/api-job.php. Wrapper rond werkbon_addresses_lookup().

acs_job_api_lookup_address(array $p): array
Parameter (in $p)Beschrijving
zipcodePostcode
numberHuisnummer
additionToevoeging (optioneel)

Return: ['streetname' => string, 'is_maasdelta' => bool] of lege array.

Aangeroepen tijdens: POST /wp-json/acs/v1/jobs/create

Functie in de flow:

  1. Normaliseert de straatnaam vanuit de database (vervangt eventuele typo’s of afwijkende schrijfwijzen uit de API-input)
  2. Detecteert of het adres een Maasdelta-woning is
  3. Zet de flag _acs_is_maasdelta_address op de parameters als het adres matcht

Twee legacy-functies verwijzen door naar acs_job_api_lookup_address():

acs_job_api_lookup_address_from_csv(array $p): array // Alias
acs_job_api_lookup_streetname_from_csv(array $p): string // Retourneert alleen streetname

De functie myproject_norm_search() in form-addresses.php biedt accent- en leestekenonafhankelijk zoeken:

myproject_norm_search(string $s): string

Stappen:

  1. Transliteratie van accenten/diacrieten naar ASCII (via transliterator_transliterate() of iconv() als fallback)
  2. Conversie naar lowercase
  3. Verwijdering van alle niet-alfanumerieke tekens

Voorbeeld: "Sint-Jacobsstraat" wordt "sintjacobsstraat"

Deze functie wordt intern gebruikt voor fuzzy matching maar is momenteel niet gekoppeld aan de standaard LIKE-queries van de lookup functies.


BestandBeschrijving
functions/db-addresses.phpDatabase tabel, import, 6 lookup functies, admin pagina
functions/form-addresses.phpAJAX endpoint, Select2 JavaScript injectie, zoeknormalisatie
functions/api-job.phpREST API integratie, adres lookup, Maasdelta detectie
assets/js/modules/wpforms-werkbon.jsAdres sync naar verborgen WP-velden, custom toggle
assets/js/modal-search-contact.jsContact zoekmodal, prefill via resolve_full
assets/csv/addresses/*.csvBronbestanden voor adresimport