feat(transport) : adresse unique par transporteur (OneToOne back + un seul bloc front) (ERP-172)

This commit is contained in:
2026-06-17 17:32:29 +02:00
parent 498cef8cc0
commit e76bd1dd63
14 changed files with 219 additions and 225 deletions
@@ -486,14 +486,13 @@ describe('useCarrierForm — copie QUALIMAT (ERP-166)', () => {
})
})
it('applyQualimatSelection pré-remplit le 1er bloc d\'adresse (RG-4.05)', async () => {
it('applyQualimatSelection pré-remplit l\'adresse unique à la création (RG-4.05)', async () => {
const form = useCarrierForm()
form.main.name = 'Acme'
await form.applyQualimatSelection(QUALIMAT_ROW)
expect(form.addresses.value).toHaveLength(1)
expect(form.addresses.value[0]).toEqual({
expect(form.address.value).toEqual({
id: null,
country: 'France',
postalCode: '86000',
@@ -504,53 +503,38 @@ describe('useCarrierForm — copie QUALIMAT (ERP-166)', () => {
})
})
describe('useCarrierForm — onglet Adresses (ERP-167)', () => {
describe('useCarrierForm — onglet Adresse (ERP-167 / ERP-172 : adresse unique)', () => {
beforeEach(() => {
mockPost.mockReset()
mockPatch.mockReset()
mockDelete.mockReset()
})
/** Transporteur créé, onglet Adresses accessible. */
/** Transporteur créé, onglet Adresse accessible. */
function createdForm() {
const form = useCarrierForm()
form.carrierId.value = 7
return form
}
/** Remplit un bloc adresse complet (CP + ville + rue). */
function fillAddress(form: ReturnType<typeof useCarrierForm>, index = 0): void {
const a = form.addresses.value[index]
if (a) {
a.postalCode = '86100'
a.city = 'Châtellerault'
a.street = '1 rue du Test'
}
/** Remplit l'unique bloc adresse (CP + ville + rue). */
function fillAddress(form: ReturnType<typeof useCarrierForm>): void {
const a = form.address.value
a.postalCode = '86100'
a.city = 'Châtellerault'
a.street = '1 rue du Test'
}
it('canAddAddress : désactivé tant que la dernière adresse est incomplète', () => {
const form = createdForm()
expect(form.canAddAddress.value).toBe(false)
form.addAddress()
expect(form.addresses.value).toHaveLength(1) // no-op tant qu'incomplète
fillAddress(form)
expect(form.canAddAddress.value).toBe(true)
form.addAddress()
expect(form.addresses.value).toHaveLength(2)
})
it('submitAddresses : POST des nouvelles adresses, capture l\'id, finalise l\'onglet', async () => {
it('submitAddress : POST sur /carriers/{id}/address, capture l\'id, finalise l\'onglet', async () => {
mockPost.mockResolvedValueOnce({ id: 88 })
const form = createdForm()
fillAddress(form)
const ok = await form.submitAddresses(vi.fn())
const ok = await form.submitAddress(vi.fn())
expect(ok).toBe(true)
const [url, body, opts] = mockPost.mock.calls[0] ?? []
expect(url).toBe('/carriers/7/addresses')
expect(url).toBe('/carriers/7/address')
expect(body).toEqual({
country: 'France',
postalCode: '86100',
@@ -559,24 +543,23 @@ describe('useCarrierForm — onglet Adresses (ERP-167)', () => {
streetComplement: null,
})
expect(opts).toMatchObject({ toast: false, headers: { Accept: 'application/ld+json' } })
expect(form.addresses.value[0]?.id).toBe(88)
expect(form.address.value.id).toBe(88)
expect(form.isValidated('addresses')).toBe(true)
})
it('submitAddresses : PATCH des adresses existantes sur /carrier_addresses/{id}', async () => {
it('submitAddress : PATCH de l\'adresse existante sur /carrier_addresses/{id}', async () => {
mockPatch.mockResolvedValueOnce({})
const form = createdForm()
fillAddress(form)
const first = form.addresses.value[0]
if (first) first.id = 88
form.address.value.id = 88
await form.submitAddresses(vi.fn())
await form.submitAddress(vi.fn())
expect(mockPost).not.toHaveBeenCalled()
expect(mockPatch).toHaveBeenCalledWith('/carrier_addresses/88', expect.objectContaining({ city: 'Châtellerault' }), { toast: false })
})
it('submitAddresses : mappe les 422 PAR LIGNE et ne finalise pas l\'onglet (RG-4.05)', async () => {
it('submitAddress : mappe les 422 inline par champ et ne finalise pas l\'onglet (RG-4.05)', async () => {
mockPost.mockRejectedValueOnce({
response: {
status: 422,
@@ -586,27 +569,12 @@ describe('useCarrierForm — onglet Adresses (ERP-167)', () => {
const form = createdForm()
fillAddress(form)
const ok = await form.submitAddresses(vi.fn())
const ok = await form.submitAddress(vi.fn())
expect(ok).toBe(false)
expect(form.addressErrors.value[0]?.city).toBe('La ville est obligatoire pour un transporteur affrété.')
expect(form.addressErrors.value.city).toBe('La ville est obligatoire pour un transporteur affrété.')
expect(form.isValidated('addresses')).toBe(false)
})
it('removeAddress : DELETE /carrier_addresses/{id} puis retrait du bloc', async () => {
mockDelete.mockResolvedValueOnce({})
const form = createdForm()
fillAddress(form)
const first = form.addresses.value[0]
if (first) first.id = 88
form.addAddress()
fillAddress(form, 1)
await form.removeAddress(0)
expect(mockDelete).toHaveBeenCalledWith('/carrier_addresses/88', {}, { toast: false })
expect(form.addresses.value).toHaveLength(1)
})
})
describe('carrierContact (util) — validité alignée M1/M2/M3 + max 2 téléphones', () => {
@@ -976,7 +944,7 @@ describe('useCarrierForm — édition (ERP-170)', () => {
id: 7,
name: 'TRANSPORTS ACME',
certificationType: 'GMP_PLUS',
addresses: [{ '@id': '/api/carrier_addresses/3', id: 3, city: 'Poitiers' }],
address: { '@id': '/api/carrier_addresses/3', id: 3, city: 'Poitiers' },
contacts: [{ '@id': '/api/carrier_contacts/9', id: 9, lastName: 'Doe', phonePrimary: '0102030405' }],
prices: [{ '@id': '/api/carrier_prices/5', id: 5, direction: 'CLIENT', client: { '@id': '/api/clients/3' }, containerType: 'BENNE', pricingUnit: 'FORFAIT', price: '120', priceState: 'EN_COURS' }],
})
@@ -985,8 +953,7 @@ describe('useCarrierForm — édition (ERP-170)', () => {
expect(form.editMode.value).toBe(true)
expect(form.main.name).toBe('TRANSPORTS ACME')
expect(form.main.certificationType).toBe('GMP_PLUS')
expect(form.addresses.value).toHaveLength(1)
expect(form.addresses.value[0]?.id).toBe(3)
expect(form.address.value.id).toBe(3)
expect(form.contacts.value[0]?.id).toBe(9)
expect(form.prices.value[0]?.clientIri).toBe('/api/clients/3')
})