import { describe, it, expect, vi } from 'vitest' // `nowIsoDateTime` est importé par le composable : on le stubbe pour un instant déterministe. vi.mock('~/shared/utils/date', () => ({ nowIsoDateTime: () => '2026-06-22T08:30:00' })) const { useWeighingTicketForm } = await import('../useWeighingTicketForm') describe('useWeighingTicketForm', () => { it('initialise les 2 blocs à la date/heure courante (RG-5.07), sans poids ni DSD', () => { const form = useWeighingTicketForm() expect(form.empty.date).toBe('2026-06-22T08:30:00') expect(form.full.date).toBe('2026-06-22T08:30:00') expect(form.empty.weight).toBeNull() expect(form.empty.dsd).toBeNull() expect(form.counterpartyType.value).toBeNull() }) // ── Omission des requis vides (compact) ────────────────────────────────── it('buildDraftPayload : brouillon vierge → pas de champ requis ni de bloc non pesé', () => { const form = useWeighingTicketForm() // Formulaire vierge : counterpartyType / immatriculation non remplis, aucune pesée. const payload = form.buildDraftPayload() // Absents (et non null) → le back laisse jouer les contraintes du groupe finalize. expect(payload).not.toHaveProperty('counterpartyType') expect(payload).not.toHaveProperty('immatriculation') // Bloc non pesé → ni poids ni date (on n'envoie pas une date de pesée sans pesée). expect(payload).not.toHaveProperty('emptyWeight') expect(payload).not.toHaveProperty('emptyDate') // Seul le booléen « Tout format » reste. expect(payload.plateFreeFormat).toBe(false) }) // ── Pesée obligatoire front-only (RG-5.07) ─────────────────────────────── it('missingWeighingFields liste Poids/DSD manquants, puis vide après pesée', () => { const form = useWeighingTicketForm() expect(form.missingWeighingFields('empty')).toEqual(['emptyWeight', 'emptyDsd']) expect(form.missingWeighingFields('full')).toEqual(['fullWeight', 'fullDsd']) form.applyReading(form.empty, { weight: 7150, dsd: 1, mode: 'AUTO' }) expect(form.missingWeighingFields('empty')).toEqual([]) }) // ── Contrepartie conditionnelle (RG-5.03) ──────────────────────────────── it('CLIENT : ne conserve que le client, purge supplier et otherLabel', () => { const form = useWeighingTicketForm() form.supplierIri.value = '/api/suppliers/3' form.otherLabel.value = 'Particulier' form.setCounterpartyType('CLIENT') form.clientIri.value = '/api/clients/629' expect(form.counterpartyField.value).toBe('client') expect(form.supplierIri.value).toBeNull() expect(form.otherLabel.value).toBeNull() const payload = form.buildDraftPayload() expect(payload.counterpartyType).toBe('CLIENT') expect(payload.client).toBe('/api/clients/629') expect(payload).not.toHaveProperty('supplier') expect(payload).not.toHaveProperty('otherLabel') }) it('FOURNISSEUR : ne conserve que le supplier', () => { const form = useWeighingTicketForm() form.clientIri.value = '/api/clients/1' form.setCounterpartyType('FOURNISSEUR') form.supplierIri.value = '/api/suppliers/7' expect(form.counterpartyField.value).toBe('supplier') expect(form.clientIri.value).toBeNull() expect(form.buildDraftPayload().supplier).toBe('/api/suppliers/7') }) it('AUTRE : ne conserve que le libellé libre', () => { const form = useWeighingTicketForm() form.clientIri.value = '/api/clients/1' form.setCounterpartyType('AUTRE') form.otherLabel.value = 'Reprise interne' expect(form.counterpartyField.value).toBe('other') expect(form.clientIri.value).toBeNull() expect(form.buildDraftPayload().otherLabel).toBe('Reprise interne') }) it('buildDraftPayload : type choisi mais champ associé vide → contrepartie omise (pas de 500 chk_wt_*_branch)', () => { const form = useWeighingTicketForm() // L'opérateur ouvre le menu « Client » mais n'a pas encore choisi le client. form.setCounterpartyType('CLIENT') const draft = form.buildDraftPayload() // On n'émet ni le type ni la FK : un brouillon incohérent serait rejeté en 500 par le back. expect(draft).not.toHaveProperty('counterpartyType') expect(draft).not.toHaveProperty('client') // En revanche la validation envoie toujours le type, pour déclencher la 422 métier. expect(form.buildValidatePayload().counterpartyType).toBe('CLIENT') }) it('buildDraftPayload : AUTRE avec libellé vide → contrepartie omise', () => { const form = useWeighingTicketForm() form.setCounterpartyType('AUTRE') form.otherLabel.value = ' ' const draft = form.buildDraftPayload() expect(draft).not.toHaveProperty('counterpartyType') expect(draft).not.toHaveProperty('otherLabel') }) // ── Immatriculation / « Tout format » partagés entre blocs (RG-5.01) ────── it('immatriculation et plateFreeFormat sont partagés (une seule valeur)', () => { const form = useWeighingTicketForm() form.immatriculation.value = 'AB-123-CD' form.plateFreeFormat.value = true // Les 2 payloads (brouillon + validation) reflètent la même valeur. expect(form.buildDraftPayload().immatriculation).toBe('AB-123-CD') expect(form.buildDraftPayload().plateFreeFormat).toBe(true) expect(form.buildValidatePayload().immatriculation).toBe('AB-123-CD') expect(form.buildValidatePayload().plateFreeFormat).toBe(true) }) // ── Application d'une lecture de pesée ──────────────────────────────────── it('applyReading remplit poids / DSD / mode et ré-horodate le bloc à l\'instant de la pesée', () => { const form = useWeighingTicketForm() // Date périmée (ouverture du formulaire bien avant la pesée). form.empty.date = '2020-01-01T00:00:00' form.applyReading(form.empty, { weight: 7150, dsd: 1, mode: 'AUTO' }) // La pesée validée ré-horodate le bloc à maintenant (stub 2026-06-22T08:30:00). expect(form.empty.date).toBe('2026-06-22T08:30:00') expect(form.empty.weight).toBe(7150) expect(form.empty.dsd).toBe(1) expect(form.empty.mode).toBe('AUTO') // Pesée manuelle : le DSD saisi (16619) est conservé tel quel (ERP-193). form.applyReading(form.full, { weight: 14300, dsd: 16619, mode: 'MANUAL' }) expect(form.full.weight).toBe(14300) expect(form.full.dsd).toBe(16619) expect(form.full.mode).toBe('MANUAL') }) it('buildDraftPayload porte les pesées effectuées ; buildValidatePayload les 4 champs du haut', () => { const form = useWeighingTicketForm() form.setCounterpartyType('CLIENT') form.clientIri.value = '/api/clients/1' form.immatriculation.value = 'AB-123-CD' form.applyReading(form.empty, { weight: 7150, dsd: 1, mode: 'AUTO' }) form.applyReading(form.full, { weight: 14300, dsd: 2, mode: 'AUTO' }) // Le brouillon porte LES DEUX pesées effectuées. const draft = form.buildDraftPayload() expect(draft.emptyWeight).toBe(7150) expect(draft.emptyMode).toBe('AUTO') expect(draft.fullWeight).toBe(14300) expect(draft.fullMode).toBe('AUTO') // La validation ne porte que les 4 champs du haut (pesées déjà persistées). const validate = form.buildValidatePayload() expect(validate.counterpartyType).toBe('CLIENT') expect(validate.client).toBe('/api/clients/1') expect(validate.immatriculation).toBe('AB-123-CD') expect(validate).not.toHaveProperty('emptyWeight') expect(validate).not.toHaveProperty('fullWeight') }) // ── Pré-remplissage (écran Modification, ERP-190) ───────────────────────── it('hydrate pré-remplit l\'état depuis le détail (datetime ISO ramené en local, heure conservée)', () => { const form = useWeighingTicketForm() form.hydrate({ id: 9, counterpartyType: 'CLIENT', client: { '@id': '/api/clients/629' }, immatriculation: 'AB-123-CD', plateFreeFormat: false, emptyDate: '2026-06-17T09:00:00+02:00', emptyWeight: 7150, emptyDsd: 1, emptyMode: 'AUTO', fullDate: '2026-06-17T09:12:00+02:00', fullWeight: 14300, fullDsd: 2, fullMode: 'AUTO', }) expect(form.ticketId.value).toBe(9) expect(form.counterpartyType.value).toBe('CLIENT') expect(form.counterpartyField.value).toBe('client') expect(form.clientIri.value).toBe('/api/clients/629') expect(form.immatriculation.value).toBe('AB-123-CD') // Datetime back (avec fuseau) -> local sans fuseau, heure conservée pour MalioDateTime. expect(form.empty.date).toBe('2026-06-17T09:00:00') expect(form.full.date).toBe('2026-06-17T09:12:00') expect(form.empty.weight).toBe(7150) expect(form.full.weight).toBe(14300) }) it('hydrate gère les champs null omis (skip_null_values) avec des défauts', () => { const form = useWeighingTicketForm() form.hydrate({ id: 5, counterpartyType: 'AUTRE', otherLabel: 'Reprise' }) expect(form.otherLabel.value).toBe('Reprise') expect(form.supplierIri.value).toBeNull() expect(form.plateFreeFormat.value).toBe(false) // Pas de date back -> repli sur l'instant courant (stub 2026-06-22T08:30:00). expect(form.empty.date).toBe('2026-06-22T08:30:00') expect(form.empty.weight).toBeNull() }) it('buildDraftPayload après hydrate porte contrepartie + véhicule + les 2 pesées', () => { const form = useWeighingTicketForm() form.hydrate({ id: 9, status: 'VALIDATED', counterpartyType: 'CLIENT', client: { '@id': '/api/clients/629' }, immatriculation: 'AB-123-CD', emptyWeight: 7150, emptyDsd: 1, emptyMode: 'AUTO', fullWeight: 14300, fullDsd: 2, fullMode: 'AUTO', }) expect(form.status.value).toBe('VALIDATED') const payload = form.buildDraftPayload() expect(payload.counterpartyType).toBe('CLIENT') expect(payload.client).toBe('/api/clients/629') expect(payload.emptyWeight).toBe(7150) expect(payload.fullWeight).toBe(14300) expect(payload.immatriculation).toBe('AB-123-CD') }) })