feat: reuse inventory items when configuring machines
This commit is contained in:
@@ -158,73 +158,61 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div class="form-control md:col-span-2">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Catégorie de composant</span>
|
||||
</label>
|
||||
<select
|
||||
class="select select-bordered select-xs md:select-sm"
|
||||
:value="entry.typeComposantId || requirement.typeComposantId || ''"
|
||||
@change="setComponentRequirementType(requirement.id, entryIndex, ($event.target && $event.target.value) || '')"
|
||||
>
|
||||
<option value="">
|
||||
{{ requirement.typeComposant?.name ? `Type du requirement (${requirement.typeComposant.name})` : 'Suivre le requirement' }}
|
||||
</option>
|
||||
<option
|
||||
v-for="type in componentTypeOptions"
|
||||
:key="type.id"
|
||||
:value="type.id"
|
||||
<div class="grid grid-cols-1 gap-3">
|
||||
<div class="space-y-2">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Composant existant</span>
|
||||
</label>
|
||||
<select
|
||||
class="select select-bordered select-sm"
|
||||
:value="entry.composantId || ''"
|
||||
@change="setComponentRequirementComponent(requirement, entryIndex, ($event.target && $event.target.value) || '')"
|
||||
>
|
||||
{{ type.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Nom du composant</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="entry.definition.name"
|
||||
type="text"
|
||||
class="input input-bordered input-sm"
|
||||
placeholder="Nom du composant"
|
||||
<option value="">
|
||||
Sélectionner un composant disponible
|
||||
</option>
|
||||
<option
|
||||
v-for="component in getComponentOptions(requirement, entry)"
|
||||
:key="component.id"
|
||||
:value="component.id"
|
||||
>
|
||||
{{ formatComponentOption(component) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p
|
||||
v-if="getComponentOptions(requirement, entry).length === 0"
|
||||
class="text-xs text-error"
|
||||
>
|
||||
Aucun composant disponible pour cette famille.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Référence (optionnel)</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="entry.definition.reference"
|
||||
type="text"
|
||||
class="input input-bordered input-sm"
|
||||
placeholder="Référence du composant"
|
||||
>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Constructeur (optionnel)</span>
|
||||
</label>
|
||||
<ConstructeurSelect
|
||||
class="w-full"
|
||||
:model-value="entry.definition.constructeurId || null"
|
||||
placeholder="Sélectionner un constructeur"
|
||||
@update:modelValue="(value) => setComponentRequirementConstructeur(requirement.id, entryIndex, value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Prix (optionnel)</span>
|
||||
</label>
|
||||
<input
|
||||
v-model.number="entry.definition.prix"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.01"
|
||||
class="input input-bordered input-sm"
|
||||
placeholder="0.00"
|
||||
|
||||
<div
|
||||
v-if="entry.composantId"
|
||||
class="bg-base-300/60 rounded-md p-3 text-xs text-gray-600 space-y-1"
|
||||
>
|
||||
<div class="font-medium">
|
||||
{{ (findComponentById(entry.composantId)?.name) || "Composant" }}
|
||||
</div>
|
||||
<div>
|
||||
Référence : {{ findComponentById(entry.composantId)?.reference || "—" }}
|
||||
</div>
|
||||
<div>
|
||||
Constructeur :
|
||||
{{ findComponentById(entry.composantId)?.constructeur?.name || findComponentById(entry.composantId)?.constructeurName || "—" }}
|
||||
</div>
|
||||
<div>
|
||||
Machine actuelle :
|
||||
{{ findComponentById(entry.composantId)?.machine?.name || findComponentById(entry.composantId)?.machineId || "Non affecté" }}
|
||||
</div>
|
||||
<div
|
||||
v-if="findComponentById(entry.composantId)?.machine?.name"
|
||||
class="text-warning mt-1"
|
||||
>
|
||||
La réaffectation détachera ce composant de sa machine actuelle lors de la création.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -286,73 +274,65 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div class="form-control md:col-span-2">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Catégorie de pièce</span>
|
||||
</label>
|
||||
<select
|
||||
class="select select-bordered select-xs md:select-sm"
|
||||
:value="entry.typePieceId || requirement.typePieceId || ''"
|
||||
@change="setPieceRequirementType(requirement.id, entryIndex, ($event.target && $event.target.value) || '')"
|
||||
>
|
||||
<option value="">
|
||||
{{ requirement.typePiece?.name ? `Type du requirement (${requirement.typePiece.name})` : 'Suivre le requirement' }}
|
||||
</option>
|
||||
<option
|
||||
v-for="type in pieceTypeOptions"
|
||||
:key="type.id"
|
||||
:value="type.id"
|
||||
<div class="grid grid-cols-1 gap-3">
|
||||
<div class="space-y-2">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Pièce existante</span>
|
||||
</label>
|
||||
<select
|
||||
class="select select-bordered select-sm"
|
||||
:value="entry.pieceId || ''"
|
||||
@change="setPieceRequirementPiece(requirement, entryIndex, ($event.target && $event.target.value) || '')"
|
||||
>
|
||||
{{ type.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Nom de la pièce</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="entry.definition.name"
|
||||
type="text"
|
||||
class="input input-bordered input-sm"
|
||||
placeholder="Nom de la pièce"
|
||||
<option value="">
|
||||
Sélectionner une pièce disponible
|
||||
</option>
|
||||
<option
|
||||
v-for="pieceOption in getPieceOptions(requirement, entry)"
|
||||
:key="pieceOption.id"
|
||||
:value="pieceOption.id"
|
||||
>
|
||||
{{ formatPieceOption(pieceOption) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p
|
||||
v-if="getPieceOptions(requirement, entry).length === 0"
|
||||
class="text-xs text-error"
|
||||
>
|
||||
Aucune pièce disponible pour cette famille.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Référence (optionnel)</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="entry.definition.reference"
|
||||
type="text"
|
||||
class="input input-bordered input-sm"
|
||||
placeholder="Référence de la pièce"
|
||||
>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Constructeur (optionnel)</span>
|
||||
</label>
|
||||
<ConstructeurSelect
|
||||
class="w-full"
|
||||
:model-value="entry.definition.constructeurId || null"
|
||||
placeholder="Sélectionner un constructeur"
|
||||
@update:modelValue="(value) => setPieceRequirementConstructeur(requirement.id, entryIndex, value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">Prix (optionnel)</span>
|
||||
</label>
|
||||
<input
|
||||
v-model.number="entry.definition.prix"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.01"
|
||||
class="input input-bordered input-sm"
|
||||
placeholder="0.00"
|
||||
|
||||
<div
|
||||
v-if="entry.pieceId"
|
||||
class="bg-base-300/60 rounded-md p-3 text-xs text-gray-600 space-y-1"
|
||||
>
|
||||
<div class="font-medium">
|
||||
{{ (findPieceById(entry.pieceId)?.name) || "Pièce" }}
|
||||
</div>
|
||||
<div>
|
||||
Référence : {{ findPieceById(entry.pieceId)?.reference || "—" }}
|
||||
</div>
|
||||
<div>
|
||||
Constructeur :
|
||||
{{ findPieceById(entry.pieceId)?.constructeur?.name || findPieceById(entry.pieceId)?.constructeurName || "—" }}
|
||||
</div>
|
||||
<div>
|
||||
Machine actuelle :
|
||||
{{ findPieceById(entry.pieceId)?.machine?.name || findPieceById(entry.pieceId)?.machineId || "Non affecté" }}
|
||||
</div>
|
||||
<div>
|
||||
Composant actuel :
|
||||
{{ findPieceById(entry.pieceId)?.composant?.name || findPieceById(entry.pieceId)?.composantId || "Non affecté" }}
|
||||
</div>
|
||||
<div
|
||||
v-if="findPieceById(entry.pieceId)?.machine?.name || findPieceById(entry.pieceId)?.composant?.name"
|
||||
class="text-warning mt-1"
|
||||
>
|
||||
Cette pièce sera détachée de son affectation actuelle pendant la création.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -597,14 +577,12 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
||||
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
|
||||
import { useMachines } from '~/composables/useMachines'
|
||||
import { useSites } from '~/composables/useSites'
|
||||
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
||||
import { useComponentTypes } from '~/composables/useComponentTypes'
|
||||
import { usePieceTypes } from '~/composables/usePieceTypes'
|
||||
import { useComposants } from '~/composables/useComposants'
|
||||
import { usePieces } from '~/composables/usePieces'
|
||||
import { useToast } from '~/composables/useToast'
|
||||
import { sanitizeDefinitionOverrides } from '~/shared/modelUtils'
|
||||
import IconLucidePlus from '~icons/lucide/plus'
|
||||
import IconLucideX from '~icons/lucide/x'
|
||||
import IconLucideEye from '~icons/lucide/eye'
|
||||
@@ -615,8 +593,8 @@ import IconLucideCircle from '~icons/lucide/circle'
|
||||
const { createMachine, createMachineFromType } = useMachines()
|
||||
const { sites, loadSites } = useSites()
|
||||
const { machineTypes, loadMachineTypes } = useMachineTypesApi()
|
||||
const { componentTypes, loadComponentTypes } = useComponentTypes()
|
||||
const { pieceTypes, loadPieceTypes } = usePieceTypes()
|
||||
const { composants, loadComposants } = useComposants()
|
||||
const { pieces, loadPieces } = usePieces()
|
||||
const toast = useToast()
|
||||
|
||||
const submitting = ref(false)
|
||||
@@ -638,9 +616,177 @@ const selectedMachineType = computed(() => {
|
||||
return machineTypes.value.find(type => type.id === newMachine.typeMachineId) || null
|
||||
})
|
||||
|
||||
const componentTypeOptions = computed(() => componentTypes.value || [])
|
||||
const pieceTypeOptions = computed(() => pieceTypes.value || [])
|
||||
const componentById = computed(() => {
|
||||
const map = new Map()
|
||||
;(composants.value || []).forEach((component) => {
|
||||
if (component?.id) {
|
||||
map.set(component.id, component)
|
||||
}
|
||||
})
|
||||
return map
|
||||
})
|
||||
|
||||
const pieceById = computed(() => {
|
||||
const map = new Map()
|
||||
;(pieces.value || []).forEach((piece) => {
|
||||
if (piece?.id) {
|
||||
map.set(piece.id, piece)
|
||||
}
|
||||
})
|
||||
return map
|
||||
})
|
||||
|
||||
const componentInventory = computed(() => composants.value || [])
|
||||
const pieceInventory = computed(() => pieces.value || [])
|
||||
|
||||
const selectedComponentIds = computed(() => {
|
||||
const ids = []
|
||||
Object.values(componentRequirementSelections).forEach((entries) => {
|
||||
;(entries || []).forEach((entry) => {
|
||||
if (entry?.composantId) {
|
||||
ids.push(entry.composantId)
|
||||
}
|
||||
})
|
||||
})
|
||||
return ids
|
||||
})
|
||||
|
||||
const selectedPieceIds = computed(() => {
|
||||
const ids = []
|
||||
Object.values(pieceRequirementSelections).forEach((entries) => {
|
||||
;(entries || []).forEach((entry) => {
|
||||
if (entry?.pieceId) {
|
||||
ids.push(entry.pieceId)
|
||||
}
|
||||
})
|
||||
})
|
||||
return ids
|
||||
})
|
||||
|
||||
const getComponentOptions = (requirement, currentEntry) => {
|
||||
const requirementTypeId = requirement?.typeComposantId || requirement?.typeComposant?.id || null
|
||||
const usedIds = new Set(
|
||||
selectedComponentIds.value.filter((id) => id && (!currentEntry || id !== currentEntry.composantId)),
|
||||
)
|
||||
|
||||
return componentInventory.value.filter((component) => {
|
||||
if (requirementTypeId && component.typeComposantId !== requirementTypeId) {
|
||||
return false
|
||||
}
|
||||
if (!component.id) {
|
||||
return false
|
||||
}
|
||||
if (currentEntry?.composantId === component.id) {
|
||||
return true
|
||||
}
|
||||
return !usedIds.has(component.id)
|
||||
})
|
||||
}
|
||||
|
||||
const getPieceOptions = (requirement, currentEntry) => {
|
||||
const requirementTypeId = requirement?.typePieceId || requirement?.typePiece?.id || null
|
||||
const usedIds = new Set(
|
||||
selectedPieceIds.value.filter((id) => id && (!currentEntry || id !== currentEntry.pieceId)),
|
||||
)
|
||||
|
||||
return pieceInventory.value.filter((piece) => {
|
||||
if (requirementTypeId && piece.typePieceId !== requirementTypeId) {
|
||||
return false
|
||||
}
|
||||
if (!piece.id) {
|
||||
return false
|
||||
}
|
||||
if (currentEntry?.pieceId === piece.id) {
|
||||
return true
|
||||
}
|
||||
return !usedIds.has(piece.id)
|
||||
})
|
||||
}
|
||||
|
||||
const formatComponentOption = (component) => {
|
||||
if (!component) {
|
||||
return ''
|
||||
}
|
||||
const parts = [component.name || 'Composant']
|
||||
if (component.reference) {
|
||||
parts.push(`Réf. ${component.reference}`)
|
||||
}
|
||||
const constructeurName = component.constructeur?.name || component.constructeurName
|
||||
if (constructeurName) {
|
||||
parts.push(constructeurName)
|
||||
}
|
||||
if (component.machine?.name) {
|
||||
parts.push(`Machine: ${component.machine.name}`)
|
||||
} else if (component.machineId) {
|
||||
parts.push(`Machine: ${component.machineId}`)
|
||||
}
|
||||
return parts.join(' • ')
|
||||
}
|
||||
|
||||
const formatPieceOption = (piece) => {
|
||||
if (!piece) {
|
||||
return ''
|
||||
}
|
||||
const parts = [piece.name || 'Pièce']
|
||||
if (piece.reference) {
|
||||
parts.push(`Réf. ${piece.reference}`)
|
||||
}
|
||||
const constructeurName = piece.constructeur?.name || piece.constructeurName
|
||||
if (constructeurName) {
|
||||
parts.push(constructeurName)
|
||||
}
|
||||
if (piece.machine?.name) {
|
||||
parts.push(`Machine: ${piece.machine.name}`)
|
||||
} else if (piece.machineId) {
|
||||
parts.push(`Machine: ${piece.machineId}`)
|
||||
}
|
||||
if (piece.composant?.name) {
|
||||
parts.push(`Composant: ${piece.composant.name}`)
|
||||
} else if (piece.composantId) {
|
||||
parts.push(`Composant: ${piece.composantId}`)
|
||||
}
|
||||
return parts.join(' • ')
|
||||
}
|
||||
|
||||
const setComponentRequirementComponent = (requirement, index, componentId) => {
|
||||
const entries = getComponentRequirementEntries(requirement.id)
|
||||
const entry = entries[index]
|
||||
if (!entry) return
|
||||
entry.composantId = componentId || null
|
||||
if (componentId) {
|
||||
const component = findComponentById(componentId)
|
||||
entry.typeComposantId = component?.typeComposantId || requirement?.typeComposantId || null
|
||||
} else {
|
||||
entry.typeComposantId = requirement?.typeComposantId || null
|
||||
}
|
||||
}
|
||||
|
||||
const setPieceRequirementPiece = (requirement, index, pieceId) => {
|
||||
const entries = getPieceRequirementEntries(requirement.id)
|
||||
const entry = entries[index]
|
||||
if (!entry) return
|
||||
entry.pieceId = pieceId || null
|
||||
if (pieceId) {
|
||||
const piece = findPieceById(pieceId)
|
||||
entry.typePieceId = piece?.typePieceId || requirement?.typePieceId || null
|
||||
} else {
|
||||
entry.typePieceId = requirement?.typePieceId || null
|
||||
}
|
||||
}
|
||||
|
||||
const findComponentById = (id) => {
|
||||
if (!id) {
|
||||
return null
|
||||
}
|
||||
return componentById.value.get(id) || null
|
||||
}
|
||||
|
||||
const findPieceById = (id) => {
|
||||
if (!id) {
|
||||
return null
|
||||
}
|
||||
return pieceById.value.get(id) || null
|
||||
}
|
||||
const getStatusBadgeClass = (status) => {
|
||||
if (status === 'ready') {
|
||||
return 'badge-success'
|
||||
@@ -651,89 +797,39 @@ const getStatusBadgeClass = (status) => {
|
||||
return 'badge-error'
|
||||
}
|
||||
|
||||
const componentTypeLabelMap = computed(() => {
|
||||
const map = new Map()
|
||||
componentTypeOptions.value.forEach((type) => {
|
||||
if (type?.id) {
|
||||
map.set(type.id, type.name || '')
|
||||
}
|
||||
})
|
||||
const requirementTypes = selectedMachineType.value?.componentRequirements || []
|
||||
requirementTypes.forEach((requirement) => {
|
||||
const type = requirement?.typeComposant
|
||||
if (type?.id && type?.name) {
|
||||
map.set(type.id, type.name)
|
||||
}
|
||||
})
|
||||
return map
|
||||
})
|
||||
|
||||
const pieceTypeLabelMap = computed(() => {
|
||||
const map = new Map()
|
||||
pieceTypeOptions.value.forEach((type) => {
|
||||
if (type?.id) {
|
||||
map.set(type.id, type.name || '')
|
||||
}
|
||||
})
|
||||
const requirementTypes = selectedMachineType.value?.pieceRequirements || []
|
||||
requirementTypes.forEach((requirement) => {
|
||||
const type = requirement?.typePiece
|
||||
if (type?.id && type?.name) {
|
||||
map.set(type.id, type.name)
|
||||
}
|
||||
})
|
||||
return map
|
||||
})
|
||||
|
||||
const resolveComponentRequirementTypeLabel = (requirement, entry) => {
|
||||
const typeId = entry?.typeComposantId || requirement?.typeComposantId || null
|
||||
if (!typeId) {
|
||||
return requirement?.typeComposant?.name || 'Type non défini'
|
||||
if (entry?.composantId) {
|
||||
const component = findComponentById(entry.composantId)
|
||||
if (component?.typeComposant?.name) {
|
||||
return component.typeComposant.name
|
||||
}
|
||||
}
|
||||
return componentTypeLabelMap.value.get(typeId) || requirement?.typeComposant?.name || 'Type non défini'
|
||||
return requirement?.typeComposant?.name || 'Type non défini'
|
||||
}
|
||||
|
||||
const resolvePieceRequirementTypeLabel = (requirement, entry) => {
|
||||
const typeId = entry?.typePieceId || requirement?.typePieceId || null
|
||||
if (!typeId) {
|
||||
return requirement?.typePiece?.name || 'Type non défini'
|
||||
if (entry?.pieceId) {
|
||||
const piece = findPieceById(entry.pieceId)
|
||||
if (piece?.typePiece?.name) {
|
||||
return piece.typePiece.name
|
||||
}
|
||||
}
|
||||
return pieceTypeLabelMap.value.get(typeId) || requirement?.typePiece?.name || 'Type non défini'
|
||||
return requirement?.typePiece?.name || 'Type non défini'
|
||||
}
|
||||
|
||||
const getComponentRequirementEntries = requirementId => componentRequirementSelections[requirementId] || []
|
||||
const getPieceRequirementEntries = requirementId => pieceRequirementSelections[requirementId] || []
|
||||
|
||||
const createComponentSelectionEntry = (requirement, source = null) => ({
|
||||
typeComposantId: source?.typeComposantId
|
||||
|| source?.typeComposant?.id
|
||||
|| requirement?.typeComposantId
|
||||
|| null,
|
||||
definition: {
|
||||
name:
|
||||
(typeof source?.name === 'string' ? source.name : null)
|
||||
|| requirement?.typeComposant?.name
|
||||
|| '',
|
||||
reference: source?.reference || '',
|
||||
constructeurId: source?.constructeurId || source?.constructeur?.id || null,
|
||||
prix: source?.prix ?? source?.price ?? null,
|
||||
},
|
||||
typeComposantId: requirement?.typeComposantId || requirement?.typeComposant?.id || null,
|
||||
composantId: source?.composantId || null,
|
||||
definition: {},
|
||||
})
|
||||
|
||||
const createPieceSelectionEntry = (requirement, source = null) => ({
|
||||
typePieceId: source?.typePieceId
|
||||
|| source?.typePiece?.id
|
||||
|| requirement?.typePieceId
|
||||
|| null,
|
||||
definition: {
|
||||
name:
|
||||
(typeof source?.name === 'string' ? source.name : null)
|
||||
|| requirement?.typePiece?.name
|
||||
|| '',
|
||||
reference: source?.reference || '',
|
||||
constructeurId: source?.constructeurId || source?.constructeur?.id || null,
|
||||
prix: source?.prix ?? source?.price ?? null,
|
||||
},
|
||||
typePieceId: requirement?.typePieceId || requirement?.typePiece?.id || null,
|
||||
pieceId: source?.pieceId || null,
|
||||
definition: {},
|
||||
})
|
||||
|
||||
const clearRequirementSelections = () => {
|
||||
@@ -763,20 +859,6 @@ const removeComponentSelectionEntry = (requirementId, index) => {
|
||||
componentRequirementSelections[requirementId] = entries.filter((_, i) => i !== index)
|
||||
}
|
||||
|
||||
const setComponentRequirementType = (requirementId, index, value) => {
|
||||
const entries = getComponentRequirementEntries(requirementId)
|
||||
const entry = entries[index]
|
||||
if (!entry) return
|
||||
entry.typeComposantId = value || null
|
||||
}
|
||||
|
||||
const setComponentRequirementConstructeur = (requirementId, index, value) => {
|
||||
const entries = getComponentRequirementEntries(requirementId)
|
||||
const entry = entries[index]
|
||||
if (!entry) return
|
||||
entry.definition.constructeurId = value || null
|
||||
}
|
||||
|
||||
const addPieceSelectionEntry = (requirement) => {
|
||||
const entries = getPieceRequirementEntries(requirement.id)
|
||||
const max = requirement.maxCount ?? null
|
||||
@@ -795,20 +877,6 @@ const removePieceSelectionEntry = (requirementId, index) => {
|
||||
pieceRequirementSelections[requirementId] = entries.filter((_, i) => i !== index)
|
||||
}
|
||||
|
||||
const setPieceRequirementType = (requirementId, index, value) => {
|
||||
const entries = getPieceRequirementEntries(requirementId)
|
||||
const entry = entries[index]
|
||||
if (!entry) return
|
||||
entry.typePieceId = value || null
|
||||
}
|
||||
|
||||
const setPieceRequirementConstructeur = (requirementId, index, value) => {
|
||||
const entries = getPieceRequirementEntries(requirementId)
|
||||
const entry = entries[index]
|
||||
if (!entry) return
|
||||
entry.definition.constructeurId = value || null
|
||||
}
|
||||
|
||||
const validateRequirementSelections = (type) => {
|
||||
const errors = []
|
||||
const componentSelectionsPayload = []
|
||||
@@ -828,23 +896,41 @@ const validateRequirementSelections = (type) => {
|
||||
}
|
||||
|
||||
entries.forEach((entry) => {
|
||||
const resolvedTypeId = entry.typeComposantId || requirement.typeComposantId || null
|
||||
if (!resolvedTypeId) {
|
||||
errors.push(`Le groupe "${requirement.label || requirement.typeComposant?.name || 'Composants'}" nécessite un type de composant.`)
|
||||
if (!entry.composantId) {
|
||||
errors.push(`Sélectionner un composant existant pour "${requirement.label || requirement.typeComposant?.name || 'Composants'}".`)
|
||||
return
|
||||
}
|
||||
|
||||
const payload = { requirementId: requirement.id }
|
||||
if (entry.typeComposantId && entry.typeComposantId !== requirement.typeComposantId) {
|
||||
payload.typeComposantId = entry.typeComposantId
|
||||
const component = findComponentById(entry.composantId)
|
||||
if (!component) {
|
||||
errors.push(`Le composant sélectionné est introuvable (ID: ${entry.composantId}).`)
|
||||
return
|
||||
}
|
||||
|
||||
const overrides = sanitizeDefinitionOverrides(entry.definition)
|
||||
if (overrides) {
|
||||
payload.definition = overrides
|
||||
if (component.machineId) {
|
||||
errors.push(`Le composant "${component.name || component.id}" est déjà affecté à une machine.`)
|
||||
return
|
||||
}
|
||||
|
||||
componentSelectionsPayload.push(payload)
|
||||
if (component.parentComposantId) {
|
||||
errors.push(`Le composant "${component.name || component.id}" est déjà rattaché à un autre composant.`)
|
||||
return
|
||||
}
|
||||
|
||||
const requiredTypeId = requirement.typeComposantId || requirement.typeComposant?.id || null
|
||||
if (
|
||||
requiredTypeId &&
|
||||
component.typeComposantId &&
|
||||
component.typeComposantId !== requiredTypeId
|
||||
) {
|
||||
errors.push(`Le composant "${component.name || component.id}" n'appartient pas à la famille attendue.`)
|
||||
return
|
||||
}
|
||||
|
||||
componentSelectionsPayload.push({
|
||||
requirementId: requirement.id,
|
||||
composantId: entry.composantId,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -862,23 +948,36 @@ const validateRequirementSelections = (type) => {
|
||||
}
|
||||
|
||||
entries.forEach((entry) => {
|
||||
const resolvedTypeId = entry.typePieceId || requirement.typePieceId || null
|
||||
if (!resolvedTypeId) {
|
||||
errors.push(`Le groupe "${requirement.label || requirement.typePiece?.name || 'Pièces'}" nécessite un type de pièce.`)
|
||||
if (!entry.pieceId) {
|
||||
errors.push(`Sélectionner une pièce existante pour "${requirement.label || requirement.typePiece?.name || 'Pièces'}".`)
|
||||
return
|
||||
}
|
||||
|
||||
const payload = { requirementId: requirement.id }
|
||||
if (entry.typePieceId && entry.typePieceId !== requirement.typePieceId) {
|
||||
payload.typePieceId = entry.typePieceId
|
||||
const piece = findPieceById(entry.pieceId)
|
||||
if (!piece) {
|
||||
errors.push(`La pièce sélectionnée est introuvable (ID: ${entry.pieceId}).`)
|
||||
return
|
||||
}
|
||||
|
||||
const overrides = sanitizeDefinitionOverrides(entry.definition)
|
||||
if (overrides) {
|
||||
payload.definition = overrides
|
||||
if (piece.machineId || piece.composantId) {
|
||||
errors.push(`La pièce "${piece.name || piece.id}" est déjà affectée.`)
|
||||
return
|
||||
}
|
||||
|
||||
pieceSelectionsPayload.push(payload)
|
||||
const requiredTypeId = requirement.typePieceId || requirement.typePiece?.id || null
|
||||
if (
|
||||
requiredTypeId &&
|
||||
piece.typePieceId &&
|
||||
piece.typePieceId !== requiredTypeId
|
||||
) {
|
||||
errors.push(`La pièce "${piece.name || piece.id}" n'appartient pas à la famille attendue.`)
|
||||
return
|
||||
}
|
||||
|
||||
pieceSelectionsPayload.push({
|
||||
requirementId: requirement.id,
|
||||
pieceId: entry.pieceId,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -945,16 +1044,22 @@ const machinePreview = computed(() => {
|
||||
const componentGroups = (type.componentRequirements || []).map((requirement) => {
|
||||
const entries = getComponentRequirementEntries(requirement.id)
|
||||
const normalizedEntries = entries.map((entry, index) => {
|
||||
const overrides = sanitizeDefinitionOverrides(entry.definition)
|
||||
const fallbackName = requirement.typeComposant?.name || 'Composant'
|
||||
const rawName = typeof entry.definition?.name === 'string' ? entry.definition.name.trim() : ''
|
||||
const displayName = overrides?.name || rawName || fallbackName
|
||||
const selectedComponent = entry.composantId
|
||||
? findComponentById(entry.composantId)
|
||||
: null
|
||||
const displayName = selectedComponent?.name
|
||||
|| requirement.typeComposant?.name
|
||||
|| 'Composant'
|
||||
|
||||
const subtitleParts = []
|
||||
if (overrides?.reference) {
|
||||
subtitleParts.push(`Réf. ${overrides.reference}`)
|
||||
if (selectedComponent?.reference) {
|
||||
subtitleParts.push(`Réf. ${selectedComponent.reference}`)
|
||||
}
|
||||
const status = entry.typeComposantId || requirement.typeComposantId ? 'complete' : 'pending'
|
||||
const constructeurName = selectedComponent?.constructeur?.name || selectedComponent?.constructeurName
|
||||
if (constructeurName) {
|
||||
subtitleParts.push(constructeurName)
|
||||
}
|
||||
const status = entry.composantId ? 'complete' : 'pending'
|
||||
|
||||
return {
|
||||
key: `${requirement.id}-${index}`,
|
||||
@@ -978,7 +1083,7 @@ const machinePreview = computed(() => {
|
||||
}
|
||||
|
||||
if (normalizedEntries.some(entry => entry.status !== 'complete')) {
|
||||
issues.push({ message: 'Sélectionner un type pour chaque composant.', kind: 'error', anchor: `component-group-${requirement.id}` })
|
||||
issues.push({ message: 'Sélectionner un composant pour chaque entrée.', kind: 'error', anchor: `component-group-${requirement.id}` })
|
||||
}
|
||||
|
||||
const status = issues.some(issue => issue.kind === 'error')
|
||||
@@ -1004,16 +1109,18 @@ const machinePreview = computed(() => {
|
||||
const pieceGroups = (type.pieceRequirements || []).map((requirement) => {
|
||||
const entries = getPieceRequirementEntries(requirement.id)
|
||||
const normalizedEntries = entries.map((entry, index) => {
|
||||
const overrides = sanitizeDefinitionOverrides(entry.definition)
|
||||
const fallbackName = requirement.typePiece?.name || 'Pièce'
|
||||
const rawName = typeof entry.definition?.name === 'string' ? entry.definition.name.trim() : ''
|
||||
const displayName = overrides?.name || rawName || fallbackName
|
||||
const selectedPiece = entry.pieceId ? findPieceById(entry.pieceId) : null
|
||||
const displayName = selectedPiece?.name || requirement.typePiece?.name || 'Pièce'
|
||||
|
||||
const subtitleParts = []
|
||||
if (overrides?.reference) {
|
||||
subtitleParts.push(`Réf. ${overrides.reference}`)
|
||||
if (selectedPiece?.reference) {
|
||||
subtitleParts.push(`Réf. ${selectedPiece.reference}`)
|
||||
}
|
||||
const status = entry.typePieceId || requirement.typePieceId ? 'complete' : 'pending'
|
||||
const constructeurName = selectedPiece?.constructeur?.name || selectedPiece?.constructeurName
|
||||
if (constructeurName) {
|
||||
subtitleParts.push(constructeurName)
|
||||
}
|
||||
const status = entry.pieceId ? 'complete' : 'pending'
|
||||
|
||||
return {
|
||||
key: `${requirement.id}-${index}`,
|
||||
@@ -1037,7 +1144,7 @@ const machinePreview = computed(() => {
|
||||
}
|
||||
|
||||
if (normalizedEntries.some(entry => entry.status !== 'complete')) {
|
||||
issues.push({ message: 'Sélectionner un type pour chaque pièce.', kind: 'error', anchor: `piece-group-${requirement.id}` })
|
||||
issues.push({ message: 'Sélectionner une pièce pour chaque entrée.', kind: 'error', anchor: `piece-group-${requirement.id}` })
|
||||
}
|
||||
|
||||
const status = issues.some(issue => issue.kind === 'error')
|
||||
@@ -1251,8 +1358,8 @@ onMounted(async () => {
|
||||
await Promise.all([
|
||||
loadSites(),
|
||||
loadMachineTypes(),
|
||||
loadComponentTypes(),
|
||||
loadPieceTypes()
|
||||
loadComposants(),
|
||||
loadPieces()
|
||||
])
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user