fix(piece) : persist slot quantity on blur and send prix as string
- Save composant piece slot quantity via PATCH on blur - Pass slotId through hierarchy and selection entries - Send prix as string (not number) to match backend expectation - Show quantity in view mode when > 1 - Allow quantity edit for all pieces (not just root-level) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -69,7 +69,7 @@
|
|||||||
<div v-show="!isCollapsed" class="space-y-4">
|
<div v-show="!isCollapsed" class="space-y-4">
|
||||||
<div class="p-4 bg-base-100 border border-base-200 rounded-lg">
|
<div class="p-4 bg-base-100 border border-base-200 rounded-lg">
|
||||||
<div class="space-y-2 text-sm">
|
<div class="space-y-2 text-sm">
|
||||||
<div v-if="!piece.parentComponentLinkId && isEditMode" class="form-control">
|
<div v-if="isEditMode" class="form-control">
|
||||||
<label class="label">
|
<label class="label">
|
||||||
<span class="label-text text-sm">Quantité</span>
|
<span class="label-text text-sm">Quantité</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -82,6 +82,10 @@
|
|||||||
@blur="updatePiece"
|
@blur="updatePiece"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="displayQuantity > 1">
|
||||||
|
<span class="font-medium">Quantité:</span>
|
||||||
|
<span class="ml-2">{{ displayQuantity }}</span>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="font-medium">Référence:</span>
|
<span class="font-medium">Référence:</span>
|
||||||
<input
|
<input
|
||||||
@@ -456,7 +460,7 @@ const updatePiece = () => {
|
|||||||
let parsedPrice = null
|
let parsedPrice = null
|
||||||
if (prixValue !== null && prixValue !== undefined && String(prixValue).trim().length > 0) {
|
if (prixValue !== null && prixValue !== undefined && String(prixValue).trim().length > 0) {
|
||||||
const numeric = Number(prixValue)
|
const numeric = Number(prixValue)
|
||||||
if (!Number.isNaN(numeric)) parsedPrice = numeric
|
if (!Number.isNaN(numeric)) parsedPrice = String(numeric)
|
||||||
}
|
}
|
||||||
const product = selectedProduct.value ? { ...selectedProduct.value } : null
|
const product = selectedProduct.value ? { ...selectedProduct.value } : null
|
||||||
emit('update', {
|
emit('update', {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useProductTypes } from '~/composables/useProductTypes'
|
|||||||
import { usePieces } from '~/composables/usePieces'
|
import { usePieces } from '~/composables/usePieces'
|
||||||
import { useProducts } from '~/composables/useProducts'
|
import { useProducts } from '~/composables/useProducts'
|
||||||
import { useCustomFields } from '~/composables/useCustomFields'
|
import { useCustomFields } from '~/composables/useCustomFields'
|
||||||
|
import type { SelectionEntry } from '~/shared/utils/structureSelectionUtils'
|
||||||
import { useApi } from '~/composables/useApi'
|
import { useApi } from '~/composables/useApi'
|
||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
import { extractRelationId } from '~/shared/apiRelations'
|
import { extractRelationId } from '~/shared/apiRelations'
|
||||||
@@ -53,7 +54,7 @@ const historyFieldLabels: Record<string, string> = {
|
|||||||
export function useComponentEdit(componentId: string) {
|
export function useComponentEdit(componentId: string) {
|
||||||
const { canEdit } = usePermissions()
|
const { canEdit } = usePermissions()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { get } = useApi()
|
const { get, patch } = useApi()
|
||||||
const { componentTypes, loadComponentTypes } = useComponentTypes()
|
const { componentTypes, loadComponentTypes } = useComponentTypes()
|
||||||
const { pieceTypes, loadPieceTypes } = usePieceTypes()
|
const { pieceTypes, loadPieceTypes } = usePieceTypes()
|
||||||
const { productTypes, loadProductTypes } = useProductTypes()
|
const { productTypes, loadProductTypes } = useProductTypes()
|
||||||
@@ -269,6 +270,21 @@ export function useComponentEdit(componentId: string) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const saveSlotQuantity = async (entry: SelectionEntry) => {
|
||||||
|
const slotId = entry.slotId
|
||||||
|
const quantity = typeof entry._definition?.quantity === 'number'
|
||||||
|
? Math.max(1, entry._definition.quantity)
|
||||||
|
: null
|
||||||
|
if (!slotId || quantity === null) return
|
||||||
|
try {
|
||||||
|
await patch(`/composant-piece-slots/${slotId}`, { quantity })
|
||||||
|
toast.showSuccess('Quantité mise à jour')
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
toast.showError(error?.message || 'Erreur lors de la mise à jour de la quantité')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const submitEdition = async () => {
|
const submitEdition = async () => {
|
||||||
if (!component.value) {
|
if (!component.value) {
|
||||||
return
|
return
|
||||||
@@ -299,10 +315,6 @@ export function useComponentEdit(componentId: string) {
|
|||||||
payload.prix = null
|
payload.prix = null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.value.structure) {
|
|
||||||
payload.structure = component.value.structure
|
|
||||||
}
|
|
||||||
|
|
||||||
saving.value = true
|
saving.value = true
|
||||||
try {
|
try {
|
||||||
const result = await updateComposant(component.value.id, payload)
|
const result = await updateComposant(component.value.id, payload)
|
||||||
@@ -457,6 +469,7 @@ export function useComponentEdit(componentId: string) {
|
|||||||
handleFilesAdded,
|
handleFilesAdded,
|
||||||
refreshDocuments,
|
refreshDocuments,
|
||||||
submitEdition,
|
submitEdition,
|
||||||
|
saveSlotQuantity,
|
||||||
resolvePieceLabel,
|
resolvePieceLabel,
|
||||||
resolveProductLabel,
|
resolveProductLabel,
|
||||||
resolveSubcomponentLabel,
|
resolveSubcomponentLabel,
|
||||||
|
|||||||
@@ -110,18 +110,18 @@ export function useMachineDetailUpdates(deps: UseMachineDetailUpdatesDeps) {
|
|||||||
const productId = updatedComponent.productId
|
const productId = updatedComponent.productId
|
||||||
? String(updatedComponent.productId)
|
? String(updatedComponent.productId)
|
||||||
: null
|
: null
|
||||||
const prix =
|
const prixStr =
|
||||||
updatedComponent.prix !== null &&
|
updatedComponent.prix !== null &&
|
||||||
updatedComponent.prix !== undefined &&
|
updatedComponent.prix !== undefined &&
|
||||||
String(updatedComponent.prix).trim() !== ''
|
String(updatedComponent.prix).trim() !== ''
|
||||||
? Number(updatedComponent.prix)
|
? String(updatedComponent.prix)
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const result: any = await updateComposantApi(updatedComponent.id as string, {
|
const result: any = await updateComposantApi(updatedComponent.id as string, {
|
||||||
name: updatedComponent.name,
|
name: updatedComponent.name,
|
||||||
reference: updatedComponent.reference,
|
reference: updatedComponent.reference,
|
||||||
constructeurIds: cIds,
|
constructeurIds: cIds,
|
||||||
prix: Number.isNaN(prix) ? null : prix,
|
prix: prixStr,
|
||||||
productId,
|
productId,
|
||||||
} as any)
|
} as any)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -140,18 +140,18 @@ export function useMachineDetailUpdates(deps: UseMachineDetailUpdatesDeps) {
|
|||||||
updatedPiece.constructeur,
|
updatedPiece.constructeur,
|
||||||
)
|
)
|
||||||
const productId = updatedPiece.productId ? String(updatedPiece.productId) : null
|
const productId = updatedPiece.productId ? String(updatedPiece.productId) : null
|
||||||
const prix =
|
const prixStr =
|
||||||
updatedPiece.prix !== null &&
|
updatedPiece.prix !== null &&
|
||||||
updatedPiece.prix !== undefined &&
|
updatedPiece.prix !== undefined &&
|
||||||
String(updatedPiece.prix).trim() !== ''
|
String(updatedPiece.prix).trim() !== ''
|
||||||
? Number(updatedPiece.prix)
|
? String(updatedPiece.prix)
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const result: any = await updatePieceApi(updatedPiece.id as string, {
|
const result: any = await updatePieceApi(updatedPiece.id as string, {
|
||||||
name: updatedPiece.name,
|
name: updatedPiece.name,
|
||||||
reference: updatedPiece.reference || null,
|
reference: updatedPiece.reference || null,
|
||||||
constructeurIds: cIds,
|
constructeurIds: cIds,
|
||||||
prix: Number.isNaN(prix) ? null : prix,
|
prix: prixStr,
|
||||||
productId,
|
productId,
|
||||||
} as any)
|
} as any)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -181,6 +181,13 @@ export function useMachineDetailUpdates(deps: UseMachineDetailUpdatesDeps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update slot quantity if this is a composant structure piece
|
||||||
|
const slotId = updatedPiece.slotId as string | null
|
||||||
|
const quantity = typeof updatedPiece.quantity === 'number' ? Math.max(1, updatedPiece.quantity) : null
|
||||||
|
if (slotId && quantity !== null) {
|
||||||
|
await apiPatch(`/composant-piece-slots/${slotId}`, { quantity })
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur lors de la mise à jour de la pièce:', error)
|
console.error('Erreur lors de la mise à jour de la pièce:', error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,6 +237,7 @@ export const buildMachineHierarchyFromLinks = (
|
|||||||
constructeurs: resolved?.constructeurs || [],
|
constructeurs: resolved?.constructeurs || [],
|
||||||
documents: [],
|
documents: [],
|
||||||
quantity,
|
quantity,
|
||||||
|
slotId: def.slotId || definition.slotId || null,
|
||||||
typePieceId: resolved?.typePieceId || definition.typePieceId || def.typePieceId || null,
|
typePieceId: resolved?.typePieceId || definition.typePieceId || def.typePieceId || null,
|
||||||
typePiece: resolved?.typePiece || null,
|
typePiece: resolved?.typePiece || null,
|
||||||
parentComponentLinkId: machineComponentLinkId,
|
parentComponentLinkId: machineComponentLinkId,
|
||||||
|
|||||||
@@ -179,6 +179,7 @@
|
|||||||
placeholder="Qté"
|
placeholder="Qté"
|
||||||
class="input input-bordered input-xs w-16 ml-auto"
|
class="input input-bordered input-xs w-16 ml-auto"
|
||||||
@input="entry._definition.quantity = Math.max(1, entry._definition.quantity || 1)"
|
@input="entry._definition.quantity = Math.max(1, entry._definition.quantity || 1)"
|
||||||
|
@blur="saveSlotQuantity(entry)"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -319,6 +320,7 @@ const {
|
|||||||
removeDocument,
|
removeDocument,
|
||||||
handleFilesAdded,
|
handleFilesAdded,
|
||||||
submitEdition,
|
submitEdition,
|
||||||
|
saveSlotQuantity,
|
||||||
resolvePieceLabel,
|
resolvePieceLabel,
|
||||||
resolveProductLabel,
|
resolveProductLabel,
|
||||||
resolveSubcomponentLabel,
|
resolveSubcomponentLabel,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export interface DefinitionOverridePayload {
|
|||||||
name?: string
|
name?: string
|
||||||
reference?: string
|
reference?: string
|
||||||
constructeurIds?: string[]
|
constructeurIds?: string[]
|
||||||
prix?: number
|
prix?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sanitizeDefinitionOverrides = (definition: any): DefinitionOverridePayload | null => {
|
export const sanitizeDefinitionOverrides = (definition: any): DefinitionOverridePayload | null => {
|
||||||
@@ -41,7 +41,7 @@ export const sanitizeDefinitionOverrides = (definition: any): DefinitionOverride
|
|||||||
if (definition.prix !== undefined && definition.prix !== null && definition.prix !== '') {
|
if (definition.prix !== undefined && definition.prix !== null && definition.prix !== '') {
|
||||||
const parsed = Number(definition.prix)
|
const parsed = Number(definition.prix)
|
||||||
if (!Number.isNaN(parsed)) {
|
if (!Number.isNaN(parsed)) {
|
||||||
payload.prix = parsed
|
payload.prix = String(parsed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export type SelectionEntry = {
|
|||||||
requirementLabel: string
|
requirementLabel: string
|
||||||
resolvedName: string
|
resolvedName: string
|
||||||
quantity?: number
|
quantity?: number
|
||||||
|
slotId?: string
|
||||||
_definition?: Record<string, any>
|
_definition?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +63,7 @@ export function collectStructureSelections(
|
|||||||
requirementLabel: resolvers.resolvePieceLabel(definition),
|
requirementLabel: resolvers.resolvePieceLabel(definition),
|
||||||
resolvedName: catalogPiece?.name || selectedId,
|
resolvedName: catalogPiece?.name || selectedId,
|
||||||
quantity: typeof definition?.quantity === 'number' ? definition.quantity : undefined,
|
quantity: typeof definition?.quantity === 'number' ? definition.quantity : undefined,
|
||||||
|
slotId: isNonEmptyString(entry?.slotId) ? entry.slotId : undefined,
|
||||||
_definition: definition,
|
_definition: definition,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user