Events: remove RSVP customer-number field; fuzzy multi-field name audience filter
- RSVP form: drop the "Votre numéro client (optionnel)" field (and its submit wiring). Magic-link tokens still resolve the customer; matching falls back to name/email/phone. - Audience name filter: was a case-sensitive ERPNext `like` on customer_name only, so "lpb" matched nothing (the account is C-LPB4 / "Louis-Paul Bourdon", matched via account ID / mandataire). Now resolved via nameLikeCustomerIds — accent/case- insensitive across customer_name + account name + mandataire + contact_name_legacy (PG pool, REST fallback), intersected with the other filters. Confirms "Tous" includes résiliés (include_resiliated) works end to end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d26722f048
commit
0853947c43
|
|
@ -507,6 +507,32 @@ async function cityCustomerIds (city) {
|
|||
for (let p = 0; p < 80; p++) { const b = await erp.list('Service Location', { filters: [['city', 'like', '%' + term + '%']], fields: ['customer'], limit: 500, start: s }); b.forEach(x => { if (x.customer) ids.add(x.customer) }); if (b.length < 500) break; s += 500 }
|
||||
return ids
|
||||
}
|
||||
|
||||
// Filtre « nom » de l'audience — FUZZY comme la recherche app-wide : accent/casse-insensible ET multi-champs
|
||||
// (customer_name + ID de compte `name` + mandataire + contact_name_legacy). Ex. « lpb » → C-LPB4 (Louis-Paul
|
||||
// Bourdon, matché par ID/mandataire, pas par customer_name). Via PG ; repli REST (customer_name, sensible à la casse).
|
||||
async function nameLikeCustomerIds (term) {
|
||||
const t = String(term || '').trim(); const ids = new Set()
|
||||
if (!t) return ids
|
||||
let pool = null
|
||||
try { pool = require('./address-db').pool() } catch { pool = null }
|
||||
if (pool) {
|
||||
const like = '%' + t + '%'
|
||||
try {
|
||||
const r = await pool.query("SELECT name FROM \"tabCustomer\" WHERE unaccent(lower(coalesce(customer_name,''))) LIKE unaccent(lower($1)) OR lower(coalesce(name,'')) LIKE lower($1)", [like])
|
||||
r.rows.forEach(x => { if (x.name) ids.add(x.name) })
|
||||
} catch (e) { log('nameLikeCustomerIds pg: ' + e.message) }
|
||||
// Champs legacy (colonnes custom possibles) — try/catch séparé pour ne pas casser si absentes.
|
||||
try {
|
||||
const r = await pool.query("SELECT name FROM \"tabCustomer\" WHERE unaccent(lower(coalesce(mandataire,''))) LIKE unaccent(lower($1)) OR unaccent(lower(coalesce(contact_name_legacy,''))) LIKE unaccent(lower($1))", [like])
|
||||
r.rows.forEach(x => { if (x.name) ids.add(x.name) })
|
||||
} catch { /* colonnes absentes → ignore */ }
|
||||
return ids
|
||||
}
|
||||
const erp = require('./erp'); let s = 0
|
||||
for (let p = 0; p < 80; p++) { const b = await erp.list('Customer', { filters: [['customer_name', 'like', '%' + t + '%']], fields: ['name'], limit: 500, start: s }); b.forEach(x => { if (x.name) ids.add(x.name) }); if (b.length < 500) break; s += 500 }
|
||||
return ids
|
||||
}
|
||||
async function resolveAudience ({ mode = 'filter', filters = {}, csv = '' } = {}) {
|
||||
let rows = []
|
||||
let resiliatedExcluded = 0
|
||||
|
|
@ -529,13 +555,14 @@ async function resolveAudience ({ mode = 'filter', filters = {}, csv = '' } = {}
|
|||
if (filters.statut === 'active') f.push(['disabled', '=', 0])
|
||||
else if (filters.statut === 'disabled') f.push(['disabled', '=', 1])
|
||||
if (filters.customer_type) f.push(['customer_type', '=', filters.customer_type]) // Individual = résidentiel · Company = commercial
|
||||
if (filters.name_like) f.push(['customer_name', 'like', '%' + String(filters.name_like).trim() + '%']) // ex. « Proprio » → comptes dont le nom le contient
|
||||
const minMonthly = Number(filters.min_monthly) || 0
|
||||
|
||||
// Ensembles d'IDs à INTERSECTER (ville via Service Location, abonnements via Service Subscription).
|
||||
// Ensembles d'IDs à INTERSECTER (nom fuzzy, ville via Service Location, abonnements via Service Subscription).
|
||||
let restrict = null // null = pas de restriction ; sinon Set d'IDs Customer
|
||||
const intersect = (set) => { restrict = restrict ? new Set([...restrict].filter(x => set.has(x))) : set }
|
||||
|
||||
// NOM (fuzzy, multi-champs, insensible casse/accents) — ex. « lpb » → C-LPB4.
|
||||
if (filters.name_like && String(filters.name_like).trim()) intersect(await nameLikeCustomerIds(String(filters.name_like)))
|
||||
// VILLE → Service Location.customer (champ `city` peuplé, contrairement à `territory`).
|
||||
if (filters.city && String(filters.city).trim()) intersect(await cityCustomerIds(String(filters.city)))
|
||||
|
||||
|
|
@ -629,7 +656,6 @@ function page (event, lang, prefill = {}, opts = {}) {
|
|||
lang = normLang(lang)
|
||||
const t = event[lang]
|
||||
const other = lang === 'fr' ? 'en' : 'fr'
|
||||
const known = !!prefill.customer_id
|
||||
const full = !!opts.full
|
||||
const greet = t.greet(firstName(prefill.name))
|
||||
const program = (t.program || []).map(p =>
|
||||
|
|
@ -726,7 +752,6 @@ button:hover{filter:brightness(1.05)}button:active{transform:translateY(1px)}but
|
|||
${field('nm', 'name', esc(t.f_name), `<input id="nm" name="name" value="${esc(prefill.name || '')}" autocomplete="name" placeholder="Jean Tremblay">`, 'e_nm', t.err_name)}
|
||||
${field('em', 'email', esc(t.f_email), `<input id="em" name="email" type="email" value="${esc(prefill.email || '')}" autocomplete="email" placeholder="vous@exemple.com">`, 'e_em', t.err_email)}
|
||||
${field('ph', 'phone', optlbl(t.f_phone), `<input id="ph" name="phone" type="tel" autocomplete="tel" placeholder="819 555-1234">`)}
|
||||
${field('cn', 'customer_number', optlbl(t.f_number), `<input id="cn" name="customer_number" value="${esc(prefill.customer_number || '')}" ${known ? 'readonly' : ''} autocomplete="off" placeholder="C-12345">`)}
|
||||
${field('ps', 'party_size', esc(t.f_party), `<input id="ps" name="party_size" type="number" min="1" max="30" inputmode="numeric" placeholder="${esc(t.f_party_ph)}">`, 'e_ps', t.err_party)}
|
||||
${field('al', 'allergies', optlbl(t.f_allerg), `<textarea id="al" name="allergies" maxlength="500"></textarea>`)}
|
||||
<button type="submit" id="sub">${esc(t.submit)}</button>
|
||||
|
|
@ -769,7 +794,7 @@ function submitRsvp(e){
|
|||
if(!ok)return false;
|
||||
var b=document.getElementById('sub');b.disabled=true;
|
||||
fetch('/rsvp/'+encodeURIComponent(EVENT)+'/submit',{method:'POST',headers:{'Content-Type':'application/json'},
|
||||
body:JSON.stringify({token:TOKEN,name:nm,email:em,phone:(f.phone.value||'').trim(),customer_number:(f.customer_number.value||'').trim(),party_size:ps,allergies:(f.allergies.value||'').trim(),lang:LANG})})
|
||||
body:JSON.stringify({token:TOKEN,name:nm,email:em,phone:(f.phone.value||'').trim(),party_size:ps,allergies:(f.allergies.value||'').trim(),lang:LANG})})
|
||||
.then(function(r){return r.json()})
|
||||
.then(function(d){
|
||||
if(d&&d.ok){
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user