fix(catalog) : M6 — mapping inline des erreurs 422 du formulaire produit
Les operations Post/Patch de Product n'avaient pas collectDenormalizationErrors : un null/type invalide sur une relation (category) levait un 400 qui court-circuitait toute la validation -> aucune violation propertyPath, donc aucune erreur mappee sous les champs (ajout comme modification). - Product : collectDenormalizationErrors: true sur Post + Patch (miroir Client/Supplier/WeighingTicket) -> 422 avec propertyPath au lieu de 400. - useProductForm : on omet la cle 'category' du payload quand aucune categorie n'est choisie (envoyer null casserait la denormalisation IRI et masquerait les autres violations) -> le back renvoie les 6 violations d'un coup, dont le NotNull propre sur category.
This commit is contained in:
@@ -171,6 +171,20 @@ describe('useProductForm', () => {
|
|||||||
expect(payload.containsMolasses).toBe(false)
|
expect(payload.containsMolasses).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('omet `category` du payload quand aucune categorie n\'est choisie', async () => {
|
||||||
|
// Envoyer category:null casserait la denormalisation back (type IRI
|
||||||
|
// attendu) et court-circuiterait les autres violations -> on l'omet.
|
||||||
|
mockPost.mockResolvedValueOnce({ id: 40 })
|
||||||
|
const { form, submit } = useProductForm()
|
||||||
|
fillValidForm(form)
|
||||||
|
form.categoryIri = null
|
||||||
|
|
||||||
|
await submit()
|
||||||
|
|
||||||
|
const payload = mockPost.mock.calls[0][1]
|
||||||
|
expect(payload).not.toHaveProperty('category')
|
||||||
|
})
|
||||||
|
|
||||||
it('mappe un 409 doublon de code sur errors.code + toast explicite', async () => {
|
it('mappe un 409 doublon de code sur errors.code + toast explicite', async () => {
|
||||||
mockPost.mockRejectedValueOnce({ response: { status: 409, _data: {} } })
|
mockPost.mockRejectedValueOnce({ response: { status: 409, _data: {} } })
|
||||||
const { form, errors, submit } = useProductForm()
|
const { form, errors, submit } = useProductForm()
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export function useProductForm() {
|
|||||||
formErrors.clearErrors()
|
formErrors.clearErrors()
|
||||||
const editing = productId.value !== null
|
const editing = productId.value !== null
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload: Record<string, unknown> = {
|
||||||
code: form.code || null,
|
code: form.code || null,
|
||||||
name: form.name || null,
|
name: form.name || null,
|
||||||
states: form.states,
|
states: form.states,
|
||||||
@@ -134,10 +134,17 @@ export function useProductForm() {
|
|||||||
// re-force, on garde le payload coherent).
|
// re-force, on garde le payload coherent).
|
||||||
manufactured: isSale.value ? form.manufactured : false,
|
manufactured: isSale.value ? form.manufactured : false,
|
||||||
containsMolasses: isSale.value ? form.containsMolasses : false,
|
containsMolasses: isSale.value ? form.containsMolasses : false,
|
||||||
category: form.categoryIri,
|
|
||||||
sites: form.siteIris,
|
sites: form.siteIris,
|
||||||
storageTypes: form.storageTypeIris,
|
storageTypes: form.storageTypeIris,
|
||||||
}
|
}
|
||||||
|
// `category` attend un IRI (string) : envoyer null declencherait une
|
||||||
|
// erreur de denormalisation API Platform qui court-circuiterait TOUTES
|
||||||
|
// les autres violations. On omet la cle quand aucune categorie n'est
|
||||||
|
// choisie -> la contrainte NotNull renvoie un message propre, et les
|
||||||
|
// autres champs sont valides dans la meme 422 (mapping inline ERP-101).
|
||||||
|
if (form.categoryIri) {
|
||||||
|
payload.category = form.categoryIri
|
||||||
|
}
|
||||||
const options = { headers: { Accept: 'application/ld+json' }, toast: false }
|
const options = { headers: { Accept: 'application/ld+json' }, toast: false }
|
||||||
if (editing) {
|
if (editing) {
|
||||||
await api.patch(`/products/${productId.value}`, payload, options)
|
await api.patch(`/products/${productId.value}`, payload, options)
|
||||||
|
|||||||
@@ -81,12 +81,18 @@ use function in_array;
|
|||||||
security: "is_granted('catalog.products.manage')",
|
security: "is_granted('catalog.products.manage')",
|
||||||
normalizationContext: ['groups' => ['product:read', 'product:item:read', 'category:read', 'site:read', 'storage_type:read', 'default:read']],
|
normalizationContext: ['groups' => ['product:read', 'product:item:read', 'category:read', 'site:read', 'storage_type:read', 'default:read']],
|
||||||
denormalizationContext: ['groups' => ['product:write']],
|
denormalizationContext: ['groups' => ['product:write']],
|
||||||
|
// Convertit les erreurs de denormalisation (type invalide / null sur une
|
||||||
|
// relation : category, sites, storageTypes) en violations 422 portant un
|
||||||
|
// propertyPath, au lieu d'un 400 qui court-circuite toute la validation
|
||||||
|
// (cf. Client/Supplier/WeighingTicket — mapping inline useFormErrors).
|
||||||
|
collectDenormalizationErrors: true,
|
||||||
processor: ProductProcessor::class,
|
processor: ProductProcessor::class,
|
||||||
),
|
),
|
||||||
new Patch(
|
new Patch(
|
||||||
security: "is_granted('catalog.products.manage')",
|
security: "is_granted('catalog.products.manage')",
|
||||||
normalizationContext: ['groups' => ['product:read', 'product:item:read', 'category:read', 'site:read', 'storage_type:read', 'default:read']],
|
normalizationContext: ['groups' => ['product:read', 'product:item:read', 'category:read', 'site:read', 'storage_type:read', 'default:read']],
|
||||||
denormalizationContext: ['groups' => ['product:write']],
|
denormalizationContext: ['groups' => ['product:write']],
|
||||||
|
collectDenormalizationErrors: true,
|
||||||
provider: ProductProvider::class,
|
provider: ProductProvider::class,
|
||||||
processor: ProductProcessor::class,
|
processor: ProductProcessor::class,
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user