feat : WIP prix au kilo et prix total sur les bovins

- Champ pricePerKg persisté sur Bovine + migration
- Getter calculé finalPrice = receivedWeight * pricePerKg
- Colonnes Prix/kg et Prix total sur inventory et case
- Ajustements de largeurs pour rentrer dans le layout

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 17:01:48 +02:00
parent 7b722bdd17
commit 7bf6a36b73
5 changed files with 112 additions and 14 deletions

View File

@@ -74,6 +74,12 @@
<template #header-age>
<UiTextInput :model-value="''" placeholder="Age" size="compact" disabled />
</template>
<template #header-pricePerKg>
<UiTextInput :model-value="''" placeholder="Prix/kg" size="compact" disabled />
</template>
<template #header-finalPrice>
<UiTextInput :model-value="''" placeholder="Prix total" size="compact" disabled />
</template>
<template #header-breedCode>
<UiTextInput
v-model="filters.breedCode"
@@ -105,6 +111,12 @@
<template #cell-buildingCase.caseNumber="{ item }">
{{ item.buildingCase?.caseNumber ?? '—' }}
</template>
<template #cell-pricePerKg="{ item }">
{{ formatPrice(item.pricePerKg) }}
</template>
<template #cell-finalPrice="{ item }">
{{ formatPrice(item.finalPrice) }}
</template>
</UiDataTable>
</div>
</div>
@@ -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'

View File

@@ -102,6 +102,12 @@
<template #header-age>
<UiTextInput :model-value="''" placeholder="Age" size="compact" disabled />
</template>
<template #header-pricePerKg>
<UiTextInput :model-value="''" placeholder="Prix/kg" size="compact" disabled />
</template>
<template #header-finalPrice>
<UiTextInput :model-value="''" placeholder="Prix total" size="compact" disabled />
</template>
<template #cell-birthDate="{ item }">
{{ formatDate(item.birthDate) }}
</template>
@@ -117,6 +123,12 @@
<template #cell-buildingCase.caseNumber="{ item }">
{{ item.buildingCase?.caseNumber ?? '—' }}
</template>
<template #cell-pricePerKg="{ item }">
{{ formatPrice(item.pricePerKg) }}
</template>
<template #cell-finalPrice="{ item }">
{{ formatPrice(item.finalPrice) }}
</template>
</UiDataTable>
</div>
</div>
@@ -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'

View File

@@ -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