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.
This commit is contained in:
@@ -741,10 +741,10 @@
|
||||
"manual": {
|
||||
"title": "Pesée manuelle",
|
||||
"weight": "Poids (Kg)",
|
||||
"number": "Numéro de pesée",
|
||||
"dsd": "DSD",
|
||||
"save": "Enregistrer",
|
||||
"weightRequired": "Le poids est obligatoire.",
|
||||
"numberRequired": "Le numéro de pesée est obligatoire."
|
||||
"dsdRequired": "Le DSD est obligatoire."
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
|
||||
@@ -26,18 +26,19 @@ describe('useWeighbridge', () => {
|
||||
expect(reading).toEqual({ weight: 23187, dsd: 42, mode: 'AUTO' })
|
||||
})
|
||||
|
||||
it('MANUAL : POST { mode: MANUAL, weight, manualNumber } et renvoie la lecture', async () => {
|
||||
mockPost.mockResolvedValue({ weight: 5000, dsd: 43, manualNumber: 'PAP-555', mode: 'MANUAL' })
|
||||
it('MANUAL : POST { mode: MANUAL, weight, dsd } et renvoie la lecture', async () => {
|
||||
// Le DSD est saisi par l'opérateur et conservé tel quel (ERP-193).
|
||||
mockPost.mockResolvedValue({ weight: 5000, dsd: 16619, mode: 'MANUAL' })
|
||||
const { triggerManual } = useWeighbridge()
|
||||
|
||||
const reading = await triggerManual(5000, 'PAP-555')
|
||||
const reading = await triggerManual(5000, 16619)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith(
|
||||
'/weighbridge_readings',
|
||||
{ mode: 'MANUAL', weight: 5000, manualNumber: 'PAP-555' },
|
||||
{ mode: 'MANUAL', weight: 5000, dsd: 16619 },
|
||||
expect.objectContaining({ toast: false }),
|
||||
)
|
||||
expect(reading.dsd).toBe(43)
|
||||
expect(reading.dsd).toBe(16619)
|
||||
})
|
||||
|
||||
it('erreur (RG-5.06) : extractWeighbridgeError privilégie le detail du 503', () => {
|
||||
|
||||
@@ -106,11 +106,12 @@ describe('useWeighingTicketForm', () => {
|
||||
expect(form.empty.weight).toBe(7150)
|
||||
expect(form.empty.dsd).toBe(1)
|
||||
expect(form.empty.mode).toBe('AUTO')
|
||||
expect(form.empty.manualNumber).toBeNull()
|
||||
|
||||
form.applyReading(form.full, { weight: 14300, dsd: 2, mode: 'MANUAL', manualNumber: 'PAP-555' })
|
||||
// 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.manualNumber).toBe('PAP-555')
|
||||
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', () => {
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* - AUTO (« Pesée bascule ») : le serveur résout le site courant, lit le poids
|
||||
* (stub aléatoire au M5) et alloue le DSD. Peut échouer (RG-5.06 → 503) : le
|
||||
* pont est indisponible, on invite l'utilisateur à passer en pesée manuelle.
|
||||
* - MANUAL (« Pesée manuelle ») : poids + numéro de pesée saisis ; le serveur
|
||||
* calcule le DSD = dernier + 1 (RG-5.04).
|
||||
* - MANUAL (« Pesée manuelle ») : poids + DSD saisis par l'opérateur ; le serveur
|
||||
* les conserve tels quels — plus d'auto-incrément (ERP-193).
|
||||
*
|
||||
* Composable UI-agnostique : il appelle l'API (`useApi`, jamais `$fetch`) et
|
||||
* renvoie la lecture, ou lève l'erreur — la gestion de la modal/de l'affichage
|
||||
@@ -24,8 +24,6 @@ export interface WeighbridgeReading {
|
||||
weight: number
|
||||
dsd: number
|
||||
mode: WeighbridgeMode
|
||||
/** Numéro de pesée saisi en mode MANUAL (absent en AUTO). */
|
||||
manualNumber?: string
|
||||
}
|
||||
|
||||
export function useWeighbridge() {
|
||||
@@ -46,13 +44,13 @@ export function useWeighbridge() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pesée manuelle (MANUAL). Le DSD est calculé serveur (dernier + 1, RG-5.04) ;
|
||||
* le `manualNumber` est la référence du ticket papier / autre bascule.
|
||||
* Pesée manuelle (MANUAL). Le poids ET le DSD sont saisis par l'opérateur (le
|
||||
* DSD = numéro du pont réellement utilisé) et conservés tels quels (ERP-193).
|
||||
*/
|
||||
async function triggerManual(weight: number, manualNumber: string): Promise<WeighbridgeReading> {
|
||||
async function triggerManual(weight: number, dsd: number): Promise<WeighbridgeReading> {
|
||||
return await api.post<WeighbridgeReading>(
|
||||
'/weighbridge_readings',
|
||||
{ mode: 'MANUAL', weight, manualNumber },
|
||||
{ mode: 'MANUAL', weight, dsd },
|
||||
{ toast: false },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,13 +25,11 @@ export interface WeighingTicketDetail {
|
||||
emptyWeight?: number | null
|
||||
emptyDsd?: number | null
|
||||
emptyMode?: WeighbridgeMode | null
|
||||
emptyManualNumber?: string | null
|
||||
// Pesée à plein
|
||||
fullDate?: string | null
|
||||
fullWeight?: number | null
|
||||
fullDsd?: number | null
|
||||
fullMode?: WeighbridgeMode | null
|
||||
fullManualNumber?: string | null
|
||||
netWeight?: number | null
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,10 @@ export interface WeighingBlockState {
|
||||
date: string | null
|
||||
/** Poids en kg — readonly, rempli par la pesée (bascule ou manuelle). */
|
||||
weight: number | null
|
||||
/** DSD — readonly, rempli par la pesée (RG-5.04). */
|
||||
/** DSD — pesée bascule : fourni par le pont ; pesée manuelle : saisi (RG-5.04, ERP-193). */
|
||||
dsd: number | null
|
||||
/** Mode de la dernière pesée appliquée au bloc. */
|
||||
mode: WeighbridgeMode | null
|
||||
/** Numéro de pesée (rempli uniquement en pesée manuelle). */
|
||||
manualNumber: string | null
|
||||
}
|
||||
|
||||
/** Cycle de vie du ticket (miroir back, ERP-193). */
|
||||
@@ -57,12 +55,10 @@ export interface WeighingTicketHydration {
|
||||
emptyWeight?: number | null
|
||||
emptyDsd?: number | null
|
||||
emptyMode?: WeighbridgeMode | null
|
||||
emptyManualNumber?: string | null
|
||||
fullDate?: string | null
|
||||
fullWeight?: number | null
|
||||
fullDsd?: number | null
|
||||
fullMode?: WeighbridgeMode | null
|
||||
fullManualNumber?: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +91,6 @@ function emptyBlock(now: string): WeighingBlockState {
|
||||
weight: null,
|
||||
dsd: null,
|
||||
mode: null,
|
||||
manualNumber: null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,13 +167,12 @@ export function useWeighingTicketForm() {
|
||||
*/
|
||||
function applyReading(
|
||||
block: WeighingBlockState,
|
||||
reading: { weight: number, dsd: number, mode: WeighbridgeMode, manualNumber?: string },
|
||||
reading: { weight: number, dsd: number, mode: WeighbridgeMode },
|
||||
): void {
|
||||
block.date = nowIsoDateTime()
|
||||
block.weight = reading.weight
|
||||
block.dsd = reading.dsd
|
||||
block.mode = reading.mode
|
||||
block.manualNumber = reading.manualNumber ?? null
|
||||
}
|
||||
|
||||
/** Partie « contrepartie » du payload (FK en IRI ou libellé libre). */
|
||||
@@ -203,7 +197,6 @@ export function useWeighingTicketForm() {
|
||||
[`${prefix}Weight`]: block.weight,
|
||||
[`${prefix}Dsd`]: block.dsd,
|
||||
[`${prefix}Mode`]: block.mode,
|
||||
[`${prefix}ManualNumber`]: block.manualNumber || null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,13 +238,11 @@ export function useWeighingTicketForm() {
|
||||
empty.weight = detail.emptyWeight ?? null
|
||||
empty.dsd = detail.emptyDsd ?? null
|
||||
empty.mode = detail.emptyMode ?? null
|
||||
empty.manualNumber = detail.emptyManualNumber ?? null
|
||||
|
||||
full.date = toLocalIsoDateTime(detail.fullDate) ?? now
|
||||
full.weight = detail.fullWeight ?? null
|
||||
full.dsd = detail.fullDsd ?? null
|
||||
full.mode = detail.fullMode ?? null
|
||||
full.manualNumber = detail.fullManualNumber ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -166,10 +166,11 @@
|
||||
:error="manualModal.errors.weight"
|
||||
/>
|
||||
<MalioInputText
|
||||
v-model="manualModal.manualNumber"
|
||||
:label="t('logistique.weighingTickets.form.manual.number')"
|
||||
v-model="manualModal.dsd"
|
||||
:mask="NUMERIC_MASK"
|
||||
:label="t('logistique.weighingTickets.form.manual.dsd')"
|
||||
:required="true"
|
||||
:error="manualModal.errors.manualNumber"
|
||||
:error="manualModal.errors.dsd"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -308,36 +309,36 @@ const manualModal = reactive({
|
||||
loading: false,
|
||||
target: 'empty' as 'empty' | 'full',
|
||||
weight: null as string | null,
|
||||
manualNumber: null as string | null,
|
||||
dsd: null as string | null,
|
||||
errors: {} as Record<string, string>,
|
||||
})
|
||||
|
||||
function openManual(target: 'empty' | 'full'): void {
|
||||
manualModal.target = target
|
||||
manualModal.weight = null
|
||||
manualModal.manualNumber = null
|
||||
manualModal.dsd = null
|
||||
manualModal.errors = {}
|
||||
manualModal.open = true
|
||||
}
|
||||
|
||||
/** Valide la saisie manuelle, remplit le bloc puis enregistre le brouillon. */
|
||||
/** Valide la saisie manuelle (poids + DSD), remplit le bloc puis enregistre le brouillon. */
|
||||
async function confirmManual(): Promise<void> {
|
||||
if (manualModal.loading) return
|
||||
manualModal.errors = {}
|
||||
|
||||
const weight = manualModal.weight === null || manualModal.weight === '' ? null : Number(manualModal.weight)
|
||||
const manualNumber = (manualModal.manualNumber ?? '').trim()
|
||||
const dsd = manualModal.dsd === null || manualModal.dsd === '' ? null : Number(manualModal.dsd)
|
||||
if (weight === null || Number.isNaN(weight)) {
|
||||
manualModal.errors = { ...manualModal.errors, weight: t('logistique.weighingTickets.form.manual.weightRequired') }
|
||||
}
|
||||
if (manualNumber === '') {
|
||||
manualModal.errors = { ...manualModal.errors, manualNumber: t('logistique.weighingTickets.form.manual.numberRequired') }
|
||||
if (dsd === null || Number.isNaN(dsd)) {
|
||||
manualModal.errors = { ...manualModal.errors, dsd: t('logistique.weighingTickets.form.manual.dsdRequired') }
|
||||
}
|
||||
if (Object.keys(manualModal.errors).length > 0) return
|
||||
|
||||
manualModal.loading = true
|
||||
try {
|
||||
const reading = await weighbridge.triggerManual(weight as number, manualNumber)
|
||||
const reading = await weighbridge.triggerManual(weight as number, dsd as number)
|
||||
form.applyReading(form[manualModal.target], reading)
|
||||
manualModal.open = false
|
||||
await saveDraft()
|
||||
|
||||
@@ -156,10 +156,11 @@
|
||||
:error="manualModal.errors.weight"
|
||||
/>
|
||||
<MalioInputText
|
||||
v-model="manualModal.manualNumber"
|
||||
:label="t('logistique.weighingTickets.form.manual.number')"
|
||||
v-model="manualModal.dsd"
|
||||
:mask="NUMERIC_MASK"
|
||||
:label="t('logistique.weighingTickets.form.manual.dsd')"
|
||||
:required="true"
|
||||
:error="manualModal.errors.manualNumber"
|
||||
:error="manualModal.errors.dsd"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -273,36 +274,36 @@ const manualModal = reactive({
|
||||
loading: false,
|
||||
target: 'empty' as 'empty' | 'full',
|
||||
weight: null as string | null,
|
||||
manualNumber: null as string | null,
|
||||
dsd: null as string | null,
|
||||
errors: {} as Record<string, string>,
|
||||
})
|
||||
|
||||
function openManual(target: 'empty' | 'full'): void {
|
||||
manualModal.target = target
|
||||
manualModal.weight = null
|
||||
manualModal.manualNumber = null
|
||||
manualModal.dsd = null
|
||||
manualModal.errors = {}
|
||||
manualModal.open = true
|
||||
}
|
||||
|
||||
/** Valide la saisie manuelle, remplit le bloc puis enregistre le brouillon. */
|
||||
/** Valide la saisie manuelle (poids + DSD), remplit le bloc puis enregistre le brouillon. */
|
||||
async function confirmManual(): Promise<void> {
|
||||
if (manualModal.loading) return
|
||||
manualModal.errors = {}
|
||||
|
||||
const weight = manualModal.weight === null || manualModal.weight === '' ? null : Number(manualModal.weight)
|
||||
const manualNumber = (manualModal.manualNumber ?? '').trim()
|
||||
const dsd = manualModal.dsd === null || manualModal.dsd === '' ? null : Number(manualModal.dsd)
|
||||
if (weight === null || Number.isNaN(weight)) {
|
||||
manualModal.errors = { ...manualModal.errors, weight: t('logistique.weighingTickets.form.manual.weightRequired') }
|
||||
}
|
||||
if (manualNumber === '') {
|
||||
manualModal.errors = { ...manualModal.errors, manualNumber: t('logistique.weighingTickets.form.manual.numberRequired') }
|
||||
if (dsd === null || Number.isNaN(dsd)) {
|
||||
manualModal.errors = { ...manualModal.errors, dsd: t('logistique.weighingTickets.form.manual.dsdRequired') }
|
||||
}
|
||||
if (Object.keys(manualModal.errors).length > 0) return
|
||||
|
||||
manualModal.loading = true
|
||||
try {
|
||||
const reading = await weighbridge.triggerManual(weight as number, manualNumber)
|
||||
const reading = await weighbridge.triggerManual(weight as number, dsd as number)
|
||||
form.applyReading(form[manualModal.target], reading)
|
||||
manualModal.open = false
|
||||
await saveDraft()
|
||||
|
||||
Reference in New Issue
Block a user