Compare commits

..

6 Commits

Author SHA1 Message Date
gitea-actions
041a04f0e9 chore : bump version to v1.9.22
All checks were successful
Auto Tag Develop / tag (push) Successful in 8s
Build & Push Docker Image / build (push) Successful in 36s
2026-04-06 15:15:37 +00:00
d089cd4873 fix(model-type) : masquer uniquement les produits, garder les champs perso
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
Ajoute une prop hideProducts au PieceModelStructureEditor pour masquer
la section « Produits inclus par défaut » sans retirer les champs
personnalisés. Utilisé pour les catégories PRODUCT.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:15:26 +02:00
gitea-actions
b304cf6684 chore : bump version to v1.9.21
All checks were successful
Auto Tag Develop / tag (push) Successful in 8s
Build & Push Docker Image / build (push) Successful in 36s
2026-04-06 15:12:40 +00:00
0fe7f3131e fix(model-type) : retirer l'éditeur de structure produit inutilisé
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
Le PieceModelStructureEditor affiché pour les catégories PRODUCT ne
fonctionnait plus et n'est plus utilisé.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:12:29 +02:00
a6bbcaf6d1 fix(custom-fields) : masquer les champs machineContextOnly hors vue machine
Ajoute context: 'standalone' aux appels useCustomFieldInputs dans les
vues composant, pièce et produit (création et édition) pour filtrer
les champs perso réservés au contexte machine.

Exclut également ces champs de la formule de référence automatique
dans le ReferenceFormulaBuilder des catégories.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:12:29 +02:00
9f2e1da6ec fix(composant) : rendre les slots de structure optionnels à la création
Les emplacements pièces, produits et sous-composants du squelette ne
bloquent plus la soumission du formulaire de création de composant.
Les slots vides restent visibles en consultation avec l'indicateur rouge
« manquant » et peuvent être remplis ultérieurement en édition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:12:29 +02:00
9 changed files with 38 additions and 34 deletions

View File

@@ -1,2 +1,2 @@
parameters: parameters:
app.version: '1.9.20' app.version: '1.9.22'

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="space-y-6"> <div class="space-y-6">
<section class="space-y-3"> <section v-if="!hideProducts" class="space-y-3">
<header> <header>
<h3 class="text-sm font-semibold"> <h3 class="text-sm font-semibold">
Produits inclus par défaut Produits inclus par défaut
@@ -166,6 +166,7 @@ defineOptions({ name: 'PieceModelStructureEditor' })
const props = defineProps<{ const props = defineProps<{
modelValue?: PieceModelStructure | null modelValue?: PieceModelStructure | null
hideProducts?: boolean
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{

View File

@@ -99,11 +99,7 @@
v-else v-else
class="space-y-3 rounded-lg border border-base-300 p-4" class="space-y-3 rounded-lg border border-base-300 p-4"
> >
<p class="text-sm text-base-content/70"> <PieceModelStructureEditor v-model="productStructure" hide-products />
Aperçu :
<span class="font-medium text-base-content">{{ productStructurePreview }}</span>
</p>
<PieceModelStructureEditor v-model="productStructure" />
</div> </div>
</template> </template>
</section> </section>
@@ -194,15 +190,16 @@ const form = reactive<ModelTypePayload & { referenceFormula?: string | null }>({
}) })
const formulaBuilderCustomFields = computed(() => { const formulaBuilderCustomFields = computed(() => {
let fields: any[] = []
if (form.category === 'PIECE') { if (form.category === 'PIECE') {
const fields = pieceStructure.value?.customFields const raw = pieceStructure.value?.customFields
return Array.isArray(fields) ? fields : [] fields = Array.isArray(raw) ? raw : []
} }
if (form.category === 'COMPONENT') { else if (form.category === 'COMPONENT') {
const fields = componentStructure.value?.customFields const raw = componentStructure.value?.customFields
return Array.isArray(fields) ? fields : [] fields = Array.isArray(raw) ? raw : []
} }
return [] return fields.filter((f: any) => !f.machineContextOnly)
}) })
const extractFormulaFields = (formula: string | null | undefined): string[] => { const extractFormulaFields = (formula: string | null | undefined): string[] => {

View File

@@ -34,7 +34,6 @@ import {
import { import {
hasAssignments, hasAssignments,
initializeStructureAssignments, initializeStructureAssignments,
isAssignmentNodeComplete,
serializeStructureAssignments, serializeStructureAssignments,
} from '~/shared/utils/structureAssignmentHelpers' } from '~/shared/utils/structureAssignmentHelpers'
import type { ComponentModelStructure } from '~/shared/types/inventory' import type { ComponentModelStructure } from '~/shared/types/inventory'
@@ -152,24 +151,14 @@ export function useComponentCreate() {
values: computed(() => []), values: computed(() => []),
entityType: 'composant', entityType: 'composant',
entityId: createdComponentId, entityId: createdComponentId,
context: 'standalone',
}) })
const structureHasRequirements = computed(() => const structureHasRequirements = computed(() =>
hasAssignments(structureAssignments.value), hasAssignments(structureAssignments.value),
) )
const structureSelectionsComplete = computed(() => { const structureSelectionsComplete = computed(() => true)
if (!structureHasRequirements.value) {
return true
}
if (structureDataLoading.value) {
return false
}
if (!structureAssignments.value) {
return false
}
return isAssignmentNodeComplete(structureAssignments.value, true)
})
const canSubmit = computed(() => Boolean( const canSubmit = computed(() => Boolean(
canEdit.value canEdit.value
@@ -307,11 +296,6 @@ export function useComponentCreate() {
payload.productId = rootProductSelection.selectedProductId.trim() payload.productId = rootProductSelection.selectedProductId.trim()
} }
if (structureHasRequirements.value && !structureSelectionsComplete.value) {
toast.showError('Complétez la sélection des pièces, produits et sous-composants.')
return
}
const serializedStructure = structureHasRequirements.value const serializedStructure = structureHasRequirements.value
? serializeStructureAssignments(structureAssignments.value) ? serializeStructureAssignments(structureAssignments.value)
: null : null
@@ -414,6 +398,7 @@ export function useComponentCreate() {
structureSelectionsComplete, structureSelectionsComplete,
canEdit, canEdit,
canSubmit, canSubmit,
requiredCustomFieldsFilled,
// Functions // Functions
typeOptionLabel, typeOptionLabel,

View File

@@ -209,6 +209,7 @@ export function useComponentEdit(componentId: string) {
values: computed(() => component.value?.customFieldValues ?? []), values: computed(() => component.value?.customFieldValues ?? []),
entityType: 'composant', entityType: 'composant',
entityId: computed(() => component.value?.id ?? null), entityId: computed(() => component.value?.id ?? null),
context: 'standalone',
onValueCreated: (newValue) => { onValueCreated: (newValue) => {
if (component.value && Array.isArray(component.value.customFieldValues)) { if (component.value && Array.isArray(component.value.customFieldValues)) {
component.value.customFieldValues.push(newValue) component.value.customFieldValues.push(newValue)
@@ -556,6 +557,7 @@ export function useComponentEdit(componentId: string) {
originalConstructeurLinks, originalConstructeurLinks,
constructeurIdsFromForm, constructeurIdsFromForm,
customFieldInputs, customFieldInputs,
requiredCustomFieldsFilled,
historyFieldLabels, historyFieldLabels,
// Computed // Computed

View File

@@ -99,6 +99,7 @@ export function usePieceEdit(pieceId: string) {
values: computed(() => piece.value?.customFieldValues ?? []), values: computed(() => piece.value?.customFieldValues ?? []),
entityType: 'piece', entityType: 'piece',
entityId: computed(() => piece.value?.id ?? null), entityId: computed(() => piece.value?.id ?? null),
context: 'standalone',
onValueCreated: (newValue) => { onValueCreated: (newValue) => {
if (piece.value && Array.isArray(piece.value.customFieldValues)) { if (piece.value && Array.isArray(piece.value.customFieldValues)) {
piece.value.customFieldValues.push(newValue) piece.value.customFieldValues.push(newValue)
@@ -435,6 +436,7 @@ export function usePieceEdit(pieceId: string) {
constructeurIdsFromForm, constructeurIdsFromForm,
productSelections, productSelections,
customFieldInputs, customFieldInputs,
requiredCustomFieldsFilled,
canEdit, canEdit,
// Computed // Computed

View File

@@ -218,6 +218,9 @@
</p> </p>
</header> </header>
<CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || submitting" /> <CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || submitting" />
<p v-if="hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-warning">
Certains champs personnalisés sont obligatoires. Veuillez les renseigner avant de valider.
</p>
</div> </div>
<EmptyState <EmptyState
v-else v-else
@@ -237,6 +240,9 @@
Créer la pièce Créer la pièce
</button> </button>
</div> </div>
<p v-if="selectedType && hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-error text-right">
Merci de renseigner tous les champs personnalisés obligatoires avant de créer la pièce.
</p>
</div> </div>
</section> </section>
</main> </main>
@@ -311,7 +317,9 @@ const { fields: customFieldInputs, requiredFilled: requiredCustomFieldsFilled, s
values: [] as any[], values: [] as any[],
entityType: 'piece' as CustomFieldEntityType, entityType: 'piece' as CustomFieldEntityType,
entityId: createdEntityId, entityId: createdEntityId,
context: 'standalone',
}) })
const hasRequiredCustomFields = computed(() => customFieldInputs.value.some(f => f.required))
const selectedDocuments = ref<File[]>([]) const selectedDocuments = ref<File[]>([])
const uploadingDocuments = ref(false) const uploadingDocuments = ref(false)

View File

@@ -274,6 +274,9 @@
</header> </header>
<template v-if="isEditMode"> <template v-if="isEditMode">
<CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || saving" /> <CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || saving" />
<p v-if="hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-warning">
Certains champs personnalisés sont obligatoires. Veuillez les renseigner avant de valider.
</p>
</template> </template>
<template v-else> <template v-else>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
@@ -338,7 +341,7 @@
Enregistrer les modifications Enregistrer les modifications
</button> </button>
</div> </div>
<p v-if="isEditMode && product && !requiredCustomFieldsFilled" class="text-xs text-error text-right"> <p v-if="isEditMode && hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-error text-right">
Merci de renseigner tous les champs personnalisés obligatoires. Merci de renseigner tous les champs personnalisés obligatoires.
</p> </p>
</div> </div>
@@ -409,6 +412,7 @@ const {
values: cfValues, values: cfValues,
entityType: 'product' as CustomFieldEntityType, entityType: 'product' as CustomFieldEntityType,
entityId, entityId,
context: 'standalone',
}) })
const loading = ref(true) const loading = ref(true)
const saving = ref(false) const saving = ref(false)
@@ -446,7 +450,7 @@ const editionForm = reactive({
supplierPrice: '' as string, supplierPrice: '' as string,
}) })
// requiredCustomFieldsFilled comes from useCustomFieldInputs composable const hasRequiredCustomFields = computed(() => customFieldInputs.value.some(f => f.required))
const canSubmit = computed(() => const canSubmit = computed(() =>
Boolean(canEdit.value && product.value && editionForm.name.trim().length >= 2 && requiredCustomFieldsFilled.value && !saving.value), Boolean(canEdit.value && product.value && editionForm.name.trim().length >= 2 && requiredCustomFieldsFilled.value && !saving.value),

View File

@@ -158,6 +158,9 @@
</p> </p>
</header> </header>
<CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || submitting" /> <CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || submitting" />
<p v-if="hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-warning">
Certains champs personnalisés sont obligatoires. Veuillez les renseigner avant de valider.
</p>
</div> </div>
<EmptyState <EmptyState
v-else v-else
@@ -177,7 +180,7 @@
Créer le produit Créer le produit
</button> </button>
</div> </div>
<p v-if="selectedType && !requiredCustomFieldsFilled" class="text-xs text-error text-right"> <p v-if="selectedType && hasRequiredCustomFields && !requiredCustomFieldsFilled" class="text-xs text-error text-right">
Merci de renseigner tous les champs personnalisés obligatoires. Merci de renseigner tous les champs personnalisés obligatoires.
</p> </p>
</div> </div>
@@ -241,7 +244,9 @@ const { fields: customFieldInputs, requiredFilled: requiredCustomFieldsFilled, s
values: [] as any[], values: [] as any[],
entityType: 'product' as CustomFieldEntityType, entityType: 'product' as CustomFieldEntityType,
entityId: createdEntityId, entityId: createdEntityId,
context: 'standalone',
}) })
const hasRequiredCustomFields = computed(() => customFieldInputs.value.some(f => f.required))
const productTypeList = computed<ProductCatalogType[]>(() => const productTypeList = computed<ProductCatalogType[]>(() =>
(productTypes.value || []) as ProductCatalogType[], (productTypes.value || []) as ProductCatalogType[],