This commit is contained in:
207
frontend/components/employees/BonusTab.vue
Normal file
207
frontend/components/employees/BonusTab.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<section class="mt-8">
|
||||
<div class="overflow-hidden bg-white">
|
||||
<div
|
||||
class="grid grid-cols-3 border border-black bg-tertiary-500 px-6 py-3 text-[20px] font-semibold text-black rounded-t-md">
|
||||
<p>Mois</p>
|
||||
<p>Montant €</p>
|
||||
<p>Commentaire</p>
|
||||
</div>
|
||||
<div v-if="bonuses.length === 0" class="px-6 py-4 text-[20px] font-bold text-primary-500 border-x border-b border-primary-500 rounded-b-md">
|
||||
Aucune prime.
|
||||
</div>
|
||||
<div v-else class="border-x border-b border-primary-500 rounded-b-md">
|
||||
<div
|
||||
v-for="item in bonuses"
|
||||
:key="item.id"
|
||||
class="grid grid-cols-3 border-b border-primary-500 px-6 py-3 text-md font-bold text-primary-500 last:border-b-0 cursor-pointer hover:bg-tertiary-500"
|
||||
@click="onOpenEditDrawer(item)"
|
||||
>
|
||||
<p>{{ formatMonth(item.month) }}</p>
|
||||
<p>{{ item.amount }} €</p>
|
||||
<p>{{ item.comment ?? '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center mb-4 mt-8">
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-[200px] items-center justify-center gap-2 rounded-md bg-primary-500 px-4 py-2 text-md text-white disabled:cursor-not-allowed disabled:opacity-50"
|
||||
@click="onOpenCreateDrawer"
|
||||
>
|
||||
+ Ajouter
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<AppDrawer v-model="isDrawerOpen" :title="isEditing ? 'Modification prime' : 'Nouvelle prime'">
|
||||
<form class="space-y-4" @submit.prevent="onSubmit">
|
||||
<div>
|
||||
<label class="text-md font-semibold text-neutral-700" for="bonus-month">
|
||||
Mois <span class="text-red-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="bonus-month"
|
||||
v-model="form.month"
|
||||
type="month"
|
||||
class="capitalize mt-2 w-full rounded-md border border-neutral-300 px-3 py-2 text-base text-neutral-900 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-secondary-500/20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="text-md font-semibold text-neutral-700" for="bonus-amount">
|
||||
Montant (€) <span class="text-red-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="bonus-amount"
|
||||
v-model.number="form.amount"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
class="mt-2 w-full rounded-md border border-neutral-300 px-3 py-2 text-base text-neutral-900 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-secondary-500/20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="text-md font-semibold text-neutral-700" for="bonus-comment">
|
||||
Commentaire
|
||||
</label>
|
||||
<textarea
|
||||
id="bonus-comment"
|
||||
v-model="form.comment"
|
||||
rows="3"
|
||||
class="mt-2 w-full rounded-md border border-neutral-300 px-3 py-2 text-base text-neutral-900 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-secondary-500/20"
|
||||
placeholder="Commentaire..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="isEditing" class="grid grid-cols-2 gap-3 pt-2">
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center justify-center rounded-md bg-red-500 px-4 py-2 text-md font-semibold text-white hover:bg-red-600"
|
||||
@click="onDelete"
|
||||
>
|
||||
Supprimer
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="flex items-center justify-center rounded-md bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
:disabled="!isFormValid"
|
||||
>
|
||||
Modifier
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="flex justify-center pt-2">
|
||||
<button
|
||||
type="submit"
|
||||
class="flex w-[200px] items-center justify-center gap-2 rounded-md bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
:disabled="!isFormValid"
|
||||
>
|
||||
+ Ajouter
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</AppDrawer>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Bonus } from '~/services/dto/bonus'
|
||||
import AppDrawer from '~/components/AppDrawer.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
bonuses: Bonus[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'create', data: { month: string; amount: number; comment?: string }): void
|
||||
(event: 'update', id: number, data: { month: string; amount: number; comment?: string }): void
|
||||
(event: 'delete', id: number): void
|
||||
}>()
|
||||
|
||||
const isDrawerOpen = ref(false)
|
||||
const isEditing = ref(false)
|
||||
const editingItem = ref<Bonus | null>(null)
|
||||
|
||||
const currentYearMonth = () => {
|
||||
const now = new Date()
|
||||
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
const form = reactive({
|
||||
month: currentYearMonth(),
|
||||
amount: 0,
|
||||
comment: ''
|
||||
})
|
||||
|
||||
const isFormValid = computed(() => {
|
||||
return form.month && form.amount > 0
|
||||
})
|
||||
|
||||
const monthLabels: Record<number, string> = {
|
||||
1: 'Janvier',
|
||||
2: 'Février',
|
||||
3: 'Mars',
|
||||
4: 'Avril',
|
||||
5: 'Mai',
|
||||
6: 'Juin',
|
||||
7: 'Juillet',
|
||||
8: 'Août',
|
||||
9: 'Septembre',
|
||||
10: 'Octobre',
|
||||
11: 'Novembre',
|
||||
12: 'Décembre'
|
||||
}
|
||||
|
||||
const formatMonth = (dateStr: string): string => {
|
||||
const date = new Date(dateStr)
|
||||
if (Number.isNaN(date.getTime())) return dateStr
|
||||
const month = date.getMonth() + 1
|
||||
const year = date.getFullYear()
|
||||
return `${monthLabels[month]} ${year}`
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
form.month = currentYearMonth()
|
||||
form.amount = 0
|
||||
form.comment = ''
|
||||
}
|
||||
|
||||
const onOpenCreateDrawer = () => {
|
||||
isEditing.value = false
|
||||
editingItem.value = null
|
||||
resetForm()
|
||||
isDrawerOpen.value = true
|
||||
}
|
||||
|
||||
const onOpenEditDrawer = (item: Bonus) => {
|
||||
isEditing.value = true
|
||||
editingItem.value = item
|
||||
form.month = item.month.substring(0, 7)
|
||||
form.amount = item.amount
|
||||
form.comment = item.comment ?? ''
|
||||
isDrawerOpen.value = true
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
const data = {
|
||||
month: `${form.month}-01`,
|
||||
amount: form.amount,
|
||||
comment: form.comment || undefined
|
||||
}
|
||||
|
||||
if (isEditing.value && editingItem.value) {
|
||||
emit('update', editingItem.value.id, data)
|
||||
} else {
|
||||
emit('create', data)
|
||||
}
|
||||
isDrawerOpen.value = false
|
||||
}
|
||||
|
||||
const onDelete = () => {
|
||||
if (!editingItem.value) return
|
||||
const ok = window.confirm('Supprimer cette prime ?')
|
||||
if (!ok) return
|
||||
emit('delete', editingItem.value.id)
|
||||
isDrawerOpen.value = false
|
||||
}
|
||||
</script>
|
||||
62
frontend/composables/useEmployeeBonus.ts
Normal file
62
frontend/composables/useEmployeeBonus.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { Ref } from 'vue'
|
||||
import type { Bonus } from '~/services/dto/bonus'
|
||||
import type { Employee } from '~/services/dto/employee'
|
||||
import {
|
||||
listBonuses,
|
||||
createBonus,
|
||||
updateBonus,
|
||||
deleteBonus
|
||||
} from '~/services/bonuses'
|
||||
|
||||
export const useEmployeeBonus = (employee: Ref<Employee | null>, reloadEmployee: () => Promise<void>) => {
|
||||
const bonuses = ref<Bonus[]>([])
|
||||
const isBonusLoading = ref(false)
|
||||
const bonusDataLoaded = ref(false)
|
||||
|
||||
const loadBonusData = async () => {
|
||||
if (!employee.value || isBonusLoading.value) return
|
||||
isBonusLoading.value = true
|
||||
try {
|
||||
bonuses.value = await listBonuses(employee.value.id)
|
||||
bonusDataLoaded.value = true
|
||||
} finally {
|
||||
isBonusLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const resetLoaded = () => {
|
||||
bonusDataLoaded.value = false
|
||||
}
|
||||
|
||||
const submitCreateBonus = async (data: { month: string; amount: number; comment?: string }) => {
|
||||
if (!employee.value) return
|
||||
await createBonus({
|
||||
employeeId: employee.value.id,
|
||||
month: data.month,
|
||||
amount: data.amount,
|
||||
comment: data.comment
|
||||
})
|
||||
await reloadEmployee()
|
||||
}
|
||||
|
||||
const submitUpdateBonus = async (id: number, data: { month: string; amount: number; comment?: string }) => {
|
||||
await updateBonus(id, data)
|
||||
await reloadEmployee()
|
||||
}
|
||||
|
||||
const submitDeleteBonus = async (id: number) => {
|
||||
await deleteBonus(id)
|
||||
await reloadEmployee()
|
||||
}
|
||||
|
||||
return {
|
||||
bonuses,
|
||||
isBonusLoading,
|
||||
bonusDataLoaded,
|
||||
loadBonusData,
|
||||
resetLoaded,
|
||||
submitCreateBonus,
|
||||
submitUpdateBonus,
|
||||
submitDeleteBonus
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ export const useEmployeeDetailPage = () => {
|
||||
const route = useRoute()
|
||||
const employee = ref<Employee | null>(null)
|
||||
const isLoading = ref(false)
|
||||
const activeTab = ref<'contract' | 'leave' | 'rtt' | 'mileage'>('contract')
|
||||
const activeTab = ref<'contract' | 'leave' | 'rtt' | 'mileage' | 'bonus'>('contract')
|
||||
|
||||
const showLeaveTab = computed(() => employee.value?.currentContractNature !== 'INTERIM')
|
||||
const showRttTab = computed(() => employee.value?.contract?.type !== CONTRACT_TYPES.FORFAIT)
|
||||
@@ -39,6 +39,7 @@ export const useEmployeeDetailPage = () => {
|
||||
leave.resetLoaded()
|
||||
rtt.resetLoaded()
|
||||
mileage.resetLoaded()
|
||||
bonus.resetLoaded()
|
||||
|
||||
if (activeTab.value === 'leave' && showLeaveTab.value) {
|
||||
await leave.loadLeaveData()
|
||||
@@ -46,6 +47,8 @@ export const useEmployeeDetailPage = () => {
|
||||
await rtt.loadRttData()
|
||||
} else if (activeTab.value === 'mileage') {
|
||||
await mileage.loadMileageData()
|
||||
} else if (activeTab.value === 'bonus') {
|
||||
await bonus.loadBonusData()
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
@@ -56,6 +59,7 @@ export const useEmployeeDetailPage = () => {
|
||||
const leave = useEmployeeLeave(employee, loadEmployee)
|
||||
const rtt = useEmployeeRtt(employee, loadEmployee)
|
||||
const mileage = useEmployeeMileage(employee, loadEmployee)
|
||||
const bonus = useEmployeeBonus(employee, loadEmployee)
|
||||
|
||||
watch(activeTab, (tab) => {
|
||||
if (tab === 'leave' && !leave.leaveDataLoaded.value && showLeaveTab.value) {
|
||||
@@ -64,6 +68,8 @@ export const useEmployeeDetailPage = () => {
|
||||
rtt.loadRttData()
|
||||
} else if (tab === 'mileage' && !mileage.mileageDataLoaded.value) {
|
||||
mileage.loadMileageData()
|
||||
} else if (tab === 'bonus' && !bonus.bonusDataLoaded.value) {
|
||||
bonus.loadBonusData()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -82,6 +88,7 @@ export const useEmployeeDetailPage = () => {
|
||||
...contract,
|
||||
...leave,
|
||||
...rtt,
|
||||
...mileage
|
||||
...mileage,
|
||||
...bonus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@
|
||||
"create": "Impossible de créer le frais kilométrique.",
|
||||
"update": "Impossible de mettre à jour le frais kilométrique.",
|
||||
"delete": "Impossible de supprimer le frais kilométrique."
|
||||
},
|
||||
"bonus": {
|
||||
"create": "Impossible de créer la prime.",
|
||||
"update": "Impossible de mettre à jour la prime.",
|
||||
"delete": "Impossible de supprimer la prime."
|
||||
}
|
||||
},
|
||||
"success": {
|
||||
@@ -77,6 +82,11 @@
|
||||
"create": "Frais kilométrique créé.",
|
||||
"update": "Frais kilométrique mis à jour.",
|
||||
"delete": "Frais kilométrique supprimé."
|
||||
},
|
||||
"bonus": {
|
||||
"create": "Prime créée.",
|
||||
"update": "Prime mise à jour.",
|
||||
"delete": "Prime supprimée."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,16 @@
|
||||
<Icon name="mdi:car-outline" size="24" class="align-self"/>
|
||||
Frais Kms
|
||||
</button>
|
||||
<button
|
||||
class="pb-2 border-b-2 flex items-center gap-3"
|
||||
:class="activeTab === 'bonus'
|
||||
? 'border-primary-500 text-primary-500'
|
||||
: 'border-transparent text-primary-500/50 hover:text-primary-500'"
|
||||
@click="activeTab = 'bonus'"
|
||||
>
|
||||
<Icon name="mdi:money-100" size="24" class="align-self"/>
|
||||
Prime
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min-h-0 flex-1">
|
||||
@@ -141,6 +151,19 @@
|
||||
@delete="submitDeleteMileage"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="activeTab === 'bonus'" class="h-full">
|
||||
<div v-if="isBonusLoading" class="mt-6 rounded-lg border border-neutral-200 bg-white p-6 text-md text-neutral-600">
|
||||
Chargement...
|
||||
</div>
|
||||
<EmployeesBonusTab
|
||||
v-else
|
||||
class="h-full"
|
||||
:bonuses="bonuses"
|
||||
@create="submitCreateBonus"
|
||||
@update="submitUpdateBonus"
|
||||
@delete="submitDeleteBonus"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -203,7 +226,12 @@ const {
|
||||
mileageApiBase,
|
||||
submitCreateMileage,
|
||||
submitUpdateMileage,
|
||||
submitDeleteMileage
|
||||
submitDeleteMileage,
|
||||
bonuses,
|
||||
isBonusLoading,
|
||||
submitCreateBonus,
|
||||
submitUpdateBonus,
|
||||
submitDeleteBonus
|
||||
} = useEmployeeDetailPage()
|
||||
|
||||
useHead(() => ({
|
||||
|
||||
54
frontend/services/bonuses.ts
Normal file
54
frontend/services/bonuses.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { Bonus } from './dto/bonus'
|
||||
import { extractItems } from '~/utils/api'
|
||||
|
||||
export const listBonuses = async (employeeId: number) => {
|
||||
const api = useApi()
|
||||
const data = await api.get<Bonus[] | { 'hydra:member'?: Bonus[] }>(
|
||||
'/bonuses',
|
||||
{ employee: `/api/employees/${employeeId}` },
|
||||
{ toast: false }
|
||||
)
|
||||
return extractItems<Bonus>(data)
|
||||
}
|
||||
|
||||
export const createBonus = async (data: {
|
||||
employeeId: number
|
||||
month: string
|
||||
amount: number
|
||||
comment?: string
|
||||
}) => {
|
||||
const api = useApi()
|
||||
return api.post<Bonus>('/bonuses', {
|
||||
employee: `/api/employees/${data.employeeId}`,
|
||||
month: data.month,
|
||||
amount: data.amount,
|
||||
comment: data.comment
|
||||
}, {
|
||||
toastSuccessKey: 'success.bonus.create',
|
||||
toastErrorKey: 'errors.bonus.create'
|
||||
})
|
||||
}
|
||||
|
||||
export const updateBonus = async (id: number, data: {
|
||||
month: string
|
||||
amount: number
|
||||
comment?: string
|
||||
}) => {
|
||||
const api = useApi()
|
||||
return api.patch<Bonus>(`/bonuses/${id}`, {
|
||||
month: data.month,
|
||||
amount: data.amount,
|
||||
comment: data.comment
|
||||
}, {
|
||||
toastSuccessKey: 'success.bonus.update',
|
||||
toastErrorKey: 'errors.bonus.update'
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteBonus = async (id: number) => {
|
||||
const api = useApi()
|
||||
return api.delete(`/bonuses/${id}`, {}, {
|
||||
toastSuccessKey: 'success.bonus.delete',
|
||||
toastErrorKey: 'errors.bonus.delete'
|
||||
})
|
||||
}
|
||||
7
frontend/services/dto/bonus.ts
Normal file
7
frontend/services/dto/bonus.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type Bonus = {
|
||||
id: number
|
||||
month: string
|
||||
amount: number
|
||||
comment: string | null
|
||||
createdAt: string
|
||||
}
|
||||
31
migrations/Version20260313151220.php
Normal file
31
migrations/Version20260313151220.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260313151220 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Create bonuses table';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE TABLE bonuses (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, employee_id INT NOT NULL, month DATE NOT NULL, amount DOUBLE PRECISION NOT NULL, comment TEXT DEFAULT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY (id))');
|
||||
$this->addSql('CREATE INDEX IDX_8535CFD28C03F15C ON bonuses (employee_id)');
|
||||
$this->addSql('COMMENT ON COLUMN bonuses.created_at IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN bonuses.month IS \'(DC2Type:date_immutable)\'');
|
||||
$this->addSql('ALTER TABLE bonuses ADD CONSTRAINT FK_8535CFD28C03F15C FOREIGN KEY (employee_id) REFERENCES employees (id) NOT DEFERRABLE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE bonuses DROP CONSTRAINT FK_8535CFD28C03F15C');
|
||||
$this->addSql('DROP TABLE bonuses');
|
||||
}
|
||||
}
|
||||
145
src/Entity/Bonus.php
Normal file
145
src/Entity/Bonus.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
|
||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
||||
use ApiPlatform\Metadata\ApiFilter;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Repository\BonusRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new Get(
|
||||
security: "is_granted('ROLE_USER')"
|
||||
),
|
||||
new GetCollection(
|
||||
security: "is_granted('ROLE_USER')"
|
||||
),
|
||||
new Post(
|
||||
security: "is_granted('ROLE_ADMIN')"
|
||||
),
|
||||
new Patch(
|
||||
security: "is_granted('ROLE_ADMIN')"
|
||||
),
|
||||
new Delete(
|
||||
security: "is_granted('ROLE_ADMIN')"
|
||||
),
|
||||
],
|
||||
normalizationContext: [
|
||||
'groups' => ['bonus:read', 'employee:read'],
|
||||
'datetime_format' => 'Y-m-d',
|
||||
],
|
||||
denormalizationContext: [
|
||||
'groups' => ['bonus:write'],
|
||||
'datetime_format' => 'Y-m-d',
|
||||
],
|
||||
order: ['month' => 'DESC'],
|
||||
paginationEnabled: false,
|
||||
)]
|
||||
#[ApiFilter(DateFilter::class, properties: ['month'])]
|
||||
#[ApiFilter(SearchFilter::class, properties: ['employee' => 'exact'])]
|
||||
#[ORM\Entity(repositoryClass: BonusRepository::class)]
|
||||
#[ORM\Table(name: 'bonuses')]
|
||||
class Bonus
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
#[Groups(['bonus:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Employee::class)]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
#[Groups(['bonus:read', 'bonus:write'])]
|
||||
private ?Employee $employee = null;
|
||||
|
||||
#[ORM\Column(type: 'date_immutable')]
|
||||
#[Groups(['bonus:read', 'bonus:write'])]
|
||||
private ?DateTimeImmutable $month = null;
|
||||
|
||||
#[ORM\Column(type: 'float')]
|
||||
#[Groups(['bonus:read', 'bonus:write'])]
|
||||
private float $amount = 0;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
#[Groups(['bonus:read', 'bonus:write'])]
|
||||
private ?string $comment = null;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
#[Groups(['bonus:read'])]
|
||||
private DateTimeImmutable $createdAt;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->createdAt = new DateTimeImmutable();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getEmployee(): ?Employee
|
||||
{
|
||||
return $this->employee;
|
||||
}
|
||||
|
||||
public function setEmployee(?Employee $employee): self
|
||||
{
|
||||
$this->employee = $employee;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMonth(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->month;
|
||||
}
|
||||
|
||||
public function setMonth(?DateTimeImmutable $month): self
|
||||
{
|
||||
$this->month = $month;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAmount(): float
|
||||
{
|
||||
return $this->amount;
|
||||
}
|
||||
|
||||
public function setAmount(float $amount): self
|
||||
{
|
||||
$this->amount = $amount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getComment(): ?string
|
||||
{
|
||||
return $this->comment;
|
||||
}
|
||||
|
||||
public function setComment(?string $comment): self
|
||||
{
|
||||
$this->comment = $comment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
}
|
||||
20
src/Repository/BonusRepository.php
Normal file
20
src/Repository/BonusRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Bonus;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Bonus>
|
||||
*/
|
||||
final class BonusRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Bonus::class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user