diff --git a/frontend/i18n/locales/fr.json b/frontend/i18n/locales/fr.json
index c09f4e9..6e99ccc 100644
--- a/frontend/i18n/locales/fr.json
+++ b/frontend/i18n/locales/fr.json
@@ -133,12 +133,6 @@
"duplicateCompany": "Un client portant ce nom de société existe déjà.",
"main": {
"companyName": "Nom du client (Entreprise)",
- "firstName": "Prénom du contact principal",
- "lastName": "Nom du contact principal",
- "email": "Email",
- "phonePrimary": "Téléphone",
- "phoneSecondary": "Téléphone (2)",
- "addPhone": "Ajouter un numéro",
"categories": "Catégorie",
"relation": "Distributeur / Courtier",
"relationDistributor": "Dépend du distributeur",
diff --git a/frontend/modules/commercial/pages/clients/[id]/edit.vue b/frontend/modules/commercial/pages/clients/[id]/edit.vue
index 06a2f77..a109988 100644
--- a/frontend/modules/commercial/pages/clients/[id]/edit.vue
+++ b/frontend/modules/commercial/pages/clients/[id]/edit.vue
@@ -29,16 +29,6 @@
:required="true"
:readonly="businessReadonly"
/>
-
-
main.categoryIris = v.map(String)"
/>
-
-
-
{
|| (main.relationType === 'distributeur' && filled(main.distributorIri))
|| (main.relationType === 'courtier' && filled(main.brokerIri))
return filled(main.companyName)
- && filled(main.email)
- && filled(main.phonePrimary)
- && (filled(main.firstName) || filled(main.lastName))
&& main.categoryIris.length >= 1
&& relationValid
})
diff --git a/frontend/modules/commercial/pages/clients/[id]/index.vue b/frontend/modules/commercial/pages/clients/[id]/index.vue
index 1951700..18909fa 100644
--- a/frontend/modules/commercial/pages/clients/[id]/index.vue
+++ b/frontend/modules/commercial/pages/clients/[id]/index.vue
@@ -52,16 +52,6 @@
:label="t('commercial.clients.form.main.companyName')"
readonly
/>
-
-
-
-
client.value?.companyName ?? t('commercial.cl
const relation = computed(() => (client.value ? relationOf(client.value) : { type: null, name: null }))
const categoryIris = computed(() => (client.value?.categories ?? []).map(c => c['@id']))
-// Telephones du formulaire principal, formates XX XX XX XX XX (RG d'affichage).
-const mainPhones = computed(() =>
- [client.value?.phonePrimary, client.value?.phoneSecondary]
- .filter((p): p is string => Boolean(p))
- .map(formatPhoneFR),
-)
-
const information = computed(() => ({
description: client.value?.description ?? null,
competitors: client.value?.competitors ?? null,
diff --git a/frontend/modules/commercial/pages/clients/new.vue b/frontend/modules/commercial/pages/clients/new.vue
index 7bbdbe6..41b015d 100644
--- a/frontend/modules/commercial/pages/clients/new.vue
+++ b/frontend/modules/commercial/pages/clients/new.vue
@@ -23,16 +23,6 @@
:required="true"
:readonly="mainLocked"
/>
-
-
main.categoryIris = v.map(String)"
/>
-
-
-
([''])
-
-/** Revele le 2e numero (le bouton « + » disparait une fois a 2, RG-1.02). */
-function addMainPhone(): void {
- if (mainPhones.value.length === 1) {
- mainPhones.value.push('')
- }
-}
-
// Pas d'option « Aucun » : le select est vide par defaut (relationType = null).
const relationOptions = computed(() => [
{ value: 'distributeur', label: t('commercial.clients.form.main.relationDistributor') },
@@ -472,10 +426,11 @@ const relationOptions = computed(() => [
])
// Validation du formulaire principal (gate le bouton « Valider ») :
-// - companyName / email / telephone principal / >= 1 categorie obligatoires ;
-// - RG-1.01 : nom OU prenom du contact principal ;
-// - relation Distributeur/Courtier obligatoire (un des deux), ET le nom
-// correspondant obligatoire selon le choix (spec fonctionnelle).
+// - companyName / >= 1 categorie obligatoires ;
+// - relation Distributeur/Courtier optionnelle, mais le nom correspondant
+// devient requis si l'un des deux est choisi (spec fonctionnelle).
+// Les coordonnees de contact ne sont plus saisies ici : elles vivent dans
+// l'onglet Contacts (RG-1.05/1.14 garantissent >= 1 contact valide).
const isMainValid = computed(() => {
const filled = (v: string | null | undefined) => v !== null && v !== undefined && v.trim() !== ''
// Relation Distributeur/Courtier OPTIONNELLE ; mais si « Depend du
@@ -485,9 +440,6 @@ const isMainValid = computed(() => {
|| (main.relationType === 'distributeur' && filled(main.distributorIri))
|| (main.relationType === 'courtier' && filled(main.brokerIri))
return filled(main.companyName)
- && filled(main.email)
- && filled(mainPhones.value[0])
- && (filled(main.firstName) || filled(main.lastName))
&& main.categoryIris.length >= 1
&& relationValid
})
@@ -512,11 +464,6 @@ async function submitMain(): Promise {
try {
const payload: Record = {
companyName: main.companyName,
- firstName: main.firstName || null,
- lastName: main.lastName || null,
- email: main.email,
- phonePrimary: mainPhones.value[0] || null,
- phoneSecondary: mainPhones.value[1] || null,
categories: main.categoryIris,
distributor: main.relationType === 'distributeur' ? main.distributorIri : null,
broker: main.relationType === 'courtier' ? main.brokerIri : null,
@@ -528,18 +475,8 @@ async function submitMain(): Promise {
})
clientId.value = created.id
- // Reaffiche les valeurs normalisees renvoyees par le serveur.
+ // Reaffiche la valeur normalisee renvoyee par le serveur.
main.companyName = created.companyName ?? main.companyName
- main.firstName = created.firstName ?? null
- main.lastName = created.lastName ?? null
- main.email = created.email ?? main.email
- // Reaffiche les telephones normalises (reformates via formatPhoneFR).
- const normalizedPhones = [formatPhoneFR(created.phonePrimary), formatPhoneFR(created.phoneSecondary)]
- .filter(p => p !== '')
- mainPhones.value = normalizedPhones.length > 0 ? normalizedPhones : ['']
-
- // Pre-remplit le 1er contact a partir du formulaire principal (editable).
- prefillFirstContact()
mainLocked.value = true
unlockedIndex.value = 0
@@ -652,18 +589,10 @@ async function submitInformation(): Promise {
}
// ── Onglet Contact ──────────────────────────────────────────────────────────
+// Au moins un bloc Contact vide au depart : c'est desormais le seul point de
+// saisie des coordonnees (le bloc principal ne porte plus de contact inline).
const contacts = ref([emptyContact()])
-/** Pre-remplit le 1er contact depuis le formulaire principal (apres creation). */
-function prefillFirstContact(): void {
- const first = contacts.value[0]
- if (!first) return
- first.lastName = main.lastName
- first.firstName = main.firstName
- first.email = main.email
- first.phonePrimary = mainPhones.value[0] ?? null
-}
-
// « + Nouveau contact » desactive tant que le dernier bloc n'a ni nom ni prenom.
const canAddContact = computed(() => {
const last = contacts.value[contacts.value.length - 1]
@@ -945,11 +874,6 @@ function runConfirm(): void {
interface ClientResponse {
id: number
companyName: string | null
- firstName: string | null
- lastName: string | null
- email: string | null
- phonePrimary: string | null
- phoneSecondary: string | null
}
interface ContactResponse {
diff --git a/frontend/modules/commercial/utils/__tests__/clientEdit.spec.ts b/frontend/modules/commercial/utils/__tests__/clientEdit.spec.ts
index 5de9d8b..b0e5ea4 100644
--- a/frontend/modules/commercial/utils/__tests__/clientEdit.spec.ts
+++ b/frontend/modules/commercial/utils/__tests__/clientEdit.spec.ts
@@ -22,12 +22,6 @@ import type { AddressFormDraft, ContactFormDraft, RibFormDraft } from '~/modules
function mainDraft(overrides: Partial = {}): MainFormDraft {
return {
companyName: 'ACME',
- firstName: 'Jean',
- lastName: 'Dupont',
- email: 'jean@acme.fr',
- phonePrimary: '05 49 11 22 33',
- phoneSecondary: null,
- hasSecondaryPhone: false,
categoryIris: ['/api/categories/1'],
relationType: null,
distributorIri: null,
@@ -64,9 +58,10 @@ function accountingDraft(overrides: Partial = {}): Accounti
}
// Champs de chaque groupe de serialisation (miroir back ClientProcessor).
+// Le contact inline (nom/prenom/telephones/email) ne fait plus partie du groupe
+// main : les coordonnees vivent desormais sur la sous-ressource ClientContact.
const MAIN_KEYS = [
- 'companyName', 'firstName', 'lastName', 'email', 'phonePrimary',
- 'phoneSecondary', 'categories', 'distributor', 'broker', 'triageService',
+ 'companyName', 'categories', 'distributor', 'broker', 'triageService',
]
const INFORMATION_KEYS = [
'description', 'competitors', 'foundedAt', 'employeesCount',
@@ -104,11 +99,6 @@ describe('buildMainPayload — scoping strict groupe client:write:main', () => {
expect(payload.distributor).toBeNull()
expect(payload.broker).toBeNull()
})
-
- it('telephone secondaire non revele : envoie null meme si une valeur traine', () => {
- const payload = buildMainPayload(mainDraft({ hasSecondaryPhone: false, phoneSecondary: '06 00 00 00 00' }))
- expect(payload.phoneSecondary).toBeNull()
- })
})
describe('buildInformationPayload — scoping strict groupe client:write:information', () => {
@@ -168,19 +158,16 @@ describe('buildContactPayload / buildAddressPayload / buildRibPayload', () => {
})
describe('mapMainDraft — pre-remplissage bloc principal', () => {
- it('formate les telephones, resout la relation et extrait les IRI', () => {
+ it('resout la relation et extrait les IRI (sans contact inline)', () => {
const client = {
'@id': '/api/clients/1', id: 1,
- companyName: 'ACME', firstName: 'Jean', lastName: 'Dupont', email: 'jean@acme.fr',
- phonePrimary: '0549112233', phoneSecondary: '0600000000', triageService: true,
+ companyName: 'ACME', triageService: true,
categories: [{ '@id': '/api/categories/1', code: 'SECTEUR' }],
distributor: { '@id': '/api/clients/9', companyName: 'DISTRIB' },
} as ClientDetail
const draft = mapMainDraft(client)
- expect(draft.phonePrimary).toBe('05 49 11 22 33')
- expect(draft.phoneSecondary).toBe('06 00 00 00 00')
- expect(draft.hasSecondaryPhone).toBe(true)
+ expect(draft.companyName).toBe('ACME')
expect(draft.categoryIris).toEqual(['/api/categories/1'])
expect(draft.relationType).toBe('distributeur')
expect(draft.distributorIri).toBe('/api/clients/9')
@@ -191,7 +178,6 @@ describe('mapMainDraft — pre-remplissage bloc principal', () => {
it('gere les cles omises (skip_null_values) sans planter', () => {
const draft = mapMainDraft({ '@id': '/api/clients/2', id: 2 } as ClientDetail)
expect(draft.companyName).toBeNull()
- expect(draft.hasSecondaryPhone).toBe(false)
expect(draft.categoryIris).toEqual([])
expect(draft.relationType).toBeNull()
expect(draft.triageService).toBe(false)
diff --git a/frontend/modules/commercial/utils/clientConsultation.ts b/frontend/modules/commercial/utils/clientConsultation.ts
index a5b0ada..1b3761f 100644
--- a/frontend/modules/commercial/utils/clientConsultation.ts
+++ b/frontend/modules/commercial/utils/clientConsultation.ts
@@ -93,11 +93,6 @@ export interface RelatedClientRead extends HydraRef {
export interface ClientDetail extends HydraRef {
id: number
companyName?: string | null
- firstName?: string | null
- lastName?: string | null
- phonePrimary?: string | null
- phoneSecondary?: string | null
- email?: string | null
triageService?: boolean
isArchived?: boolean
categories?: CategoryRead[]
diff --git a/frontend/modules/commercial/utils/clientEdit.ts b/frontend/modules/commercial/utils/clientEdit.ts
index 9031076..6b949d7 100644
--- a/frontend/modules/commercial/utils/clientEdit.ts
+++ b/frontend/modules/commercial/utils/clientEdit.ts
@@ -24,23 +24,16 @@ import {
type ClientDetail,
} from '~/modules/commercial/utils/clientConsultation'
import type { AddressFormDraft, ContactFormDraft, RibFormDraft } from '~/modules/commercial/types/clientForm'
-import { formatPhoneFR } from '~/shared/utils/phone'
/**
* Etat « plat » du bloc principal (groupe client:write:main). Distinct des
* brouillons Contact : ces champs vivent sur le Client lui-meme (companyName,
- * contact principal, telephones, email, categories, relation, triage), pas sur
- * une sous-ressource ClientContact.
+ * categories, relation, triage), pas sur une sous-ressource ClientContact. Les
+ * coordonnees de contact (nom, prenom, telephones, email) ne sont plus portees
+ * par le Client : elles vivent exclusivement dans l'onglet Contacts.
*/
export interface MainFormDraft {
companyName: string | null
- firstName: string | null
- lastName: string | null
- email: string | null
- phonePrimary: string | null
- phoneSecondary: string | null
- /** UI : le 2e numero a ete revele (ou existait deja au chargement). */
- hasSecondaryPhone: boolean
/** IRI des categories rattachees (M2M). */
categoryIris: string[]
relationType: 'distributeur' | 'courtier' | null
@@ -96,22 +89,15 @@ export interface TabEditability {
// ── Pre-remplissage (GET detail -> brouillons) ──────────────────────────────
/**
- * Mappe le detail client vers le brouillon du bloc principal. Les telephones
- * sont reformates XX XX XX XX XX (RG d'affichage). La relation Distributeur/
- * Courtier est resolue par exclusivite (RG-1.03) et son IRI extrait de l'embed.
+ * Mappe le detail client vers le brouillon du bloc principal. La relation
+ * Distributeur/Courtier est resolue par exclusivite (RG-1.03) et son IRI extrait
+ * de l'embed.
*/
export function mapMainDraft(client: ClientDetail): MainFormDraft {
const relation = relationOf(client)
- const phoneSecondary = client.phoneSecondary ?? null
return {
companyName: client.companyName ?? null,
- firstName: client.firstName ?? null,
- lastName: client.lastName ?? null,
- email: client.email ?? null,
- phonePrimary: client.phonePrimary ? formatPhoneFR(client.phonePrimary) : null,
- phoneSecondary: phoneSecondary ? formatPhoneFR(phoneSecondary) : null,
- hasSecondaryPhone: phoneSecondary !== null && phoneSecondary !== '',
categoryIris: (client.categories ?? []).map(c => c['@id']),
relationType: relation.type,
distributorIri: iriOf(client.distributor),
@@ -157,11 +143,6 @@ export function mapAccountingFormDraft(client: ClientDetail): AccountingFormDraf
export function buildMainPayload(main: MainFormDraft): Record {
return {
companyName: main.companyName,
- firstName: main.firstName || null,
- lastName: main.lastName || null,
- email: main.email,
- phonePrimary: main.phonePrimary || null,
- phoneSecondary: main.hasSecondaryPhone ? (main.phoneSecondary || null) : null,
categories: main.categoryIris,
distributor: main.relationType === 'distributeur' ? main.distributorIri : null,
broker: main.relationType === 'courtier' ? main.brokerIri : null,