feat(constructeur) : update piece edit flow with supplier references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-03-31 15:55:18 +02:00
parent 63a56c47ba
commit c5988ec7a6
3 changed files with 59 additions and 35 deletions

View File

@@ -7,11 +7,13 @@ import { useApi } from '~/composables/useApi'
import { useToast } from '~/composables/useToast' import { useToast } from '~/composables/useToast'
import { useDocuments } from '~/composables/useDocuments' import { useDocuments } from '~/composables/useDocuments'
import { useConstructeurs } from '~/composables/useConstructeurs' import { useConstructeurs } from '~/composables/useConstructeurs'
import { useConstructeurLinks } from '~/composables/useConstructeurLinks'
import { usePieceHistory } from '~/composables/usePieceHistory' import { usePieceHistory } from '~/composables/usePieceHistory'
import { extractRelationId } from '~/shared/apiRelations' import { extractRelationId } from '~/shared/apiRelations'
import { canPreviewDocument } from '~/utils/documentPreview' import { canPreviewDocument } from '~/utils/documentPreview'
import { formatPieceStructurePreview } from '~/shared/modelUtils' import { formatPieceStructurePreview } from '~/shared/modelUtils'
import { uniqueConstructeurIds } from '~/shared/constructeurUtils' import { uniqueConstructeurIds, constructeurIdsFromLinks } from '~/shared/constructeurUtils'
import type { ConstructeurLinkEntry } from '~/shared/constructeurUtils'
import type { PieceModelStructure } from '~/shared/types/inventory' import type { PieceModelStructure } from '~/shared/types/inventory'
import type { ModelType } from '~/services/modelTypes' import type { ModelType } from '~/services/modelTypes'
import { import {
@@ -46,6 +48,7 @@ export function usePieceEdit(pieceId: string) {
const toast = useToast() const toast = useToast()
const { loadDocumentsByPiece, uploadDocuments, deleteDocument } = useDocuments() const { loadDocumentsByPiece, uploadDocuments, deleteDocument } = useDocuments()
const { ensureConstructeurs } = useConstructeurs() const { ensureConstructeurs } = useConstructeurs()
const { fetchLinks, syncLinks } = useConstructeurLinks()
const { const {
history, history,
loading: historyLoading, loading: historyLoading,
@@ -82,6 +85,9 @@ export function usePieceEdit(pieceId: string) {
constructeurIds: [] as string[], constructeurIds: [] as string[],
prix: '' as string, prix: '' as string,
}) })
const constructeurLinks = ref<ConstructeurLinkEntry[]>([])
const originalConstructeurLinks = ref<ConstructeurLinkEntry[]>([])
const constructeurIdsFromForm = computed(() => constructeurIdsFromLinks(constructeurLinks.value))
const productSelections = ref<(string | null)[]>([]) const productSelections = ref<(string | null)[]>([])
const customFieldInputs = ref<CustomFieldInput[]>([]) const customFieldInputs = ref<CustomFieldInput[]>([])
@@ -303,15 +309,16 @@ export function usePieceEdit(pieceId: string) {
editionForm.name = currentPiece.name || '' editionForm.name = currentPiece.name || ''
editionForm.description = currentPiece.description || '' editionForm.description = currentPiece.description || ''
editionForm.reference = currentPiece.reference || '' editionForm.reference = currentPiece.reference || ''
editionForm.constructeurIds = uniqueConstructeurIds( // Load constructeur links
currentPiece, fetchLinks('piece', pieceId).then((links) => {
Array.isArray(currentPiece.constructeurs) ? currentPiece.constructeurs : [], constructeurLinks.value = links
currentPiece.constructeur ? [currentPiece.constructeur] : [], originalConstructeurLinks.value = links.map(l => ({ ...l }))
) editionForm.constructeurIds = constructeurIdsFromLinks(links)
if (editionForm.constructeurIds.length) {
void ensureConstructeurs(editionForm.constructeurIds)
}
})
editionForm.prix = currentPiece.prix !== null && currentPiece.prix !== undefined ? String(currentPiece.prix) : '' editionForm.prix = currentPiece.prix !== null && currentPiece.prix !== undefined ? String(currentPiece.prix) : ''
if (editionForm.constructeurIds.length) {
void ensureConstructeurs(editionForm.constructeurIds)
}
const existingProductIds = Array.isArray(currentPiece.productIds) && currentPiece.productIds.length const existingProductIds = Array.isArray(currentPiece.productIds) && currentPiece.productIds.length
? currentPiece.productIds.map((id: unknown) => String(id)) ? currentPiece.productIds.map((id: unknown) => String(id))
@@ -370,12 +377,9 @@ export function usePieceEdit(pieceId: string) {
? '' ? ''
: String(editionForm.prix).trim() : String(editionForm.prix).trim()
const constructeurIds = uniqueConstructeurIds(editionForm.constructeurIds)
const payload: Record<string, any> = { const payload: Record<string, any> = {
name: editionForm.name.trim(), name: editionForm.name.trim(),
description: editionForm.description.trim() || null, description: editionForm.description.trim() || null,
constructeurIds,
} }
const reference = editionForm.reference.trim() const reference = editionForm.reference.trim()
@@ -412,6 +416,8 @@ export function usePieceEdit(pieceId: string) {
], ],
{ customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast }, { customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
) )
await syncLinks('piece', piece.value.id, originalConstructeurLinks.value, constructeurLinks.value)
originalConstructeurLinks.value = constructeurLinks.value.map(l => ({ ...l }))
toast.showSuccess('Pièce mise à jour avec succès.') toast.showSuccess('Pièce mise à jour avec succès.')
} }
} }
@@ -441,6 +447,9 @@ export function usePieceEdit(pieceId: string) {
previewVisible, previewVisible,
selectedTypeId, selectedTypeId,
editionForm, editionForm,
constructeurLinks,
originalConstructeurLinks,
constructeurIdsFromForm,
productSelections, productSelections,
customFieldInputs, customFieldInputs,
canEdit, canEdit,

View File

@@ -1,7 +1,7 @@
import { ref } from 'vue' import { ref } from 'vue'
import { useToast } from './useToast' import { useToast } from './useToast'
import { useApi } from './useApi' import { useApi } from './useApi'
import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils' import { uniqueConstructeurIds } from '~/shared/constructeurUtils'
import { useConstructeurs, type Constructeur } from './useConstructeurs' import { useConstructeurs, type Constructeur } from './useConstructeurs'
import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations' import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations'
import { extractCollection } from '~/shared/utils/apiHelpers' import { extractCollection } from '~/shared/utils/apiHelpers'
@@ -196,7 +196,8 @@ export function usePieces() {
const createPiece = async (pieceData: Partial<Piece>): Promise<PieceSingleResult> => { const createPiece = async (pieceData: Partial<Piece>): Promise<PieceSingleResult> => {
loading.value = true loading.value = true
try { try {
const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(pieceData)) const { constructeurIds, constructeurs, constructeurId, constructeur, ...cleanPayload } = pieceData as any
const normalizedPayload = normalizeRelationIds(cleanPayload)
const result = await post('/pieces', normalizedPayload) const result = await post('/pieces', normalizedPayload)
if (result.success && result.data) { if (result.success && result.data) {
const enriched = await withResolvedConstructeurs(result.data as Piece) const enriched = await withResolvedConstructeurs(result.data as Piece)
@@ -223,7 +224,8 @@ export function usePieces() {
const updatePieceData = async (id: string, pieceData: Partial<Piece>): Promise<PieceSingleResult> => { const updatePieceData = async (id: string, pieceData: Partial<Piece>): Promise<PieceSingleResult> => {
loading.value = true loading.value = true
try { try {
const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(pieceData)) const { constructeurIds, constructeurs, constructeurId, constructeur, ...cleanPayload } = pieceData as any
const normalizedPayload = normalizeRelationIds(cleanPayload)
const result = await patch(`/pieces/${id}`, normalizedPayload) const result = await patch(`/pieces/${id}`, normalizedPayload)
if (result.success && result.data) { if (result.success && result.data) {
const updated = await withResolvedConstructeurs(result.data as Piece) const updated = await withResolvedConstructeurs(result.data as Piece)

View File

@@ -146,27 +146,29 @@
</div> </div>
</div> </div>
<div v-if="isEditMode || editionForm.constructeurIds.length" class="form-control"> <div v-if="isEditMode || constructeurLinks.length" class="form-control">
<label class="label"> <label class="label">
<span class="label-text">Fournisseur</span> <span class="label-text">Fournisseur</span>
</label> </label>
<ConstructeurSelect <template v-if="isEditMode">
v-if="isEditMode" <ConstructeurSelect
v-model="editionForm.constructeurIds" v-model="editionForm.constructeurIds"
class="w-full" class="w-full"
:disabled="!canEdit || saving" :disabled="!canEdit || saving"
placeholder="Rechercher un ou plusieurs fournisseurs..." placeholder="Rechercher un ou plusieurs fournisseurs..."
:initial-options="piece?.constructeurs || []" :initial-options="piece?.constructeurs || []"
/>
<ConstructeurLinksTable
v-model="constructeurLinks"
class="mt-2"
@remove="handleConstructeurRemoved"
/>
</template>
<ConstructeurLinksTable
v-else
:model-value="constructeurLinks"
readonly
/> />
<div v-else class="flex flex-wrap gap-2">
<span
v-for="id in editionForm.constructeurIds"
:key="id"
class="badge badge-outline"
>
{{ getConstructeurById(id)?.name || id }}
</span>
</div>
</div> </div>
</div> </div>
@@ -389,16 +391,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref, watch } from 'vue'
import { useRoute } from '#imports' import { useRoute } from '#imports'
import { usePieceEdit } from '~/composables/usePieceEdit' import { usePieceEdit } from '~/composables/usePieceEdit'
import { useDocuments } from '~/composables/useDocuments' import { useDocuments } from '~/composables/useDocuments'
import { usePermissions } from '~/composables/usePermissions' import { usePermissions } from '~/composables/usePermissions'
import { useConstructeurs } from '~/composables/useConstructeurs'
const route = useRoute() const route = useRoute()
const { canEdit } = usePermissions() const { canEdit } = usePermissions()
const { getConstructeurById } = useConstructeurs()
const { updateDocument } = useDocuments() const { updateDocument } = useDocuments()
const isEditMode = ref(false) const isEditMode = ref(false)
@@ -416,6 +416,7 @@ const {
previewVisible, previewVisible,
selectedTypeId, selectedTypeId,
editionForm, editionForm,
constructeurLinks,
productSelections, productSelections,
customFieldInputs, customFieldInputs,
pieceTypeList, pieceTypeList,
@@ -448,6 +449,18 @@ const submitEdition = async () => {
} }
} }
// Sync ConstructeurSelect changes → constructeurLinks
watch(() => editionForm.constructeurIds, (newIds) => {
const existing = new Map(constructeurLinks.value.map(l => [l.constructeurId, l]))
constructeurLinks.value = newIds.map(id =>
existing.get(id) || { constructeurId: id, supplierReference: null },
)
})
const handleConstructeurRemoved = (constructeurId: string) => {
editionForm.constructeurIds = editionForm.constructeurIds.filter(id => id !== constructeurId)
}
const editingDocument = ref<any | null>(null) const editingDocument = ref<any | null>(null)
const editModalVisible = ref(false) const editModalVisible = ref(false)