[#256] Créer une nouvelle réception (étape 3 - bovin) #11

Merged
tristan merged 6 commits from feat/256-reception-etape-3-bovin into develop 2026-02-05 09:29:29 +00:00
8 changed files with 219 additions and 100 deletions
Showing only changes of commit 015a8c49fb - Show all commits

113
.idea/workspace.xml generated
View File

@@ -4,18 +4,16 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="feat : mise à jour du bon de réception">
<change afterPath="$PROJECT_DIR$/frontend/components/reception/reception-bovine-received.vue" afterDir="false" />
<change afterPath="$PROJECT_DIR$/frontend/components/ui/UiNumberInput.vue" afterDir="false" />
<change afterPath="$PROJECT_DIR$/frontend/services/bovine-type.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/frontend/services/dto/bovine-type-data.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/dataSources.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.xml" afterDir="false" />
<list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)">
<change afterPath="$PROJECT_DIR$/frontend/services/dto/reception-bovine-data.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/frontend/services/reception-bovine.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/config/reference.php" beforeDir="false" afterPath="$PROJECT_DIR$/config/reference.php" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-bovine-received.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-bovine-received.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/components/ui/UiTextInput.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/ui/UiTextInput.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/i18n/locales/fr.json" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/i18n/locales/fr.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/utils/constants.ts" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/utils/constants.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/components/ui/UiNumberInput.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/ui/UiNumberInput.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/pages/reception/[[id]].vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/pages/reception/[[id]].vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/services/dto/reception-data.ts" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/services/dto/reception-data.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/Entity/Reception.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/Entity/Reception.php" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
@@ -27,6 +25,11 @@
<pharConfigPath>$PROJECT_DIR$/composer.json</pharConfigPath>
<execution />
</component>
<component name="CopilotPersistence">
<persistenceIdMap>
<entry key="_//wsl.localhost/Ubuntu-24.04/home/kevin/Stage/Ferme" value="381AhnCm9yPeOiWgMObKHhtgv2C" />
</persistenceIdMap>
</component>
<component name="EmbeddingIndexingInfo">
<option name="cachedIndexableFilesCount" value="151" />
<option name="fileBasedEmbeddingIndicesEnabled" value="true" />
@@ -60,7 +63,7 @@
</server>
</servers>
</component>
<component name="PhpWorkspaceProjectConfiguration" interpreter_name="/bin/php">
<component name="PhpWorkspaceProjectConfiguration" interpreter_name="C:/php-8.4.3/php.exe">
<include_path>
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/psr/event-dispatcher" />
@@ -223,36 +226,36 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.MCP Project settings loaded": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
"git-widget-placeholder": "feat/256-reception-etape-3-bovin",
"last_opened_file_path": "/home/sroy/Documents/test/Ferme",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "configurable.group.appearance",
"ts.external.directory.path": "/opt/phpstorm/plugins/javascript-plugin/jsLanguageServicesImpl/external",
"vue.rearranger.settings.migration": "true"
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;RunOnceActivity.MCP Project settings loaded&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;feat/256-reception-etape-3-bovin&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/sroy/Documents/test/Ferme&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;,
&quot;ts.external.directory.path&quot;: &quot;/opt/phpstorm/plugins/javascript-plugin/jsLanguageServicesImpl/external&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
},
"keyToStringList": {
"DatabaseDriversLRU": [
"postgresql"
&quot;keyToStringList&quot;: {
&quot;DatabaseDriversLRU&quot;: [
&quot;postgresql&quot;
],
"com.intellij.ide.scratch.ScratchImplUtil$2/New Scratch File": [
"TEXT"
&quot;com.intellij.ide.scratch.ScratchImplUtil$2/New Scratch File&quot;: [
&quot;TEXT&quot;
],
"vue.recent.templates": [
"Vue Composition API Component"
&quot;vue.recent.templates&quot;: [
&quot;Vue Composition API Component&quot;
]
}
}]]></component>
}</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="\\wsl.localhost\Ubuntu-24.04\home\m-tristan\workspace\Ferme" />
@@ -265,7 +268,6 @@
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-PS-253.30387.85" />
<option value="bundled-php-predefined-a98d8de5180a-0e0d91225499-com.jetbrains.php.sharedIndexes-PS-253.30387.85" />
</set>
</attachedChunks>
@@ -296,6 +298,9 @@
<workItem from="1770055690365" duration="370000" />
<workItem from="1770056515646" duration="21000" />
<workItem from="1770102495553" duration="2280000" />
<workItem from="1770195604082" duration="90000" />
<workItem from="1770195718952" duration="215000" />
<workItem from="1770195959162" duration="8094000" />
</task>
<task id="LOCAL-00001" summary="feat : Ajout de pinia, création de la table weight et reception mise en place du système de step pour les receptions (WIP)">
<option name="closed" value="true" />
@@ -665,7 +670,15 @@
<option name="project" value="LOCAL" />
<updated>1769782099473</updated>
</task>
<option name="localTasksCounter" value="47" />
<task id="LOCAL-00047" summary="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)">
<option name="closed" value="true" />
<created>1770131226364</created>
<option name="number" value="00047" />
<option name="presentableId" value="LOCAL-00047" />
<option name="project" value="LOCAL" />
<updated>1770131226364</updated>
</task>
<option name="localTasksCounter" value="48" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -715,7 +728,6 @@
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="ci : ajout du script et de la doc déploiement" />
<MESSAGE value="fix : correction du path URI pour la création d'un poids dans une réception" />
<MESSAGE value="feat : Ajout du bundle Monolog pour la gestion des logs" />
<MESSAGE value="fix : affiche plus détail dans les logs en recette/prod" />
@@ -740,7 +752,8 @@
<MESSAGE value="feat : ajout de colonne pour les Supplier, Address et modification du numéro de réception" />
<MESSAGE value="feat : ajout de colonne pour les Supplier, Address. Modification du numéro de réception et ajout de fixtures" />
<MESSAGE value="feat : mise à jour du bon de réception" />
<option name="LAST_COMMIT_MESSAGE" value="feat : mise à jour du bon de réception" />
<MESSAGE value="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)" />
<option name="LAST_COMMIT_MESSAGE" value="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
@@ -750,10 +763,16 @@
<line>6</line>
<option name="timeStamp" value="3" />
</line-breakpoint>
<line-breakpoint enabled="true" type="php">
<url>file://$PROJECT_DIR$/src/Entity/Reception.php</url>
<line>6</line>
<option name="timeStamp" value="5" />
<line-breakpoint enabled="true" type="javascript">
<url>file://$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue</url>
<properties lambdaOrdinal="-1" />
<option name="timeStamp" value="7" />
</line-breakpoint>
<line-breakpoint enabled="true" type="javascript">
<url>file://$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue</url>
<line>103</line>
<properties lambdaOrdinal="-1" />
<option name="timeStamp" value="9" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
@@ -762,4 +781,12 @@
<expand />
<select />
</component>
<component name="github-copilot-workspace">
<instructionFileLocations>
<option value=".github/instructions" />
</instructionFileLocations>
<promptFileLocations>
<option value=".github/prompts" />
</promptFileLocations>
</component>
</project>

View File

@@ -1,5 +1,10 @@
<template>
<div class="flex flex-row gap-16 items-center w-full">
<div
v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.BOVINS"
class="flex flex-col items-center gap-16">
<h1 class="text-4xl uppercase font-bold">Sélection des marchandises réceptionnnées</h1>
<div
class="flex flex-row gap-16 items-center w-full">
<div
v-for="type in bovineType"
:key="type.id"
@@ -7,6 +12,8 @@
<UiNumberInput
:label="type.label"
:code="type.code"
v-model="selectedBovineTypeIds[type.id]"
:value="String(type.id)"
/>
</div>
<div
@@ -16,17 +23,29 @@
/>
</div>
</div>
<button
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
@click="goNext"
>Peser
</button>
</div>
</template>
<script setup lang="ts">
import {getTruckList} from "~/services/truck";
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
import {getBovineTypeList} from "~/services/bovine-type";
import {RECEPTION_TYPE_CODES} from "~/utils/constants";
import {useReceptionStore} from '~/stores/reception'
import {
createReceptionBovine,
deleteReceptionBovine,
getReceptionBovineList
} from "~/services/reception-bovine";
import {ref} from "vue";
const isLoadingBovineType = ref(false)
const bovineType = ref<BovineTypeData[]>([])
const receptionStore = useReceptionStore()
const selectedBovineTypeIds = ref<string[]>
const loadBovineType = async () => {
isLoadingBovineType.value = true
try {
@@ -35,7 +54,29 @@ const loadBovineType = async () => {
isLoadingBovineType.value = false
}
}
onMounted(async () => {
await loadBovineType()
selectedBovineTypeIds.value=bovineType.value.map((type) => String(type.id))
})
async function goNext() {
if (!receptionStore.current) {
return
}
const nextStep = receptionStore.current.currentStep + 1
const receptionIri = `/api/receptions/${receptionStore.current.id}`
console.log(selectedBovineTypeIds.value)
await receptionStore.updateReception(receptionStore.current.id, {
merchandiseType: null,
merchandiseDetail: null,
buildings: null,
bovinesTypes : selectedBovineTypeIds.value.map((id) => `/api/bovines_types/${id}`),
currentStep: nextStep
})
}
</script>

View File

@@ -5,7 +5,6 @@
v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES"
class="flex flex-col gap-16 items-center w-full">
<h1 class="text-4xl uppercase font-bold">Sélection des marchandises réceptionnnées</h1>
<!-- ICI UNE ERREUR DE RESPECT MAQUETTE -->
<UiSelect
id="merchandise-type"
v-model="selectedMerchandiseTypeId"
@@ -13,7 +12,6 @@
:options="merchandiseTypes.map((type) => ({ value: String(type.id), label: type.label }))"
wrapper-class="w-[550px]"
/>
<div
v-if="selectedMerchandiseTypeId && isAutres"
class="flex flex-col w-full max-w-[550px]"
@@ -67,12 +65,6 @@
</div>
</div>
</div>
<div v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.BOVINS"
>
<h1 class="text-4xl uppercase font-bold">Sélection des races réceptionnnées</h1>
<ReceptionBovineReceived />
</div>
<button
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
@click="goNext"

View File

@@ -1,34 +1,26 @@
<template>
<div :class="['flex flex-row items-center gap-2', wrapperClass]">
<div :class="['flex items-center gap-4', wrapperClass]">
<label
v-if="label && label !== 'Autres'"
v-if="label"
:for="id"
class="font-bold text-xl"
class="font-bold uppercase text-xl"
:class="labelClass"
>
{{ label }} <span v-if="code">({{ code }})</span>
{{ label }}
</label>
<!-- Champ texte si Autres -->
<input
v-else-if="label === 'Autres'"
type="text"
:placeholder="'Autres'"
class="border-b border-black text-xl bg-transparent- w-48"
/>
<input
:id="id"
type="number"
:value="modelValue ?? 0"
:placeholder="placeholder"
:value="modelValue ?? ''"
:min="min"
:max="max"
:step="step"
:disabled="disabled"
v-bind="attrs"
class="border-2 border-black text-xl pb-[6px] w-12 bg-transparent ma"
class="border-b border-black text-xl pb-[6px] bg-transparent text-right"
:class="[
disabled ? 'cursor-not-allowed text-neutral-400' : 'cursor-text text-black',
isEmpty ? 'text-neutral-400' : 'text-black',
disabled ? 'cursor-not-allowed' : 'cursor-text',
inputClass
]"
@input="onInput"
@@ -37,43 +29,48 @@
</template>
<script setup lang="ts">
import {useAttrs} from 'vue'
import { computed, useAttrs } from 'vue'
defineOptions({inheritAttrs: false})
defineOptions({ inheritAttrs: false })
const props = withDefaults(
defineProps<{
id?: string
label?: string
code?: string
modelValue: number | null | undefined
placeholder?: string
min?: number
max?: number
step?: number
modelValue: number | string | null | undefined
min?: number | string
max?: number | string
step?: number | string
disabled?: boolean
wrapperClass?: string
labelClass?: string
inputClass?: string
}>(),
{
placeholder: '',
min: 0,
step: 1,
min: undefined,
max: undefined,
step: undefined,
disabled: false,
wrapperClass: '',
labelClass: '',
inputClass: ''
}
)
const emit = defineEmits<{
(event: 'update:modelValue', value: number): void
(event: 'update:modelValue', value: number | null): void
}>()
const attrs = useAttrs()
const isEmpty = computed(() => props.modelValue === null || props.modelValue === undefined || props.modelValue === '')
const onInput = (event: Event) => {
const target = event.target as HTMLInputElement
emit('update:modelValue', Number(target.value))
if (target.value === '') {
emit('update:modelValue', null)
return
}
const numeric = Number(target.value)
emit('update:modelValue', Number.isNaN(numeric) ? null : numeric)
}
</script>

View File

@@ -16,7 +16,8 @@
</div>
<ReceptionForm v-if="!storeReception || storeReception.currentStep === 0"/>
<ReceptionWeight v-if="storeReception?.currentStep === 1" mode="gross"/>
<ReceptionProductReceived v-if="storeReception?.currentStep === 2"/>
<ReceptionProductReceived v-if="storeReception?.currentStep === 2 && receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES"/>
<ReceptionBovineReceived v-if="storeReception?.currentStep === 2 && receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.BOVINS"/>
<ReceptionWeight v-if="storeReception?.currentStep !== null && storeReception?.currentStep >= 3" mode="tare"/>
</div>
</template>
@@ -25,6 +26,7 @@
import {useReceptionStore} from '~/stores/reception'
import {storeToRefs} from 'pinia'
import {RECEPTION_STEP_LABELS} from '~/constants/steps'
import {RECEPTION_TYPE_CODES} from "~/utils/constants";
const route = useRoute()
const router = useRouter()

View File

@@ -0,0 +1,8 @@
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
export interface ReceptionBovineTypeData{
id: number
quantity : number
reception?: string
bovineType: BovineTypeData
}

View File

@@ -8,6 +8,7 @@ import type { AddressData } from '~/services/dto/address-data'
import type { TruckData } from '~/services/dto/truck-data'
import type { CarrierData } from '~/services/dto/carrier-data'
import type { DriverData } from '~/services/dto/driver-data'
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
export interface ReceptionData {
id: number
@@ -21,6 +22,7 @@ export interface ReceptionData {
merchandiseType?: MerchandiseTypeData | null
merchandiseDetail?: string | null
buildings?: BuildingData[] | null
bovinesTypes?: BovineTypeData[] | null
pelletBuildings?: ReceptionPelletBuildingData[] | null
user?: UserData | null
supplier?: SupplierData | null
@@ -47,6 +49,7 @@ export type ReceptionPayload = {
merchandiseType?: string | null
merchandiseDetail?: string | null
buildings?: string[] | null
bovinesTypes?: string[] | null
user?: string | null
supplier?: string | null
address?: string | null

View File

@@ -0,0 +1,49 @@
import { useApi } from '~/composables/useApi'
import type { ReceptionBovineTypeData } from '~/services/dto/reception-bovine-data'
export type ReceptionBovineListResponse =
| ReceptionBovineTypeData[]
| { 'hydra:member'?: ReceptionBovineTypeData[] }
export type ReceptionBovinePayload = {
quantity: number
reception: string
bovineType: string
}
export async function getReceptionBovineList(
receptionIri: string
): Promise<ReceptionBovineTypeData[]> {
const api = useApi()
const response = await api.get<ReceptionBovineListResponse>(
'reception_bovines',
{ reception: receptionIri },
{
toastErrorKey: 'errors.receptionBovine.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 createReceptionBovine(
payload: ReceptionBovinePayload
): Promise<ReceptionBovineTypeData> {
const api = useApi()
return api.post<ReceptionBovineTypeData>('reception_bovines', payload, {
toastErrorKey: 'errors.receptionBovine.create'
})
}
export async function deleteReceptionBovine(id: number): Promise<void> {
const api = useApi()
await api.delete<void>(`reception_bovines/${id}`, {}, {
toastErrorKey: 'errors.receptionBovine.delete'
})
}