feat(commercial) : submitRows collecte les erreurs de tous les blocs de collection (ERP-110)
This commit is contained in:
@@ -52,3 +52,71 @@ describe('useClientFormErrors', () => {
|
|||||||
expect(f.addressErrors.value[0]).toBeUndefined()
|
expect(f.addressErrors.value[0]).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Construit une erreur facon useApi : 422 avec violations Hydra.
|
||||||
|
function http422(path: string, message: string) {
|
||||||
|
return { response: { status: 422, _data: { violations: [{ propertyPath: path, message }] } } }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `submitRows` factorise la soumission d'une collection de blocs (contacts /
|
||||||
|
* adresses / RIB) : on tente TOUS les blocs et on collecte les erreurs par index
|
||||||
|
* sans stopper au premier echec (ERP-110 / ERP-101).
|
||||||
|
*/
|
||||||
|
describe('useClientFormErrors.submitRows', () => {
|
||||||
|
it('tente TOUS les blocs et mappe les erreurs par index, sans stopper au premier echec', async () => {
|
||||||
|
const { contactErrors, submitRows } = useClientFormErrors()
|
||||||
|
const seen: number[] = []
|
||||||
|
const onUnmapped = vi.fn()
|
||||||
|
|
||||||
|
const saveRow = async (_row: unknown, index: number) => {
|
||||||
|
seen.push(index)
|
||||||
|
if (index === 1) throw http422('email', 'Email invalide')
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasError = await submitRows(
|
||||||
|
[{ a: 0 }, { a: 1 }, { a: 2 }],
|
||||||
|
contactErrors,
|
||||||
|
saveRow,
|
||||||
|
onUnmapped,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(seen).toEqual([0, 1, 2]) // tous les blocs tentes
|
||||||
|
expect(hasError).toBe(true)
|
||||||
|
expect(contactErrors.value[1]).toEqual({ email: 'Email invalide' })
|
||||||
|
expect(contactErrors.value[0]).toBeUndefined()
|
||||||
|
expect(onUnmapped).not.toHaveBeenCalled() // 422 mappee, pas de fallback
|
||||||
|
})
|
||||||
|
|
||||||
|
it('delegue le fallback onUnmappedError pour une erreur non mappable et marque hasError', async () => {
|
||||||
|
const { ribErrors, submitRows } = useClientFormErrors()
|
||||||
|
const onUnmapped = vi.fn()
|
||||||
|
|
||||||
|
const hasError = await submitRows(
|
||||||
|
[{ a: 0 }],
|
||||||
|
ribErrors,
|
||||||
|
async () => { throw { response: { status: 500, _data: {} } } },
|
||||||
|
onUnmapped,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(hasError).toBe(true)
|
||||||
|
expect(onUnmapped).toHaveBeenCalledTimes(1)
|
||||||
|
expect(ribErrors.value[0]).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('saute les lignes filtrees par shouldSkip et renvoie false si tout passe', async () => {
|
||||||
|
const { contactErrors, submitRows } = useClientFormErrors()
|
||||||
|
const saved: number[] = []
|
||||||
|
|
||||||
|
const hasError = await submitRows(
|
||||||
|
[{ skip: true }, { skip: false }],
|
||||||
|
contactErrors,
|
||||||
|
async (_row, index) => { saved.push(index) },
|
||||||
|
vi.fn(),
|
||||||
|
(row: { skip: boolean }) => row.skip,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(saved).toEqual([1])
|
||||||
|
expect(hasError).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -43,6 +43,44 @@ export function useClientFormErrors() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Soumet TOUS les blocs d'une collection (contacts / adresses / RIB) en
|
||||||
|
* collectant les erreurs par index : on n'arrete PAS au premier bloc en echec
|
||||||
|
* (decision ERP-110 / ERP-101). Reinitialise le tableau d'erreurs cible, tente
|
||||||
|
* chaque ligne via `saveRow`, mappe les 422 inline (mapRowError) ou delegue le
|
||||||
|
* fallback a `onUnmappedError`. `shouldSkip` permet d'ignorer les blocs vides
|
||||||
|
* (non remplis). Retourne true si au moins un bloc a echoue (le caller ne valide
|
||||||
|
* alors pas l'onglet et n'affiche pas de toast succes).
|
||||||
|
*/
|
||||||
|
async function submitRows<T>(
|
||||||
|
rows: T[],
|
||||||
|
target: Ref<Record<string, string>[]>,
|
||||||
|
saveRow: (row: T, index: number) => Promise<void>,
|
||||||
|
onUnmappedError: (error: unknown, index: number) => void,
|
||||||
|
shouldSkip?: (row: T, index: number) => boolean,
|
||||||
|
): Promise<boolean> {
|
||||||
|
target.value = []
|
||||||
|
let hasError = false
|
||||||
|
for (let index = 0; index < rows.length; index++) {
|
||||||
|
// L'index reste borne par rows.length : la ligne existe forcement.
|
||||||
|
const row = rows[index] as T
|
||||||
|
if (shouldSkip?.(row, index)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await saveRow(row, index)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (!mapRowError(error, target, index)) {
|
||||||
|
onUnmappedError(error, index)
|
||||||
|
}
|
||||||
|
hasError = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasError
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mainErrors,
|
mainErrors,
|
||||||
informationErrors,
|
informationErrors,
|
||||||
@@ -51,5 +89,6 @@ export function useClientFormErrors() {
|
|||||||
addressErrors,
|
addressErrors,
|
||||||
ribErrors,
|
ribErrors,
|
||||||
mapRowError,
|
mapRowError,
|
||||||
|
submitRows,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user