feat(front) : regles metier + types + stub autocomplete creation client (ERP-63)

- clientFormRules : RG-1.05/1.14 (contacts), RG-1.06/07/08/11 (exclusivite
  adresse + email facturation), RG-1.12/1.13 (banque/RIB selon type de
  reglement), construction des onglets (gating Comptabilite). 18 tests Vitest.
- types/clientForm : brouillons locaux (contact/adresse/RIB) + fabriques.
- useAddressAutocomplete : STUB ERP-63 conforme a la signature ERP-66 (mode
  degrade), a remplacer par l'implementation BAN d'ERP-66.

Note : RG-1.04 (Information obligatoire pour la Commerciale) volontairement non
miroitee cote front (le payload /api/me ne porte pas le code de role ; Bureau et
Commerciale partagent les memes permissions). Le back l'applique de maniere fiable.
This commit is contained in:
2026-06-02 17:20:19 +02:00
parent 583d634a83
commit 1193832505
4 changed files with 445 additions and 0 deletions
@@ -0,0 +1,60 @@
// STUB ERP-63 — remplacé par l'implémentation BAN d'ERP-66.
//
// Ce fichier appartient fonctionnellement à ERP-66 (#66). ERP-63 n'en livre
// qu'un STUB pour ne pas se bloquer : la vraie implémentation (appels
// api-adresse.data.gouv.fr) viendra remplacer le CORPS des deux méthodes SANS
// changer leur signature ni l'usage côté composant.
//
// Contrat figé par ERP-66 (c'est lui qui fait foi) :
// searchCity(postalCode) -> liste { city, postalCode }
// searchAddress(query, cp?) -> liste { label, street, postalCode, city }
// En cas d'erreur/timeout, la méthode THROW. Le composant catch l'erreur,
// affiche un toast d'avertissement et bascule en saisie libre (MalioInputText).
//
// Comportement du stub : les deux méthodes throw systématiquement → l'onglet
// Adresse part directement en mode dégradé (Ville + Adresse en saisie libre,
// Code postal saisi manuellement). Aucun appel réseau n'est émis ici.
/** Une suggestion de ville renvoyée à partir d'un code postal. */
export interface CitySuggestion {
city: string
postalCode: string
}
/** Une suggestion d'adresse complète (saisie assistée du champ « Adresse »). */
export interface AddressSuggestion {
label: string
street: string
postalCode: string
city: string
}
export interface AddressAutocomplete {
searchCity(postalCode: string): Promise<CitySuggestion[]>
searchAddress(query: string, postalCode?: string): Promise<AddressSuggestion[]>
}
/** Erreur signalant que le service d'autocomplétion BAN n'est pas disponible. */
export class AddressAutocompleteUnavailableError extends Error {
constructor() {
// Message technique (non affiché tel quel) : le composant remonte son
// propre libellé i18n. Sert au debug / aux logs uniquement.
super('Address autocomplete (BAN) is not available yet — ERP-66 stub.')
this.name = 'AddressAutocompleteUnavailableError'
}
}
/**
* STUB : renvoie un composable conforme au contrat ERP-66 dont les méthodes
* échouent toujours, forçant le mode dégradé côté onglet Adresse.
*/
export function useAddressAutocomplete(): AddressAutocomplete {
return {
async searchCity(_postalCode: string): Promise<CitySuggestion[]> {
throw new AddressAutocompleteUnavailableError()
},
async searchAddress(_query: string, _postalCode?: string): Promise<AddressSuggestion[]> {
throw new AddressAutocompleteUnavailableError()
},
}
}