diff --git a/apps/ops/src/pages/EventsPage.vue b/apps/ops/src/pages/EventsPage.vue index fdb1a26..82d1366 100644 --- a/apps/ops/src/pages/EventsPage.vue +++ b/apps/ops/src/pages/EventsPage.vue @@ -138,9 +138,15 @@ :options="[{ label: 'Liste clients', value: 'list' }, { label: 'Import CSV', value: 'csv' }]" />
- +
+ + +
-
Astuce : « Clients actifs » = ~7900 clients avec courriel. « Abonnement actif » restreint à ceux ayant un service en cours.
+ + + +
« Total mensuel » = somme des abonnements actifs du client (0 = aucun filtre). « Résidentiel/Commercial » = type de compte.
@@ -209,7 +215,7 @@ const sendMode = ref('test') const template = ref('') const templates = ref([{ value: '', label: 'Invitation Fête 20 ans (TARGO)' }]) const audienceSource = ref('list') -const audFilters = ref({ statut: 'active', active_sub: false }) +const audFilters = ref({ statut: 'active', customer_type: '', active_sub: false, min_monthly: 0 }) const csvFile = ref(null) const audCsvText = ref('') const audPreview = ref(null) diff --git a/services/targo-hub/lib/events.js b/services/targo-hub/lib/events.js index fc322d4..491c0ca 100644 --- a/services/targo-hub/lib/events.js +++ b/services/targo-hub/lib/events.js @@ -273,24 +273,26 @@ async function resolveAudience ({ mode = 'filter', filters = {}, csv = '' } = {} const f = [] if (filters.statut === 'active') f.push(['disabled', '=', 0]) else if (filters.statut === 'disabled') f.push(['disabled', '=', 1]) - if (filters.territory) f.push(['territory', '=', filters.territory]) - if (filters.group) f.push(['customer_group', '=', filters.group]) - const CFIELDS = ['name', 'customer_name', 'email_id', 'language', 'territory', 'customer_group'] + if (filters.customer_type) f.push(['customer_type', '=', filters.customer_type]) // Individual = résidentiel · Company = commercial + const CFIELDS = ['name', 'customer_name', 'email_id', 'language', 'customer_type'] + const minMonthly = Number(filters.min_monthly) || 0 let customers = [] - if (filters.active_sub) { - // Clients ayant ≥1 Service Subscription « Actif ». - const subs = []; let s = 0 - for (let p = 0; p < 30; p++) { const b = await erp.list('Service Subscription', { filters: [['status', '=', 'Actif']], fields: ['customer'], limit: 500, start: s }); subs.push(...b); if (b.length < 500) break; s += 500 } - const ids = [...new Set(subs.map(x => x.customer).filter(Boolean))] + if (filters.active_sub || minMonthly > 0) { + // Agrège les abonnements ACTIFS par client (somme mensuelle) ; filtre par minimum si demandé. + const byCust = {}; let s = 0 + for (let p = 0; p < 40; p++) { const b = await erp.list('Service Subscription', { filters: [['status', '=', 'Actif']], fields: ['customer', 'monthly_price'], limit: 500, start: s }); b.forEach(x => { if (x.customer) byCust[x.customer] = (byCust[x.customer] || 0) + (Number(x.monthly_price) || 0) }); if (b.length < 500) break; s += 500 } + let ids = Object.keys(byCust) + if (minMonthly > 0) ids = ids.filter(c => byCust[c] >= minMonthly) for (let i = 0; i < ids.length; i += 100) { const b = await erp.list('Customer', { filters: [...f, ['name', 'in', ids.slice(i, i + 100)]], fields: CFIELDS, limit: 500 }) customers.push(...b) } + customers.forEach(c => { c._monthly = byCust[c.name] || 0 }) } else { let s = 0 for (let p = 0; p < 60; p++) { const b = await erp.list('Customer', { filters: f, fields: CFIELDS, limit: 500, start: s }); customers.push(...b); if (b.length < 500) break; s += 500 } } - rows = customers.map(c => { const n = splitName(c.customer_name); return { email: firstEmail(c.email_id), firstname: n.firstname, lastname: n.lastname, language: normLang(c.language), customer_id: c.name } }) + rows = customers.map(c => { const n = splitName(c.customer_name); return { email: firstEmail(c.email_id), firstname: n.firstname, lastname: n.lastname, language: normLang(c.language), customer_id: c.name, customer_type: c.customer_type || '', monthly: Math.round(c._monthly || 0) } }) } const seen = new Set(); const recipients = []; let dropped = 0 for (const r of rows) {