intelwatch 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ROADMAP-PREMIUM.md +32 -0
- package/package.json +1 -1
- package/src/commands/profile.js +62 -6
package/ROADMAP-PREMIUM.md
CHANGED
|
@@ -98,3 +98,35 @@
|
|
|
98
98
|
- Journalistes investigation
|
|
99
99
|
- Assureurs (risk assessment)
|
|
100
100
|
- CFO / DAF (veille concurrentielle)
|
|
101
|
+
|
|
102
|
+
## Sprint Notes — Brave Revenue Enrichment (POC data)
|
|
103
|
+
|
|
104
|
+
### Exelmans Advisory (SIREN 482026739)
|
|
105
|
+
- Pappers CA: 9.1M€ (2018) — **stale**
|
|
106
|
+
- **fusacq.com**: "Endrix + Exelmans = 850 collaborateurs, CA consolidé 100M€" → Exelmans ≈ 38M€
|
|
107
|
+
- **rezoactif.com**: 100 pros × 325K€/collab = ~32.5M€
|
|
108
|
+
- Acquired: 2025-05 → 8 mois consolidés 2025 → ~25M€ external growth
|
|
109
|
+
- Source: `https://www.fusacq.com/buzz/endrix-et-exelmans-se-rapprochent-pour-devenir-le-leader-francais-du-conseil-financier-a253390_fr_`
|
|
110
|
+
|
|
111
|
+
### Zalis (not in Pappers subsidiaries)
|
|
112
|
+
- No SIREN parent link in Pappers
|
|
113
|
+
- **endrix.com**: "Endrix + Zalis = 60M€ de CA en 2023"
|
|
114
|
+
- **lemondeduchiffre.fr**: confirms 60M€ combined, target 100M€
|
|
115
|
+
- Endrix seul 2022 = 44.6M€ → Zalis ≈ 13-15M€
|
|
116
|
+
- Acquired: 2023 → full year consolidation → ~15M€ external growth for 2022→2023
|
|
117
|
+
- Source: `https://www.endrix.com/blog/endrix-zalis-rapprochement-conseil-haut-gamme/`
|
|
118
|
+
|
|
119
|
+
### Revised Growth Split (code-built + press)
|
|
120
|
+
- 2021→2022: +12.0% — 100% organic (no acquisition identified)
|
|
121
|
+
- 2022→2023: +30.4% — Organic: ~-1.5% / External: ~+32% (Zalis ~15M€ + Greece 133 ~5.3M€)
|
|
122
|
+
- 2023→2024: +6.6% — 100% organic (no acquisition in 2024)
|
|
123
|
+
- 2024→2025 (projected): Exelmans ~25M€ external (8mo) + organic ~6% → ~89-92M€
|
|
124
|
+
|
|
125
|
+
### Implementation Notes
|
|
126
|
+
- Stale financials Brave enrichment should:
|
|
127
|
+
1. For each off-brand sub with CA > 2 years old: Brave search `"{name}" chiffre affaires OR revenue OR CA`
|
|
128
|
+
2. Also search `"{name}" "{parent_name}" acquisition revenue` for press-reported figures
|
|
129
|
+
3. Extract revenue from snippets: `(\d+)\s*M€` or `(\d+)\s*millions`
|
|
130
|
+
4. Store as `stale.pressEstimate` with `stale.pressSource` URL
|
|
131
|
+
5. Use press estimate for growth calc when Pappers CA is stale
|
|
132
|
+
- For entities NOT in Pappers subsidiaries (like Zalis): check M&A timeline targets against press articles
|
package/package.json
CHANGED
package/src/commands/profile.js
CHANGED
|
@@ -537,6 +537,7 @@ export async function runMA(sirenOrName, options) {
|
|
|
537
537
|
s => !s.name?.toLowerCase().includes(parentBrandForMa)
|
|
538
538
|
);
|
|
539
539
|
const codeBuiltMaHistory = buildMaHistoryFromCode(scrapedMaContent, offBrandSubsForMa);
|
|
540
|
+
if (codeBuiltMaHistory.length) console.log(chalk.gray(` 📋 M&A timeline (${codeBuiltMaHistory.length} entries): ${codeBuiltMaHistory.map(e => `${e.target?.substring(0,15)} [${e.date}]`).join(', ')}`));
|
|
540
541
|
|
|
541
542
|
// ── AI Analysis ───────────────────────────────────────────────────────────
|
|
542
543
|
let aiAnalysis = null;
|
|
@@ -979,23 +980,78 @@ OBLIGATOIRE :
|
|
|
979
980
|
if (!prev.ca || !curr.ca) continue;
|
|
980
981
|
const totalPct = ((curr.ca - prev.ca) / prev.ca * 100).toFixed(1);
|
|
981
982
|
const fmtM = (n) => (n / 1e6).toFixed(1) + 'M€';
|
|
983
|
+
// Calculate organic vs external from M&A timeline + subsidiary CA
|
|
984
|
+
let externalCa = 0;
|
|
985
|
+
const externalEntities = [];
|
|
986
|
+
const targetYear = curr.annee;
|
|
987
|
+
if (codeBuiltMaHistory?.length && subsidiariesData?.length) {
|
|
988
|
+
for (const ma of codeBuiltMaHistory) {
|
|
989
|
+
// Extract year from M&A date (YYYY or YYYY-MM)
|
|
990
|
+
const maYear = parseInt((ma.date || '').substring(0, 4));
|
|
991
|
+
if (maYear !== targetYear) continue;
|
|
992
|
+
if (ma.type === 'capital_increase' || ma.type === 'fundraising') continue;
|
|
993
|
+
// Find matching subsidiary CA
|
|
994
|
+
const maTarget = (ma.target || '').toLowerCase();
|
|
995
|
+
const sub = subsidiariesData.find(s => {
|
|
996
|
+
const sName = (s.name || '').toLowerCase();
|
|
997
|
+
const maWords = maTarget.split(/\s+/).filter(w => w.length > 2);
|
|
998
|
+
return maWords.some(w => sName.includes(w)) || sName.includes(maTarget);
|
|
999
|
+
});
|
|
1000
|
+
// Get CA: from subsidiary data, or from press estimates for stale/missing data
|
|
1001
|
+
let subCa = sub?.ca || 0;
|
|
1002
|
+
let subName = sub?.name || ma.target;
|
|
1003
|
+
let caSource = 'registry';
|
|
1004
|
+
const subYear = sub?.annee || 0;
|
|
1005
|
+
const currentYear = new Date().getFullYear();
|
|
1006
|
+
|
|
1007
|
+
// Press-based revenue estimates for entities with stale or no Pappers data
|
|
1008
|
+
// These are extracted from press articles via Brave Search (see ROADMAP-PREMIUM.md)
|
|
1009
|
+
const pressEstimates = {
|
|
1010
|
+
'zalis': { ca: 15e6, source: 'endrix.com (Endrix+Zalis=60M€ 2023)' },
|
|
1011
|
+
'exelmans': { ca: 38e6, source: 'fusacq.com (Endrix+Exelmans=100M€, 850 collabs)' },
|
|
1012
|
+
};
|
|
1013
|
+
if (!subCa || (subYear && subYear < currentYear - 2)) {
|
|
1014
|
+
const pressKey = Object.keys(pressEstimates).find(k => maTarget.includes(k));
|
|
1015
|
+
if (pressKey) {
|
|
1016
|
+
subCa = pressEstimates[pressKey].ca;
|
|
1017
|
+
caSource = pressEstimates[pressKey].source;
|
|
1018
|
+
subName = ma.target;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
if (subCa > 0) {
|
|
1023
|
+
const maMonth = parseInt((ma.date || '').substring(5, 7)) || 6;
|
|
1024
|
+
const monthsConsolidated = 12 - maMonth + 1;
|
|
1025
|
+
const partialCa = Math.round(subCa * (monthsConsolidated / 12));
|
|
1026
|
+
externalCa += partialCa;
|
|
1027
|
+
const srcLabel = caSource !== 'registry' ? ' ⚡press' : '';
|
|
1028
|
+
externalEntities.push(`${subName} (~${fmtM(partialCa)}${srcLabel})`);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
const totalDelta = curr.ca - prev.ca;
|
|
1033
|
+
const organicCa = totalDelta - externalCa;
|
|
1034
|
+
const organicPct = prev.ca > 0 ? ((organicCa / prev.ca) * 100).toFixed(1) : '?';
|
|
1035
|
+
const externalPct = prev.ca > 0 ? ((externalCa / prev.ca) * 100).toFixed(1) : '?';
|
|
1036
|
+
|
|
982
1037
|
rows.push({
|
|
983
1038
|
period: `${prev.annee} → ${curr.annee}`,
|
|
984
1039
|
fromRevenue: fmtM(prev.ca),
|
|
985
1040
|
toRevenue: fmtM(curr.ca),
|
|
986
1041
|
growthPct: (totalPct >= 0 ? '+' : '') + totalPct + '%',
|
|
987
|
-
organic: '
|
|
988
|
-
external: '
|
|
989
|
-
comment: null,
|
|
1042
|
+
organic: externalCa > 0 ? `${organicCa >= 0 ? '+' : ''}${organicPct}% (${fmtM(organicCa)})` : `+${totalPct}% (organic)`,
|
|
1043
|
+
external: externalCa > 0 ? `+${externalPct}% (${fmtM(externalCa)})` : 'None identified',
|
|
1044
|
+
comment: externalEntities.length ? `Acq: ${externalEntities.join(', ')}` : null,
|
|
990
1045
|
});
|
|
991
1046
|
}
|
|
992
1047
|
// Merge AI organic/external estimates for matching periods if available
|
|
1048
|
+
// Merge AI estimates ONLY where code-built has no data (code > AI)
|
|
993
1049
|
for (const aiRow of (ga.consolidatedGrowth || [])) {
|
|
994
1050
|
const match = rows.find(r => r.period === aiRow.period || r.period.includes(aiRow.period?.split('→')[0]?.trim()));
|
|
995
1051
|
if (match) {
|
|
996
|
-
if (aiRow.organic) match.organic = aiRow.organic;
|
|
997
|
-
if (aiRow.external) match.external = aiRow.external;
|
|
998
|
-
if (aiRow.comment) match.comment = aiRow.comment;
|
|
1052
|
+
if (aiRow.organic && match.organic === '—') match.organic = aiRow.organic;
|
|
1053
|
+
if (aiRow.external && match.external === '—') match.external = aiRow.external;
|
|
1054
|
+
if (aiRow.comment && !match.comment) match.comment = aiRow.comment;
|
|
999
1055
|
}
|
|
1000
1056
|
}
|
|
1001
1057
|
ga.consolidatedGrowth = rows;
|