d6790dd37d
Auto Tag Develop / tag (push) Successful in 7s
ERP-94 (etape front 7/7 du M2). **Stack sur #97** (base = `feature/ERP-97-suppliers-i18n-sidebar`, elle-meme sur #93) pour un diff isole. A recibler sur `develop` une fois #93 (MR #81) et #97 (MR #82) mergees. Page « Ajouter un fournisseur » — **replique a l'identique le fonctionnement de l'ecran Client** (workflow inline par onglets, blocs reutilisables, validation 422 inline ERP-101), avec les specificites M2. ## Architecture (miroir Client) - Workflow par onglets **inline dans `suppliers/new.vue`** (comme `clients/new.vue` — il n'existe pas de `useClientForm` monolithique). Helpers paralleles : `useSupplierReferentials`, `useSupplierFormErrors`, `supplierFormRules`, `supplierEdit` (payloads), `types/supplierForm`. - Blocs `SupplierContactBlock` / `SupplierAddressBlock` (miroir des blocs Client). - POST `/suppliers` puis PATCH partiels par onglet (mode strict, groupes de serialisation). Sous-ressources : `/suppliers/{id}/contacts|addresses|ribs`. - Validation ERP-101 : 422 `violations[].propertyPath` mappees inline par champ (`useFormErrors` / `mapViolationsToRecord`), `{ toast: false }`, bouton Valider toujours actif. ## Specificites M2 (vs M1) - Formulaire principal **sans contact inline** (ERP-106) : Entreprise + Categorie (type FOURNISSEUR, `?typeCode=FOURNISSEUR`). - Adresse : **radio exclusif** Prospect/Depart/Rendu (`addressType` enum, RG-2.09), champs **Bennes** (stepper) + **Prestation de triage**, **pas d'email de facturation**. - Information : champ **Volume previsionnel** (8e champ). - Compta (Admin+Compta) : banque si VIREMENT (RG-2.07), RIB si LCR (RG-2.08) ; RIB sous-ressource gardee par `accounting.manage`. ## Tests (mirroir strategie Client) - `make nuxt-test` : 338 passed (specs ajoutees : supplierFormRules, supplierEdit, useSupplierReferentials, SupplierContactBlock, SupplierAddressBlock). - ESLint propre ; `nuxi typecheck` (lance en container) : **0 erreur**. - Golden path navigateur valide end-to-end : POST /suppliers OK, companyName normalise UPPERCASE (RG-2.12), gating des onglets (Information actif, Contacts deverrouille). ## Note de revue ~30 `WARN Duplicated imports` au typecheck : les helpers Supplier exportent les memes noms generiques que leurs equivalents Client (`buildMainPayload`, `omitEmptyRequired`, `RefOption`...), tous deux auto-importes par Nuxt. **Sans impact runtime** : tous les consommateurs utilisent des imports explicites (qui priment). Consequence directe du miroir 1:1 ; une factorisation des generiques dans `shared/` pourrait etre un suivi. Reviewed-on: #83 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
64 lines
2.5 KiB
TypeScript
64 lines
2.5 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
|
|
// `useApi` est un auto-import Nuxt : on le stubbe globalement pour intercepter
|
|
// les appels de chargement des referentiels et controler les reponses Hydra.
|
|
const mockGet = vi.hoisted(() => vi.fn())
|
|
vi.stubGlobal('useApi', () => ({ get: mockGet }))
|
|
|
|
const { useSupplierReferentials } = await import('../useSupplierReferentials')
|
|
|
|
describe('useSupplierReferentials', () => {
|
|
beforeEach(() => {
|
|
mockGet.mockReset()
|
|
mockGet.mockResolvedValue({ member: [] })
|
|
})
|
|
|
|
it('charge les categories filtrees sur le type FOURNISSEUR (RG-2.10)', async () => {
|
|
await useSupplierReferentials().loadCommon()
|
|
|
|
expect(mockGet).toHaveBeenCalledWith(
|
|
'/categories',
|
|
expect.objectContaining({ pagination: 'false', typeCode: 'FOURNISSEUR' }),
|
|
expect.objectContaining({ toast: false }),
|
|
)
|
|
})
|
|
|
|
it('mappe les categories en options { value: IRI, label: name, code }', async () => {
|
|
mockGet.mockImplementation((url: string) => {
|
|
if (url === '/categories') {
|
|
return Promise.resolve({ member: [{ '@id': '/api/categories/9', code: 'NEGOCIANT', name: 'Négociant' }] })
|
|
}
|
|
return Promise.resolve({ member: [] })
|
|
})
|
|
|
|
const refs = useSupplierReferentials()
|
|
await refs.loadCommon()
|
|
|
|
expect(refs.categories.value).toEqual([{ value: '/api/categories/9', label: 'Négociant', code: 'NEGOCIANT' }])
|
|
})
|
|
|
|
it('ne charge ni distributeurs ni courtiers (absents du modele fournisseur)', async () => {
|
|
await useSupplierReferentials().loadCommon()
|
|
|
|
const urls = mockGet.mock.calls.map(c => c[0])
|
|
expect(urls).not.toContain('/clients')
|
|
expect(urls).toEqual(
|
|
expect.arrayContaining(['/categories', '/sites', '/tva_modes', '/payment_delays', '/payment_types', '/banks']),
|
|
)
|
|
})
|
|
|
|
it('reste resilient : un referentiel en echec n\'empeche pas les autres', async () => {
|
|
mockGet.mockImplementation((url: string) => {
|
|
if (url === '/categories') return Promise.reject(new Error('403'))
|
|
if (url === '/banks') return Promise.resolve({ member: [{ '@id': '/api/banks/1', code: 'SG', label: 'Société Générale' }] })
|
|
return Promise.resolve({ member: [] })
|
|
})
|
|
|
|
const refs = useSupplierReferentials()
|
|
await refs.loadCommon()
|
|
|
|
expect(refs.categories.value).toEqual([])
|
|
expect(refs.banks.value).toEqual([{ value: '/api/banks/1', label: 'Société Générale' }])
|
|
})
|
|
})
|