feat : Ajout d'un écran pour afficher les informations d'un bovin

This commit is contained in:
2026-02-02 11:35:10 +01:00
parent 086279f962
commit 6421419812
7 changed files with 192 additions and 71 deletions

31
.idea/workspace.xml generated
View File

@@ -4,12 +4,15 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="feat : mise à jour du bon de réception"> <list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="fix : correction du téléchargement du bon de réception pour Chrome">
<change afterPath="$PROJECT_DIR$/frontend/pages/identification-bovin.vue" afterDir="false" />
<change afterPath="$PROJECT_DIR$/frontend/services/dto/identification-bovin-data.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/frontend/services/identification-bovin.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" 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$/config/reference.php" beforeDir="false" afterPath="$PROJECT_DIR$/config/reference.php" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-weight.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-weight.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/composables/usePdfPrinter.ts" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/composables/usePdfPrinter.ts" afterDir="false" /> <change beforePath="$PROJECT_DIR$/frontend/layouts/default.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/layouts/default.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/templates/reception_voucher.html.twig" beforeDir="false" afterPath="$PROJECT_DIR$/templates/reception_voucher.html.twig" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/ApiResource/BovinIdentification.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/ApiResource/BovinIdentification.php" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -27,8 +30,8 @@
<component name="FileTemplateManagerImpl"> <component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES"> <option name="RECENT_TEMPLATES">
<list> <list>
<option value="TypeScript File" />
<option value="Vue Composition API Component" /> <option value="Vue Composition API Component" />
<option value="TypeScript File" />
</list> </list>
</option> </option>
</component> </component>
@@ -222,7 +225,7 @@
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true", "RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.typescript.service.memoryLimit.init": "true", "RunOnceActivity.typescript.service.memoryLimit.init": "true",
"git-widget-placeholder": "feat/finalisation-reception-marchandise", "git-widget-placeholder": "feat/poc-identification-bovin",
"node.js.detected.package.eslint": "true", "node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true", "node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)", "node.js.selected.package.eslint": "(autodetect)",
@@ -278,7 +281,7 @@
<workItem from="1769612160652" duration="23952000" /> <workItem from="1769612160652" duration="23952000" />
<workItem from="1769696465294" duration="8573000" /> <workItem from="1769696465294" duration="8573000" />
<workItem from="1769756623432" duration="21592000" /> <workItem from="1769756623432" duration="21592000" />
<workItem from="1770015653091" duration="73000" /> <workItem from="1770015653091" duration="9682000" />
</task> </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)"> <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" /> <option name="closed" value="true" />
@@ -648,7 +651,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1769782099473</updated> <updated>1769782099473</updated>
</task> </task>
<option name="localTasksCounter" value="47" /> <task id="LOCAL-00047" summary="fix : correction du téléchargement du bon de réception pour Chrome">
<option name="closed" value="true" />
<created>1770015863605</created>
<option name="number" value="00047" />
<option name="presentableId" value="LOCAL-00047" />
<option name="project" value="LOCAL" />
<updated>1770015863605</updated>
</task>
<option name="localTasksCounter" value="48" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -698,7 +709,6 @@
</option> </option>
</component> </component>
<component name="VcsManagerConfiguration"> <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="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="feat : Ajout du bundle Monolog pour la gestion des logs" />
<MESSAGE value="fix : affiche plus détail dans les logs en recette/prod" /> <MESSAGE value="fix : affiche plus détail dans les logs en recette/prod" />
@@ -723,7 +733,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 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 : 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" /> <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="fix : correction du téléchargement du bon de réception pour Chrome" />
<option name="LAST_COMMIT_MESSAGE" value="fix : correction du téléchargement du bon de réception pour Chrome" />
</component> </component>
<component name="XSLT-Support.FileAssociations.UIState"> <component name="XSLT-Support.FileAssociations.UIState">
<expand /> <expand />

View File

@@ -1,64 +1,67 @@
{ {
"errors": { "errors": {
"http": { "http": {
"get": "Impossible de récupérer les données.", "get": "Impossible de récupérer les données.",
"post": "Impossible de créer la ressource.", "post": "Impossible de créer la ressource.",
"put": "Impossible de mettre à jour la ressource.", "put": "Impossible de mettre à jour la ressource.",
"patch": "Impossible de mettre à jour la ressource.", "patch": "Impossible de mettre à jour la ressource.",
"delete": "Impossible de supprimer la ressource." "delete": "Impossible de supprimer la ressource."
},
"reception": {
"list": "Impossible de récupérer la liste des réceptions.",
"fetch": "Impossible de récupérer la réception.",
"create": "Impossible de créer la réception.",
"update": "Impossible de mettre à jour la réception.",
"weigh": "Impossible de récupérer la pesée."
},
"receptionType": {
"list": "Impossible de récupérer la liste des types de réception."
},
"merchandiseType": {
"list": "Impossible de récupérer la liste des types de marchandises."
},
"building": {
"list": "Impossible de récupérer la liste des bâtiments."
},
"pelletType": {
"list": "Impossible de récupérer la liste des types de granulés."
},
"receptionPelletBuilding": {
"list": "Impossible de récupérer la liste des dépôts de granulés.",
"create": "Impossible d'enregistrer le dépôt de granulés.",
"delete": "Impossible de supprimer le dépôt de granulés."
},
"supplier": {
"list": "Impossible de récupérer la liste des fournisseurs."
},
"truck": {
"list": "Impossible de récupérer la liste des camions."
},
"carrier": {
"list": "Impossible de récupérer la liste des transporteurs."
},
"driver": {
"list": "Impossible de récupérer la liste des chauffeurs."
},
"vehicle": {
"list": "Impossible de récupérer la liste des immatriculations."
},
"auth": {
"login": "Identifiants invalides.",
"users": "Impossible de récupérer les utilisateurs.",
"logout": "Impossible de se déconnecter."
},
"identificationBovin": {
"get": "Impossible de récupérer les informations du bovin"
}
}, },
"reception": { "success": {
"list": "Impossible de récupérer la liste des réceptions.", "reception": {
"fetch": "Impossible de récupérer la réception.", "update": "Réception mise à jour avec succès."
"create": "Impossible de créer la réception.", },
"update": "Impossible de mettre à jour la réception.", "auth": {
"weigh": "Impossible de récupérer la pesée." "login": "Connexion réussie.",
}, "logout": "Déconnexion réussie."
"receptionType": { }
"list": "Impossible de récupérer la liste des types de réception."
},
"merchandiseType": {
"list": "Impossible de récupérer la liste des types de marchandises."
},
"building": {
"list": "Impossible de récupérer la liste des bâtiments."
},
"pelletType": {
"list": "Impossible de récupérer la liste des types de granulés."
},
"receptionPelletBuilding": {
"list": "Impossible de récupérer la liste des dépôts de granulés.",
"create": "Impossible d'enregistrer le dépôt de granulés.",
"delete": "Impossible de supprimer le dépôt de granulés."
},
"supplier": {
"list": "Impossible de récupérer la liste des fournisseurs."
},
"truck": {
"list": "Impossible de récupérer la liste des camions."
},
"carrier": {
"list": "Impossible de récupérer la liste des transporteurs."
},
"driver": {
"list": "Impossible de récupérer la liste des chauffeurs."
},
"vehicle": {
"list": "Impossible de récupérer la liste des immatriculations."
},
"auth": {
"login": "Identifiants invalides.",
"users": "Impossible de récupérer les utilisateurs.",
"logout": "Impossible de se déconnecter."
} }
},
"success": {
"reception": {
"update": "Réception mise à jour avec succès."
},
"auth": {
"login": "Connexion réussie.",
"logout": "Déconnexion réussie."
}
}
} }

View File

@@ -28,6 +28,15 @@
Reception Reception
</a> </a>
</NuxtLink> </NuxtLink>
<NuxtLink to="/identification-bovin" custom v-slot="{ href, navigate, isActive }">
<a
:href="href"
@click="navigate"
:class="isIdentificationActive ? 'opacity-100' : 'opacity-50'"
>
Identification
</a>
</NuxtLink>
</nav> </nav>
<button <button
type="button" type="button"
@@ -50,6 +59,7 @@ import { useAuthStore } from '~/stores/auth'
const route = useRoute() const route = useRoute()
const auth = useAuthStore() const auth = useAuthStore()
const isReceptionActive = computed(() => route.path.startsWith('/reception')) const isReceptionActive = computed(() => route.path.startsWith('/reception'))
const isIdentificationActive = computed(() => route.path.startsWith('/identification-bovin'))
const handleLogout = async () => { const handleLogout = async () => {
try { try {

View File

@@ -0,0 +1,49 @@
<template>
<div>
<div class="flex justify-between">
<h1 class="font-bold text-4xl uppercase">Passeport du bovin</h1>
<p v-if="bovinData">{{ bovinData.presencePeriod }}</p>
</div>
<div class="overflow-x-auto mt-12" v-if="bovinData">
<table class="w-full border-collapse border border-black text-sm">
<tr>
<th
rowspan="2"
class="w-10 border border-black p-0 align-middle"
>
<div class="flex h-full w-full items-center justify-center">
<span class="-rotate-90 whitespace-nowrap font-semibold tracking-widest">
VEAU
</span>
</div>
</th>
<th class="border border-black px-4 py-3 text-center font-semibold">N° de travail</th>
<th class="border border-black px-4 py-3 text-center font-semibold">Sexe</th>
<th class="border border-black px-4 py-3 text-center font-semibold">Code Race</th>
<th class="border border-black px-4 py-3 text-center font-semibold">Code pays</th>
<th class="border border-black px-4 py-3 text-center font-semibold">Type de racial</th>
<th class="border border-black px-4 py-3 text-center font-semibold">Date de naissance</th>
</tr>
<tr>
<th class="border border-black px-4 py-3 text-center font-semibold">{{ bovinData.workNumber }}</th>
<th class="border border-black px-4 py-3 text-center font-semibold">{{ bovinData.sex }}</th>
<th class="border border-black px-4 py-3 text-center font-semibold">{{ bovinData.breedType }}</th>
<th class="border border-black px-4 py-3 text-center font-semibold">FR {{ bovinData.numeroNational }}</th>
<th class="border border-black px-4 py-3 text-center font-semibold">???</th>
<th class="border border-black px-4 py-3 text-center font-semibold">{{ bovinData.birthDate }}</th>
</tr>
</table>
</div>
</div>
</template>
<script setup lang="ts">
import {getBovinData} from "~/services/identification-bovin";
const bovinData = ref<IdentificationBovinData|null>()
onMounted(async () => {
bovinData.value = await getBovinData('7979580026');
})
</script>

View File

@@ -0,0 +1,22 @@
interface IdentificationBovinData {
numeroNational: string,
sex: string | null,
breedType: string | null,
workNumber: string | null,
birthDate: string | null,
birthDateCompletenessFlag: string | null,
isFilie: boolean | null,
motherNationalNumber: string | null,
motherBreedType: string | null,
fatherNationalNumber: string | null,
fatherBreedType: string | null,
birthExploitationNumber: string | null,
presencePeriod: PresencePeriod[]
}
interface PresencePeriod {
entryDate: string | null,
entryCause: string | null,
exitDate: string | null,
exitCause: string | null
}

View File

@@ -0,0 +1,25 @@
import { useApi } from '~/composables/useApi'
export type BovinDataResponse =
| IdentificationBovinData
| { 'hydra:member'?: IdentificationBovinData }
export async function getBovinData(
nationalNumber: string
): Promise<IdentificationBovinData | null> {
const api = useApi()
const response = await api.get<BovinDataResponse>(
`bovins/${nationalNumber}/identification`,
{},
{ toastErrorKey: 'errors.building.list' }
)
if (response && typeof response === 'object') {
// direct item
if (!('hydra:member' in response)) return response as IdentificationBovinData
// hydra format
if (response['hydra:member']) return response['hydra:member']
}
return null
}

View File

@@ -15,7 +15,8 @@ use App\State\BovinIdentificationProvider;
uriTemplate: '/bovins/{numeroNational}/identification', uriTemplate: '/bovins/{numeroNational}/identification',
provider: BovinIdentificationProvider::class provider: BovinIdentificationProvider::class
), ),
] ],
security: "is_granted('ROLE_USER')",
)] )]
final class BovinIdentification final class BovinIdentification
{ {