Integrare i database CAP nel flusso d’ordine permette di autocompilare Comune/Provincia/Regione,
validare gli indirizzi e applicare regole tariffarie/logistiche per aree.
In questa guida trovi architettura consigliata, schema dati, esempi pronti, checklist e best practice per
ridurre errori, tempi e costi di consegna.
1) Panoramica e obiettivi
- Ridurre gli errori di digitazione con autocompilazione CAP → Comune/Provincia/Regione.
- Velocizzare il checkout e migliorare la conversione.
- Uniformare le anagrafiche per report e BI (Regione/Provincia coerenti).
- Ottimizzare le spedizioni: supplementi/tempi per isole e aree remote, blocco CAP non serviti.
- Taggare geograficamente i clienti per campagne e analisi.
2) Architettura consigliata (client/server)
Client (form): intercetta l’evento sul campo CAP e chiama un endpoint di lookup. Effettua autofill e prime verifiche UX.
Server (API/DB): espone un endpoint veloce (<100ms) che restituisce Comune/Provincia/Regione (+ opzionali lat/lon/zone).
Valida di nuovo lato server prima di salvare l’ordine.
- Fallback offline: bundle JSON leggero con i CAP più usati per resilienza.
- Cache: transient/Redis per ridurre i roundtrip al DB.
- Versioning dataset: aggiorna i CAP con pacchetti delta (nuovi, cambiati, deprecati).
3) Schema dati e indici
Campi minimi: cap
, comune
, provincia
, regione
. Consigliati: lat
, lon
, zona_logistica
, note
, stato
.
-- MySQL/MariaDB
CREATE TABLE cap_it (
cap CHAR(5) PRIMARY KEY,
comune VARCHAR(128) NOT NULL,
provincia CHAR(2) NOT NULL,
regione VARCHAR(64) NOT NULL,
lat DECIMAL(9,6) NULL,
lon DECIMAL(9,6) NULL,
zona_logistica VARCHAR(32) NULL, -- es. ISOLE, REMOTE, STD
note VARCHAR(255) NULL,
stato ENUM('ATTIVO','DEPRECATO') DEFAULT 'ATTIVO'
) ENGINE=InnoDB;
-- Indici utili (separati per lookup secondari)
CREATE INDEX idx_cap_regione ON cap_it (regione);
CREATE INDEX idx_cap_provincia ON cap_it (provincia);
Nota: mantieni comune in forma standardizzata (maiuscolo, senza accenti dove serve per matching).
4) Normalizzazione e validazione indirizzi
- Case folding: confronta sempre in UPPER.
- Rimozione accenti/punteggiatura per il matching (es. “Sant’Antimo” ≈ “SANTANTIMO”).
- Regola CAP→Comune come fonte primaria: se l’utente sovrascrive, verifica e avvisa.
- CAP deprecati: mantieni mapping e suggerisci aggiornamento all’utente.
- Prefissi CAP: utili per logistica rapida (prime 2–3 cifre) ma non sostituiscono il lookup puntuale.
5) Implementazione (step-by-step)
- Ingest: importa CSV/SQL/JSON; validalo (righe duplicate, CAP non numerici, campi mancanti).
- Endpoint: esponi
/cap/lookup?cap=XXXXX
con output JSON coerente. - UI Hook: al blur/change del CAP → chiama l’endpoint → autocompila i campi.
- Regole business: supplementi, blocchi, tempi aggiuntivi per zona.
- Verifica server-side: prima di salvare l’ordine, riesegui il controllo CAP–Comune.
- Log & monitor: salva mismatch/fallimenti per migliorare copy e dataset.
6) Esempi pratici (Excel/SQL/JSON/JS/PHP)
Excel — autocompilazione Comune/Provincia
=LET(cap; F2; XLOOKUP(cap; TabCAP[cap]; TabCAP[comune]; "CAP non trovato"))
=LET(cap; F2; XLOOKUP(cap; TabCAP[cap]; TabCAP[provincia]; "CAP non trovato"))
SQL — verifica massiva di incongruenze CAP–Comune
SELECT o.order_id, o.cap, UPPER(o.comune_digitato) AS comune_user,
UPPER(c.comune) AS comune_ref
FROM ordini o
JOIN cap_it c ON o.cap = c.cap
WHERE o.comune_digitato IS NOT NULL
AND UPPER(o.comune_digitato) <> UPPER(c.comune);
JSON — risposta minima per autocompletamento
{
"cap": "20121",
"comune": "Milano",
"provincia": "MI",
"regione": "Lombardia",
"lat": 45.4669,
"lon": 9.1900,
"zona_logistica": "STD"
}
JavaScript — lookup lato frontend (mock semplice)
<form id="addr-form">
<input id="cap" name="cap" maxlength="5" placeholder="CAP" />
<input id="comune" name="comune" placeholder="Comune" />
<input id="provincia" name="provincia" placeholder="Provincia" />
<input id="regione" name="regione" placeholder="Regione" />
</form>
<script>
const CAP = {
"20121": { comune:"MILANO", provincia:"MI", regione:"LOMBARDIA" },
"80138": { comune:"NAPOLI", provincia:"NA", regione:"CAMPANIA" }
};
const $ = (s) => document.querySelector(s);
$("#cap").addEventListener("blur", () => {
const v = $("#cap").value.trim();
const r = CAP[v];
if (r) {
$("#comune").value = r.comune;
$("#provincia").value = r.provincia;
$("#regione").value = r.regione;
} else { alert("CAP non trovato"); }
});
</script>
WordPress/WooCommerce — endpoint REST + hook form
<?php
// functions.php — endpoint di lookup
add_action('rest_api_init', function(){
register_rest_route('cap/v1', '/lookup', [
'methods' => 'GET',
'callback' => function($req){
$cap = preg_replace('/[^0-9]/','', $req->get_param('cap'));
if(strlen($cap) !== 5){ return new WP_Error('bad_request','Formato CAP non valido',['status'=>400]); }
global $wpdb;
$row = $wpdb->get_row($wpdb->prepare(
"SELECT comune, provincia, regione, zona_logistica FROM {$wpdb->prefix}cap_it WHERE cap=%s", $cap
));
return $row ?: new WP_Error('not_found','CAP non trovato',['status'=>404]);
}
]);
});
// Verifica server-side in checkout
add_action('woocommerce_after_checkout_validation', function($data, $errors){
$cap = isset($data['billing_postcode']) ? preg_replace('/[^0-9]/','', $data['billing_postcode']) : '';
if($cap){
global $wpdb;
$row = $wpdb->get_row($wpdb->prepare(
"SELECT comune FROM {$wpdb->prefix}cap_it WHERE cap=%s", $cap
));
if($row && !empty($data['billing_city']) && strtoupper($data['billing_city']) !== strtoupper($row->comune)){
$errors->add('validation', 'Il Comune non corrisponde al CAP inserito.');
}
}
}, 10, 2);
?>
7) Spedizioni avanzate: zone, supplementi, blocchi
Gestisci logiche per ISOLE, AREE REMOTE, NON SERVITI con una tabella di mapping e prefissi.
-- Esempio tabella zone logistiche
CREATE TABLE cap_zone (
cap CHAR(5) PRIMARY KEY,
zona ENUM('STD','ISOLE','REMOTE','BLOCCO') NOT NULL DEFAULT 'STD',
extra_costo DECIMAL(8,2) DEFAULT 0.00,
extra_giorni TINYINT DEFAULT 0
);
WooCommerce — modifica tariffe a runtime
<?php
add_filter('woocommerce_package_rates', function($rates){
$postcode = WC()->customer->get_shipping_postcode();
if(!$postcode) return $rates;
global $wpdb;
$z = $wpdb->get_row($wpdb->prepare("SELECT zona, extra_costo FROM {$wpdb->prefix}cap_zone WHERE cap=%s", $postcode));
if($z && $z->zona === 'BLOCCO'){
wc_add_notice('CAP non servito: scegli un altro indirizzo', 'error');
return [];
}
foreach($rates as $id => $rate){
if($z && floatval($z->extra_costo)>0){ $rates[$id]->cost += floatval($z->extra_costo); }
}
return $rates;
}, 10); ?>
Pseudocodice calcolo ETA/costi
costo = base; eta = base_eta;
if (zona == 'ISOLE') { costo += 4.90; eta += 1; }
if (zona == 'REMOTE') { costo += 6.90; eta += 2; }
if (zona == 'BLOCCO') { stop("CAP non servito"); }
8) UX & accessibilità
- Microcopy: “Inserisci il tuo CAP (5 cifre) — esempio: 20121”.
- Messaggi chiari: “Il CAP 20121 corrisponde a Milano (MI)”.
- Non bloccare subito: consenti override con warning e spiega come correggere.
- Keyboard-first: focus management e ARIA live region per gli esiti di lookup.
- Colori & contrasto: avvisi leggibili e conformi a WCAG AA.
9) Performance, caching e scalabilità
- Indice sul CAP (Primary Key) → lookup O(1).
- Cache risultati per CAP ripetuti (transient/Redis).
- Minimizza payload: JSON essenziale (comune/provincia/regione).
- CDN per script statici e compressione GZIP/Brotli.
- Monitor: 95° percentile < 150ms per l’endpoint.
10) QA, KPI e controllo qualità
- Test unitari su CAP noti (campione per ogni regione).
- Test regressione a ogni aggiornamento dataset (diff su righe cambiate).
- KPI: % ordini con mismatch, tempo medio compilazione form, tasso resi per indirizzo errato, % CAP non trovati.
- A/B test: verifica impatto dell’autofill su conversione.
11) Privacy/GDPR e sicurezza
- CAP non è dato personale ma rientra nei dati di contatto: trattalo con policy trasparente.
- Limitazione dati: restituisci solo ciò che serve (principio di minimizzazione).
- Rate limiting e sanitizzazione input per l’endpoint.
12) Estensione multi-paese
Adotta un campo Paese e tabelle specifiche (IT/ES/FR/DE). Ogni nazione ha regole proprie (formato, lunghezza, prefissi, codici INSEE/INE).
CREATE TABLE cap_xx (
country CHAR(2) NOT NULL, -- IT, ES, FR...
cap VARCHAR(12) NOT NULL,
comune VARCHAR(128),
provincia VARCHAR(8),
regione VARCHAR(64),
PRIMARY KEY(country, cap)
);
FAQ
Come gestire CAP con più località? Il CAP è la chiave: restituisci il comune prevalente o proponi una lista se necessario.
E i CAP “storici” o deprecati? Mantieni una tabella di mapping e suggerisci l’aggiornamento verso il CAP attivo.
Serve lat/lon? Non obbligatorio, ma utile per ETA/distanze e mappe “ritiro vicino a te”.