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:
@@ -74,6 +74,12 @@
|
|||||||
<template #header-age>
|
<template #header-age>
|
||||||
<UiTextInput :model-value="''" placeholder="Age" size="compact" disabled />
|
<UiTextInput :model-value="''" placeholder="Age" size="compact" disabled />
|
||||||
</template>
|
</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>
|
<template #header-breedCode>
|
||||||
<UiTextInput
|
<UiTextInput
|
||||||
v-model="filters.breedCode"
|
v-model="filters.breedCode"
|
||||||
@@ -105,6 +111,12 @@
|
|||||||
<template #cell-buildingCase.caseNumber="{ item }">
|
<template #cell-buildingCase.caseNumber="{ item }">
|
||||||
{{ item.buildingCase?.caseNumber ?? '—' }}
|
{{ item.buildingCase?.caseNumber ?? '—' }}
|
||||||
</template>
|
</template>
|
||||||
|
<template #cell-pricePerKg="{ item }">
|
||||||
|
{{ formatPrice(item.pricePerKg) }}
|
||||||
|
</template>
|
||||||
|
<template #cell-finalPrice="{ item }">
|
||||||
|
{{ formatPrice(item.finalPrice) }}
|
||||||
|
</template>
|
||||||
</UiDataTable>
|
</UiDataTable>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -175,15 +187,17 @@ const arrivalDateFilter = singleDateFilter('arrivalDate[after]', 'arrivalDate[st
|
|||||||
const birthDateFilter = singleDateFilter('birthDate[after]', 'birthDate[strictly_before]')
|
const birthDateFilter = singleDateFilter('birthDate[after]', 'birthDate[strictly_before]')
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'nationalNumber', label: 'N° National', width: '160px' },
|
{ key: 'nationalNumber', label: 'N° National', width: '80px' },
|
||||||
{ key: 'workNumber', label: 'N° Travail', width: '85px' },
|
{ key: 'workNumber', label: 'N° Travail', width: '43px' },
|
||||||
{ key: 'sex', label: 'Sexe', width: '70px' },
|
{ 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: 'age', label: 'Age', width: '110px' },
|
||||||
{ key: 'breedCode', label: 'Race' },
|
{ key: 'breedCode', label: 'Race', width: '70px' },
|
||||||
{ key: 'buildingCase.building.label', label: 'Bâtiment', width: '1.5fr' },
|
{ key: 'buildingCase.building.label', label: 'Bâtiment', width: '75px' },
|
||||||
{ key: 'buildingCase.caseNumber', label: 'Case', width: '80px' },
|
{ key: 'buildingCase.caseNumber', label: 'Case', width: '60px' },
|
||||||
{ key: 'arrivalDate', label: 'Entrée le', width: '120px' }
|
{ 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(() => {
|
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 => {
|
const rowClass = (item: BovineData): string => {
|
||||||
if (item.ageMonths === null || item.ageMonths === undefined) return ''
|
if (item.ageMonths === null || item.ageMonths === undefined) return ''
|
||||||
if (item.ageMonths >= 24) return 'bg-violet-300 hover:bg-violet-400'
|
if (item.ageMonths >= 24) return 'bg-violet-300 hover:bg-violet-400'
|
||||||
|
|||||||
@@ -102,6 +102,12 @@
|
|||||||
<template #header-age>
|
<template #header-age>
|
||||||
<UiTextInput :model-value="''" placeholder="Age" size="compact" disabled />
|
<UiTextInput :model-value="''" placeholder="Age" size="compact" disabled />
|
||||||
</template>
|
</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 }">
|
<template #cell-birthDate="{ item }">
|
||||||
{{ formatDate(item.birthDate) }}
|
{{ formatDate(item.birthDate) }}
|
||||||
</template>
|
</template>
|
||||||
@@ -117,6 +123,12 @@
|
|||||||
<template #cell-buildingCase.caseNumber="{ item }">
|
<template #cell-buildingCase.caseNumber="{ item }">
|
||||||
{{ item.buildingCase?.caseNumber ?? '—' }}
|
{{ item.buildingCase?.caseNumber ?? '—' }}
|
||||||
</template>
|
</template>
|
||||||
|
<template #cell-pricePerKg="{ item }">
|
||||||
|
{{ formatPrice(item.pricePerKg) }}
|
||||||
|
</template>
|
||||||
|
<template #cell-finalPrice="{ item }">
|
||||||
|
{{ formatPrice(item.finalPrice) }}
|
||||||
|
</template>
|
||||||
</UiDataTable>
|
</UiDataTable>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -256,15 +268,17 @@ const arrivalDateFilter = singleDateFilter('arrivalDate[after]', 'arrivalDate[st
|
|||||||
const birthDateFilter = singleDateFilter('birthDate[after]', 'birthDate[strictly_before]')
|
const birthDateFilter = singleDateFilter('birthDate[after]', 'birthDate[strictly_before]')
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'nationalNumber', label: 'N° National', width: '160px' },
|
{ key: 'nationalNumber', label: 'N° National', width: '80px' },
|
||||||
{ key: 'workNumber', label: 'N° Travail', width: '85px' },
|
{ key: 'workNumber', label: 'N° Travail', width: '43px' },
|
||||||
{ key: 'sex', label: 'Sexe', width: '70px' },
|
{ 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: 'age', label: 'Age', width: '110px' },
|
||||||
{ key: 'breedCode', label: 'Race' },
|
{ key: 'breedCode', label: 'Race', width: '70px' },
|
||||||
{ key: 'buildingCase.building.label', label: 'Bâtiment', width: '1.5fr' },
|
{ key: 'buildingCase.building.label', label: 'Bâtiment', width: '75px' },
|
||||||
{ key: 'buildingCase.caseNumber', label: 'Case', width: '80px' },
|
{ key: 'buildingCase.caseNumber', label: 'Case', width: '60px' },
|
||||||
{ key: 'arrivalDate', label: 'Entrée le', width: '120px' }
|
{ 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) => {
|
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 => {
|
const rowClass = (item: BovineData): string => {
|
||||||
if (item.ageMonths === null || item.ageMonths === undefined) return ''
|
if (item.ageMonths === null || item.ageMonths === undefined) return ''
|
||||||
if (item.ageMonths >= 24) return 'bg-violet-300 hover:bg-violet-400'
|
if (item.ageMonths >= 24) return 'bg-violet-300 hover:bg-violet-400'
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export interface BovineData {
|
|||||||
id: number
|
id: number
|
||||||
nationalNumber: string
|
nationalNumber: string
|
||||||
receivedWeight: number | null
|
receivedWeight: number | null
|
||||||
|
pricePerKg: number | null
|
||||||
|
finalPrice: number | null
|
||||||
arrivalDate: string | null
|
arrivalDate: string | null
|
||||||
exitDate: string | null
|
exitDate: string | null
|
||||||
buildingCase: BovineBuildingCaseRef | null
|
buildingCase: BovineBuildingCaseRef | null
|
||||||
@@ -22,6 +24,7 @@ export interface BovineData {
|
|||||||
export type BovinePayload = {
|
export type BovinePayload = {
|
||||||
nationalNumber?: string
|
nationalNumber?: string
|
||||||
receivedWeight?: number | null
|
receivedWeight?: number | null
|
||||||
|
pricePerKg?: number | null
|
||||||
arrivalDate?: string | null
|
arrivalDate?: string | null
|
||||||
buildingCase?: string | null
|
buildingCase?: string | null
|
||||||
supplier?: string | null
|
supplier?: string | null
|
||||||
|
|||||||
31
migrations/Version20260424132554.php
Normal file
31
migrations/Version20260424132554.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?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 Version20260424132554 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 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,10 @@ class Bovine
|
|||||||
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
|
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
|
||||||
private ?int $receivedWeight = null;
|
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)]
|
#[ORM\Column(type: 'date_immutable', nullable: true)]
|
||||||
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
|
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
|
||||||
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
|
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
|
||||||
@@ -151,6 +155,28 @@ class Bovine
|
|||||||
return $this;
|
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
|
public function getArrivalDate(): ?DateTimeImmutable
|
||||||
{
|
{
|
||||||
return $this->arrivalDate;
|
return $this->arrivalDate;
|
||||||
|
|||||||
Reference in New Issue
Block a user