Compare commits
7 Commits
v0.0.51
...
945ffc823a
| Author | SHA1 | Date | |
|---|---|---|---|
| 945ffc823a | |||
| 57551f2b4f | |||
| 6bd400d1a2 | |||
| 86299a18f3 | |||
| d47f237bac | |||
|
|
c52f22472d | ||
| e7421e985e |
@@ -49,6 +49,8 @@ Ajouter dans le fichier .env du frontend
|
|||||||
* fix layout admin
|
* fix layout admin
|
||||||
* Creation page admin listing bovins
|
* Creation page admin listing bovins
|
||||||
* Creation page admin ajout/modification bovins
|
* Creation page admin ajout/modification bovins
|
||||||
|
* [#331] Mettre à jour l'entité Shipment et bovin_shipment
|
||||||
|
* [#332] Refonte écran réception terminée
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
parameters:
|
parameters:
|
||||||
app.version: '0.0.51'
|
app.version: '0.0.52'
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtLink :to="link">
|
<NuxtLink :to="link">
|
||||||
<div class="w-[300px] h-[216px] border border-black rounded-lg p-6 flex flex-col justify-between gap-4">
|
<div class="w-[300px] h-[216px] border border-primary-700 rounded-lg p-6 flex flex-col justify-between gap-4">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div class="rounded-full w-[80px] h-[80px] bg-[#D9D9D9] flex justify-center items-center">
|
<div class="rounded-full w-[80px] h-[80px] bg-[#D9D9D9] flex justify-center items-center">
|
||||||
<Icon :name="iconName" style="color: black" size="44" />
|
<Icon :name="iconName" class="!text-primary-700" size="44" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Icon name="mdi:plus" style="color: black" size="44" />
|
<Icon name="mdi:plus" style="color: black" size="44" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="uppercase font-bold">
|
<div class="uppercase font-bold">
|
||||||
<p class="text-3xl text-primary-500">
|
<p class="text-3xl text-primary-700">
|
||||||
<slot name="label">{{ label }}</slot>
|
<slot name="label">{{ label }}</slot>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.BOVINS"
|
v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.BOVINS"
|
||||||
class="flex flex-col items-center gap-16">
|
class="flex flex-col gap-16">
|
||||||
<h1 class="text-4xl uppercase font-bold text-primary-500">Sélection des races réceptionnées</h1>
|
<h1 class="text-4xl uppercase font-bold text-primary-500">Sélection des races réceptionnées</h1>
|
||||||
<div
|
<div
|
||||||
class="flex flex-row gap-8 items-center">
|
class="flex flex-row gap-8 items-center w-full">
|
||||||
<div
|
<div
|
||||||
v-for="type in bovineType"
|
v-for="type in bovineType"
|
||||||
:key="type.id"
|
:key="type.id"
|
||||||
class="mt-8 flex flex-row mb-2 gap-6">
|
class="mt-8 flex flex-row mb-2 w-full">
|
||||||
<UiNumberInput
|
<UiNumberInput
|
||||||
:id="type.id"
|
:id="type.id"
|
||||||
:label="type.label"
|
:label="type.label"
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
:placeholder="0"
|
:placeholder="0"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="10"
|
:max="10"
|
||||||
|
class="max-w-[150px]"
|
||||||
|
wrapper-class="gap-3"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -24,6 +26,8 @@
|
|||||||
<UiNumberInput
|
<UiNumberInput
|
||||||
label="Autres"
|
label="Autres"
|
||||||
v-model="otherQuantity"
|
v-model="otherQuantity"
|
||||||
|
class="max-w-[80px]"
|
||||||
|
wrapper-class="gap-3"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="validate">
|
<form>
|
||||||
<div
|
<div class="flex flex-row justify-between gap-x-12 font-bold uppercase mb-8">
|
||||||
class="flex flex-col items-center gap-16">
|
|
||||||
<div
|
|
||||||
class="flex flex-row gap-6 items-center">
|
|
||||||
<div
|
<div
|
||||||
v-for="type in bovineType"
|
v-for="type in bovineType"
|
||||||
:key="type.id"
|
:key="type.id"
|
||||||
class="flex flex-row mb-2 gap-6 ">
|
>
|
||||||
<UiNumberInput
|
<UiNumberInput
|
||||||
:label="type.label"
|
:label="type.label"
|
||||||
:code="type.code"
|
:code="type.code"
|
||||||
@@ -16,52 +13,46 @@
|
|||||||
:placeholder="0"
|
:placeholder="0"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="10"
|
:max="10"
|
||||||
|
wrapperClass="w-44 flex-col"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<UiNumberInput
|
||||||
class=" flex flex-row mb-2 gap-6">
|
label="Autres"
|
||||||
<UiNumberInput
|
v-model="otherQuantity"
|
||||||
label="Autres"
|
:disabled="!auth.isAdmin"
|
||||||
v-model="otherQuantity"
|
wrapperClass="w-44 flex-col"
|
||||||
:disabled="!auth.isAdmin"
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<UiButton
|
|
||||||
type="submit"
|
|
||||||
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
|
|
||||||
:disabled="!auth.isAdmin"
|
|
||||||
>Valider
|
|
||||||
</UiButton>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
|
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
|
||||||
import {getBovineTypeList} from "~/services/bovine-type";
|
import {getBovineTypeList} from "~/services/bovine-type";
|
||||||
import {
|
import {createReceptionBovine, deleteReceptionBovine, getReceptionBovineList, updateReceptionBovine} from "~/services/reception-bovine";
|
||||||
createReceptionBovine,
|
|
||||||
deleteReceptionBovine,
|
|
||||||
getReceptionBovineList,
|
|
||||||
updateReceptionBovine
|
|
||||||
} from "~/services/reception-bovine";
|
|
||||||
import {computed, onMounted, reactive, ref, watch} from "vue";
|
import {computed, onMounted, reactive, ref, watch} from "vue";
|
||||||
import {getReception, updateReception} from "~/services/reception";
|
import {getReception, updateReception} from "~/services/reception";
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const isLoadingBovineType = ref(false)
|
const isLoadingBovineType = ref(false)
|
||||||
const bovineType = ref<BovineTypeData[]>([])
|
const bovineType = ref<BovineTypeData[]>([])
|
||||||
const bovineQuantities = reactive<Record<string, number | null>>({})
|
const bovineQuantities = reactive<Record<string, number | null>>({})
|
||||||
const otherQuantity = ref<number | null>(0)
|
const otherQuantity = ref<number | null>(0)
|
||||||
|
const initialBovineQuantities = ref<Record<string, number | null>>({})
|
||||||
|
const initialOtherQuantity = ref<number | null>(0)
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
idReception: number
|
idReception: number
|
||||||
|
isValidate: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const receptionId = props.idReception
|
const receptionId = props.idReception
|
||||||
const reception = await getReception(receptionId)
|
const reception = await getReception(receptionId)
|
||||||
|
|
||||||
const receptionIri = computed(() =>
|
const receptionIri = computed(() =>
|
||||||
receptionId ? `/api/receptions/${receptionId}` : null
|
receptionId ? `/api/receptions/${receptionId}` : null
|
||||||
)
|
)
|
||||||
|
|
||||||
const totalBovines = computed(() => {
|
const totalBovines = computed(() => {
|
||||||
const base = Object.values(bovineQuantities).reduce((sum, value) => {
|
const base = Object.values(bovineQuantities).reduce((sum, value) => {
|
||||||
return sum + (value ?? 0)
|
return sum + (value ?? 0)
|
||||||
@@ -69,6 +60,20 @@ const totalBovines = computed(() => {
|
|||||||
return base + (otherQuantity.value ?? 0)
|
return base + (otherQuantity.value ?? 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const hasBovineChanged = () => {
|
||||||
|
if ((initialOtherQuantity.value ?? 0) !== (otherQuantity.value ?? 0)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(bovineQuantities)) {
|
||||||
|
if ((initialBovineQuantities.value[key] ?? 0) !== (value ?? 0)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const loadBovineType = async () => {
|
const loadBovineType = async () => {
|
||||||
isLoadingBovineType.value = true
|
isLoadingBovineType.value = true
|
||||||
try {
|
try {
|
||||||
@@ -107,10 +112,12 @@ watch(
|
|||||||
|
|
||||||
const existingOther = reception.bovineDetail
|
const existingOther = reception.bovineDetail
|
||||||
const parsedOther =
|
const parsedOther =
|
||||||
typeof existingOther === 'string' && existingOther.trim() !== ''
|
typeof existingOther === 'string' && existingOther.trim() !== ''
|
||||||
? Number(existingOther)
|
? Number(existingOther)
|
||||||
: 0
|
: 0
|
||||||
otherQuantity.value = Number.isFinite(parsedOther) ? parsedOther : 0
|
otherQuantity.value = Number.isFinite(parsedOther) ? parsedOther : 0
|
||||||
|
initialBovineQuantities.value = {...selectionMap}
|
||||||
|
initialOtherQuantity.value = otherQuantity.value ?? 0
|
||||||
},
|
},
|
||||||
{immediate: true}
|
{immediate: true}
|
||||||
)
|
)
|
||||||
@@ -151,7 +158,6 @@ async function syncBovineSelections(receptionIri: string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (existingMap.has(bovineTypeId)) {
|
if (existingMap.has(bovineTypeId)) {
|
||||||
// Déjà à jour
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
await createReceptionBovine({
|
await createReceptionBovine({
|
||||||
@@ -162,7 +168,18 @@ async function syncBovineSelections(receptionIri: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validate() {
|
watch(
|
||||||
|
() => props.isValidate,
|
||||||
|
async (val) => {
|
||||||
|
if (!val) return
|
||||||
|
await runValidate()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const runValidate = async () => {
|
||||||
|
if (!hasBovineChanged()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const receptionIri = `/api/receptions/${reception.id}`
|
||||||
// @TODO Ajouter un composable pour le toaster qui gère les key i18n
|
// @TODO Ajouter un composable pour le toaster qui gère les key i18n
|
||||||
if (totalBovines.value > 52) {
|
if (totalBovines.value > 52) {
|
||||||
toast.error({
|
toast.error({
|
||||||
@@ -172,12 +189,16 @@ async function validate() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await syncBovineSelections(receptionIri.value)
|
await syncBovineSelections(receptionIri)
|
||||||
|
|
||||||
await updateReception(receptionId, {
|
await updateReception(receptionId, {
|
||||||
merchandiseType: null,
|
merchandiseType: null,
|
||||||
merchandiseDetail: null,
|
merchandiseDetail: null,
|
||||||
bovineDetail: otherQuantity.value ? String(otherQuantity.value) : null,
|
bovineDetail: otherQuantity.value ? String(otherQuantity.value) : null,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
initialBovineQuantities.value = {...bovineQuantities}
|
||||||
|
initialOtherQuantity.value = otherQuantity.value ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,33 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="validate">
|
<form>
|
||||||
<div class="flex flex-col items-center gap-16">
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-col gap-16 items-center w-full">
|
class="flex flex-col">
|
||||||
<UiTextInput
|
<div class="w-full col-start-1 row-start-1">
|
||||||
|
<UiRadioGroup
|
||||||
id="merchandise-type"
|
id="merchandise-type"
|
||||||
v-model="selectedMerchandiseTypeId"
|
v-model="selectedMerchandiseTypeId"
|
||||||
label="Type de marchandises"
|
label="Type de marchandises"
|
||||||
:value="reception.merchandiseType?.label"
|
:options="merchandiseTypes.map((type) => ({
|
||||||
wrapper-class="w-[550px]"
|
value: String(type.id),
|
||||||
:disabled="true"
|
label: type.label
|
||||||
|
}))"
|
||||||
|
input-class="accent-primary-700 focus:ring-primary-700"
|
||||||
|
item-class="text-primary-700/50 [&:has(input:checked)]:text-primary-700"
|
||||||
|
option-label-class="uppercase"
|
||||||
|
wrapper-class="w-full uppercase"
|
||||||
|
group-class="grid grid-cols-[336px_336px_355px_200px] w-[160px_160px_200px_180px] mt-9 mb-7"
|
||||||
|
:disabled="!auth.isAdmin"
|
||||||
/>
|
/>
|
||||||
<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>
|
||||||
|
|
||||||
|
<div class="w-full grid grid-cols-[3fr_1fr] gap-12 col-start-2 row-start-1">
|
||||||
<div
|
<div
|
||||||
v-if="merchandiseTypeId && !isGranule"
|
v-if="merchandiseTypeId && !isGranule"
|
||||||
class="flex gap-4 w-[550px] justify-evenly"
|
class="flex gap-[218px]"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="building in buildings"
|
v-for="building in buildings"
|
||||||
@@ -38,28 +34,45 @@
|
|||||||
:value="String(building.id)"
|
:value="String(building.id)"
|
||||||
:label="building.label"
|
:label="building.label"
|
||||||
:disabled="!auth.isAdmin"
|
:disabled="!auth.isAdmin"
|
||||||
label-class="text-xl"
|
input-class="accent-primary-700 focus:ring-primary-700"
|
||||||
|
label-class="text-xl uppercase"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="merchandiseTypeId && isAutres"
|
||||||
|
class="flex flex-col justify-self-end max-w-[182px]"
|
||||||
|
>
|
||||||
|
<UiTextInput
|
||||||
|
id="merchandise-detail"
|
||||||
|
:disabled="!auth.isAdmin"
|
||||||
|
v-model="merchandiseDetail"
|
||||||
|
placeholder="Préciser"
|
||||||
|
:maxlength="255"
|
||||||
|
class="h-6"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="merchandiseTypeId && isGranule"
|
v-if="merchandiseTypeId && isGranule"
|
||||||
class="flex flex-col gap-10 w-full max-w-[1100px]"
|
class="flex flex-col gap-10 w-full col-start-2 row-start-1 "
|
||||||
>
|
>
|
||||||
<div class="grid grid-cols-1 gap-10 md:grid-cols-4">
|
<div class="grid grid-cols-1 md:grid-cols-[max-content_max-content_max-content_max-content] justify-between">
|
||||||
<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="type in pelletTypes" :key="type.id" class="flex flex-col gap-4">
|
||||||
|
<p class="mb-1 font-medium uppercase">{{ type.label }}</p>
|
||||||
<div
|
<div
|
||||||
v-for="building in buildings"
|
v-for="building in buildings"
|
||||||
:key="building.id"
|
:key="building.id"
|
||||||
class="flex items-center gap-2 text-lg"
|
class="flex text-lg"
|
||||||
>
|
>
|
||||||
<UiCheckbox
|
<UiCheckbox
|
||||||
v-model="selectedPelletBuildingIds[String(type.id)]"
|
v-model="selectedPelletBuildingIds[String(type.id)]"
|
||||||
:value="String(building.id)"
|
:value="String(building.id)"
|
||||||
:label="building.label"
|
:label="building.label"
|
||||||
:disabled="!auth.isAdmin"
|
:disabled="!auth.isAdmin"
|
||||||
|
input-class="accent-primary-700 focus:ring-primary-700"
|
||||||
label-class="text-lg"
|
label-class="text-lg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,19 +80,11 @@
|
|||||||
</div>
|
</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>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {computed, onMounted, ref} from 'vue'
|
import {computed, onMounted, ref, watch} from 'vue'
|
||||||
import {getBuildingList} from '~/services/building'
|
import {getBuildingList} from '~/services/building'
|
||||||
import {getMerchandiseTypeList} from '~/services/merchandise-type'
|
import {getMerchandiseTypeList} from '~/services/merchandise-type'
|
||||||
import type {MerchandiseTypeData} from '~/services/dto/merchandise-type-data'
|
import type {MerchandiseTypeData} from '~/services/dto/merchandise-type-data'
|
||||||
@@ -102,12 +107,17 @@ const selectedBuildingIds = ref<string[]>([])
|
|||||||
const selectedPelletBuildingIds = ref<Record<string, string[]>>({})
|
const selectedPelletBuildingIds = ref<Record<string, string[]>>({})
|
||||||
const merchandiseDetail = ref('')
|
const merchandiseDetail = ref('')
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
const initialMerchandiseTypeId = ref<string | null>(null)
|
||||||
|
const initialMerchandiseDetail = ref<string | null>(null)
|
||||||
|
const initialBuildingIds = ref<string[]>([])
|
||||||
|
const initialPelletSelections = ref<Record<string, string[]>>({})
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
idReception: number
|
idReception: number
|
||||||
|
isValidate: boolean
|
||||||
}>()
|
}>()
|
||||||
const receptionId = props.idReception
|
const receptionId = props.idReception
|
||||||
const reception = await getReception(receptionId)
|
const reception = await getReception(receptionId)
|
||||||
const merchandiseTypeId = await reception.receptionType?.id
|
const merchandiseTypeId = reception.receptionType?.id
|
||||||
|
|
||||||
// Extrait l'ID d'une relation depuis un IRI ou un objet complet.
|
// Extrait l'ID d'une relation depuis un IRI ou un objet complet.
|
||||||
const getRelationId = (value: unknown): string | null => {
|
const getRelationId = (value: unknown): string | null => {
|
||||||
@@ -142,6 +152,52 @@ const isGranule = computed(() => selectedMerchandiseType.value?.code === MERCHAN
|
|||||||
// Indique si le type est "Autres"
|
// Indique si le type est "Autres"
|
||||||
const isAutres = computed(() => selectedMerchandiseType.value?.code === MERCHANDISE_TYPE_CODES.AUTRES)
|
const isAutres = computed(() => selectedMerchandiseType.value?.code === MERCHANDISE_TYPE_CODES.AUTRES)
|
||||||
|
|
||||||
|
const hasMerchandiseChanged = () => {
|
||||||
|
const currentTypeId = selectedMerchandiseTypeId.value || null
|
||||||
|
if (initialMerchandiseTypeId.value !== currentTypeId) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDetail = isAutres.value ? merchandiseDetail.value.trim() : ''
|
||||||
|
if ((initialMerchandiseDetail.value ?? '') !== currentDetail) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentBuildings = isGranule.value ? [] : [...selectedBuildingIds.value].sort()
|
||||||
|
const initialBuildings = [...initialBuildingIds.value].sort()
|
||||||
|
if (currentBuildings.length !== initialBuildings.length) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (let i = 0; i < currentBuildings.length; i += 1) {
|
||||||
|
if (currentBuildings[i] !== initialBuildings[i]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPellets = normalizePelletSelections(selectedPelletBuildingIds.value)
|
||||||
|
const initialPellets = normalizePelletSelections(initialPelletSelections.value)
|
||||||
|
if (currentPellets.length !== initialPellets.length) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (let i = 0; i < currentPellets.length; i += 1) {
|
||||||
|
if (currentPellets[i] !== initialPellets[i]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizePelletSelections = (selections: Record<string, string[]>) => {
|
||||||
|
const pairs: string[] = []
|
||||||
|
for (const [pelletTypeId, buildingIds] of Object.entries(selections)) {
|
||||||
|
for (const buildingId of buildingIds) {
|
||||||
|
pairs.push(`${pelletTypeId}:${buildingId}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pairs.sort()
|
||||||
|
}
|
||||||
|
|
||||||
// Charge les référentiels et hydrate le formulaire depuis la réception
|
// Charge les référentiels et hydrate le formulaire depuis la réception
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const [merchandiseTypeList, buildingList, pelletTypeList] = await Promise.all([
|
const [merchandiseTypeList, buildingList, pelletTypeList] = await Promise.all([
|
||||||
@@ -183,14 +239,36 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
selectedPelletBuildingIds.value = selectionMap
|
selectedPelletBuildingIds.value = selectionMap
|
||||||
|
initialMerchandiseTypeId.value = selectedMerchandiseTypeId.value || null
|
||||||
|
initialMerchandiseDetail.value = isAutres.value ? merchandiseDetail.value.trim() : ''
|
||||||
|
initialBuildingIds.value = [...selectedBuildingIds.value]
|
||||||
|
initialPelletSelections.value = JSON.parse(JSON.stringify(selectedPelletBuildingIds.value))
|
||||||
})
|
})
|
||||||
// Enregistre les sélections et passe à l'étape suivante
|
|
||||||
async function validate() {
|
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.isValidate,
|
||||||
|
async (val) => {
|
||||||
|
if (!val) return
|
||||||
|
await runValidate()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const runValidate = async () => {
|
||||||
|
if (!hasMerchandiseChanged()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const receptionIri = `/api/receptions/${reception.id}`
|
const receptionIri = `/api/receptions/${reception.id}`
|
||||||
|
|
||||||
|
// 1) supprimer toutes les anciennes associations
|
||||||
|
await clearPelletSelections(receptionIri)
|
||||||
|
|
||||||
|
// 2) update reception (type + détails + buildings non-granulé)
|
||||||
await updateReception(reception.id, {
|
await updateReception(reception.id, {
|
||||||
merchandiseDetail: isAutres.value ? merchandiseDetail.value.trim() : null,
|
merchandiseType: selectedMerchandiseTypeId.value
|
||||||
|
? `/api/merchandise_types/${selectedMerchandiseTypeId.value}`
|
||||||
|
: null,
|
||||||
|
merchandiseDetail: isAutres.value ? merchandiseDetail.value.trim() :
|
||||||
|
null,
|
||||||
buildings: isGranule.value
|
buildings: isGranule.value
|
||||||
? []
|
? []
|
||||||
: selectedBuildingIds.value.map((id) => `/api/buildings/${id}`),
|
: selectedBuildingIds.value.map((id) => `/api/buildings/${id}`),
|
||||||
@@ -198,11 +276,15 @@ async function validate() {
|
|||||||
bovinesTypes: null,
|
bovinesTypes: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 3) si granulé ALORS créer les nouvelles associations
|
||||||
if (isGranule.value) {
|
if (isGranule.value) {
|
||||||
await syncPelletSelections(receptionIri)
|
await syncPelletSelections(receptionIri)
|
||||||
} else {
|
|
||||||
await clearPelletSelections(receptionIri)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialMerchandiseTypeId.value = selectedMerchandiseTypeId.value || null
|
||||||
|
initialMerchandiseDetail.value = isAutres.value ? merchandiseDetail.value.trim() : ''
|
||||||
|
initialBuildingIds.value = [...selectedBuildingIds.value]
|
||||||
|
initialPelletSelections.value = JSON.parse(JSON.stringify(selectedPelletBuildingIds.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Supprime toutes les associations granulés/bâtiments existantes
|
// Supprime toutes les associations granulés/bâtiments existantes
|
||||||
@@ -255,4 +337,5 @@ async function syncPelletSelections(receptionIri: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,45 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="validate">
|
<form>
|
||||||
<div class="grid grid-cols-2 gap-x-40 gap-y-8 mb-8">
|
<div class="grid grid-cols-3 gap-x-40 gap-y-8 mb-8">
|
||||||
|
<UiNumberInput
|
||||||
|
:key="weight.type"
|
||||||
|
:label="'POIDS'"
|
||||||
|
labelClass="font-bold uppercase text-xl "
|
||||||
|
v-model="weight.weight"
|
||||||
|
:disabled="!auth.isAdmin"
|
||||||
|
:min="0"
|
||||||
|
:max="48000"
|
||||||
|
wrapper-class="flex-col"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiDateInput
|
||||||
|
label="Date pesée"
|
||||||
|
v-model="sharedWeightMeta.weighedAt"
|
||||||
|
:disabled="!auth.isAdmin"
|
||||||
|
/>
|
||||||
|
|
||||||
<UiNumberInput
|
<UiNumberInput
|
||||||
label="Dsd"
|
label="Dsd"
|
||||||
class="col-start-2"
|
class="col-start-2"
|
||||||
labelClass="font-bold uppercase"
|
labelClass="font-bold uppercase"
|
||||||
v-model="sharedWeightMeta.dsd"
|
v-model="sharedWeightMeta.dsd"
|
||||||
:disabled="!auth.isAdmin"
|
:disabled="!auth.isAdmin"
|
||||||
/>
|
wrapper-class="flex-col"
|
||||||
<UiDateInput
|
|
||||||
label="Date pesée"
|
|
||||||
v-model="sharedWeightMeta.weighedAt"
|
|
||||||
:disabled="!auth.isAdmin"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 gap-x-40 mb-16">
|
|
||||||
<UiNumberInput
|
|
||||||
v-for="weight in form.weights"
|
|
||||||
:key="weight.type"
|
|
||||||
:label="getWeightLabel(weight.type)"
|
|
||||||
labelClass="font-bold uppercase text-xl"
|
|
||||||
inputClass="w-24"
|
|
||||||
v-model="weight.weight"
|
|
||||||
:wrapper-class="weight.type === 'tare' ? 'col-start-1 row-start-1' : 'col-start-2 row-start-1'"
|
|
||||||
:disabled="!auth.isAdmin"
|
|
||||||
:min="0"
|
|
||||||
:max="48000"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<UiButton
|
|
||||||
v-if="auth.isAdmin"
|
|
||||||
type="submit"
|
|
||||||
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
|
|
||||||
|
|
||||||
>
|
|
||||||
Valider
|
|
||||||
</UiButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -48,20 +35,27 @@ import type {ReceptionFormWeight} from '~/services/dto/reception-data'
|
|||||||
import {getReception} from '~/services/reception'
|
import {getReception} from '~/services/reception'
|
||||||
import {updateWeight} from "~/services/weight";
|
import {updateWeight} from "~/services/weight";
|
||||||
import {useAuthStore} from "~/stores/auth";
|
import {useAuthStore} from "~/stores/auth";
|
||||||
|
import {ref, watch} from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
idReception: number
|
idReception: number
|
||||||
|
weightType: string
|
||||||
|
isValidate: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const idReception = props.idReception
|
|
||||||
const auth = useAuthStore()
|
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
weights: [
|
weights: [
|
||||||
{id: 0, type: 'tare' as const, weight: 0, dsd: null, weighedAt: null},
|
{id: 0, type: 'tare' as const, weight: 0, dsd: null, weighedAt: null},
|
||||||
{id: 0, type: 'gross' as const, weight: 0, dsd: null, weighedAt: null}
|
{id: 0, type: 'gross' as const, weight: 0, dsd: null, weighedAt: null}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const idReception = props.idReception
|
||||||
|
const weightType = props.weightType
|
||||||
|
const auth = useAuthStore()
|
||||||
|
const weight = form.weights.find(w => w.type === weightType)
|
||||||
|
const initialWeight = ref<{ weight: number | null; dsd: number | null; weighedAt: string | null } | null>(null)
|
||||||
|
|
||||||
// DSD et date de pesée sont partagés entre tare et gross dans l'UI.
|
// DSD et date de pesée sont partagés entre tare et gross dans l'UI.
|
||||||
const sharedWeightMeta = reactive<{
|
const sharedWeightMeta = reactive<{
|
||||||
dsd: number | string | null
|
dsd: number | string | null
|
||||||
@@ -71,8 +65,20 @@ const sharedWeightMeta = reactive<{
|
|||||||
weighedAt: null
|
weighedAt: null
|
||||||
})
|
})
|
||||||
|
|
||||||
const getWeightLabel = (type: 'tare' | 'gross'): string => {
|
const normalizeMeta = () => {
|
||||||
return type === 'tare' ? 'Pesée à vide' : 'Pesée à plein'
|
const sharedDsd =
|
||||||
|
sharedWeightMeta.dsd === null || sharedWeightMeta.dsd === undefined || sharedWeightMeta.dsd === ''
|
||||||
|
? null
|
||||||
|
: Number(sharedWeightMeta.dsd)
|
||||||
|
const sharedWeighedAt =
|
||||||
|
sharedWeightMeta.weighedAt === null || sharedWeightMeta.weighedAt === undefined || sharedWeightMeta.weighedAt === ''
|
||||||
|
? null
|
||||||
|
: sharedWeightMeta.weighedAt
|
||||||
|
|
||||||
|
return {
|
||||||
|
dsd: Number.isFinite(sharedDsd) ? sharedDsd : null,
|
||||||
|
weighedAt: sharedWeighedAt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hydrateFromReception = (reception: ReceptionFormWeight) => {
|
const hydrateFromReception = (reception: ReceptionFormWeight) => {
|
||||||
@@ -94,6 +100,15 @@ const hydrateFromReception = (reception: ReceptionFormWeight) => {
|
|||||||
sharedWeightMeta.dsd = weightWithMeta.dsd ?? null
|
sharedWeightMeta.dsd = weightWithMeta.dsd ?? null
|
||||||
sharedWeightMeta.weighedAt = weightWithMeta.weighedAt ?? null
|
sharedWeightMeta.weighedAt = weightWithMeta.weighedAt ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (weight) {
|
||||||
|
const normalized = normalizeMeta()
|
||||||
|
initialWeight.value = {
|
||||||
|
weight: weight.weight ?? null,
|
||||||
|
dsd: normalized.dsd,
|
||||||
|
weighedAt: normalized.weighedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -101,24 +116,48 @@ onMounted(async () => {
|
|||||||
hydrateFromReception(reception)
|
hydrateFromReception(reception)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function validate() {
|
watch(
|
||||||
const sharedDsd =
|
() => props.isValidate,
|
||||||
sharedWeightMeta.dsd === null || sharedWeightMeta.dsd === undefined || sharedWeightMeta.dsd === ''
|
async (val) => {
|
||||||
? null
|
if (!val) return
|
||||||
: Number(sharedWeightMeta.dsd)
|
await runValidate()
|
||||||
const sharedWeighedAt =
|
}
|
||||||
sharedWeightMeta.weighedAt === null || sharedWeightMeta.weighedAt === undefined || sharedWeightMeta.weighedAt === ''
|
)
|
||||||
? null
|
|
||||||
: sharedWeightMeta.weighedAt
|
const hasChanged = (current: { weight: number | null; dsd: number | null; weighedAt: string | null }) => {
|
||||||
for (const weight of form.weights) {
|
if (!initialWeight.value) {
|
||||||
if (weight.id) {
|
return true
|
||||||
await updateWeight(weight.id, {
|
}
|
||||||
weight: weight.weight,
|
return (
|
||||||
dsd: Number.isFinite(sharedDsd) ? sharedDsd : null,
|
(current.weight ?? null) !== (initialWeight.value.weight ?? null) ||
|
||||||
weighedAt: sharedWeighedAt
|
(current.dsd ?? null) !== (initialWeight.value.dsd ?? null) ||
|
||||||
})
|
(current.weighedAt ?? null) !== (initialWeight.value.weighedAt ?? null)
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const runValidate = async () => {
|
||||||
|
if (!weight?.id) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalized = normalizeMeta()
|
||||||
|
const current = {
|
||||||
|
weight: weight.weight ?? null,
|
||||||
|
dsd: normalized.dsd,
|
||||||
|
weighedAt: normalized.weighedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasChanged(current)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateWeight(weight.id, {
|
||||||
|
weight: current.weight,
|
||||||
|
dsd: current.dsd,
|
||||||
|
weighedAt: current.weighedAt
|
||||||
|
})
|
||||||
|
|
||||||
|
initialWeight.value = current
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -152,15 +152,9 @@ import type {ShipmentFormData} from '~/services/dto/shipment-data'
|
|||||||
import {SUPPLIER_CODE} from "~/utils/constants"
|
import {SUPPLIER_CODE} from "~/utils/constants"
|
||||||
import {useAuthStore} from '~/stores/auth'
|
import {useAuthStore} from '~/stores/auth'
|
||||||
import {useShipmentStore} from '~/stores/shipment'
|
import {useShipmentStore} from '~/stores/shipment'
|
||||||
import { computed, reactive, ref, watch, onMounted } from 'vue'
|
import {computed, reactive, ref, watch, onMounted} from 'vue'
|
||||||
import type {ShipmentTypeData} from "~/services/dto/shipment-type-data";
|
import type {ShipmentTypeData} from "~/services/dto/shipment-type-data";
|
||||||
import {getShipmentTypeList} from "~/services/shipment-type";
|
import {getShipmentTypeList} from "~/services/shipment-type";
|
||||||
import {
|
|
||||||
createShipmentBovine,
|
|
||||||
deleteShipmentBovine,
|
|
||||||
getBovinShipmentList,
|
|
||||||
updateShipmentBovine
|
|
||||||
} from "~/services/bovin-shipment";
|
|
||||||
|
|
||||||
const users = ref<UserData[]>([])
|
const users = ref<UserData[]>([])
|
||||||
const customers = ref<CustomerData[]>([])
|
const customers = ref<CustomerData[]>([])
|
||||||
@@ -332,23 +326,15 @@ watch(
|
|||||||
form.carrierId = shipment?.carrier?.id ? String(shipment.carrier.id) : ''
|
form.carrierId = shipment?.carrier?.id ? String(shipment.carrier.id) : ''
|
||||||
form.driverId = shipment?.driver?.id ? String(shipment.driver.id) : ''
|
form.driverId = shipment?.driver?.id ? String(shipment.driver.id) : ''
|
||||||
form.vehicleId = shipment?.vehicle?.id ? String(shipment.vehicle.id) : ''
|
form.vehicleId = shipment?.vehicle?.id ? String(shipment.vehicle.id) : ''
|
||||||
if (!shipment || !shipment.bovinShipments) {
|
|
||||||
selectedShipmentTypeId.value = ''
|
|
||||||
shipmentQuantity.value = 0
|
|
||||||
} else {
|
|
||||||
const selectedEntry = shipment.bovinShipments.find((entry) => {
|
|
||||||
const typeId = entry.shipmentType?.id
|
|
||||||
return Boolean(typeId) && Number(entry.nbBovinSend ?? 0) > 0
|
|
||||||
}) ?? shipment.bovinShipments.find((entry) => Boolean(entry.shipmentType?.id))
|
|
||||||
|
|
||||||
if (!selectedEntry?.shipmentType?.id) {
|
|
||||||
selectedShipmentTypeId.value = ''
|
selectedShipmentTypeId.value = shipment?.shipmentType?.id
|
||||||
shipmentQuantity.value = 0
|
? String(shipment.shipmentType.id)
|
||||||
} else {
|
: ''
|
||||||
selectedShipmentTypeId.value = String(selectedEntry.shipmentType.id)
|
|
||||||
shipmentQuantity.value = selectedEntry.nbBovinSend ?? 0
|
shipmentQuantity.value = shipment?.nbBovinSend ?? 0
|
||||||
}
|
|
||||||
}
|
|
||||||
isHydrating.value = false
|
isHydrating.value = false
|
||||||
},
|
},
|
||||||
{immediate: true}
|
{immediate: true}
|
||||||
@@ -474,68 +460,7 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const buildDesiredBovinShipments = () => {
|
|
||||||
const typeId = Number(selectedShipmentTypeId.value)
|
|
||||||
if (!Number.isFinite(typeId)) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
const type = bovineShipment.value.find((entry) => entry.id === typeId)
|
|
||||||
if (!type) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
const raw = shipmentQuantity.value
|
|
||||||
const quantity = raw === null || raw === undefined ? 0 : Number(raw)
|
|
||||||
const normalizedQuantity = Number.isFinite(quantity) ? Math.max(0, Math.trunc(quantity)) : 0
|
|
||||||
if (normalizedQuantity <= 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
return [{type, quantity: normalizedQuantity}]
|
|
||||||
}
|
|
||||||
const syncBovinShipments = async (
|
|
||||||
shipmentId: number,
|
|
||||||
existing: Array<{ id?: number; nbBovinSend: number | null; shipmentType?: unknown }> = []
|
|
||||||
) => {
|
|
||||||
const shipmentIri = `/api/shipments/${shipmentId}`
|
|
||||||
const desired = buildDesiredBovinShipments()
|
|
||||||
const desiredByTypeId = new Map<number, number>()
|
|
||||||
for (const entry of desired) {
|
|
||||||
desiredByTypeId.set(entry.type.id, entry.quantity)
|
|
||||||
}
|
|
||||||
for (const entry of existing) {
|
|
||||||
if (!entry.id) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
const rawType = entry.shipmentType
|
|
||||||
let typeId: number | null = null
|
|
||||||
if (rawType && typeof rawType === 'object' && 'id' in rawType) {
|
|
||||||
typeId = Number((rawType as { id: number }).id)
|
|
||||||
} else if (typeof rawType === 'string') {
|
|
||||||
const match = rawType.match(/\/shipment_types\/(\\d+)$/)
|
|
||||||
typeId = match ? Number(match[1]) : null
|
|
||||||
}
|
|
||||||
if (!typeId) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
const desiredQuantity = desiredByTypeId.get(typeId)
|
|
||||||
if (!desiredQuantity) {
|
|
||||||
await deleteShipmentBovine(entry.id)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (entry.nbBovinSend !== desiredQuantity) {
|
|
||||||
await updateShipmentBovine(entry.id, {nbBovinSend: desiredQuantity})
|
|
||||||
}
|
|
||||||
desiredByTypeId.delete(typeId)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [typeId, quantity] of desiredByTypeId.entries()) {
|
|
||||||
await createShipmentBovine({
|
|
||||||
shipment: shipmentIri,
|
|
||||||
shipmentType: `/api/shipment_types/${typeId}`,
|
|
||||||
nbBovinSend: quantity
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const buildPayload = () => {
|
const buildPayload = () => {
|
||||||
const normalizedLicensePlate = form.licencePlate.trim()
|
const normalizedLicensePlate = form.licencePlate.trim()
|
||||||
const normalizedShipmentDate = form.shipmentDate.trim()
|
const normalizedShipmentDate = form.shipmentDate.trim()
|
||||||
@@ -563,6 +488,14 @@ const buildPayload = () => {
|
|||||||
const addressIri = normalizedAddressId
|
const addressIri = normalizedAddressId
|
||||||
? `/api/addresses/${normalizedAddressId}`
|
? `/api/addresses/${normalizedAddressId}`
|
||||||
: null
|
: null
|
||||||
|
const normalizedShipmentTypeId = selectedShipmentTypeId.value.trim()
|
||||||
|
const shipmentTypeIri = normalizedShipmentTypeId
|
||||||
|
? `/api/shipment_types/${normalizedShipmentTypeId}`
|
||||||
|
: null
|
||||||
|
|
||||||
|
const rawQuantity = Number(shipmentQuantity.value ?? 0)
|
||||||
|
const normalizedQuantity = Number.isFinite(rawQuantity) ? Math.max(0,
|
||||||
|
Math.trunc(rawQuantity)) : 0
|
||||||
|
|
||||||
return {
|
return {
|
||||||
licencePlate: normalizedLicensePlate,
|
licencePlate: normalizedLicensePlate,
|
||||||
@@ -572,20 +505,19 @@ const buildPayload = () => {
|
|||||||
carrier: carrierIri,
|
carrier: carrierIri,
|
||||||
driver: driverIri,
|
driver: driverIri,
|
||||||
user: userIri,
|
user: userIri,
|
||||||
address: addressIri
|
address: addressIri,
|
||||||
|
shipmentType: shipmentTypeIri,
|
||||||
|
nbBovinSend: normalizedQuantity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveDraft = async () => {
|
const saveDraft = async () => {
|
||||||
const payload = buildPayload()
|
const payload = buildPayload()
|
||||||
if (!shipmentStore.current) {
|
if (!shipmentStore.current) {
|
||||||
const created = await shipmentStore.createShipment({
|
await shipmentStore.createShipment({
|
||||||
currentStep: 0,
|
currentStep: 0,
|
||||||
...payload
|
...payload
|
||||||
})
|
})
|
||||||
if (created) {
|
|
||||||
await syncBovinShipments(created.id, [])
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,10 +525,6 @@ const saveDraft = async () => {
|
|||||||
currentStep: shipmentStore.current.currentStep,
|
currentStep: shipmentStore.current.currentStep,
|
||||||
...payload
|
...payload
|
||||||
})
|
})
|
||||||
await syncBovinShipments(
|
|
||||||
shipmentStore.current.id,
|
|
||||||
shipmentStore.current?.bovinShipments ?? []
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({saveDraft})
|
defineExpose({saveDraft})
|
||||||
@@ -610,7 +538,6 @@ const validate = async () => {
|
|||||||
})
|
})
|
||||||
if (created) {
|
if (created) {
|
||||||
await shipmentStore.loadShipment(created.id)
|
await shipmentStore.loadShipment(created.id)
|
||||||
await syncBovinShipments(created.id, shipmentStore.current?.bovinShipments ?? [])
|
|
||||||
await router.push(`/shipment/${created.id}`)
|
await router.push(`/shipment/${created.id}`)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -621,6 +548,5 @@ const validate = async () => {
|
|||||||
...payload
|
...payload
|
||||||
})
|
})
|
||||||
await shipmentStore.loadShipment(shipmentStore.current.id)
|
await shipmentStore.loadShipment(shipmentStore.current.id)
|
||||||
await syncBovinShipments(shipmentStore.current.id, shipmentStore.current?.bovinShipments ?? [])
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center gap-[118px]">
|
<div class="flex flex-col items-center gap-[118px]">
|
||||||
<h1 class="font-bold text-5xl uppercase text-primary-500">Charment des bovins</h1>
|
<h1 class="font-bold text-5xl uppercase text-primary-500">Chargement des bovins</h1>
|
||||||
<div
|
<div
|
||||||
class="w-full flex flex-col items-center justify-center">
|
class="w-full flex flex-col items-center justify-center">
|
||||||
<UiLoadingDots />
|
<UiLoadingDots />
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="wrapperClass">
|
||||||
<label
|
<label
|
||||||
class="flex items-center gap-2 cursor-pointer text-primary-500"
|
class="flex items-center gap-2 cursor-pointer text-primary-700"
|
||||||
:class="labelClass"
|
:class="labelClass"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="checked"
|
:checked="checked"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:class="['cursor-pointer text-primary-500', inputClass]"
|
:class="['h-4 w-4 cursor-pointer text-primary-500', inputClass]"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
>
|
>
|
||||||
<span v-if="label">{{ label }}</span>
|
<span v-if="label">{{ label }}</span>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<label
|
<label
|
||||||
v-if="label"
|
v-if="label"
|
||||||
:for="id"
|
:for="id"
|
||||||
class="font-bold uppercase text-xl text-primary-500"
|
class="font-bold uppercase text-xl text-primary-700"
|
||||||
:class="labelClass"
|
:class="labelClass"
|
||||||
>
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
@@ -14,9 +14,9 @@
|
|||||||
:value="modelValue ?? ''"
|
:value="modelValue ?? ''"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
class="border-b border-black justify-self-start text-xl text-primary-500 py-[6px] uppercase bg-transparent appearance-none h-[34px]"
|
class="border-b border-primary-700 justify-self-start text-xl text-primary-700 py-[6px] uppercase bg-transparent appearance-none h-[34px]"
|
||||||
:class="[
|
:class="[
|
||||||
isEmpty ? 'text-neutral-400' : 'text-black',
|
isEmpty ? 'text-neutral-400' : 'text-primary-700',
|
||||||
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
||||||
inputClass
|
inputClass
|
||||||
]"
|
]"
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
// flex row passer en class wraper class flex col ainsi que le wfull 34
|
||||||
<template>
|
<template>
|
||||||
<div :class="['flex flex-row items-center gap-2', wrapperClass]">
|
<div :class="['flex', wrapperClass]">
|
||||||
<label
|
<label
|
||||||
v-if="label"
|
v-if="label"
|
||||||
:for="id"
|
:for="id"
|
||||||
class="text-xl flex items-center gap-2 text-primary-500"
|
class="text-xl flex items-center gap-2 text-primary-700"
|
||||||
:class="labelClass"
|
:class="labelClass"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
:step="step"
|
:step="step"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
class="border-b border-black text-xl bg-transparent w-16 text-primary-500"
|
class="border-b border-primary-700 justify-self-start text-xl text-primary-700 py-[6px] uppercase bg-transparent appearance-none h-[34px]"
|
||||||
:class="[
|
:class="[
|
||||||
isEmpty ? 'text-neutral-400' : 'text-black',
|
isEmpty ? 'text-neutral-400' : 'text-black',
|
||||||
disabled ? 'cursor-not-allowed' : 'cursor-text',
|
disabled ? 'cursor-not-allowed' : 'cursor-text',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div :class="['flex flex-col', wrapperClass]">
|
<div :class="['flex flex-col', wrapperClass]">
|
||||||
<label
|
<label
|
||||||
v-if="label"
|
v-if="label"
|
||||||
class="font-bold uppercase text-xl text-primary-500"
|
class="font-bold uppercase text-xl text-primary-700"
|
||||||
:class="labelClass"
|
:class="labelClass"
|
||||||
>
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
v-for="option in options"
|
v-for="option in options"
|
||||||
:key="String(option.value)"
|
:key="String(option.value)"
|
||||||
:for="`${id || 'radio'}-${option.value}`"
|
:for="`${id || 'radio'}-${option.value}`"
|
||||||
class="flex items-center gap-2 text-primary-500"
|
class="flex items-center gap-2 text-primary-700"
|
||||||
:class="itemClass"
|
:class="itemClass"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
:checked="String(modelValue ?? '') === String(option.value)"
|
:checked="String(modelValue ?? '') === String(option.value)"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
class="h-4 w-4 border-slate-300 text-primary-500 focus:ring-primary-500"
|
class="h-4 w-4 border-primary-700/50 text-primary-700 focus:ring-primary-700"
|
||||||
:class="[
|
:class="[
|
||||||
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
||||||
inputClass
|
inputClass
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<label
|
<label
|
||||||
v-if="label"
|
v-if="label"
|
||||||
:for="id"
|
:for="id"
|
||||||
class="font-bold uppercase text-xl text-primary-500"
|
class="font-bold uppercase text-xl text-primary-700"
|
||||||
:class="labelClass"
|
:class="labelClass"
|
||||||
>
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
@@ -13,9 +13,9 @@
|
|||||||
:value="modelValue ?? ''"
|
:value="modelValue ?? ''"
|
||||||
:disabled="disabled || loading"
|
:disabled="disabled || loading"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
class="border-b border-black justify-self-start text-xl text-primary-500 py-[6px] bg-transparent"
|
class="border-b border-primary-700 justify-self-start text-xl text-primary-700 py-[6px] bg-transparent"
|
||||||
:class="[
|
:class="[
|
||||||
isEmpty ? 'text-neutral-400' : 'text-black',
|
isEmpty ? 'text-neutral-400' : 'text-primary-700',
|
||||||
disabled || loading ? 'cursor-not-allowed' : 'cursor-pointer',
|
disabled || loading ? 'cursor-not-allowed' : 'cursor-pointer',
|
||||||
selectClass
|
selectClass
|
||||||
]"
|
]"
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
v-for="option in options"
|
v-for="option in options"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
class="text-black"
|
class="text-primary-700"
|
||||||
>
|
>
|
||||||
{{ option.label }}
|
{{ option.label }}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<form @submit.prevent="validate">
|
<form @submit.prevent="validate">
|
||||||
<div class="flex items-center justify-between mt-12 mb-8 ">
|
<div class="grid grid-cols-2 items-start gap-y-8 gap-x-40 mb-[60px]">
|
||||||
<h1 class="font-bold text-5xl uppercase">Réception {{ receptionLoad?.identificationNumber }}</h1>
|
<div class="flex items-center justify-between gap-10 relative">
|
||||||
</div>
|
<div class="flex flex-row absolute -left-[60px] justify-between">
|
||||||
<div class="grid grid-cols-2 items-start gap-y-8 gap-x-40 mb-12">
|
<Icon @click="router.push('/')" name="gg:arrow-left-o" size="44" class="cursor-pointer text-primary-500"/>
|
||||||
|
</div>
|
||||||
|
<h1 class="font-bold text-4xl col-start-1 row-start-1 text-primary-500 uppercase">Réception {{ form.identificationNumber }}</h1>
|
||||||
|
<Icon @click="router.push('/')" name="mdi:printer-outline" size="44" class="cursor-pointer text-primary-500"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Nom de l'utilisateur -->
|
<!-- Nom de l'utilisateur -->
|
||||||
<UiSelect
|
<UiSelect
|
||||||
id="reception-user"
|
id="reception-user"
|
||||||
@@ -16,7 +21,7 @@
|
|||||||
label: user.username
|
label: user.username
|
||||||
}))"
|
}))"
|
||||||
:loading="isLoadingUsers"
|
:loading="isLoadingUsers"
|
||||||
wrapper-class="col-start-1 row-start-1"
|
wrapper-class="col-start-1 row-start-2"
|
||||||
/>
|
/>
|
||||||
<!-- Date de réception -->
|
<!-- Date de réception -->
|
||||||
<UiDateInput
|
<UiDateInput
|
||||||
@@ -24,7 +29,20 @@
|
|||||||
:disabled="!auth.isAdmin"
|
:disabled="!auth.isAdmin"
|
||||||
v-model="form.receptionDate"
|
v-model="form.receptionDate"
|
||||||
label="Date de réception"
|
label="Date de réception"
|
||||||
wrapper-class="col-start-1 row-start-2"
|
wrapper-class="col-start-1 row-start-3"
|
||||||
|
/>
|
||||||
|
<!-- type de reception -->
|
||||||
|
<UiSelect
|
||||||
|
id="reception-supplier"
|
||||||
|
v-model="form.receptionTypeId"
|
||||||
|
:disabled="!auth.isAdmin"
|
||||||
|
label="Type de Réception"
|
||||||
|
:options="receptionTypes.map((receptionType) => ({
|
||||||
|
value: String(receptionType.id),
|
||||||
|
label: receptionType.label
|
||||||
|
}))"
|
||||||
|
:loading="isLoadingSuppliers"
|
||||||
|
wrapper-class="col-start-1 row-start-4"
|
||||||
/>
|
/>
|
||||||
<!-- Fournisseur -->
|
<!-- Fournisseur -->
|
||||||
<UiSelect
|
<UiSelect
|
||||||
@@ -37,7 +55,7 @@
|
|||||||
label: supplier.name
|
label: supplier.name
|
||||||
}))"
|
}))"
|
||||||
:loading="isLoadingSuppliers"
|
:loading="isLoadingSuppliers"
|
||||||
wrapper-class="col-start-1 row-start-3"
|
wrapper-class="col-start-1 row-start-5"
|
||||||
/>
|
/>
|
||||||
<!-- Adresse fournisseur -->
|
<!-- Adresse fournisseur -->
|
||||||
<UiSelect
|
<UiSelect
|
||||||
@@ -49,7 +67,7 @@
|
|||||||
label: address.fullAddress
|
label: address.fullAddress
|
||||||
}))"
|
}))"
|
||||||
:disabled="(isLoadingSuppliers || supplierAddresses.length === 0) && !auth.isAdmin"
|
:disabled="(isLoadingSuppliers || supplierAddresses.length === 0) && !auth.isAdmin"
|
||||||
wrapper-class="col-start-1 row-start-4"
|
wrapper-class="col-start-2 row-start-1"
|
||||||
/>
|
/>
|
||||||
<!-- Camion -->
|
<!-- Camion -->
|
||||||
<UiSelect
|
<UiSelect
|
||||||
@@ -62,7 +80,7 @@
|
|||||||
label: truck.name
|
label: truck.name
|
||||||
}))"
|
}))"
|
||||||
:loading="isLoadingTrucks"
|
:loading="isLoadingTrucks"
|
||||||
wrapper-class="col-start-2 row-start-1"
|
wrapper-class="col-start-2 row-start-2"
|
||||||
/>
|
/>
|
||||||
<!-- Transporteur -->
|
<!-- Transporteur -->
|
||||||
<UiSelect
|
<UiSelect
|
||||||
@@ -76,7 +94,7 @@
|
|||||||
}))"
|
}))"
|
||||||
:loading="isLoadingCarriers"
|
:loading="isLoadingCarriers"
|
||||||
select-class="h-[34px]"
|
select-class="h-[34px]"
|
||||||
wrapper-class="col-start-2 row-start-2"
|
wrapper-class="col-start-2 row-start-3"
|
||||||
/>
|
/>
|
||||||
<!-- Chauffeur (LIOT) -->
|
<!-- Chauffeur (LIOT) -->
|
||||||
<UiSelect
|
<UiSelect
|
||||||
@@ -89,10 +107,10 @@
|
|||||||
label: driver.name
|
label: driver.name
|
||||||
}))"
|
}))"
|
||||||
:loading="isLoadingDrivers"
|
:loading="isLoadingDrivers"
|
||||||
wrapper-class="col-start-2 row-start-3"
|
wrapper-class="col-start-2 row-start-4"
|
||||||
/>
|
/>
|
||||||
<!-- Plaque d'immatriculation -->
|
<!-- Plaque d'immatriculation -->
|
||||||
<div v-if="!isLiotCarrier" class="col-start-2 row-start-4">
|
<div v-if="!isLiotCarrier" class="col-start-2 row-start-5">
|
||||||
<UiLicensePlateInput
|
<UiLicensePlateInput
|
||||||
:disabled="!auth.isAdmin"
|
:disabled="!auth.isAdmin"
|
||||||
v-model="form.licensePlate"
|
v-model="form.licensePlate"
|
||||||
@@ -111,30 +129,28 @@
|
|||||||
}))"
|
}))"
|
||||||
:loading="isLoadingVehicles"
|
:loading="isLoadingVehicles"
|
||||||
:disabled="(isLoadingVehicles || filteredVehicles.length === 0) && !auth.isAdmin"
|
:disabled="(isLoadingVehicles || filteredVehicles.length === 0) && !auth.isAdmin"
|
||||||
wrapper-class="col-start-2 row-start-4"
|
wrapper-class="col-start-2 row-start-5"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center mb-2">
|
<div v-if="formIsLoading">
|
||||||
<UiButton
|
<div class="flex justify-evenly gap-y-8 gap-x-41 mb-10 border-b border-primary-500/60">
|
||||||
v-if="auth.isAdmin"
|
|
||||||
type="submit"
|
|
||||||
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px] mb-16"
|
|
||||||
|
|
||||||
>
|
|
||||||
Enregistrer
|
|
||||||
</UiButton>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-evenly gap-y-8 gap-x-40 mb-8 border-b border-slate-400">
|
|
||||||
<h1
|
<h1
|
||||||
class="font-bold text-3xl uppercase col-start-1 row-start-1 cursor-pointer"
|
class="font-bold text-3xl uppercase px-12 col-start-1 row-start-1 cursor-pointer "
|
||||||
:class="activeTab === 'weights' ? 'underline' : ''"
|
:class="activeTab === 'weightsEmpty' ? 'border-b-[6px] border-primary-500 text-primary-500' : 'text-primary-500/50'"
|
||||||
@click="activeTab = 'weights'"
|
@click="activeTab = 'weightsEmpty'"
|
||||||
>
|
>
|
||||||
pesées
|
pesée à plein
|
||||||
</h1>
|
</h1>
|
||||||
<h1
|
<h1
|
||||||
class="font-bold text-3xl uppercase col-start-2 row-start-1 cursor-pointer"
|
class="font-bold text-3xl uppercase col-start-1 row-start-1 px-12 cursor-pointer "
|
||||||
:class="activeTab === 'merchandise' ? 'underline' : ''"
|
:class="activeTab === 'weights' ? 'border-b-[6px] border-primary-500 text-primary-500 ' : 'text-primary-500/50'"
|
||||||
|
@click="activeTab = 'weights'"
|
||||||
|
>
|
||||||
|
pesée à vide
|
||||||
|
</h1>
|
||||||
|
<h1
|
||||||
|
class="font-bold text-3xl uppercase px-12 col-start-2 row-start-1 cursor-pointer "
|
||||||
|
:class="activeTab === 'merchandise' ? 'border-b-[6px] border-primary-500 text-primary-500' : 'text-primary-500/50'"
|
||||||
@click="activeTab = 'merchandise'"
|
@click="activeTab = 'merchandise'"
|
||||||
>
|
>
|
||||||
{{ isMerchandise ? "Marchandise" : "Bovins" }}
|
{{ isMerchandise ? "Marchandise" : "Bovins" }}
|
||||||
@@ -142,22 +158,47 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<update-weight
|
<update-weight
|
||||||
v-if="activeTab === 'weights'"
|
v-show="activeTab === 'weights'"
|
||||||
:idReception="idReception"
|
:idReception="idReception"
|
||||||
|
:weightType="'tare'"
|
||||||
:disabled="!auth.isAdmin"
|
:disabled="!auth.isAdmin"
|
||||||
|
:isValidate="isValid"
|
||||||
/>
|
/>
|
||||||
|
<div class="mb-12 ">
|
||||||
|
<update-weight
|
||||||
|
v-show="activeTab === 'weightsEmpty'"
|
||||||
|
:idReception="idReception"
|
||||||
|
:weightType="'gross'"
|
||||||
|
:disabled="!auth.isAdmin"
|
||||||
|
:isValidate="isValid"
|
||||||
|
/>
|
||||||
|
|
||||||
<update-merchandise
|
<update-merchandise
|
||||||
v-else-if="activeTab === 'merchandise' && isMerchandise"
|
v-show="activeTab === 'merchandise' && isMerchandise"
|
||||||
:idReception="idReception"
|
:idReception="idReception"
|
||||||
:disabled="!auth.isAdmin"
|
:disabled="!auth.isAdmin"
|
||||||
/>
|
:isValidate="isValidMerchandise"
|
||||||
|
/>
|
||||||
|
|
||||||
<update-bovin
|
<update-bovin
|
||||||
v-else
|
v-show="activeTab === 'merchandise' && !isMerchandise"
|
||||||
:idReception="idReception"
|
:idReception="idReception"
|
||||||
:disabled="!auth.isAdmin"
|
:disabled="!auth.isAdmin"
|
||||||
/>
|
:isValidate="isValidBovin"
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<UiButton
|
||||||
|
v-if="auth.isAdmin"
|
||||||
|
type="submit"
|
||||||
|
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px] mb-16"
|
||||||
|
|
||||||
|
>
|
||||||
|
Enregistrer
|
||||||
|
</UiButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -176,18 +217,21 @@ import type {DriverData} from '~/services/dto/driver-data'
|
|||||||
import {getDriverList} from '~/services/driver'
|
import {getDriverList} from '~/services/driver'
|
||||||
import type {VehicleData} from '~/services/dto/vehicle-data'
|
import type {VehicleData} from '~/services/dto/vehicle-data'
|
||||||
import {getVehicleList} from '~/services/vehicle'
|
import {getVehicleList} from '~/services/vehicle'
|
||||||
import {SUPPLIER_CODE} from "~/utils/constants";
|
import {RECEPTION_TYPE_CODES, SUPPLIER_CODE} from "~/utils/constants";
|
||||||
import {deleteReceptionBovine, getReceptionBovineList} from "~/services/reception-bovine";
|
import {deleteReceptionBovine, getReceptionBovineList,} from "~/services/reception-bovine";
|
||||||
import type {ReceptionData, ReceptionFormData} from "~/services/dto/reception-data";
|
import type {ReceptionData, ReceptionFormData} from "~/services/dto/reception-data";
|
||||||
import {getReception} from "~/services/reception";
|
import {getReception, updateReception} from "~/services/reception";
|
||||||
import UpdateWeight from "~/components/reception/update-weight.vue";
|
import UpdateWeight from "~/components/reception/update-weight.vue";
|
||||||
import UpdateMerchandise from "~/components/reception/update-merchandise.vue";
|
import UpdateMerchandise from "~/components/reception/update-merchandise.vue";
|
||||||
import UpdateBovin from "~/components/reception/update-bovin.vue";
|
import UpdateBovin from "~/components/reception/update-bovin.vue";
|
||||||
|
import {getReceptionTypeList} from "~/services/reception-type";
|
||||||
|
import type {ReceptionTypeData} from "~/services/dto/reception-type-data";
|
||||||
|
import {computed} from "vue";
|
||||||
|
import {updateWeight} from "~/services/weight";
|
||||||
|
import {deleteReceptionPelletBuilding, getReceptionPelletBuildingList} from "~/services/reception-pellet-building";
|
||||||
|
|
||||||
const activeTab = ref<'weights' | 'merchandise'>('weights')
|
|
||||||
const router = useRouter()
|
|
||||||
const receptionStore = useReceptionStore()
|
|
||||||
const form = reactive<ReceptionFormData>({
|
const form = reactive<ReceptionFormData>({
|
||||||
|
identificationNumber: null,
|
||||||
licensePlate: '',
|
licensePlate: '',
|
||||||
receptionDate: new Date().toISOString().slice(0, 10),
|
receptionDate: new Date().toISOString().slice(0, 10),
|
||||||
receptionTypeId: '',
|
receptionTypeId: '',
|
||||||
@@ -199,11 +243,17 @@ const form = reactive<ReceptionFormData>({
|
|||||||
driverId: '',
|
driverId: '',
|
||||||
vehicleId: ''
|
vehicleId: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const activeTab = ref<'weights' | 'weightsEmpty' | 'merchandise'>('weights')
|
||||||
|
const router = useRouter()
|
||||||
|
const receptionStore = useReceptionStore()
|
||||||
const allowAnyLicensePlate = ref(false)
|
const allowAnyLicensePlate = ref(false)
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const users = ref<UserData[]>([])
|
const users = ref<UserData[]>([])
|
||||||
const isLoadingUsers = ref(false)
|
const isLoadingUsers = ref(false)
|
||||||
|
const isLoadingTypes = ref(false)
|
||||||
const suppliers = ref<SupplierData[]>([])
|
const suppliers = ref<SupplierData[]>([])
|
||||||
|
const receptionTypes = ref<ReceptionTypeData[]>([])
|
||||||
const isLoadingSuppliers = ref(false)
|
const isLoadingSuppliers = ref(false)
|
||||||
const trucks = ref<TruckData[]>([])
|
const trucks = ref<TruckData[]>([])
|
||||||
const isLoadingTrucks = ref(false)
|
const isLoadingTrucks = ref(false)
|
||||||
@@ -214,23 +264,26 @@ const isLoadingDrivers = ref(false)
|
|||||||
const vehicles = ref<VehicleData[]>([])
|
const vehicles = ref<VehicleData[]>([])
|
||||||
const isLoadingVehicles = ref(false)
|
const isLoadingVehicles = ref(false)
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
const isValid = ref(false)
|
||||||
|
const isValidBovin = ref(false)
|
||||||
|
const isValidMerchandise = ref(false)
|
||||||
|
const formIsLoading = ref(false)
|
||||||
|
const route = useRoute()
|
||||||
|
const idReception = Number(route.params.id)
|
||||||
|
const auth = useAuthStore()
|
||||||
|
const isMerchandise = ref(false)
|
||||||
|
|
||||||
// Empêche les watchers de reset des champs pendant le remplissage initial
|
// Empêche les watchers de reset des champs pendant le remplissage initial
|
||||||
const isHydrating = ref(false)
|
const isHydrating = ref(false)
|
||||||
const route = useRoute()
|
|
||||||
const idReception = Number(route.params.id)
|
|
||||||
const receptionLoad = await getReception(idReception)
|
|
||||||
const receptionType = receptionLoad.receptionType
|
|
||||||
const auth = useAuthStore()
|
|
||||||
const isBtWeight = ref(true)
|
|
||||||
const isMerchandise = ref(receptionType.code === 'MARCHANDISES')
|
|
||||||
|
|
||||||
// Transporteur sélectionné dans le formulaire
|
// Transporteur sélectionné dans le formulaire
|
||||||
const selectedCarrier = computed(() =>
|
const selectedCarrier = computed(() =>
|
||||||
carriers.value.find((carrier) => String(carrier.id) === form.carrierId) ?? null
|
carriers.value.find((carrier) => String(carrier.id) === form.carrierId) ?? null
|
||||||
)
|
)
|
||||||
|
|
||||||
// Indique si le transporteur est LIOT
|
// Indique si le transporteur est LIOT
|
||||||
const isLiotCarrier = computed(() => selectedCarrier.value?.code === SUPPLIER_CODE.LIOT)
|
const isLiotCarrier = computed(() => selectedCarrier.value?.code === SUPPLIER_CODE.LIOT)
|
||||||
|
|
||||||
// Adresses disponibles pour le fournisseur sélectionné
|
// Adresses disponibles pour le fournisseur sélectionné
|
||||||
const supplierAddresses = computed(() => {
|
const supplierAddresses = computed(() => {
|
||||||
const supplierId = Number(form.supplierId)
|
const supplierId = Number(form.supplierId)
|
||||||
@@ -239,6 +292,7 @@ const supplierAddresses = computed(() => {
|
|||||||
}
|
}
|
||||||
return suppliers.value.find((supplier) => supplier.id === supplierId)?.addresses ?? []
|
return suppliers.value.find((supplier) => supplier.id === supplierId)?.addresses ?? []
|
||||||
})
|
})
|
||||||
|
|
||||||
// Chauffeurs filtrés par transporteur (LIOT)
|
// Chauffeurs filtrés par transporteur (LIOT)
|
||||||
const filteredDrivers = computed<DriverData[]>(() => {
|
const filteredDrivers = computed<DriverData[]>(() => {
|
||||||
if (!form.carrierId) {
|
if (!form.carrierId) {
|
||||||
@@ -246,6 +300,7 @@ const filteredDrivers = computed<DriverData[]>(() => {
|
|||||||
}
|
}
|
||||||
return drivers.value.filter((driver) => String(driver.carrier?.id) === form.carrierId)
|
return drivers.value.filter((driver) => String(driver.carrier?.id) === form.carrierId)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Véhicules filtrés par transporteur + type de camion
|
// Véhicules filtrés par transporteur + type de camion
|
||||||
const filteredVehicles = computed<VehicleData[]>(() => {
|
const filteredVehicles = computed<VehicleData[]>(() => {
|
||||||
if (!form.carrierId) {
|
if (!form.carrierId) {
|
||||||
@@ -259,18 +314,43 @@ const filteredVehicles = computed<VehicleData[]>(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Supprime les données bovines si on change de type de réception
|
// Supprime les données bovines si on change de type de réception
|
||||||
const clearReceptionBovines = async (receptionIri: string) => {
|
const clearReceptionBovines = async (receptionId: number) => {
|
||||||
|
const receptionIri = `/api/receptions/${receptionId}`
|
||||||
const existing = await getReceptionBovineList(receptionIri)
|
const existing = await getReceptionBovineList(receptionIri)
|
||||||
for (const selection of existing) {
|
for (const selection of existing) {
|
||||||
await deleteReceptionBovine(selection.id)
|
await deleteReceptionBovine(selection.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hydrateFromUser = (reception: ReceptionData | null) => {
|
const syncMerchandiseFlag = () => {
|
||||||
|
const receptionType =
|
||||||
|
receptionTypes.value.find((type) => String(type.id) === form.receptionTypeId) ?? null
|
||||||
|
isMerchandise.value = receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearReceptionMerchandise = async (receptionId: number) => {
|
||||||
|
const receptionIri = `/api/receptions/${receptionId}`
|
||||||
|
|
||||||
|
// supprime toutes les associations granulés/bâtiments
|
||||||
|
const existing = await getReceptionPelletBuildingList(receptionIri)
|
||||||
|
for (const selection of existing) {
|
||||||
|
await deleteReceptionPelletBuilding(selection.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset des champs marchandise
|
||||||
|
await updateReception(receptionId, {
|
||||||
|
merchandiseType: null,
|
||||||
|
merchandiseDetail: null,
|
||||||
|
buildings: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const hydrateFromReception = (reception: ReceptionData | null) => {
|
||||||
if (!reception) {
|
if (!reception) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isHydrating.value = true
|
isHydrating.value = true
|
||||||
|
form.identificationNumber = reception?.identificationNumber ?? ''
|
||||||
form.licensePlate = reception?.licensePlate ?? ''
|
form.licensePlate = reception?.licensePlate ?? ''
|
||||||
form.receptionDate = reception?.receptionDate ?? new Date().toISOString().slice(0, 10)
|
form.receptionDate = reception?.receptionDate ?? new Date().toISOString().slice(0, 10)
|
||||||
form.userId = reception?.user?.id
|
form.userId = reception?.user?.id
|
||||||
@@ -291,7 +371,11 @@ const hydrateFromUser = (reception: ReceptionData | null) => {
|
|||||||
form.driverId = reception?.driver?.id
|
form.driverId = reception?.driver?.id
|
||||||
? String(reception.driver.id)
|
? String(reception.driver.id)
|
||||||
: ''
|
: ''
|
||||||
|
form.receptionTypeId = reception?.receptionType?.id
|
||||||
|
? String(reception.receptionType.id)
|
||||||
|
: ''
|
||||||
isHydrating.value = false
|
isHydrating.value = false
|
||||||
|
syncMerchandiseFlag()
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -302,8 +386,8 @@ watch(
|
|||||||
}
|
}
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
try {
|
try {
|
||||||
const user = await getReception(id)
|
const reception = await getReception(id)
|
||||||
hydrateFromUser(user)
|
hydrateFromReception(reception)
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -311,6 +395,14 @@ watch(
|
|||||||
{immediate: true}
|
{immediate: true}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => form.receptionTypeId,
|
||||||
|
() => {
|
||||||
|
const receptionType = receptionTypes.value.find((type) => String(type.id) === form.receptionTypeId) ?? null
|
||||||
|
isMerchandise.value = receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Charge la liste des users pour le select
|
// Charge la liste des users pour le select
|
||||||
const loadUsers = async () => {
|
const loadUsers = async () => {
|
||||||
isLoadingUsers.value = true
|
isLoadingUsers.value = true
|
||||||
@@ -331,6 +423,16 @@ const loadSuppliers = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//charge la liste des types pour le select
|
||||||
|
const loadTypes = async () => {
|
||||||
|
isLoadingTypes.value = true
|
||||||
|
try {
|
||||||
|
receptionTypes.value = await getReceptionTypeList()
|
||||||
|
} finally {
|
||||||
|
isLoadingSuppliers.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Charge la liste des camions pour le select
|
// Charge la liste des camions pour le select
|
||||||
const loadTrucks = async () => {
|
const loadTrucks = async () => {
|
||||||
isLoadingTrucks.value = true
|
isLoadingTrucks.value = true
|
||||||
@@ -371,7 +473,7 @@ const loadVehicles = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// On met le user connecté par défaut dans le select
|
// On met l'user connecté par défaut dans le select
|
||||||
const setDefaultUser = () => {
|
const setDefaultUser = () => {
|
||||||
if (form.userId) {
|
if (form.userId) {
|
||||||
return
|
return
|
||||||
@@ -383,6 +485,9 @@ const setDefaultUser = () => {
|
|||||||
|
|
||||||
// On récupère toutes les données des selects au chargement du composant
|
// On récupère toutes les données des selects au chargement du composant
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
await loadTypes()
|
||||||
|
syncMerchandiseFlag()
|
||||||
|
formIsLoading.value =true
|
||||||
await loadUsers()
|
await loadUsers()
|
||||||
await loadSuppliers()
|
await loadSuppliers()
|
||||||
await loadTrucks()
|
await loadTrucks()
|
||||||
@@ -507,11 +612,10 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Valide le formulaire et crée/met à jour la réception
|
|
||||||
async function validate() {
|
async function validate() {
|
||||||
const normalizedLicensePlate = form.licensePlate.trim()
|
const normalizedLicensePlate = form.licensePlate.trim()
|
||||||
const normalizedReceptionDate = form.receptionDate.trim()
|
const normalizedReceptionDate = form.receptionDate.trim()
|
||||||
|
const normalizedReceptionTypeId = form.receptionTypeId.trim()
|
||||||
const normalizedUserId = form.userId.trim()
|
const normalizedUserId = form.userId.trim()
|
||||||
const normalizedSupplierId = form.supplierId.trim()
|
const normalizedSupplierId = form.supplierId.trim()
|
||||||
const normalizedAddressId = form.addressId.trim()
|
const normalizedAddressId = form.addressId.trim()
|
||||||
@@ -536,10 +640,14 @@ async function validate() {
|
|||||||
const driverIri = normalizedDriverId
|
const driverIri = normalizedDriverId
|
||||||
? `/api/drivers/${normalizedDriverId}`
|
? `/api/drivers/${normalizedDriverId}`
|
||||||
: null
|
: null
|
||||||
|
const typeIri = normalizedReceptionTypeId
|
||||||
|
? `/api/reception_types/${normalizedReceptionTypeId}`
|
||||||
|
: null
|
||||||
|
|
||||||
const basePayload = {
|
const basePayload = {
|
||||||
licensePlate: normalizedLicensePlate,
|
licensePlate: normalizedLicensePlate,
|
||||||
receptionDate: normalizedReceptionDate,
|
receptionDate: normalizedReceptionDate,
|
||||||
|
receptionType: typeIri,
|
||||||
user: userIri,
|
user: userIri,
|
||||||
supplier: supplierIri,
|
supplier: supplierIri,
|
||||||
address: addressIri,
|
address: addressIri,
|
||||||
@@ -549,17 +657,27 @@ async function validate() {
|
|||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
...basePayload,
|
...basePayload,
|
||||||
...(isLiotCarrier.value && driverIri ? {driver: driverIri} : {})
|
...(isLiotCarrier.value && driverIri ? {driver: driverIri} : {}),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idReception) {
|
if (idReception) {
|
||||||
const updated = await receptionStore.updateReception(idReception, {
|
isValid.value = true
|
||||||
|
isMerchandise.value ? (isValidMerchandise.value = true) : (isValidBovin.value = true)
|
||||||
|
await receptionStore.updateReception(idReception, {
|
||||||
...payload
|
...payload
|
||||||
})
|
})
|
||||||
if (updated) {
|
|
||||||
await router.push(`/reception/update/${updated.id}`)
|
if (isMerchandise.value) {
|
||||||
|
await clearReceptionBovines(idReception)
|
||||||
|
} else {
|
||||||
|
await clearReceptionMerchandise(idReception)
|
||||||
}
|
}
|
||||||
router.push("/reception/finish-reception")
|
|
||||||
|
isValid.value = false
|
||||||
|
isValidBovin.value = false
|
||||||
|
isValidMerchandise.value = false
|
||||||
|
|
||||||
|
await router.push("/reception/finish-reception")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,9 @@
|
|||||||
<div>{{ shipment.customer?.name }}</div>
|
<div>{{ shipment.customer?.name }}</div>
|
||||||
<div>{{ shipment.address?.fullAddress }}</div>
|
<div>{{ shipment.address?.fullAddress }}</div>
|
||||||
<div>
|
<div>
|
||||||
<template v-if="formatBovinShipmentLines(shipment).length">
|
<template v-if="formatShipmentLines(shipment).length">
|
||||||
<div
|
<div
|
||||||
v-for="(line, index) in formatBovinShipmentLines(shipment)"
|
v-for="(line, index) in formatShipmentLines(shipment)"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="leading-5"
|
class="leading-5"
|
||||||
>
|
>
|
||||||
@@ -62,16 +62,17 @@ const formatWeighing = (shipment: ShipmentData) => {
|
|||||||
return `${gross - tare} kg`
|
return `${gross - tare} kg`
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatBovinShipmentLines = (shipment: ShipmentData) => {
|
|
||||||
if (!shipment.bovinShipments?.length) {
|
const formatShipmentLines = (shipment: ShipmentData) => {
|
||||||
|
if (!shipment.shipmentType && shipment.nbBovinSend == null) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
return shipment.bovinShipments.map((entry) => {
|
|
||||||
const label = typeof entry.shipmentType === 'string'
|
const label = typeof shipment.shipmentType === 'string'
|
||||||
? entry.shipmentType
|
? shipment.shipmentType
|
||||||
: entry.shipmentType?.label
|
: shipment.shipmentType?.label
|
||||||
return `${label ?? '—'} : ${entry.nbBovinSend ?? '—'}`
|
|
||||||
})
|
return [`${label ?? '—'} : ${shipment.nbBovinSend ?? '—'}`]
|
||||||
}
|
}
|
||||||
|
|
||||||
const goShipment = (id: number) => {
|
const goShipment = (id: number) => {
|
||||||
|
|||||||
@@ -24,12 +24,12 @@
|
|||||||
@click="goToShipment(shipment.id)"
|
@click="goToShipment(shipment.id)"
|
||||||
@keydown.enter="goToShipment(shipment.id)"
|
@keydown.enter="goToShipment(shipment.id)"
|
||||||
>
|
>
|
||||||
<div>{{ shipment.customer?.label }}</div>
|
<div>{{ shipment.customer?.name }}</div>
|
||||||
<div>{{ shipment.address?.fullAddress }}</div>
|
<div>{{ shipment.address?.fullAddress }}</div>
|
||||||
<div>
|
<div>
|
||||||
<template v-if="formatBovinShipmentLines(shipment).length">
|
<template v-if="formatShipmentLines(shipment).length">
|
||||||
<div
|
<div
|
||||||
v-for="(line, index) in formatBovinShipmentLines(shipment)"
|
v-for="(line, index) in formatShipmentLines(shipment)"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="leading-5"
|
class="leading-5"
|
||||||
>
|
>
|
||||||
@@ -55,16 +55,17 @@ const router = useRouter()
|
|||||||
const goToShipment = (id: number) => {
|
const goToShipment = (id: number) => {
|
||||||
router.push(`/shipment/${id}`)
|
router.push(`/shipment/${id}`)
|
||||||
}
|
}
|
||||||
const formatBovinShipmentLines = (shipment: ShipmentData) => {
|
|
||||||
if (!shipment.bovinShipments?.length) {
|
const formatShipmentLines = (shipment: ShipmentData) => {
|
||||||
|
if (!shipment.shipmentType && shipment.nbBovinSend == null) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
return shipment.bovinShipments.map((entry) => {
|
|
||||||
const label = typeof entry.shipmentType === 'string'
|
const label = typeof shipment.shipmentType === 'string'
|
||||||
? entry.shipmentType
|
? shipment.shipmentType
|
||||||
: entry.shipmentType?.label
|
: shipment.shipmentType?.label
|
||||||
return `${label ?? '—'} : ${entry.nbBovinSend ?? '—'}`
|
|
||||||
})
|
return [`${label ?? '—'} : ${shipment.nbBovinSend ?? '—'}`]
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
import { useApi } from '~/composables/useApi'
|
|
||||||
import type { BovinShipmentData } from '~/services/dto/bovin-shipment-data'
|
|
||||||
import type { ShipmentBovinePayload, BovinShipmentListResponse } from '~/services/dto/bovin-shipment-data'
|
|
||||||
|
|
||||||
export async function getBovinShipmentList(
|
|
||||||
shipmentIri: string
|
|
||||||
): Promise<BovinShipmentData[]> {
|
|
||||||
const api = useApi()
|
|
||||||
const response = await api.get<BovinShipmentListResponse>(
|
|
||||||
'bovin_shipments',
|
|
||||||
{ shipment: shipmentIri },
|
|
||||||
{
|
|
||||||
toastErrorKey: 'errors.shipmentBovine.list'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (Array.isArray(response)) {
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
if (response && typeof response === 'object' && Array.isArray(response['hydra:member'])) {
|
|
||||||
return response['hydra:member']
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createShipmentBovine(
|
|
||||||
payload: ShipmentBovinePayload
|
|
||||||
): Promise<BovinShipmentData> {
|
|
||||||
const api = useApi()
|
|
||||||
return api.post<BovinShipmentData>('bovin_shipments', payload, {
|
|
||||||
toastErrorKey: 'errors.shipmentBovine.create'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deleteShipmentBovine(id: number): Promise<void> {
|
|
||||||
const api = useApi()
|
|
||||||
await api.delete<void>(`bovin_shipments/${id}`, {}, {
|
|
||||||
toastErrorKey: 'errors.shipmentBovine.delete'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function updateShipmentBovine(
|
|
||||||
id: number,
|
|
||||||
payload: Partial<ShipmentBovinePayload>
|
|
||||||
): Promise<BovinShipmentData> {
|
|
||||||
const api = useApi()
|
|
||||||
return api.patch<BovinShipmentData>(`bovin_shipments/${id}`, payload, {
|
|
||||||
toastErrorKey: 'errors.shipmentBovine.update'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import type {ShipmentTypeData} from "~/services/dto/shipment-type-data";
|
|
||||||
|
|
||||||
export interface BovinShipmentData {
|
|
||||||
id: number
|
|
||||||
nbBovinSend: number | null
|
|
||||||
shipment?: string | null
|
|
||||||
shipmentType?: ShipmentTypeData | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ShipmentBovinePayload = {
|
|
||||||
nbBovinSend: number
|
|
||||||
shipment: string
|
|
||||||
shipmentType: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type BovinShipmentListResponse =
|
|
||||||
| BovinShipmentData[]
|
|
||||||
| { 'hydra:member'?: BovinShipmentData[] }
|
|
||||||
@@ -69,6 +69,7 @@ export type ReceptionPayload = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ReceptionFormData = {
|
export type ReceptionFormData = {
|
||||||
|
identificationNumber?: null|string,
|
||||||
licensePlate: string
|
licensePlate: string
|
||||||
receptionDate: string
|
receptionDate: string
|
||||||
receptionTypeId: string
|
receptionTypeId: string
|
||||||
|
|||||||
@@ -9,12 +9,6 @@ export interface ShipmentTypeData {
|
|||||||
code: string
|
code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BovinShipmentData {
|
|
||||||
id?: number
|
|
||||||
shipmentType?: ShipmentTypeData | string | null
|
|
||||||
nbBovinSend: number | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ShipmentData = {
|
export type ShipmentData = {
|
||||||
id: number
|
id: number
|
||||||
identificationNumber?: string | null
|
identificationNumber?: string | null
|
||||||
@@ -26,7 +20,8 @@ export type ShipmentData = {
|
|||||||
carrier?: CarrierData | null
|
carrier?: CarrierData | null
|
||||||
truck?: TruckData | null
|
truck?: TruckData | null
|
||||||
customer?: CustomerData | null
|
customer?: CustomerData | null
|
||||||
bovinShipments?: BovinShipmentData[] | null
|
shipmentType?: ShipmentTypeData | null
|
||||||
|
nbBovinSend?: number | null
|
||||||
weights?: WeightShipmentEntryData[] | null
|
weights?: WeightShipmentEntryData[] | null
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -59,9 +54,9 @@ export type ShipmentPayload = {
|
|||||||
carrier?: string | null
|
carrier?: string | null
|
||||||
truck?: string | null
|
truck?: string | null
|
||||||
customer?: string | null
|
customer?: string | null
|
||||||
bovinShipments?: string[] | null
|
|
||||||
address?: string | null
|
address?: string | null
|
||||||
user?: string | null
|
user?: string | null
|
||||||
driver?: string | null
|
driver?: string | null
|
||||||
|
shipmentType?: string | null
|
||||||
|
nbBovinSend?: number | null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ export default <Partial<Config>>{
|
|||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
primary: {
|
primary: {
|
||||||
|
700: '#35453C',
|
||||||
500: '#456452',
|
500: '#456452',
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
52
migrations/Version20260218144828.php
Normal file
52
migrations/Version20260218144828.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20260218144828 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE bovin_shipment DROP CONSTRAINT fk_7049f4502ee48a36');
|
||||||
|
$this->addSql('ALTER TABLE bovin_shipment DROP CONSTRAINT fk_7049f4507be036fc');
|
||||||
|
$this->addSql('DROP TABLE bovin_shipment');
|
||||||
|
$this->addSql('ALTER TABLE shipment ADD nb_bovin_send INT NOT NULL');
|
||||||
|
$this->addSql('ALTER TABLE shipment ADD shipment_type_id INT DEFAULT NULL');
|
||||||
|
$this->addSql('ALTER TABLE shipment ADD CONSTRAINT FK_2CB20DC2EE48A36 FOREIGN KEY (shipment_type_id) REFERENCES shipment_type (id) NOT DEFERRABLE');
|
||||||
|
$this->addSql('CREATE INDEX IDX_2CB20DC2EE48A36 ON shipment (shipment_type_id)');
|
||||||
|
$this->addSql('DROP INDEX uniq_weight_shipment_type');
|
||||||
|
$this->addSql('DROP INDEX uniq_weight_reception_type');
|
||||||
|
$this->addSql('ALTER INDEX idx_weight_shipment RENAME TO IDX_7CD55417BE036FC');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE TABLE bovin_shipment (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, nb_bovin_send INT NOT NULL, shipment_id INT DEFAULT NULL, shipment_type_id INT DEFAULT NULL, PRIMARY KEY (id))');
|
||||||
|
$this->addSql('CREATE INDEX idx_7049f4507be036fc ON bovin_shipment (shipment_id)');
|
||||||
|
$this->addSql('CREATE INDEX idx_7049f4502ee48a36 ON bovin_shipment (shipment_type_id)');
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX uniq_bovin_shipment_one_type ON bovin_shipment (shipment_id)');
|
||||||
|
$this->addSql('ALTER TABLE bovin_shipment ADD CONSTRAINT fk_7049f4502ee48a36 FOREIGN KEY (shipment_type_id) REFERENCES shipment_type (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
$this->addSql('ALTER TABLE bovin_shipment ADD CONSTRAINT fk_7049f4507be036fc FOREIGN KEY (shipment_id) REFERENCES shipment (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
$this->addSql('ALTER TABLE shipment DROP CONSTRAINT FK_2CB20DC2EE48A36');
|
||||||
|
$this->addSql('DROP INDEX IDX_2CB20DC2EE48A36');
|
||||||
|
$this->addSql('ALTER TABLE shipment DROP nb_bovin_send');
|
||||||
|
$this->addSql('ALTER TABLE shipment DROP shipment_type_id');
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX uniq_weight_shipment_type ON weight (shipment_id, type)');
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX uniq_weight_reception_type ON weight (reception_id, type)');
|
||||||
|
$this->addSql('ALTER INDEX idx_7cd55417be036fc RENAME TO idx_weight_shipment');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Entity;
|
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
|
||||||
use ApiPlatform\Metadata\Delete;
|
|
||||||
use ApiPlatform\Metadata\Get;
|
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
|
||||||
use ApiPlatform\Metadata\Patch;
|
|
||||||
use ApiPlatform\Metadata\Post;
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
|
||||||
use Symfony\Component\Serializer\Attribute\Groups;
|
|
||||||
|
|
||||||
#[ORM\Entity]
|
|
||||||
#[ApiFilter(SearchFilter::class, properties: ['shipment' => 'exact'])]
|
|
||||||
#[ORM\UniqueConstraint(name: 'uniq_bovin_shipment_one_type', columns: ['shipment_id'])]
|
|
||||||
#[ORM\Table(name: 'bovin_shipment')]
|
|
||||||
#[ApiResource(
|
|
||||||
operations: [
|
|
||||||
new Get(
|
|
||||||
requirements: ['id' => '\d+'],
|
|
||||||
normalizationContext: ['groups' => ['shipment-bovine:read']],
|
|
||||||
),
|
|
||||||
new GetCollection(
|
|
||||||
normalizationContext: ['groups' => ['shipment-bovine:read']],
|
|
||||||
),
|
|
||||||
|
|
||||||
new Post(
|
|
||||||
normalizationContext: ['groups' => ['shipment-bovine:read']],
|
|
||||||
denormalizationContext: ['groups' => ['shipment-bovine:write']],
|
|
||||||
),
|
|
||||||
new Patch(
|
|
||||||
normalizationContext: ['groups' => ['shipment-bovine:read']],
|
|
||||||
denormalizationContext: ['groups' => ['shipment-bovine:write']],
|
|
||||||
),
|
|
||||||
new Delete(),
|
|
||||||
],
|
|
||||||
security: "is_granted('ROLE_USER')",
|
|
||||||
)]
|
|
||||||
class BovinShipment
|
|
||||||
{
|
|
||||||
#[ORM\Id]
|
|
||||||
#[ORM\GeneratedValue]
|
|
||||||
#[ORM\Column]
|
|
||||||
#[Groups(['shipment:read', 'shipment-bovine:read'])]
|
|
||||||
private ?int $id = null;
|
|
||||||
|
|
||||||
#[ORM\ManyToOne(inversedBy: 'bovinShipments')]
|
|
||||||
#[Groups(['shipment-bovine:read', 'shipment-bovine:write'])]
|
|
||||||
#[ApiProperty(readableLink: true)]
|
|
||||||
private ?Shipment $shipment = null;
|
|
||||||
|
|
||||||
#[ORM\ManyToOne]
|
|
||||||
#[Groups(['shipment:read', 'shipment-bovine:write', 'shipment-bovine:read'])]
|
|
||||||
#[ApiProperty(readableLink: true)]
|
|
||||||
private ?ShipmentType $shipmentType = null;
|
|
||||||
|
|
||||||
#[ORM\Column]
|
|
||||||
#[Groups(['shipment:read', 'shipment-bovine:write', 'shipment-bovine:read'])]
|
|
||||||
private ?int $nbBovinSend = null;
|
|
||||||
|
|
||||||
public function getId(): ?int
|
|
||||||
{
|
|
||||||
return $this->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getShipment(): ?Shipment
|
|
||||||
{
|
|
||||||
return $this->shipment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setShipment(?Shipment $shipment): void
|
|
||||||
{
|
|
||||||
$this->shipment = $shipment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getShipmentType(): ?ShipmentType
|
|
||||||
{
|
|
||||||
return $this->shipmentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setShipmentType(?ShipmentType $shipmentType): void
|
|
||||||
{
|
|
||||||
$this->shipmentType = $shipmentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNbBovinSend(): ?int
|
|
||||||
{
|
|
||||||
return $this->nbBovinSend;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setNbBovinSend(?int $nbBovinSend): void
|
|
||||||
{
|
|
||||||
$this->nbBovinSend = $nbBovinSend;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -123,17 +123,15 @@ class Shipment
|
|||||||
#[ApiProperty(readableLink: true)]
|
#[ApiProperty(readableLink: true)]
|
||||||
private ?Customer $customer = null;
|
private ?Customer $customer = null;
|
||||||
|
|
||||||
/**
|
#[ORM\ManyToOne(inversedBy: 'shipments')]
|
||||||
* @var Collection<int, BovinShipment>
|
#[ORM\JoinColumn(nullable: true)]
|
||||||
*/
|
|
||||||
#[ORM\OneToMany(
|
|
||||||
targetEntity: BovinShipment::class,
|
|
||||||
mappedBy: 'shipment',
|
|
||||||
cascade: ['persist', 'remove'],
|
|
||||||
orphanRemoval: true
|
|
||||||
)]
|
|
||||||
#[Groups(['shipment:read', 'shipment:write'])]
|
#[Groups(['shipment:read', 'shipment:write'])]
|
||||||
private Collection $bovinShipments;
|
#[ApiProperty(readableLink: true)]
|
||||||
|
private ?ShipmentType $shipmentType = null;
|
||||||
|
|
||||||
|
#[ORM\Column]
|
||||||
|
#[Groups(['shipment:read', 'shipment:write'])]
|
||||||
|
private int $nbBovinSend = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<int, Weight>
|
* @var Collection<int, Weight>
|
||||||
@@ -156,8 +154,7 @@ class Shipment
|
|||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->bovinShipments = new ArrayCollection();
|
$this->weights = new ArrayCollection();
|
||||||
$this->weights = new ArrayCollection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
@@ -195,6 +192,26 @@ class Shipment
|
|||||||
$this->currentStep = $currentStep;
|
$this->currentStep = $currentStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getShipmentType(): ?ShipmentType
|
||||||
|
{
|
||||||
|
return $this->shipmentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setShipmentType(?ShipmentType $shipmentType): void
|
||||||
|
{
|
||||||
|
$this->shipmentType = $shipmentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNbBovinSend(): int
|
||||||
|
{
|
||||||
|
return $this->nbBovinSend;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setNbBovinSend(int $nbBovinSend): void
|
||||||
|
{
|
||||||
|
$this->nbBovinSend = $nbBovinSend;
|
||||||
|
}
|
||||||
|
|
||||||
public function getIsValid(): ?bool
|
public function getIsValid(): ?bool
|
||||||
{
|
{
|
||||||
return $this->isValid;
|
return $this->isValid;
|
||||||
@@ -261,37 +278,6 @@ class Shipment
|
|||||||
$this->customer = $customer;
|
$this->customer = $customer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBovinShipments(): Collection
|
|
||||||
{
|
|
||||||
return $this->bovinShipments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setBovinShipments(Collection $bovinShipments): void
|
|
||||||
{
|
|
||||||
$this->bovinShipments = $bovinShipments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addBovinShipment(BovinShipment $bovinShipment): self
|
|
||||||
{
|
|
||||||
if (!$this->bovinShipments->contains($bovinShipment)) {
|
|
||||||
$this->bovinShipments->add($bovinShipment);
|
|
||||||
$bovinShipment->setShipment($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeBovinShipment(BovinShipment $bovinShipment): self
|
|
||||||
{
|
|
||||||
if ($this->bovinShipments->removeElement($bovinShipment)) {
|
|
||||||
if ($bovinShipment->getShipment() === $this) {
|
|
||||||
$bovinShipment->setShipment(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection<int, Weight>
|
* @return Collection<int, Weight>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ namespace App\Entity;
|
|||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Serializer\Attribute\Groups;
|
use Symfony\Component\Serializer\Attribute\Groups;
|
||||||
|
|
||||||
@@ -40,6 +42,14 @@ class ShipmentType
|
|||||||
#[Groups(['shipment-type:read', 'shipment:read'])]
|
#[Groups(['shipment-type:read', 'shipment:read'])]
|
||||||
private ?string $code = null;
|
private ?string $code = null;
|
||||||
|
|
||||||
|
#[ORM\OneToMany(mappedBy: 'shipmentType', targetEntity: Shipment::class)]
|
||||||
|
private Collection $shipments;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->shipments = new ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
|
|||||||
@@ -250,13 +250,11 @@
|
|||||||
<td>
|
<td>
|
||||||
<strong>Bovin</strong><br><br>
|
<strong>Bovin</strong><br><br>
|
||||||
<div class="bigtable-notes">
|
<div class="bigtable-notes">
|
||||||
{% if shipment.bovinShipments is not empty %}
|
{% if shipment.shipmentType %}
|
||||||
{% for entry in shipment.bovinShipments %}
|
<p>
|
||||||
<p>
|
{{ shipment.shipmentType.label ?? '-' }} :
|
||||||
{{ entry.shipmentType ? entry.shipmentType.label : '-' }} :
|
{{ shipment.nbBovinSend ?? 0 }}
|
||||||
{{ entry.nbBovinSend ?? 0 }}
|
</p>
|
||||||
</p>
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>-</p>
|
<p>-</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
Reference in New Issue
Block a user