fix(front) : meme correctif champ requis vide en edition client (M1)

Port du fix fournisseur (blankEmptyRequired + flag forUpdate) a l'edition client :
en PATCH d'une ligne existante, un champ requis vide (companyName / postalCode /
city / street / label / bic / iban) est envoye en '' au lieu d'etre omis, sinon le
merge-patch garde l'ancienne valeur (faux 200). Creation (POST) inchangee (omit).
This commit is contained in:
2026-06-11 08:36:54 +02:00
parent 8eff37186d
commit 1f22fd142a
4 changed files with 101 additions and 14 deletions
+36 -11
View File
@@ -23,6 +23,7 @@ import {
} from '~/modules/commercial/utils/clientConsultation'
import {
ADDRESS_REQUIRED_NON_NULLABLE_KEYS,
blankEmptyRequired,
MAIN_REQUIRED_NON_NULLABLE_KEYS,
omitEmptyRequired,
RIB_REQUIRED_NON_NULLABLE_KEYS,
@@ -139,12 +140,35 @@ export function mapAccountingFormDraft(client: ClientDetail): AccountingFormDraf
// ── Scoping strict des payloads PATCH ────────────────────────────────────────
/**
* Options de construction d'un payload d'ecriture.
* - `forUpdate: false` (defaut, CREATION/POST) : champs requis vides OMIS -> 422
* NotBlank (le back ne reçoit pas la cle, la propriete garde son defaut).
* - `forUpdate: true` (EDITION/PATCH d'une ligne existante) : champs requis vides
* envoyes en `''` -> 422 NotBlank (sinon une cle omise laisse la valeur serveur
* inchangee, faux 200 — cf. blankEmptyRequired).
*/
export interface BuildPayloadOptions {
forUpdate?: boolean
}
/** Selectionne le finaliseur des champs requis selon création (omit) vs édition (blank). */
function finalizeRequired<T extends Record<string, unknown>>(
payload: T,
requiredKeys: readonly string[],
options: BuildPayloadOptions,
): T {
return options.forUpdate
? blankEmptyRequired(payload, requiredKeys)
: omitEmptyRequired(payload, requiredKeys)
}
/**
* Payload du bloc principal — groupe client:write:main UNIQUEMENT. La relation
* Distributeur/Courtier est mutuellement exclusive (RG-1.03) : on ne renseigne
* que la FK correspondant au type choisi, l'autre est forcee a null.
*/
export function buildMainPayload(main: MainFormDraft): Record<string, unknown> {
export function buildMainPayload(main: MainFormDraft, options: BuildPayloadOptions = {}): Record<string, unknown> {
// companyName omis si vide -> 422 NotBlank au lieu d'un 400 de type (ERP-119).
// relationType : champ transitoire (non persiste cote back) qui porte
// l'intention UI « ce client depend d'un distributeur / courtier ». Il sert
@@ -152,14 +176,14 @@ export function buildMainPayload(main: MainFormDraft): Record<string, unknown> {
// la FK correspondante devient obligatoire -> 422 sur distributor / broker.
// Sans equivalent derivable cote back (FK nullable), c'est la seule facon de
// rester sur « on soumet, le back tranche » plutot qu'une garde front-only.
return omitEmptyRequired({
return finalizeRequired({
companyName: main.companyName,
categories: main.categoryIris,
relationType: main.relationType,
distributor: main.relationType === 'distributeur' ? main.distributorIri : null,
broker: main.relationType === 'courtier' ? main.brokerIri : null,
triageService: main.triageService,
}, MAIN_REQUIRED_NON_NULLABLE_KEYS)
}, MAIN_REQUIRED_NON_NULLABLE_KEYS, options)
}
/** Payload de l'onglet Information — groupe client:write:information UNIQUEMENT. */
@@ -211,9 +235,10 @@ export function buildContactPayload(contact: ContactFormDraft): Record<string, u
export function buildAddressPayload(
address: AddressFormDraft,
isBillingEmailRequired: boolean,
options: BuildPayloadOptions = {},
): Record<string, unknown> {
// postalCode / city / street omis si vides -> 422 NotBlank (ERP-119).
return omitEmptyRequired({
// postalCode / city / street : omis a la creation, `''` en edition -> 422 NotBlank (ERP-119).
return finalizeRequired({
isProspect: address.isProspect,
isDelivery: address.isDelivery,
isBilling: address.isBilling,
@@ -229,18 +254,18 @@ export function buildAddressPayload(
contacts: address.contactIris,
billingEmail: isBillingEmailRequired ? (address.billingEmail || null) : null,
billingEmailSecondary: isBillingEmailRequired && address.hasSecondaryBillingEmail ? (address.billingEmailSecondary || null) : null,
}, ADDRESS_REQUIRED_NON_NULLABLE_KEYS)
}, ADDRESS_REQUIRED_NON_NULLABLE_KEYS, options)
}
/** Payload d'un RIB (sous-ressource client_rib). */
export function buildRibPayload(rib: RibFormDraft): Record<string, unknown> {
// label / bic / iban omis si vides -> 422 NotBlank au lieu d'un 400 de type
// sur un RIB partiel (ex. IBAN seul). ERP-119.
return omitEmptyRequired({
export function buildRibPayload(rib: RibFormDraft, options: BuildPayloadOptions = {}): Record<string, unknown> {
// label / bic / iban : omis a la creation, `''` en edition -> 422 NotBlank au lieu
// d'un 400 de type (ou d'un faux 200 PATCH qui garderait l'ancienne valeur). ERP-119.
return finalizeRequired({
label: rib.label,
bic: rib.bic,
iban: rib.iban,
}, RIB_REQUIRED_NON_NULLABLE_KEYS)
}, RIB_REQUIRED_NON_NULLABLE_KEYS, options)
}
// ── Gating par permission ────────────────────────────────────────────────────