259 lines
9.6 KiB
Vue
259 lines
9.6 KiB
Vue
<template>
|
|
<form @submit.prevent="validate">
|
|
<div class="flex flex-col items-center gap-16">
|
|
<div
|
|
class="flex flex-col gap-16 items-center w-full">
|
|
<UiTextInput
|
|
id="merchandise-type"
|
|
v-model="selectedMerchandiseTypeId"
|
|
label="Type de marchandises"
|
|
:value="reception.merchandiseType?.label"
|
|
wrapper-class="w-[550px]"
|
|
:disabled="true"
|
|
/>
|
|
<div
|
|
v-if="merchandiseTypeId && isAutres"
|
|
class="flex flex-col w-full max-w-[550px]"
|
|
>
|
|
<UiTextInput
|
|
id="merchandise-detail"
|
|
:disabled="!auth.isAdmin"
|
|
v-model="merchandiseDetail"
|
|
label="Préciser"
|
|
placeholder="Précisions complémentaires"
|
|
:maxlength="255"
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
v-if="merchandiseTypeId && !isGranule"
|
|
class="flex gap-4 w-[550px] justify-evenly"
|
|
>
|
|
<div
|
|
v-for="building in buildings"
|
|
:key="building.id"
|
|
>
|
|
<UiCheckbox
|
|
v-model="selectedBuildingIds"
|
|
:value="String(building.id)"
|
|
:label="building.label"
|
|
:disabled="!auth.isAdmin"
|
|
label-class="text-xl"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-if="merchandiseTypeId && isGranule"
|
|
class="flex flex-col gap-10 w-full max-w-[1100px]"
|
|
>
|
|
<div class="grid grid-cols-1 gap-10 md:grid-cols-4">
|
|
<div v-for="type in pelletTypes" :key="type.id" class="flex flex-col gap-4">
|
|
<p class="font-bold uppercase">{{ type.label }}</p>
|
|
<div
|
|
v-for="building in buildings"
|
|
:key="building.id"
|
|
class="flex items-center gap-2 text-lg"
|
|
>
|
|
<UiCheckbox
|
|
v-model="selectedPelletBuildingIds[String(type.id)]"
|
|
:value="String(building.id)"
|
|
:label="building.label"
|
|
:disabled="!auth.isAdmin"
|
|
label-class="text-lg"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<UiButton
|
|
v-if="auth.isAdmin"
|
|
type="submit"
|
|
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
|
|
:disabled="!auth.isAdmin"
|
|
>Valider
|
|
</UiButton>
|
|
</div>
|
|
</form>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {computed, onMounted, ref} from 'vue'
|
|
import {getBuildingList} from '~/services/building'
|
|
import {getMerchandiseTypeList} from '~/services/merchandise-type'
|
|
import type {MerchandiseTypeData} from '~/services/dto/merchandise-type-data'
|
|
import type {BuildingData} from '~/services/dto/building-data'
|
|
import type {PelletTypeData} from '~/services/dto/pellet-type-data'
|
|
import {getPelletTypeList} from '~/services/pellet-type'
|
|
import {
|
|
createReceptionPelletBuilding,
|
|
deleteReceptionPelletBuilding,
|
|
getReceptionPelletBuildingList
|
|
} from '~/services/reception-pellet-building'
|
|
import {MERCHANDISE_TYPE_CODES} from '~/utils/constants'
|
|
import {getReception, updateReception} from "~/services/reception";
|
|
|
|
const merchandiseTypes = ref<MerchandiseTypeData[]>([])
|
|
const buildings = ref<BuildingData[]>([])
|
|
const pelletTypes = ref<PelletTypeData[]>([])
|
|
const selectedMerchandiseTypeId = ref('')
|
|
const selectedBuildingIds = ref<string[]>([])
|
|
const selectedPelletBuildingIds = ref<Record<string, string[]>>({})
|
|
const merchandiseDetail = ref('')
|
|
const auth = useAuthStore()
|
|
const props = defineProps<{
|
|
idReception: number
|
|
}>()
|
|
const receptionId = props.idReception
|
|
const reception = await getReception(receptionId)
|
|
const merchandiseTypeId = await reception.receptionType?.id
|
|
|
|
// Extrait l'ID d'une relation depuis un IRI ou un objet complet.
|
|
const getRelationId = (value: unknown): string | null => {
|
|
if (!value) {
|
|
return null
|
|
}
|
|
|
|
if (typeof value === 'string') {
|
|
const match = value.match(/\/(\d+)$/)
|
|
return match ? match[1] : null
|
|
}
|
|
|
|
if (typeof value === 'object' && 'id' in value) {
|
|
const record = value as { id?: number | string }
|
|
if (typeof record.id === 'number') {
|
|
return String(record.id)
|
|
}
|
|
if (typeof record.id === 'string') {
|
|
return record.id
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
// Type de marchandise sélectionné dans le select
|
|
const selectedMerchandiseType = computed(() =>
|
|
merchandiseTypes.value.find((type) => String(type.id) === selectedMerchandiseTypeId.value)
|
|
)
|
|
// Indique si le type est "Granulé"
|
|
const isGranule = computed(() => selectedMerchandiseType.value?.code === MERCHANDISE_TYPE_CODES.GRANULE)
|
|
// Indique si le type est "Autres"
|
|
const isAutres = computed(() => selectedMerchandiseType.value?.code === MERCHANDISE_TYPE_CODES.AUTRES)
|
|
|
|
// Charge les référentiels et hydrate le formulaire depuis la réception
|
|
onMounted(async () => {
|
|
const [merchandiseTypeList, buildingList, pelletTypeList] = await Promise.all([
|
|
getMerchandiseTypeList(),
|
|
getBuildingList(),
|
|
getPelletTypeList()
|
|
])
|
|
merchandiseTypes.value = merchandiseTypeList
|
|
buildings.value = buildingList
|
|
pelletTypes.value = pelletTypeList
|
|
|
|
const currentId = reception.merchandiseType?.id
|
|
if (currentId) {
|
|
selectedMerchandiseTypeId.value = String(currentId)
|
|
}
|
|
merchandiseDetail.value = reception.merchandiseDetail ?? ''
|
|
|
|
selectedBuildingIds.value =
|
|
reception.buildings?.map((building) => String(building.id)) ?? []
|
|
|
|
const existingPelletSelections = reception.pelletBuildings ?? []
|
|
const selectionMap: Record<string, string[]> = {}
|
|
for (const selection of existingPelletSelections) {
|
|
// L'API peut renvoyer les relations comme IRI ou comme objets selon le contexte.
|
|
const pelletTypeId = getRelationId(selection.pelletType)
|
|
const buildingId = getRelationId(selection.building)
|
|
if (!pelletTypeId || !buildingId) {
|
|
continue
|
|
}
|
|
if (!selectionMap[pelletTypeId]) {
|
|
selectionMap[pelletTypeId] = []
|
|
}
|
|
selectionMap[pelletTypeId].push(buildingId)
|
|
}
|
|
for (const pelletType of pelletTypes.value) {
|
|
const key = String(pelletType.id)
|
|
if (!selectionMap[key]) {
|
|
selectionMap[key] = []
|
|
}
|
|
}
|
|
selectedPelletBuildingIds.value = selectionMap
|
|
})
|
|
// Enregistre les sélections et passe à l'étape suivante
|
|
async function validate() {
|
|
|
|
const receptionIri = `/api/receptions/${reception.id}`
|
|
|
|
await updateReception(reception.id, {
|
|
merchandiseDetail: isAutres.value ? merchandiseDetail.value.trim() : null,
|
|
buildings: isGranule.value
|
|
? []
|
|
: selectedBuildingIds.value.map((id) => `/api/buildings/${id}`),
|
|
bovineDetail: null,
|
|
bovinesTypes: null,
|
|
})
|
|
|
|
if (isGranule.value) {
|
|
await syncPelletSelections(receptionIri)
|
|
} else {
|
|
await clearPelletSelections(receptionIri)
|
|
}
|
|
}
|
|
|
|
// Supprime toutes les associations granulés/bâtiments existantes
|
|
async function clearPelletSelections(receptionIri: string) {
|
|
const existing = await getReceptionPelletBuildingList(receptionIri)
|
|
for (const selection of existing) {
|
|
await deleteReceptionPelletBuilding(selection.id)
|
|
}
|
|
}
|
|
// Synchronise les associations granulés/bâtiments avec l'état du formulaire
|
|
async function syncPelletSelections(receptionIri: string) {
|
|
const existing = await getReceptionPelletBuildingList(receptionIri)
|
|
const existingMap = new Map<string, number>()
|
|
for (const selection of existing) {
|
|
// Construit la table de correspondance avec des IDs normalisés pour éviter les doublons.
|
|
const pelletTypeId = getRelationId(selection.pelletType)
|
|
const buildingId = getRelationId(selection.building)
|
|
if (!pelletTypeId || !buildingId) {
|
|
continue
|
|
}
|
|
const key = `${pelletTypeId}:${buildingId}`
|
|
existingMap.set(key, selection.id)
|
|
}
|
|
|
|
const desiredEntries: Array<{ pelletTypeId: string; buildingId: string }> = []
|
|
for (const [pelletTypeId, buildingIds] of Object.entries(selectedPelletBuildingIds.value)) {
|
|
for (const buildingId of buildingIds) {
|
|
desiredEntries.push({pelletTypeId, buildingId})
|
|
}
|
|
}
|
|
|
|
const desiredKeys = new Set(desiredEntries.map(
|
|
(entry) => `${entry.pelletTypeId}:${entry.buildingId}`
|
|
))
|
|
|
|
for (const [key, id] of existingMap.entries()) {
|
|
if (!desiredKeys.has(key)) {
|
|
await deleteReceptionPelletBuilding(id)
|
|
}
|
|
}
|
|
|
|
for (const entry of desiredEntries) {
|
|
const key = `${entry.pelletTypeId}:${entry.buildingId}`
|
|
if (!existingMap.has(key)) {
|
|
await createReceptionPelletBuilding({
|
|
reception: receptionIri,
|
|
pelletType: `/api/pellet_types/${entry.pelletTypeId}`,
|
|
building: `/api/buildings/${entry.buildingId}`
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|