@mostajs/aop-fiche 0.1.0

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/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @mostajs/aop-fiche
2
+
3
+ **Auteur** : Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
4
+
5
+ Génère **fiche AO** + **état des lieux** au **format DEVRULES §8.4** (Source obligatoire) depuis les données ORM. L **emplacement** est laissé à l app (par client, configurable — `Entreprise/appels-d-offres/` reste la veille INTERNE de MostaJS). Pur. Stack `mosta-aop-stack`.
package/llms.txt ADDED
@@ -0,0 +1,7 @@
1
+ # @mostajs/aop-fiche — fiche LLM
2
+ > Génère fiche AO + état des lieux au format DEVRULES §8.4 (Source obligatoire) depuis l ORM. Emplacement = choix de l app (par client).
3
+
4
+ ## EXPORTS
5
+ ficheAO(tender,{now,decision,mapping,analysis}) -> md (modèle _TEMPLATE-FICHE-AO) · etatDesLieux(tenders,{theme,now}) -> md (tableau classé + colonne Source)
6
+ sourceOf(tender) -> {portal,url,ref,published,collected} · aoSlug(tender,now) -> AO-<org>-<objet>-<DDMMYYYY>
7
+ PUR (Markdown). NB: Entreprise/appels-d-offres = veille INTERNE MostaJS ; ici emplacement libre/par client. Stack mosta-aop-stack.
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@mostajs/aop-fiche",
3
+ "version": "0.1.0",
4
+ "description": "Génère fiche AO + état des lieux au format DEVRULES §8.4 (Source obligatoire) depuis l'ORM. Emplacement laissé à l'app (par client). Pur, compose @mostajs/aop-classify.",
5
+ "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
+ "license": "AGPL-3.0-or-later",
7
+ "type": "module",
8
+ "main": "src/index.js",
9
+ "exports": {
10
+ ".": "./src/index.js"
11
+ },
12
+ "files": [
13
+ "src",
14
+ "README.md",
15
+ "llms.txt"
16
+ ],
17
+ "dependencies": {
18
+ "@mostajs/aop-classify": "^0.1.0"
19
+ },
20
+ "devDependencies": {
21
+ "@mostajs/mjs-unit": "^0.3.0"
22
+ },
23
+ "scripts": {
24
+ "test": "node test-scripts/unit/aop-fiche.test.mjs && node examples/run.mjs"
25
+ }
26
+ }
package/src/index.js ADDED
@@ -0,0 +1,85 @@
1
+ // @mostajs/aop-fiche — génère fiche AO + état des lieux au FORMAT DEVRULES §8.4 (Source obligatoire),
2
+ // depuis les données ORM. L'EMPLACEMENT est laissé à l'app (par client, configurable — pas forcément
3
+ // Entreprise/appels-d-offres/). PUR (retourne du Markdown). Compose @mostajs/aop-classify.
4
+ // @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
5
+ import { temperatureEmoji, classifyTemperature } from '@mostajs/aop-classify';
6
+
7
+ const day = (d) => (d ? String(d).slice(0, 10) : '—');
8
+ /** Source vérifiable (RÈGLE §8.4) extraite du tender. */
9
+ export function sourceOf(t = {}) {
10
+ return { portal: t.sourceType || '—', url: t.sourceUrl || t.connection?.baseUrl || '—', ref: t.sourceId || '—', published: day(t.dates?.publication), collected: day(t.lastSyncAt) };
11
+ }
12
+
13
+ /** Fiche AO Markdown (modèle _TEMPLATE-FICHE-AO.md). `decision` et `mapping` optionnels. */
14
+ export function ficheAO(t = {}, { now = new Date(), decision = null, mapping = [], analysis = null } = {}) {
15
+ const s = sourceOf(t);
16
+ const map = mapping.length ? mapping.map((m) => `| ${m.req} | ${m.brick} | ${m.state || '⬜'} |`).join('\n') : '| <exigence> | <`@mostajs/*`> | ⬜ |';
17
+ return `# Fiche AO — ${t.organization?.name || '<ORGANISME>'} — ${t.title || '<OBJET>'}
18
+
19
+ **Auteur** : Dr Hamid MADANI <drmdh@msn.com>
20
+ **Date de saisie** : ${day(now.toISOString())}
21
+ **Statut** : 🔎 veille | 📝 instruction | ✅ go | ⛔ no-go | 📤 réponse déposée | 🏁 résultat
22
+
23
+ ## 1. Identification
24
+ - **Organisme** : ${t.organization?.name || '—'}
25
+ - **Référence AO** : ${s.ref}
26
+ - **Source / portail** : ${s.portal}
27
+ - **URL source** : ${s.url}
28
+ - **Date de publication** : ${s.published}
29
+ - **Date de relevé** : ${s.collected}
30
+ - **Date limite de remise** : ${day(t.dates?.deadline)} ⚠
31
+ - **Montant estimé** : ${t.financial?.estimatedValue != null ? `${Number(t.financial.estimatedValue).toLocaleString('fr-FR')} ${t.financial.currency || ''}` : '—'}
32
+
33
+ ## 2. Objet & périmètre
34
+ ${t.description || '<description du besoin>'}
35
+ ${(t.cpvCodes || []).length ? `\n**CPV** : ${t.cpvCodes.join(', ')}` : ''}
36
+
37
+ ## 3. Pertinence (argumentaire go/no-go)
38
+ - **Température** : ${temperatureEmoji(t.classification?.temperature || classifyTemperature(t.dates?.deadline, now))} ${t.classification?.temperature || ''} · **Score** : ${t.classification?.score ?? '—'}
39
+ ${analysis ? `- **Analyse IA** : ${analysis.goNoGo} — ${analysis.summary}` : '- **Ce qu’on offre** : <briques `@mostajs/*` mobilisées>'}
40
+
41
+ ## 4. Exigences d'éligibilité
42
+ - [ ] Agrément / certification
43
+ - [ ] Références similaires
44
+ - [ ] Documents administratifs (RC, NIF/NIS, fiscale/CNAS/CASNOS)
45
+
46
+ ## 5. Mapping technique → briques
47
+ | Exigence | Brique / dialecte | État |
48
+ |---|---|---|
49
+ ${map}
50
+
51
+ ## 6. Décision
52
+ **Go / No-go** : ${decision?.decision || '<…>'} — **motif** : ${decision?.reason || '<…>'} — **décidé le** : ${decision?.at ? day(decision.at) : '<…>'}
53
+
54
+ ## 7. Suivi
55
+ - [ ] Dossier scaffolé · [ ] Mémoire technique · [ ] Offre financière (BPU/DQE) · [ ] Déposé le <…> · [ ] Résultat <…>
56
+ `;
57
+ }
58
+
59
+ /** État des lieux Markdown : tableau classé (priorité/échéance) avec colonne Source obligatoire. */
60
+ export function etatDesLieux(tenders = [], { theme = 'veille', now = new Date() } = {}) {
61
+ const rows = [...tenders]
62
+ .map((t) => ({ t, temp: t.classification?.temperature || classifyTemperature(t.dates?.deadline, now), score: t.classification?.score ?? 0 }))
63
+ .sort((a, b) => b.score - a.score)
64
+ .map(({ t, temp, score }) => { const s = sourceOf(t);
65
+ return `| ${temperatureEmoji(temp)} ${temp} | ${score} | ${(t.title || '').slice(0, 40)} | ${t.organization?.name || '—'} | ${day(t.dates?.deadline)} | ${s.portal} | ${s.url} |`;
66
+ }).join('\n');
67
+ return `# État des lieux AO — ${theme}
68
+
69
+ **Auteur** : Dr Hamid MADANI <drmdh@msn.com> · **Relevé** : ${day(now.toISOString())} · ${tenders.length} opportunité(s)
70
+
71
+ > Classé par score/pertinence. **Source obligatoire** (§8.4) : portail + URL pour chaque ligne.
72
+
73
+ | Temp. | Score | Objet | Organisme | Échéance | Source | URL |
74
+ |---|---|---|---|---|---|---|
75
+ ${rows || '| — | — | _(aucune)_ | — | — | — | — |'}
76
+ `;
77
+ }
78
+
79
+ /** Slug de dossier AO (§8.4) : AO-<organisme>-<objet>-<DDMMYYYY>. */
80
+ export function aoSlug(t = {}, now = new Date()) {
81
+ const k = (x) => String(x || '').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 24);
82
+ const d = now.toISOString().slice(0, 10).split('-').reverse().join('');
83
+ return `AO-${k(t.organization?.name) || 'org'}-${k(t.title) || 'objet'}-${d}`;
84
+ }
85
+ export default { ficheAO, etatDesLieux, sourceOf, aoSlug };