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 @@ + + + + @@ -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 @@ + + @@ -117,6 +123,12 @@ + + @@ -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;