diff --git a/frontend/pages/infrastructure/case.vue b/frontend/pages/infrastructure/case.vue
index 18df4b2..7479157 100644
--- a/frontend/pages/infrastructure/case.vue
+++ b/frontend/pages/infrastructure/case.vue
@@ -74,6 +74,12 @@
+
+
+
+
+
+
{{ item.buildingCase?.caseNumber ?? '—' }}
+
+ {{ formatPrice(item.pricePerKg) }}
+
+
+ {{ formatPrice(item.finalPrice) }}
+
@@ -175,15 +187,17 @@ const arrivalDateFilter = singleDateFilter('arrivalDate[after]', 'arrivalDate[st
const birthDateFilter = singleDateFilter('birthDate[after]', 'birthDate[strictly_before]')
const columns = [
- { key: 'nationalNumber', label: 'N° National', width: '160px' },
- { key: 'workNumber', label: 'N° Travail', width: '85px' },
+ { key: 'nationalNumber', label: 'N° National', width: '80px' },
+ { key: 'workNumber', label: 'N° Travail', width: '43px' },
{ key: 'sex', label: 'Sexe', width: '70px' },
- { key: 'birthDate', label: 'Né le', width: '120px' },
+ { key: 'birthDate', label: 'Né le', width: '72px' },
{ key: 'age', label: 'Age', width: '110px' },
- { key: 'breedCode', label: 'Race' },
- { key: 'buildingCase.building.label', label: 'Bâtiment', width: '1.5fr' },
- { key: 'buildingCase.caseNumber', label: 'Case', width: '80px' },
- { key: 'arrivalDate', label: 'Entrée le', width: '120px' }
+ { key: 'breedCode', label: 'Race', width: '70px' },
+ { key: 'buildingCase.building.label', label: 'Bâtiment', width: '75px' },
+ { key: 'buildingCase.caseNumber', label: 'Case', width: '60px' },
+ { key: 'arrivalDate', label: 'Entrée le', width: '90px' },
+ { key: 'pricePerKg', label: 'Prix/kg', width: '65px' },
+ { key: 'finalPrice', label: 'Prix total', width: '100px' }
]
const title = computed(() => {
@@ -209,6 +223,11 @@ const formatDate = (date: string | null) => {
})
}
+const formatPrice = (price: number | null) => {
+ if (price === null || price === undefined) return '—'
+ return `${price.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} €`
+}
+
const rowClass = (item: BovineData): string => {
if (item.ageMonths === null || item.ageMonths === undefined) return ''
if (item.ageMonths >= 24) return 'bg-violet-300 hover:bg-violet-400'
diff --git a/frontend/pages/inventory.vue b/frontend/pages/inventory.vue
index 6948db7..399acde 100644
--- a/frontend/pages/inventory.vue
+++ b/frontend/pages/inventory.vue
@@ -102,6 +102,12 @@
+
+
+
+
+
+
{{ formatDate(item.birthDate) }}
@@ -117,6 +123,12 @@
{{ item.buildingCase?.caseNumber ?? '—' }}
+
+ {{ formatPrice(item.pricePerKg) }}
+
+
+ {{ formatPrice(item.finalPrice) }}
+
@@ -256,15 +268,17 @@ const arrivalDateFilter = singleDateFilter('arrivalDate[after]', 'arrivalDate[st
const birthDateFilter = singleDateFilter('birthDate[after]', 'birthDate[strictly_before]')
const columns = [
- { key: 'nationalNumber', label: 'N° National', width: '160px' },
- { key: 'workNumber', label: 'N° Travail', width: '85px' },
+ { key: 'nationalNumber', label: 'N° National', width: '80px' },
+ { key: 'workNumber', label: 'N° Travail', width: '43px' },
{ key: 'sex', label: 'Sexe', width: '70px' },
- { key: 'birthDate', label: 'Né le', width: '120px' },
+ { key: 'birthDate', label: 'Né le', width: '72px' },
{ key: 'age', label: 'Age', width: '110px' },
- { key: 'breedCode', label: 'Race' },
- { key: 'buildingCase.building.label', label: 'Bâtiment', width: '1.5fr' },
- { key: 'buildingCase.caseNumber', label: 'Case', width: '80px' },
- { key: 'arrivalDate', label: 'Entrée le', width: '120px' }
+ { key: 'breedCode', label: 'Race', width: '70px' },
+ { key: 'buildingCase.building.label', label: 'Bâtiment', width: '75px' },
+ { key: 'buildingCase.caseNumber', label: 'Case', width: '60px' },
+ { key: 'arrivalDate', label: 'Entrée le', width: '90px' },
+ { key: 'pricePerKg', label: 'Prix/kg', width: '65px' },
+ { key: 'finalPrice', label: 'Prix total', width: '100px' }
]
const formatDate = (date: string | null) => {
@@ -278,6 +292,11 @@ const formatDate = (date: string | null) => {
})
}
+const formatPrice = (price: number | null) => {
+ if (price === null || price === undefined) return '—'
+ return `${price.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} €`
+}
+
const rowClass = (item: BovineData): string => {
if (item.ageMonths === null || item.ageMonths === undefined) return ''
if (item.ageMonths >= 24) return 'bg-violet-300 hover:bg-violet-400'
diff --git a/frontend/services/dto/bovine-data.ts b/frontend/services/dto/bovine-data.ts
index db5d857..06b250f 100644
--- a/frontend/services/dto/bovine-data.ts
+++ b/frontend/services/dto/bovine-data.ts
@@ -7,6 +7,8 @@ export interface BovineData {
id: number
nationalNumber: string
receivedWeight: number | null
+ pricePerKg: number | null
+ finalPrice: number | null
arrivalDate: string | null
exitDate: string | null
buildingCase: BovineBuildingCaseRef | null
@@ -22,6 +24,7 @@ export interface BovineData {
export type BovinePayload = {
nationalNumber?: string
receivedWeight?: number | null
+ pricePerKg?: number | null
arrivalDate?: string | null
buildingCase?: string | null
supplier?: string | null
diff --git a/migrations/Version20260424132554.php b/migrations/Version20260424132554.php
new file mode 100644
index 0000000..b8afa70
--- /dev/null
+++ b/migrations/Version20260424132554.php
@@ -0,0 +1,31 @@
+addSql('ALTER TABLE bovine ADD price_per_kg DOUBLE PRECISION DEFAULT NULL');
+ }
+
+ public function down(Schema $schema): void
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->addSql('ALTER TABLE bovine DROP price_per_kg');
+ }
+}
diff --git a/src/Entity/Bovine.php b/src/Entity/Bovine.php
index 25d59a4..0c7cd09 100644
--- a/src/Entity/Bovine.php
+++ b/src/Entity/Bovine.php
@@ -77,6 +77,10 @@ class Bovine
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
private ?int $receivedWeight = null;
+ #[ORM\Column(type: 'float', nullable: true)]
+ #[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
+ private ?float $pricePerKg = null;
+
#[ORM\Column(type: 'date_immutable', nullable: true)]
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
@@ -151,6 +155,28 @@ class Bovine
return $this;
}
+ public function getPricePerKg(): ?float
+ {
+ return $this->pricePerKg;
+ }
+
+ public function setPricePerKg(?float $pricePerKg): static
+ {
+ $this->pricePerKg = $pricePerKg;
+
+ return $this;
+ }
+
+ #[Groups(['bovine:read', 'building_case:read'])]
+ public function getFinalPrice(): ?float
+ {
+ if (null === $this->receivedWeight || null === $this->pricePerKg) {
+ return null;
+ }
+
+ return $this->receivedWeight * $this->pricePerKg;
+ }
+
public function getArrivalDate(): ?DateTimeImmutable
{
return $this->arrivalDate;