@amsom-habitat/cresol 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 +508 -0
- package/dist/cresol.js +2628 -0
- package/dist/cresol.umd.cjs +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
# Cresol
|
|
2
|
+
|
|
3
|
+
Assistant de **création de sollicitation**, extrait de Cally2 et réutilisable par les outils
|
|
4
|
+
AMSOM Habitat.
|
|
5
|
+
|
|
6
|
+
L'assistant est **autonome** : il charge lui-même la nomenclature, l'arbre patrimonial et les
|
|
7
|
+
collaborateurs, puis poste la création et les documents joints. Il ne connaît rien de l'outil
|
|
8
|
+
qui l'héberge. Celui-ci lui donne :
|
|
9
|
+
|
|
10
|
+
- les **réclamants** possibles (le dossier consulté) ;
|
|
11
|
+
- éventuellement un **brouillon pré-alimenté** et des **champs figés** ;
|
|
12
|
+
- le contenu des **volets latéraux** (sollicitations similaires, évènements), qui restent les
|
|
13
|
+
siens.
|
|
14
|
+
|
|
15
|
+
En retour, il reçoit des **événements** à chaque moment clé : avant et après la création, et
|
|
16
|
+
aux changements de réclamant, de motif et d'étape.
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
┌──────────────── outil hôte ────────────────┐
|
|
20
|
+
│ props : reclamants, modelValue, readonly… │
|
|
21
|
+
│ slots : volets similaires / évènements │
|
|
22
|
+
└───────────────┬────────────────▲───────────┘
|
|
23
|
+
│ │ événements : beforeCreate, created,
|
|
24
|
+
▼ │ createError, motifChange…
|
|
25
|
+
┌──────────────── Cresol ────────────────┐
|
|
26
|
+
│ [Réclamant] › Catégorisation › Cible › │
|
|
27
|
+
│ Détails → POST création + documents │
|
|
28
|
+
└────────────────────────────────────────┘
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Sommaire
|
|
32
|
+
|
|
33
|
+
- [Installation](#installation)
|
|
34
|
+
- [Configuration](#configuration)
|
|
35
|
+
- [Démarrage rapide](#démarrage-rapide)
|
|
36
|
+
- [Props](#props)
|
|
37
|
+
- [Événements](#événements)
|
|
38
|
+
- [Slots](#slots)
|
|
39
|
+
- [Le choix du réclamant](#le-choix-du-réclamant)
|
|
40
|
+
- [Champs figés (`readonly`)](#champs-figés-readonly)
|
|
41
|
+
- [Restreindre la nomenclature](#restreindre-la-nomenclature)
|
|
42
|
+
- [Le brouillon (`v-model`)](#le-brouillon-v-model)
|
|
43
|
+
- [La création](#la-création)
|
|
44
|
+
- [Exports](#exports)
|
|
45
|
+
- [Endpoints consommés](#endpoints-consommés)
|
|
46
|
+
- [Développement](#développement)
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm i @amsom-habitat/cresol
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Pas de feuille de style à importer : le CSS est injecté par le JS du paquet. L'outil fournit, lui,
|
|
55
|
+
Bootstrap (`@amsom-habitat/bootstrap-5`) et le composant global `font-awesome-icon`. Les icônes
|
|
56
|
+
portées par les données (motifs, catégories…) sont enregistrées à la volée par le paquet.
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
À poser **une fois** au démarrage de l'outil :
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
import { configureCresol } from '@amsom-habitat/cresol'
|
|
64
|
+
|
|
65
|
+
configureCresol({ baseUrl: import.meta.env.VITE_API_URL })
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| Option | Défaut (`@amsom-habitat/user-manager`) | Rôle |
|
|
69
|
+
| --------------- | -------------------------------------- | ----------------------------------- |
|
|
70
|
+
| `baseUrl` | — (**obligatoire**) | Racine de l'API métier |
|
|
71
|
+
| `getToken` | cookie partagé du domaine | Jeton envoyé à chaque appel |
|
|
72
|
+
| `setToken` | écrit le cookie | Jeton renouvelé (refresh 449) |
|
|
73
|
+
| `logout` | déconnexion du domaine | Refresh impossible |
|
|
74
|
+
| `goToLoginPage` | page de login du domaine | 401 / 498 sur un appel qui redirige |
|
|
75
|
+
|
|
76
|
+
Un outil du domaine ne pose que `baseUrl` : le jeton suit le SSO. Les autres options servent à un
|
|
77
|
+
outil qui gère son authentification lui-même.
|
|
78
|
+
|
|
79
|
+
Deux props surchargent la configuration **pour une instance** : `baseUrl` et `token`. Ordre de
|
|
80
|
+
priorité : **prop › `configureCresol` › défaut**. Elles servent aux cas de bord : storybook, démo,
|
|
81
|
+
seconde API, outil hors du domaine. Sans `baseUrl` nulle part, l'appel échoue avec une erreur
|
|
82
|
+
explicite plutôt que de partir en relatif sur l'origine de la page.
|
|
83
|
+
|
|
84
|
+
## Démarrage rapide
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<template>
|
|
88
|
+
<creer-sollicitation-modal
|
|
89
|
+
v-if="open"
|
|
90
|
+
:reclamants="[{ type: 'L', id: 2200523, libelle: 'Contrat' }]"
|
|
91
|
+
:model-value="{ provenance: 'T' }"
|
|
92
|
+
:readonly="['reclamant']"
|
|
93
|
+
@created="onCreated"
|
|
94
|
+
@error="$toast.error($event)"
|
|
95
|
+
@close="open = false"
|
|
96
|
+
>
|
|
97
|
+
<template #aside-similaires="{ idMotif, codeProgramme, idCible }">
|
|
98
|
+
<mes-similaires :id-motif="idMotif" :code-programme="codeProgramme" :id-cible="idCible" />
|
|
99
|
+
</template>
|
|
100
|
+
<template #aside-evenements>
|
|
101
|
+
<mes-evenements-de-patrimoine />
|
|
102
|
+
</template>
|
|
103
|
+
</creer-sollicitation-modal>
|
|
104
|
+
</template>
|
|
105
|
+
|
|
106
|
+
<script>
|
|
107
|
+
import { CreerSollicitationModal } from '@amsom-habitat/cresol'
|
|
108
|
+
|
|
109
|
+
export default {
|
|
110
|
+
components: { CreerSollicitationModal },
|
|
111
|
+
data: () => ({ open: true }),
|
|
112
|
+
methods: {
|
|
113
|
+
onCreated(sollicitations) {
|
|
114
|
+
this.$toast.success(`${sollicitations.length} sollicitation(s) créée(s)`)
|
|
115
|
+
// … recharger le listing de l'outil
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
</script>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Deux composants, **même contrat** (props, événements, slots, définis une seule fois dans
|
|
123
|
+
`src/props.js`) :
|
|
124
|
+
|
|
125
|
+
- **`CreerSollicitationForm`** : l'assistant seul, à placer dans une page ou dans sa propre modale ;
|
|
126
|
+
- **`CreerSollicitationModal`** : le même, dans une `AmsomModal`. Il ajoute les props `titre`
|
|
127
|
+
(défaut `'Nouvelle sollicitation'`) et `closeOnCreated` (défaut `true` : émet `close` juste
|
|
128
|
+
après `created`), et l'événement `close` (croix, ou création réussie). Ses attributs non
|
|
129
|
+
déclarés (`reducible`, `title`…) descendent sur `AmsomModal`.
|
|
130
|
+
|
|
131
|
+
## Props
|
|
132
|
+
|
|
133
|
+
| Prop | Type | Défaut | Rôle |
|
|
134
|
+
| -------------------- | ---------------- | ---------- | -------------------------------------------------------------------------------------- |
|
|
135
|
+
| `reclamants` | Array | `[]` | Réclamants proposables : `[{ type, id, libelle }]` ([détail](#le-choix-du-réclamant)) |
|
|
136
|
+
| `reclamantMode` | String | `'select'` | Présentation du choix : `select`, `switch`, `autocomplete`, `step` |
|
|
137
|
+
| `modelValue` | Object | `null` | Brouillon pré-alimenté, partiel ; suivi en `v-model` ([détail](#le-brouillon-v-model)) |
|
|
138
|
+
| `readonly` | Boolean \| Array | `false` | Champs figés : `true`, `false` ou une liste ([détail](#champs-figés-readonly)) |
|
|
139
|
+
| `modules` | Array | `null` | Modules du réclamant ; laissés `null`, chargés pour un réclamant contrat |
|
|
140
|
+
| `idModule` | String \| Number | `null` | Module à préférer comme cible par défaut parmi ceux du réclamant |
|
|
141
|
+
| `nomenclatureFilter` | Function | `null` | Prédicat : quels motifs sont proposables ([détail](#restreindre-la-nomenclature)) |
|
|
142
|
+
| `aside` | Boolean | `true` | Affiche le volet latéral (ses onglets viennent des slots) |
|
|
143
|
+
| `nbSimilaires` | Number | `0` | Badge de l'onglet « Similarité » |
|
|
144
|
+
| `baseUrl` | String | `null` | API de cette instance (sinon `configureCresol`) |
|
|
145
|
+
| `token` | String | `null` | Jeton de cette instance (sinon `configureCresol`) |
|
|
146
|
+
|
|
147
|
+
Chaque prop porte **une seule responsabilité**. Les valeurs de la saisie passent par
|
|
148
|
+
`modelValue`, les choix possibles par `reclamants`, ce qui est modifiable par `readonly`, et la
|
|
149
|
+
présentation du réclamant par `reclamantMode`. Il n'y a donc pas de prop `provenance` : on la
|
|
150
|
+
passe dans `modelValue` (`{ provenance: 'M' }`).
|
|
151
|
+
|
|
152
|
+
`modelValue` n'est lu qu'**à l'ouverture** : le modifier ensuite ne réinitialise pas l'assistant.
|
|
153
|
+
Pour repartir d'un autre brouillon, on remonte le composant (`v-if` ou `:key`).
|
|
154
|
+
|
|
155
|
+
## Événements
|
|
156
|
+
|
|
157
|
+
| Événement | Quand | Charge utile |
|
|
158
|
+
| ------------------- | ----------------------------------------- | ------------------------------------------------------------ |
|
|
159
|
+
| `update:modelValue` | à chaque modification du brouillon | le brouillon |
|
|
160
|
+
| `reclamantChange` | l'utilisateur change de réclamant | l'entrée de `reclamants` choisie |
|
|
161
|
+
| `motifChange` | le motif ou les motifs associés changent | `{ idCategorie, idType, idNature, idMotif, motifsAssocies }` |
|
|
162
|
+
| `stepChange` | changement d'étape | `{ step, key, previous: { step, key } }` |
|
|
163
|
+
| `beforeCreate` | clic sur « Créer », **avant** l'appel API | `{ payload, cancel, waitUntil }` |
|
|
164
|
+
| `created` | création réussie (documents compris) | `(sollicitations, { payload, fichiers })` |
|
|
165
|
+
| `createError` | l'API a refusé la création | `{ error, message, payload }` |
|
|
166
|
+
| `error` | **toute** erreur affichable | `message` (chaîne) |
|
|
167
|
+
| `close` | (modale) fermeture demandée | — |
|
|
168
|
+
|
|
169
|
+
`reclamantChange`, `motifChange` et `stepChange` ne sont **pas émis pour l'état initial** : l'outil
|
|
170
|
+
le connaît déjà, puisque c'est lui qui l'a fourni. Ils rendent compte de ce que fait l'utilisateur.
|
|
171
|
+
|
|
172
|
+
### Le cycle de la création
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
clic « Créer »
|
|
176
|
+
│
|
|
177
|
+
├─ beforeCreate ── cancel() / waitUntil(false | rejet) ──▶ rien n'est envoyé, l'assistant reste ouvert
|
|
178
|
+
│
|
|
179
|
+
├─ POST creer_sollicitation ── échec ──▶ createError + error, l'assistant reste ouvert
|
|
180
|
+
│
|
|
181
|
+
├─ POST upload_sollicitation_ged (un par document, en parallèle)
|
|
182
|
+
│ échec d'un document ──▶ error (la création, elle, est acquise)
|
|
183
|
+
│
|
|
184
|
+
└─ created(sollicitations, { payload, fichiers }) (modale : puis close)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Pendant tout le cycle, l'assistant est sous overlay et un second clic est ignoré.
|
|
188
|
+
|
|
189
|
+
**Succès et échec sont séparés** (`created` / `createError`) : l'outil n'y fait pas la même
|
|
190
|
+
chose (fermer et recharger d'un côté, garder la saisie et expliquer de l'autre), et chacun
|
|
191
|
+
porte une charge utile différente. `error` reste le **canal unique des messages** pour qui ne
|
|
192
|
+
veut que toaster : il reçoit aussi les échecs de création, ceux des documents et ceux des
|
|
193
|
+
chargements. Règle simple :
|
|
194
|
+
|
|
195
|
+
- pour **afficher** une erreur, écouter `error` ;
|
|
196
|
+
- pour **réagir** à l'échec de la création (journaliser, proposer autre chose…), écouter
|
|
197
|
+
`createError`.
|
|
198
|
+
|
|
199
|
+
### `beforeCreate` : intervenir avant l'envoi
|
|
200
|
+
|
|
201
|
+
L'événement porte un objet :
|
|
202
|
+
|
|
203
|
+
| Clé | Rôle |
|
|
204
|
+
| -------------------- | ------------------------------------------------------------------------------ |
|
|
205
|
+
| `payload` | le corps qui va partir ([forme](#la-création)) ; **modifiable en place** |
|
|
206
|
+
| `cancel()` | annule la création |
|
|
207
|
+
| `waitUntil(promise)` | fait attendre l'envoi ; résolue à `false`, ou rejetée, elle annule la création |
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
// Confirmation
|
|
211
|
+
onBeforeCreate({ payload, waitUntil }) {
|
|
212
|
+
waitUntil(this.$confirm(`Créer ${payload.lignes.length} sollicitation(s) ?`))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Veto synchrone
|
|
216
|
+
onBeforeCreate({ payload, cancel }) {
|
|
217
|
+
if (!this.peutCreer(payload)) cancel()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Enrichir le payload
|
|
221
|
+
onBeforeCreate({ payload }) {
|
|
222
|
+
payload.lignes.forEach((ligne) => (ligne.description += `\n[${this.origine}]`))
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Une annulation est **silencieuse** : rien n'est émis ensuite. C'est l'outil qui l'a décidée, il
|
|
227
|
+
sait déjà pourquoi.
|
|
228
|
+
|
|
229
|
+
### `created` : le compte rendu
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
onCreated(sollicitations, { payload, fichiers }) {
|
|
233
|
+
// sollicitations : une par ligne, sérialisées par l'API ([{ id, idMotif, … }])
|
|
234
|
+
// fichiers : un compte rendu par document joint
|
|
235
|
+
// [{ idMotif, nom, idSollicitation, ok: true | false, message }]
|
|
236
|
+
const enEchec = fichiers.filter((f) => !f.ok)
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Un document en échec **n'annule pas** la création : il est signalé par `error` et marqué
|
|
241
|
+
`ok: false`, et l'utilisateur pourra le joindre depuis le détail de la sollicitation.
|
|
242
|
+
|
|
243
|
+
### Les autres événements
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
// Recharger ce qui dépend du réclamant (sollicitations similaires, évènements…)
|
|
247
|
+
onReclamantChange(reclamant) {}
|
|
248
|
+
|
|
249
|
+
// Suggestions, alertes métier propres à l'outil sur un motif
|
|
250
|
+
onMotifChange({ idMotif, motifsAssocies }) {}
|
|
251
|
+
|
|
252
|
+
// Suivi d'usage, aide contextuelle par étape
|
|
253
|
+
onStepChange({ key, previous }) {} // key : 'reclamant' | 'categorie' | 'cible' | 'details'
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Slots
|
|
257
|
+
|
|
258
|
+
| Slot | Contenu | Absent ⇒ |
|
|
259
|
+
| ------------------ | ----------------------------------------------- | --------------- |
|
|
260
|
+
| `aside-similaires` | onglet « Similarité » ; scopé (voir ci-dessous) | pas d'onglet |
|
|
261
|
+
| `aside-evenements` | onglet « Événements » | pas d'onglet |
|
|
262
|
+
| `aside` | remplace tout le volet latéral | volet à onglets |
|
|
263
|
+
|
|
264
|
+
Les deux volets appartiennent à l'**outil** : c'est lui qui interroge son API et rend ses cartes.
|
|
265
|
+
Le paquet ne lui fournit que le **scope** de la saisie en cours :
|
|
266
|
+
|
|
267
|
+
| Clé du scope | Valeur |
|
|
268
|
+
| --------------- | ----------------------------------------------------------------------- |
|
|
269
|
+
| `idMotif` | motif de la ligne principale |
|
|
270
|
+
| `codeProgramme` | programme de la localisation ciblée (nul quand la cible est la demande) |
|
|
271
|
+
| `idCible` | id de la demande ciblée (nul sinon) |
|
|
272
|
+
| `reclamant` | le réclamant sélectionné |
|
|
273
|
+
|
|
274
|
+
Le badge de l'onglet revient par la prop `nbSimilaires`.
|
|
275
|
+
|
|
276
|
+
## Le choix du réclamant
|
|
277
|
+
|
|
278
|
+
`reclamants` est une **liste ordonnée** d'entrées `{ type, id, libelle }` :
|
|
279
|
+
|
|
280
|
+
- `type` est le code `KRCLENT.RCLT` : `D` = demande, `U` = individu « Autre », `T` = tiers, tout
|
|
281
|
+
autre code = **contrat** (`L`, `P`…) ;
|
|
282
|
+
- `libelle` est ce qui est affiché (côté Cally : « Contrat », « Demande », « Partie externe ») ;
|
|
283
|
+
- il n'y a pas de clé technique : un réclamant s'identifie par son couple `type` + `id`.
|
|
284
|
+
|
|
285
|
+
Le premier de la liste est sélectionné, sauf si `modelValue.reclamant` (`{ type, id }`) en
|
|
286
|
+
désigne un autre. Le type choisi pilote la suite :
|
|
287
|
+
|
|
288
|
+
| Réclamant | Cible proposée |
|
|
289
|
+
| ------------------------- | ------------------------------------------------------------------------------ |
|
|
290
|
+
| contrat | le contrat lui-même, sur l'un de ses modules (chargés si `modules` est `null`) |
|
|
291
|
+
| demande (`D`) | toujours la demande, sans localisation |
|
|
292
|
+
| partie externe (`U`, `T`) | une localisation du patrimoine (pas de contrat propre) |
|
|
293
|
+
|
|
294
|
+
Présentation, selon `reclamantMode` :
|
|
295
|
+
|
|
296
|
+
| `reclamantMode` | Rendu |
|
|
297
|
+
| --------------- | --------------------------------------------------------------------- |
|
|
298
|
+
| `select` | liste déroulante (défaut) |
|
|
299
|
+
| `switch` | boutons côte à côte |
|
|
300
|
+
| `autocomplete` | `AmsomAutocomplete`, pour une liste longue (recherche sur le libellé) |
|
|
301
|
+
| `step` | une **étape dédiée**, avant la catégorisation |
|
|
302
|
+
|
|
303
|
+
Avec un seul réclamant, ou `'reclamant'` dans `readonly`, il est seulement affiché, quel que soit
|
|
304
|
+
le mode. En mode `step`, le stepper compte une étape de plus et `stepChange` émet
|
|
305
|
+
`key: 'reclamant'`.
|
|
306
|
+
|
|
307
|
+
## Champs figés (`readonly`)
|
|
308
|
+
|
|
309
|
+
Une seule prop, trois formes :
|
|
310
|
+
|
|
311
|
+
```vue
|
|
312
|
+
<!-- tout se saisit (défaut) -->
|
|
313
|
+
<creer-sollicitation-form :readonly="false" />
|
|
314
|
+
|
|
315
|
+
<!-- partiel : l'outil impose le réclamant et le canal -->
|
|
316
|
+
<creer-sollicitation-form :readonly="['reclamant', 'provenance']" />
|
|
317
|
+
|
|
318
|
+
<!-- tout est figé : l'utilisateur relit et valide -->
|
|
319
|
+
<creer-sollicitation-form readonly />
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
| Champ | Figé, il affiche… | … et l'assistant ne fait plus |
|
|
323
|
+
| ------------- | -------------------------------------------------------------------- | ----------------------------------------------- |
|
|
324
|
+
| `reclamant` | son libellé | — |
|
|
325
|
+
| `provenance` | le canal (icône + libellé) | — |
|
|
326
|
+
| `motif` | la chaîne catégorie › type › nature › motif ; motifs associés grisés | — |
|
|
327
|
+
| `cible` | un résumé par ligne | déduire la cible du réclamant ou de ses modules |
|
|
328
|
+
| `description` | le texte | — |
|
|
329
|
+
| `dateFaits` | la date | — |
|
|
330
|
+
| `responsable` | son nom, ou « Déterminé automatiquement » | suggérer un responsable d'après le paramétrage |
|
|
331
|
+
| `fichier` | le document, sans ajout ni suppression | — |
|
|
332
|
+
|
|
333
|
+
La liste est exportée (`READONLY_FIELDS`) et une valeur inconnue est signalée par Vue.
|
|
334
|
+
|
|
335
|
+
Un champ figé affiche **ce qu'on lui fournit**, par `reclamants` et `modelValue`. Les champs
|
|
336
|
+
obligatoires restent exigés pour avancer : figer `provenance`, `motif`, `cible` ou `description`
|
|
337
|
+
engage l'outil à les fournir, sans quoi l'utilisateur reste bloqué à l'étape. Un responsable figé
|
|
338
|
+
et vide est déterminé côté serveur.
|
|
339
|
+
|
|
340
|
+
## Restreindre la nomenclature
|
|
341
|
+
|
|
342
|
+
`nomenclatureFilter` est un **prédicat** qui reçoit le motif entier : on filtre sur n'importe
|
|
343
|
+
quel champ, pas seulement sur les identifiants.
|
|
344
|
+
|
|
345
|
+
```javascript
|
|
346
|
+
// Sur une demande, seuls certains motifs sont proposables (règle d'office de Cally).
|
|
347
|
+
(motif, { reclamant }) => reclamant?.type !== 'D' || MOTIFS_DEMANDE.includes(motif.id)
|
|
348
|
+
|
|
349
|
+
// N'importe quel champ du référentiel fait l'affaire.
|
|
350
|
+
(motif) => !motif.libelle.startsWith('[INTERNE]')
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Il s'applique à la recherche **et** à la sélection guidée, et il est rejoué quand le réclamant
|
|
354
|
+
change. Absent, toute la nomenclature active est proposée.
|
|
355
|
+
|
|
356
|
+
## Le brouillon (`v-model`)
|
|
357
|
+
|
|
358
|
+
```javascript
|
|
359
|
+
{
|
|
360
|
+
reclamant: { type, id },
|
|
361
|
+
provenance: 'T', // PROVENANCES_CONTACT
|
|
362
|
+
lignes: [ lignePrincipale, ...motifsAssociés ],
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Un brouillon **partiel** suffit : il est fusionné avec le squelette (`getDefaultDraft`,
|
|
367
|
+
`getDefaultLigne`, `getDefaultPatrimoine`, exportés), l'appelant n'a pas à connaître toutes les
|
|
368
|
+
clés.
|
|
369
|
+
|
|
370
|
+
Une ligne :
|
|
371
|
+
|
|
372
|
+
| Clé | Rôle |
|
|
373
|
+
| ---------------------------------------------- | ------------------------------------------------------------------- |
|
|
374
|
+
| `idCategorie`, `idType`, `idNature`, `idMotif` | nomenclature (hiérarchique : changer un niveau efface les suivants) |
|
|
375
|
+
| `cibleMode` | `contrat`, `patrimoine` ou `demande` (voir ci-dessous) |
|
|
376
|
+
| `patrimoine` | localisation : commune, adresse, étage, module… |
|
|
377
|
+
| `contratReclamant`, `contratCible` | contrat ciblé en mode `contrat` |
|
|
378
|
+
| `reprendreLocataireActuel` | viser l'occupant actuel du module plutôt que le contrat ciblé |
|
|
379
|
+
| `description`, `dateFaits` | détail (`dateFaits` en timestamp Unix) |
|
|
380
|
+
| `responsable` | collaborateur, ou son id ; `null` = déterminé côté serveur |
|
|
381
|
+
| `fichier` | document joint (`AmsomUploadFile`), envoyé après la création |
|
|
382
|
+
|
|
383
|
+
- **Cible** (`cibleMode`) :
|
|
384
|
+
- `contrat` : un contrat et l'un de ses modules. C'est celui du réclamant si
|
|
385
|
+
`contratReclamant`, sinon le `contratCible` trouvé par la recherche. Quand ce contrat n'occupe
|
|
386
|
+
plus le module, `reprendreLocataireActuel` bascule sur son occupant actuel ;
|
|
387
|
+
- `patrimoine` : une adresse (partie commune) et, facultativement, un module (le logement) ;
|
|
388
|
+
- `demande` : la demande elle-même, pour un réclamant demande.
|
|
389
|
+
- **Héritage des motifs associés** : `lignes[0]` porte toujours sa cible et son détail. Sur une
|
|
390
|
+
ligne associée :
|
|
391
|
+
- `description: null` = **hérite** de la principale, une valeur la surcharge ;
|
|
392
|
+
- la cible se surcharge par `cibleOverride` ;
|
|
393
|
+
- la date se surcharge par `dateFaitsOverride`, parce que `null` est une date valide
|
|
394
|
+
(« pas de date ») et ne peut pas marquer l'héritage ;
|
|
395
|
+
- le **responsable n'hérite jamais**.
|
|
396
|
+
|
|
397
|
+
## La création
|
|
398
|
+
|
|
399
|
+
`beforeCreate` expose le corps envoyé à `POST /cally2/creer_sollicitation`. L'héritage y est
|
|
400
|
+
**déjà résolu** : chaque ligne est autonome et porte les mêmes clés.
|
|
401
|
+
|
|
402
|
+
```javascript
|
|
403
|
+
{
|
|
404
|
+
typeReclamant: 'L',
|
|
405
|
+
reclamant: 2200523,
|
|
406
|
+
provenance: 'T',
|
|
407
|
+
lignes: [
|
|
408
|
+
{
|
|
409
|
+
categorie, type, nature, motif,
|
|
410
|
+
description, dateFaits, responsable, // responsable : id ou null
|
|
411
|
+
patrimoine, // absent quand la cible est la demande
|
|
412
|
+
codeTypeCible, idCible, // cible contractuelle explicite…
|
|
413
|
+
deduireCible // …ou à déduire du module par le serveur
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
L'API renvoie les sollicitations créées (une par ligne). Chaque document joint part ensuite par
|
|
420
|
+
`POST /cally2/upload_sollicitation_ged`, apparié à la sollicitation **du même motif**. Le fichier
|
|
421
|
+
ne figure jamais dans le corps de création.
|
|
422
|
+
|
|
423
|
+
## Exports
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
import {
|
|
427
|
+
CreerSollicitationForm,
|
|
428
|
+
CreerSollicitationModal,
|
|
429
|
+
configureCresol, // configuration du transport
|
|
430
|
+
resetCresolCache, // vide le cache des référentiels
|
|
431
|
+
getDefaultDraft, // squelettes du brouillon
|
|
432
|
+
getDefaultLigne,
|
|
433
|
+
getDefaultPatrimoine,
|
|
434
|
+
READONLY_FIELDS, // champs acceptés par `readonly`
|
|
435
|
+
PROVENANCES_CONTACT, // [{ id, libelle, icon }]
|
|
436
|
+
DEFAULT_PROVENANCE_CONTACT
|
|
437
|
+
} from '@amsom-habitat/cresol'
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Les référentiels et l'arbre patrimonial sont mis en **cache au niveau du module**, par API : deux
|
|
441
|
+
ouvertures de l'assistant ne rechargent rien. `resetCresolCache()` le vide (changement
|
|
442
|
+
d'utilisateur, tests).
|
|
443
|
+
|
|
444
|
+
## Endpoints consommés
|
|
445
|
+
|
|
446
|
+
| Méthode | Chemin | Usage |
|
|
447
|
+
| ------- | ----------------------------------------------------------- | -------------------------------- |
|
|
448
|
+
| GET | `/v2/sollicitation/{motifs,categories,types,natures,etats}` | Nomenclature (sans jeton) |
|
|
449
|
+
| GET | `/v2/sollicitation/responsables` | Paramétrage des responsables |
|
|
450
|
+
| GET | `/collaborateurs?actif=true` | Responsables proposables |
|
|
451
|
+
| GET | `/cally2/patrimoine_communes` · `_entrees` · `_modules` | Arbre patrimonial (cible) |
|
|
452
|
+
| GET | `/cally2/contrat_modules` | Modules d'un contrat |
|
|
453
|
+
| GET | `/cockpit/circuit_achat/contrats` | Recherche de contrat (n° ou nom) |
|
|
454
|
+
| POST | `/cally2/creer_sollicitation` | Création (1 à n lignes) |
|
|
455
|
+
| POST | `/cally2/upload_sollicitation_ged` | Document joint à une ligne |
|
|
456
|
+
|
|
457
|
+
## Développement
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
npm i
|
|
461
|
+
cp .env.example .env # VITE_API_URL=http://…
|
|
462
|
+
npm run storybook # http://localhost:6006
|
|
463
|
+
npm run lint
|
|
464
|
+
npm run build
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Les stories parlent à une **vraie API** : la nomenclature, le patrimoine et les collaborateurs
|
|
468
|
+
viennent du serveur désigné par `.env`. Les props `baseUrl` et `token` restent disponibles en
|
|
469
|
+
contrôle de story pour surcharger ponctuellement. Le journal sous chaque story liste les
|
|
470
|
+
événements émis, avec leur charge utile.
|
|
471
|
+
|
|
472
|
+
| Story | Montre |
|
|
473
|
+
| -------------------------------------------------------------- | ------------------------------ |
|
|
474
|
+
| `Contrat` / `Demande` | un réclamant de chaque sorte |
|
|
475
|
+
| `ReclamantSwitch` / `ReclamantAutocomplete` / `ReclamantEtape` | les présentations du réclamant |
|
|
476
|
+
| `ReclamantEtProvenanceFiges` | `readonly` partiel |
|
|
477
|
+
| `ConfirmationAvantCreation` | `beforeCreate` + `waitUntil` |
|
|
478
|
+
| `BrouillonPrealimente` | `modelValue` partiel |
|
|
479
|
+
| `SansVolet` | `aside: false` |
|
|
480
|
+
|
|
481
|
+
### Organisation du code
|
|
482
|
+
|
|
483
|
+
```text
|
|
484
|
+
src/
|
|
485
|
+
CreerSollicitationForm.vue racine : fournit brouillon, données et options ; porte la création
|
|
486
|
+
CreerSollicitationModal.vue coquille modale (props et événements relayés)
|
|
487
|
+
props.js contrat public unique (props + événements)
|
|
488
|
+
components/ étapes et blocs de saisie (lisent le brouillon injecté)
|
|
489
|
+
composables/
|
|
490
|
+
useSollicitationDraft.js le brouillon : état, validation, champs figés, payload
|
|
491
|
+
useCresolData.js référentiels et patrimoine (cache module, par API)
|
|
492
|
+
useCresolContext.js options de l'outil (injectées, sans prop-drilling)
|
|
493
|
+
api/ un fichier par domaine ; transport/ = axios-overlay + jeton
|
|
494
|
+
utils/ fonctions pures (texte, dates, erreurs, icônes)
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Toute la logique de saisie (héritage, validation, champs figés, payload) vit dans
|
|
498
|
+
`useSollicitationDraft` : les composants ne font qu'afficher et appeler ses setters.
|
|
499
|
+
|
|
500
|
+
### Publier
|
|
501
|
+
|
|
502
|
+
Le numéro de `package.json` doit avoir son entrée `## VX.Y.…` dans `changelog.md`
|
|
503
|
+
(`check-version.sh`).
|
|
504
|
+
|
|
505
|
+
```bash
|
|
506
|
+
make publish # main : lint, build, tag, npm publish
|
|
507
|
+
make alpha_publish # dev : version prerelease beta
|
|
508
|
+
```
|