fix(commercial) : validation onglet compta LCR + controle croise BIC/IBAN (ERP-118) #78

Merged
tristan merged 2 commits from feature/ERP-118-fix-validation-onglet-compta-lcr into develop 2026-06-09 08:44:12 +00:00
2 changed files with 51 additions and 48 deletions
Showing only changes of commit c73111121f - Show all commits
@@ -956,35 +956,21 @@ function askRemoveRib(index: number): void {
}
/**
* Valide l'onglet Comptabilite : PATCH des scalaires (groupe client:write:accounting,
* exige accounting.manage cote back) PUIS DELETE/POST/PATCH des RIB sur la
* sous-ressource. Aucun champ main/information dans le payload (mode strict
* RG-1.28 : sinon 403 sur tout le payload).
* Valide l'onglet Comptabilite : POST/PATCH des RIB sur la sous-ressource PUIS
* PATCH des scalaires (groupe client:write:accounting, exige accounting.manage cote
* back) PUIS DELETE des RIB retires. Les RIB crees d'abord : le back valide RG-1.13
* (LCR => au moins un RIB persiste) sur le PATCH scalaires ; les suppressions en
* dernier (le guard back n'autorise la suppression du dernier RIB qu'une fois quitte
* LCR). Aucun champ main/information dans le payload (mode strict RG-1.28 : sinon
* 403 sur tout le payload).
*/
async function submitAccounting(): Promise<void> {
if (accountingReadonly.value || !canValidateAccounting.value || tabSubmitting.value) return
tabSubmitting.value = true
accountingErrors.clearErrors()
// Reset des erreurs RIB des le debut : l'etape 1 (PATCH scalaires) peut
// echouer et `return` avant submitRows (qui porte sinon le reset), laissant
// des erreurs de RIB obsoletes affichees sous les blocs.
ribErrors.value = []
try {
// 1) PATCH des scalaires comptables (erreurs inline sur leurs champs).
try {
await api.patch(`/clients/${clientId}`, buildAccountingPayload(accounting, isBankRequired.value), { toast: false })
}
catch (error) {
accountingErrors.handleApiError(error, { fallbackMessage: t('commercial.clients.toast.error') })
return
}
for (const id of removedRibIds.value) {
await api.delete(`/client_ribs/${id}`, {}, { toast: false })
}
removedRibIds.value = []
// 2) POST/PATCH des RIB (erreurs inline par ligne, tous les blocs tentes).
// 1) POST/PATCH des RIB d'abord (erreurs inline par ligne, tous les blocs
// tentes). Le back exige >=1 RIB persiste pour valider une LCR a l'etape 2.
// Seuls les blocs RIB TOTALEMENT vides sont ignores : un RIB partiel (ex.
// IBAN seul) est soumis -> 422 NotBlank (label / bic / iban) inline.
const ribHasError = await submitRows(
@@ -1011,6 +997,23 @@ async function submitAccounting(): Promise<void> {
rib => rib.id === null && isRibBlank(rib),
)
if (ribHasError) return
// 2) PATCH des scalaires comptables (erreurs inline sur leurs champs).
try {
await api.patch(`/clients/${clientId}`, buildAccountingPayload(accounting, isBankRequired.value), { toast: false })
}
catch (error) {
accountingErrors.handleApiError(error, { fallbackMessage: t('commercial.clients.toast.error') })
return
}
// 3) DELETE des RIB retires : APRES le PATCH scalaires (si on quitte LCR, le
// guard back n'autorise la suppression du dernier RIB qu'une fois le type change).
for (const id of removedRibIds.value) {
await api.delete(`/client_ribs/${id}`, {}, { toast: false })
}
removedRibIds.value = []
toast.success({ title: t('commercial.clients.toast.updateSuccess') })
}
catch (e) {
@@ -939,37 +939,20 @@ function askRemoveRib(index: number): void {
}
/**
* Valide l'onglet Comptabilite : PATCH des scalaires (groupe client:write:accounting)
* PUIS POST des RIB sur /clients/{id}/ribs. Deux appels distincts (mode strict
* RG-1.28 : il n'existe pas d'endpoint /accounting, cf. recon back).
* Valide l'onglet Comptabilite : POST/PATCH des RIB sur /clients/{id}/ribs PUIS
* PATCH des scalaires (groupe client:write:accounting). Les RIB d'abord : le back
* valide RG-1.13 (LCR => au moins un RIB persiste) sur le PATCH scalaires, les RIB
* doivent donc exister en base AVANT (sinon 422 « Au moins un RIB est obligatoire
* pour le type de reglement LCR »). Deux appels distincts (mode strict RG-1.28 :
* il n'existe pas d'endpoint /accounting, cf. recon back).
*/
async function submitAccounting(): Promise<void> {
if (clientId.value === null || !canValidateAccounting.value || tabSubmitting.value) return
tabSubmitting.value = true
accountingErrors.clearErrors()
// Reset des erreurs RIB des le debut : l'etape 1 (PATCH scalaires) peut
// echouer et `return` avant submitRows (qui porte sinon le reset), laissant
// des erreurs de RIB obsoletes affichees sous les blocs.
ribErrors.value = []
try {
// 1) PATCH des scalaires comptables (erreurs inline sur leurs champs).
try {
await api.patch(`/clients/${clientId.value}`, {
siren: accounting.siren || null,
accountNumber: accounting.accountNumber || null,
tvaMode: accounting.tvaModeIri,
nTva: accounting.nTva || null,
paymentDelay: accounting.paymentDelayIri,
paymentType: accounting.paymentTypeIri,
bank: isBankRequired.value ? accounting.bankIri : null,
}, { toast: false })
}
catch (error) {
accountingErrors.handleApiError(error, { fallbackMessage: t('commercial.clients.toast.error') })
return
}
// 2) POST/PATCH des RIB (erreurs inline par ligne, tous les blocs tentes).
// 1) POST/PATCH des RIB d'abord (erreurs inline par ligne, tous les blocs
// tentes). Le back exige >=1 RIB persiste pour valider une LCR a l'etape 2.
// Seuls les blocs RIB TOTALEMENT vides sont ignores : un RIB partiel (ex.
// IBAN seul) est soumis -> 422 NotBlank (label / bic / iban) inline.
const ribHasError = await submitRows(
@@ -997,6 +980,23 @@ async function submitAccounting(): Promise<void> {
)
if (ribHasError) return
// 2) PATCH des scalaires comptables (erreurs inline sur leurs champs).
try {
await api.patch(`/clients/${clientId.value}`, {
siren: accounting.siren || null,
accountNumber: accounting.accountNumber || null,
tvaMode: accounting.tvaModeIri,
nTva: accounting.nTva || null,
paymentDelay: accounting.paymentDelayIri,
paymentType: accounting.paymentTypeIri,
bank: isBankRequired.value ? accounting.bankIri : null,
}, { toast: false })
}
catch (error) {
accountingErrors.handleApiError(error, { fallbackMessage: t('commercial.clients.toast.error') })
return
}
completeTab('accounting')
toast.success({ title: t('commercial.clients.toast.updateSuccess') })
}