[#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
10 changed files with 230 additions and 76 deletions
Showing only changes of commit 81c2a5802b - Show all commits

68
.idea/workspace.xml generated
View File

@@ -5,16 +5,16 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<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)"> <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$/.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-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-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/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/UiNumberInput.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/ui/UiNumberInput.vue" 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/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$/frontend/services/reception-bovine.ts" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/services/reception-bovine.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/Entity/BovineType.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/Entity/BovineType.php" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/Entity/Reception.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/Entity/Reception.php" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/Entity/Reception.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/Entity/Reception.php" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/Entity/ReceptionBovine.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/Entity/ReceptionBovine.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" />
@@ -226,36 +226,36 @@
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent">{ <component name="PropertiesComponent"><![CDATA[{
&quot;keyToString&quot;: { "keyToString": {
&quot;RunOnceActivity.MCP Project settings loaded&quot;: &quot;true&quot;, "RunOnceActivity.MCP Project settings loaded": "true",
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, "RunOnceActivity.ShowReadmeOnStart": "true",
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;, "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;, "RunOnceActivity.git.unshallow": "true",
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;, "RunOnceActivity.typescript.service.memoryLimit.init": "true",
&quot;git-widget-placeholder&quot;: &quot;feat/256-reception-etape-3-bovin&quot;, "git-widget-placeholder": "feat/256-reception-etape-3-bovin",
&quot;last_opened_file_path&quot;: &quot;/home/sroy/Documents/test/Ferme&quot;, "last_opened_file_path": "/home/sroy/Documents/test/Ferme",
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;, "node.js.detected.package.eslint": "true",
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;, "node.js.detected.package.tslint": "true",
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;, "node.js.selected.package.eslint": "(autodetect)",
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;, "node.js.selected.package.tslint": "(autodetect)",
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;, "nodejs_package_manager_path": "npm",
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;, "settings.editor.selected.configurable": "configurable.tailwindcss",
&quot;ts.external.directory.path&quot;: &quot;/opt/phpstorm/plugins/javascript-plugin/jsLanguageServicesImpl/external&quot;, "ts.external.directory.path": "/opt/phpstorm/plugins/javascript-plugin/jsLanguageServicesImpl/external",
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot; "vue.rearranger.settings.migration": "true"
}, },
&quot;keyToStringList&quot;: { "keyToStringList": {
&quot;DatabaseDriversLRU&quot;: [ "DatabaseDriversLRU": [
&quot;postgresql&quot; "postgresql"
], ],
&quot;com.intellij.ide.scratch.ScratchImplUtil$2/New Scratch File&quot;: [ "com.intellij.ide.scratch.ScratchImplUtil$2/New Scratch File": [
&quot;TEXT&quot; "TEXT"
], ],
&quot;vue.recent.templates&quot;: [ "vue.recent.templates": [
&quot;Vue Composition API Component&quot; "Vue Composition API Component"
] ]
} }
}</component> }]]></component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS"> <key name="MoveFile.RECENT_KEYS">
<recent name="\\wsl.localhost\Ubuntu-24.04\home\m-tristan\workspace\Ferme" /> <recent name="\\wsl.localhost\Ubuntu-24.04\home\m-tristan\workspace\Ferme" />
@@ -300,7 +300,7 @@
<workItem from="1770102495553" duration="2280000" /> <workItem from="1770102495553" duration="2280000" />
<workItem from="1770195604082" duration="90000" /> <workItem from="1770195604082" duration="90000" />
<workItem from="1770195718952" duration="215000" /> <workItem from="1770195718952" duration="215000" />
<workItem from="1770195959162" duration="8094000" /> <workItem from="1770195959162" duration="18482000" />
</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" />
@@ -678,7 +678,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1770131226364</updated> <updated>1770131226364</updated>
</task> </task>
<option name="localTasksCounter" value="48" /> <task id="LOCAL-00048" summary="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)">
<option name="closed" value="true" />
<created>1770206668867</created>
<option name="number" value="00048" />
<option name="presentableId" value="LOCAL-00048" />
<option name="project" value="LOCAL" />
<updated>1770206668867</updated>
</task>
<option name="localTasksCounter" value="49" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">

View File

@@ -4,7 +4,7 @@
class="flex flex-col items-center gap-16"> class="flex flex-col items-center gap-16">
<h1 class="text-4xl uppercase font-bold">Sélection des marchandises réceptionnnées</h1> <h1 class="text-4xl uppercase font-bold">Sélection des marchandises réceptionnnées</h1>
<div <div
class="flex flex-row gap-16 items-center w-full"> class="flex flex-row gap-8 items-center">
<div <div
v-for="type in bovineType" v-for="type in bovineType"
:key="type.id" :key="type.id"
@@ -12,14 +12,17 @@
<UiNumberInput <UiNumberInput
:label="type.label" :label="type.label"
:code="type.code" :code="type.code"
v-model="selectedBovineTypeIds[type.id]" v-model="bovineQuantities[String(type.id)]"
:value="String(type.id)" :placeholder="0"
:min="0"
:max="10"
/> />
</div> </div>
<div <div
class="mt-8 flex flex-row mb-2 gap-6"> class="mt-8 flex flex-row mb-2 gap-6">
<UiNumberInput <UiNumberInput
label="Autres" label="Autres"
v-model="otherQuantity"
/> />
</div> </div>
</div> </div>
@@ -38,14 +41,20 @@ import {useReceptionStore} from '~/stores/reception'
import { import {
createReceptionBovine, createReceptionBovine,
deleteReceptionBovine, deleteReceptionBovine,
getReceptionBovineList getReceptionBovineList,
updateReceptionBovine
} from "~/services/reception-bovine"; } from "~/services/reception-bovine";
import {ref} from "vue"; import {computed, onMounted, reactive, ref, watch} from "vue";
const isLoadingBovineType = ref(false) const isLoadingBovineType = ref(false)
const bovineType = ref<BovineTypeData[]>([]) const bovineType = ref<BovineTypeData[]>([])
const receptionStore = useReceptionStore() const receptionStore = useReceptionStore()
const selectedBovineTypeIds = ref<string[]> const bovineQuantities = reactive<Record<string, number | null>>({})
const otherQuantity = ref<number | null>(0)
const receptionId = computed(() => receptionStore.current?.id ?? null)
const receptionIri = computed(() =>
receptionId.value ? `/api/receptions/${receptionId.value}` : null
)
const loadBovineType = async () => { const loadBovineType = async () => {
isLoadingBovineType.value = true isLoadingBovineType.value = true
try { try {
@@ -55,28 +64,94 @@ const loadBovineType = async () => {
} }
} }
onMounted(async () => { onMounted(async () => {
await loadBovineType() await loadBovineType()
selectedBovineTypeIds.value=bovineType.value.map((type) => String(type.id))
}) })
watch(
() => receptionId.value,
async (id) => {
if (!id || !receptionIri.value) {
return
}
const selectionMap: Record<string, number | null> = {}
for (const type of bovineType.value) {
selectionMap[String(type.id)] = 0
}
const existing = await getReceptionBovineList(receptionIri.value)
for (const selection of existing) {
const bovineTypeId = String(selection.bovineType.id)
selectionMap[bovineTypeId] = selection.quantity ?? 0
}
for (const key of Object.keys(bovineQuantities)) {
delete bovineQuantities[key]
}
Object.assign(bovineQuantities, selectionMap)
},
{immediate: true}
)
async function syncBovineSelections(receptionIri: string) {
const existing = await getReceptionBovineList(receptionIri)
const existingMap = new Map<string, { id: number; quantity: number | null }>()
for (const selection of existing) {
const bovineTypeId = String(selection.bovineType.id)
existingMap.set(bovineTypeId, {
id: selection.id,
quantity: selection.quantity ?? 0
})
}
// Supprime les entrées supprimées ou modifiées
for (const [bovineTypeId, entry] of existingMap.entries()) {
const selectedQuantity = bovineQuantities[bovineTypeId] ?? 0
if (!selectedQuantity) {
await deleteReceptionBovine(entry.id)
existingMap.delete(bovineTypeId)
continue
}
if (selectedQuantity !== entry.quantity) {
await updateReceptionBovine(entry.id, { quantity: selectedQuantity })
existingMap.set(bovineTypeId, {
id: entry.id,
quantity: selectedQuantity
})
}
}
// Crée les entrées manquantes
for (const [bovineTypeId, quantity] of Object.entries(bovineQuantities)) {
if (!quantity) {
continue
}
if (existingMap.has(bovineTypeId)) {
// Déjà à jour
continue
}
await createReceptionBovine({
reception: receptionIri,
bovineType: `/api/bovine_types/${bovineTypeId}`,
quantity
})
}
}
async function goNext() { async function goNext() {
if (!receptionStore.current) { if (!receptionStore.current || !receptionIri.value) {
return return
} }
const nextStep = receptionStore.current.currentStep + 1 const nextStep = receptionStore.current.currentStep + 1
const receptionIri = `/api/receptions/${receptionStore.current.id}`
console.log(selectedBovineTypeIds.value) await syncBovineSelections(receptionIri.value)
await receptionStore.updateReception(receptionStore.current.id, { await receptionStore.updateReception(receptionStore.current.id, {
merchandiseType: null, merchandiseType: null,
merchandiseDetail: null, merchandiseDetail: null,
buildings: null,
bovinesTypes : selectedBovineTypeIds.value.map((id) => `/api/bovines_types/${id}`),
currentStep: nextStep currentStep: nextStep
}) })
} }
</script> </script>

View File

@@ -175,7 +175,6 @@ onMounted(async () => {
} }
selectedPelletBuildingIds.value = selectionMap selectedPelletBuildingIds.value = selectionMap
}) })
// Enregistre les sélections et passe à l'étape suivante // Enregistre les sélections et passe à l'étape suivante
async function goNext() { async function goNext() {
if (!receptionStore.current) { if (!receptionStore.current) {
@@ -193,6 +192,7 @@ async function goNext() {
buildings: isGranule.value buildings: isGranule.value
? [] ? []
: selectedBuildingIds.value.map((id) => `/api/buildings/${id}`), : selectedBuildingIds.value.map((id) => `/api/buildings/${id}`),
bovinesTypes: null,
currentStep: nextStep currentStep: nextStep
}) })
@@ -210,7 +210,6 @@ async function clearPelletSelections(receptionIri: string) {
await deleteReceptionPelletBuilding(selection.id) await deleteReceptionPelletBuilding(selection.id)
} }
} }
// Synchronise les associations granulés/bâtiments avec l'état du formulaire // Synchronise les associations granulés/bâtiments avec l'état du formulaire
async function syncPelletSelections(receptionIri: string) { async function syncPelletSelections(receptionIri: string) {
const existing = await getReceptionPelletBuildingList(receptionIri) const existing = await getReceptionPelletBuildingList(receptionIri)

View File

@@ -1,12 +1,19 @@
<template> <template>
<div :class="['flex items-center gap-4', wrapperClass]"> <div :class="['flex flex-row items-center gap-2', wrapperClass]">
<label <label
v-if="label" v-if="label"
:for="id" :for="id"
class="font-bold uppercase text-xl" class="text-xl text-bold flex items-center gap-2"
:class="labelClass" :class="labelClass"
> >
{{ label }} <span
v-if="label">
{{ label }}
</span>
<span
v-if="code" class="text-neutral-600">
({{ code }})
</span>
</label> </label>
<input <input
:id="id" :id="id"
@@ -17,7 +24,7 @@
:step="step" :step="step"
:disabled="disabled" :disabled="disabled"
v-bind="attrs" v-bind="attrs"
class="border-b border-black text-xl pb-[6px] bg-transparent text-right" class="border-b border-black text-xl bg-transparent w-48"
: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',
@@ -29,14 +36,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, useAttrs } from 'vue' import {computed, useAttrs} from 'vue'
defineOptions({ inheritAttrs: false }) defineOptions({inheritAttrs: false})
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
id?: string id?: string
label?: string label?: string
code?: string
modelValue: number | string | null | undefined modelValue: number | string | null | undefined
min?: number | string min?: number | string
max?: number | string max?: number | string

View File

@@ -16,8 +16,12 @@
</div> </div>
<ReceptionForm v-if="!storeReception || storeReception.currentStep === 0"/> <ReceptionForm v-if="!storeReception || storeReception.currentStep === 0"/>
<ReceptionWeight v-if="storeReception?.currentStep === 1" mode="gross"/> <ReceptionWeight v-if="storeReception?.currentStep === 1" mode="gross"/>
<ReceptionProductReceived v-if="storeReception?.currentStep === 2 && receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES"/> <ReceptionProductReceived
<ReceptionBovineReceived v-if="storeReception?.currentStep === 2 && receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.BOVINS"/> 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"/> <ReceptionWeight v-if="storeReception?.currentStep !== null && storeReception?.currentStep >= 3" mode="tare"/>
</div> </div>
</template> </template>

View File

@@ -47,3 +47,13 @@ export async function deleteReceptionBovine(id: number): Promise<void> {
toastErrorKey: 'errors.receptionBovine.delete' toastErrorKey: 'errors.receptionBovine.delete'
}) })
} }
export async function updateReceptionBovine(
id: number,
payload: Partial<ReceptionBovinePayload>
): Promise<ReceptionBovineTypeData> {
const api = useApi()
return api.patch<ReceptionBovineTypeData>(`reception_bovines/${id}`, payload, {
toastErrorKey: 'errors.receptionBovine.update'
})
}

View File

@@ -0,0 +1,33 @@
<?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 Version20260204141406 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 reception_bovine ALTER quantity SET DEFAULT 0');
$this->addSql('CREATE UNIQUE INDEX uniq_reception_bovine_type ON reception_bovine (reception_id, bovine_type_id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP INDEX uniq_reception_bovine_type');
$this->addSql('ALTER TABLE reception_bovine ALTER quantity DROP DEFAULT');
}
}

View File

@@ -28,15 +28,15 @@ class BovineType
#[ORM\Id] #[ORM\Id]
#[ORM\GeneratedValue] #[ORM\GeneratedValue]
#[ORM\Column] #[ORM\Column]
#[Groups(['bovine-type:read', 'reception:read'])] #[Groups(['bovine-type:read', 'reception:read', 'reception-bovine:read'])]
private ?int $id = null; private ?int $id = null;
#[ORM\Column(length: 120)] #[ORM\Column(length: 120)]
#[Groups(['bovine-type:read', 'reception:read'])] #[Groups(['bovine-type:read', 'reception:read', 'reception-bovine:read'])]
private ?string $label = null; private ?string $label = null;
#[ORM\Column(length: 50)] #[ORM\Column(length: 50)]
#[Groups(['bovine-type:read', 'reception:read'])] #[Groups(['bovine-type:read', 'reception:read', 'reception-bovine:read'])]
private ?string $code = null; private ?string $code = null;
public function getId(): ?int public function getId(): ?int

View File

@@ -72,27 +72,27 @@ class Reception
#[ORM\Id] #[ORM\Id]
#[ORM\GeneratedValue] #[ORM\GeneratedValue]
#[ORM\Column] #[ORM\Column]
#[Groups(['reception:read'])] #[Groups(['reception:read', 'reception-bovine:read'])]
private ?int $id = null; private ?int $id = null;
#[ORM\Column(length: 20, nullable: true)] #[ORM\Column(length: 20, nullable: true)]
#[Groups(['reception:read', 'reception:write'])] #[Groups(['reception:read', 'reception:write', 'reception-bovine:read'])]
private ?string $licensePlate = null; private ?string $licensePlate = null;
#[ORM\Column(length: 20, unique: true, nullable: true)] #[ORM\Column(length: 20, unique: true, nullable: true)]
#[Groups(['reception:read'])] #[Groups(['reception:read', 'reception-bovine:read'])]
private ?string $identificationNumber = null; private ?string $identificationNumber = null;
#[ORM\Column(options: ['default' => 0])] #[ORM\Column(options: ['default' => 0])]
#[Groups(['reception:read', 'reception:write'])] #[Groups(['reception:read', 'reception:write', 'reception-bovine:read'])]
private int $currentStep = 0; private int $currentStep = 0;
#[ORM\Column(options: ['default' => false])] #[ORM\Column(options: ['default' => false])]
#[Groups(['reception:read', 'reception:write'])] #[Groups(['reception:read', 'reception:write', 'reception-bovine:read'])]
private bool $isValid = false; private bool $isValid = false;
#[ORM\Column(name: 'date_reception', type: 'datetime_immutable')] #[ORM\Column(name: 'date_reception', type: 'datetime_immutable')]
#[Groups(['reception:read', 'reception:write'])] #[Groups(['reception:read', 'reception:write', 'reception-bovine:read'])]
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])] #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
private ?DateTimeImmutable $receptionDate = null; private ?DateTimeImmutable $receptionDate = null;

View File

@@ -4,15 +4,21 @@ declare(strict_types=1);
namespace App\Entity; namespace App\Entity;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete; use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post; use ApiPlatform\Metadata\Post;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups; use Symfony\Component\Serializer\Attribute\Groups;
#[ORM\Entity] #[ORM\Entity]
#[ApiFilter(SearchFilter::class, properties: ['reception' => 'exact'])]
#[ORM\UniqueConstraint(name: 'uniq_reception_bovine_type', columns: ['reception_id', 'bovine_type_id'])]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get( new Get(
@@ -26,6 +32,10 @@ use Symfony\Component\Serializer\Attribute\Groups;
normalizationContext: ['groups' => ['reception-bovine:read']], normalizationContext: ['groups' => ['reception-bovine:read']],
denormalizationContext: ['groups' => ['reception-bovine:write']], denormalizationContext: ['groups' => ['reception-bovine:write']],
), ),
new Patch(
normalizationContext: ['groups' => ['reception-bovine:read']],
denormalizationContext: ['groups' => ['reception-bovine:write']],
),
new Delete(), new Delete(),
], ],
security: "is_granted('ROLE_USER')", security: "is_granted('ROLE_USER')",
@@ -36,20 +46,27 @@ class ReceptionBovine
#[ORM\GeneratedValue] #[ORM\GeneratedValue]
#[ORM\Column] #[ORM\Column]
#[Groups(['reception-bovine:read', 'reception:read'])] #[Groups(['reception-bovine:read', 'reception:read'])]
private ?int $id = null; private ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'bovines_types')] #[ORM\ManyToOne(inversedBy: 'bovines_types')]
#[Groups(['reception-bovine:read'])] #[Groups(['reception-bovine:read', 'reception-bovine:write'])]
private ?Reception $reception = null; private ?Reception $reception = null;
#[ORM\ManyToOne] #[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
#[Groups(['reception-bovine:read', 'reception:read'])] #[Groups(['reception-bovine:read', 'reception-bovine:write', 'reception:read'])]
private ?BovineType $BovineType = null; #[ApiProperty(readableLink: true)]
private ?BovineType $bovineType = null;
#[ORM\Column(options: ['default' => 0])] #[ORM\Column(options: ['default' => 0])]
#[Groups(['reception-bovine:read', 'reception:read'])] // #[Assert\Range(
private ?int $Quantity = null; // min: 0,
// max: 10,
// notInRangeMessage: 'La quantité doit être comprise entre {{ min }} et {{ max }}.'
// )]
#[Groups(['reception-bovine:read', 'reception-bovine:write', 'reception:read'])]
private ?int $quantity = null;
public function getId(): ?int public function getId(): ?int
{ {
@@ -70,24 +87,24 @@ class ReceptionBovine
public function getBovineType(): ?BovineType public function getBovineType(): ?BovineType
{ {
return $this->BovineType; return $this->bovineType;
} }
public function setBovineType(?BovineType $BovineType): static public function setBovineType(?BovineType $bovineType): static
{ {
$this->BovineType = $BovineType; $this->bovineType = $bovineType;
return $this; return $this;
} }
public function getQuantity(): ?int public function getQuantity(): ?int
{ {
return $this->Quantity; return $this->quantity;
} }
public function setQuantity(int $Quantity): static public function setQuantity(int $quantity): static
{ {
$this->Quantity = $Quantity; $this->quantity = $quantity;
return $this; return $this;
} }