Files
Starseed/frontend/modules/logistique/composables/__tests__/useWeighingTicketForm.spec.ts
T
tristan 9e2206a7d6
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 2m12s
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 3m45s
fix : DSD saisi conservé en pesée manuelle (ERP-193)
En pesée manuelle, le serveur incrémentait automatiquement le DSD et ignorait la
saisie de l'opérateur. Désormais l'opérateur saisit le poids ET le DSD (le numéro
du pont réellement utilisé), conservés tels quels — plus d'auto-incrément. Le
champ « Numéro de pesée » séparé (manualNumber) est supprimé : pour le client
c'est la même chose que le DSD. Pas de contrainte d'unicité sur le DSD (doublons
autorisés). Colonnes empty_manual_number/full_manual_number droppées.
2026-06-24 15:33:12 +02:00

205 lines
9.7 KiB
TypeScript

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')
})
// ── 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')
})
})