274 lines
8.2 KiB
Markdown
274 lines
8.2 KiB
Markdown
# Employee Entry Date Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
**Goal:** Add an `entryDate` field to Employee, automatically populated from `contractStartDate` at creation.
|
|
|
|
**Architecture:** New nullable `DATE` column on `employees` table. The `EmployeeWriteProcessor` sets `entryDate` from the first contract period's start date during employee creation. Exposed read-only in the API. No fallback needed — existing employees will be updated manually in prod DB.
|
|
|
|
**Tech Stack:** Symfony/Doctrine (backend), Nuxt/Vue/TypeScript (frontend)
|
|
|
|
---
|
|
|
|
## File Structure
|
|
|
|
| File | Action | Responsibility |
|
|
|------|--------|----------------|
|
|
| `src/Entity/Employee.php` | Modify | Add `entryDate` column + getter/setter, expose in `employee:read` |
|
|
| `src/State/EmployeeWriteProcessor.php` | Modify | Set `entryDate` from `contractStartDate` on creation |
|
|
| `migrations/Version20260312120000.php` | Create | Add `entry_date` column to `employees` table |
|
|
| `tests/State/EmployeeWriteProcessorTest.php` | Modify | Assert `entryDate` is set on new employee |
|
|
| `frontend/services/dto/employee.ts` | Modify | Add `entryDate` field to `Employee` type |
|
|
| `frontend/pages/employees/index.vue` | Modify | Display entry date in employee list |
|
|
|
|
---
|
|
|
|
## Chunk 1: Backend
|
|
|
|
### Task 1: Migration
|
|
|
|
**Files:**
|
|
- Create: `migrations/Version20260312120000.php`
|
|
|
|
- [ ] **Step 1: Create the migration file**
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace DoctrineMigrations;
|
|
|
|
use Doctrine\DBAL\Schema\Schema;
|
|
use Doctrine\Migrations\AbstractMigration;
|
|
|
|
final class Version20260312120000 extends AbstractMigration
|
|
{
|
|
public function getDescription(): string
|
|
{
|
|
return 'Add entry_date column to employees table';
|
|
}
|
|
|
|
public function up(Schema $schema): void
|
|
{
|
|
$this->addSql('ALTER TABLE employees ADD entry_date DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\'');
|
|
}
|
|
|
|
public function down(Schema $schema): void
|
|
{
|
|
$this->addSql('ALTER TABLE employees DROP entry_date');
|
|
}
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Run migration**
|
|
|
|
Run: `php bin/console doctrine:migrations:migrate --no-interaction`
|
|
Expected: Migration applied successfully
|
|
|
|
- [ ] **Step 3: Commit**
|
|
|
|
```bash
|
|
git add migrations/Version20260312120000.php
|
|
git commit -m "feat : ajout colonne entry_date sur employees"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 2: Entity — add `entryDate` property
|
|
|
|
**Files:**
|
|
- Modify: `src/Entity/Employee.php:56-61` (insert after `displayOrder`)
|
|
|
|
- [ ] **Step 1: Add the column, getter, and setter to Employee entity**
|
|
|
|
Add after the `displayOrder` property (line 58):
|
|
|
|
```php
|
|
#[ORM\Column(type: 'date_immutable', nullable: true)]
|
|
#[Groups(['employee:read'])]
|
|
private ?\DateTimeImmutable $entryDate = null;
|
|
```
|
|
|
|
Add getter and setter after `setDisplayOrder()` (after line 167):
|
|
|
|
```php
|
|
public function getEntryDate(): ?\DateTimeImmutable
|
|
{
|
|
return $this->entryDate;
|
|
}
|
|
|
|
public function setEntryDate(?\DateTimeImmutable $entryDate): self
|
|
{
|
|
$this->entryDate = $entryDate;
|
|
|
|
return $this;
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Verify schema is in sync**
|
|
|
|
Run: `php bin/console doctrine:schema:validate`
|
|
Expected: OK (or only existing unrelated warnings)
|
|
|
|
- [ ] **Step 3: Commit**
|
|
|
|
```bash
|
|
git add src/Entity/Employee.php
|
|
git commit -m "feat : ajout propriete entryDate sur Employee"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 3: Set `entryDate` on employee creation
|
|
|
|
**Files:**
|
|
- Modify: `src/State/EmployeeWriteProcessor.php:60-71`
|
|
- Modify: `tests/State/EmployeeWriteProcessorTest.php`
|
|
|
|
- [ ] **Step 1: Write the failing test**
|
|
|
|
Add `use ApiPlatform\Metadata\Post;` to the imports at the top of the test file (alongside the existing `Delete` and `Patch` imports).
|
|
|
|
Then add this test method to `EmployeeWriteProcessorTest`:
|
|
|
|
```php
|
|
public function testSetsEntryDateOnNewEmployee(): void
|
|
{
|
|
$employee = new Employee();
|
|
$employee->setFirstName('Jane');
|
|
$employee->setLastName('Doe');
|
|
$employee->setContractStartDate('2026-04-01');
|
|
$employee->setContractNature('CDI');
|
|
|
|
$contract = new Contract()
|
|
->setName('35h')
|
|
->setTrackingMode(Contract::TRACKING_TIME)
|
|
->setWeeklyHours(35);
|
|
$employee->setContract($contract);
|
|
|
|
$persistProcessor = $this->createMock(ProcessorInterface::class);
|
|
$removeProcessor = $this->createStub(ProcessorInterface::class);
|
|
$entityManager = $this->createStub(EntityManagerInterface::class);
|
|
$periodRepository = $this->createStub(EmployeeContractPeriodReadRepositoryInterface::class);
|
|
$changeRequestFactory = new EmployeeContractChangeRequestFactory();
|
|
$periodManager = $this->createMock(EmployeeContractPeriodManagerInterface::class);
|
|
|
|
$persistProcessor
|
|
->expects(self::once())
|
|
->method('process')
|
|
->willReturn($employee);
|
|
|
|
$periodManager
|
|
->expects(self::once())
|
|
->method('ensureContractPeriodExists');
|
|
|
|
$processor = new EmployeeWriteProcessor(
|
|
$persistProcessor,
|
|
$removeProcessor,
|
|
$entityManager,
|
|
$periodRepository,
|
|
$changeRequestFactory,
|
|
$periodManager
|
|
);
|
|
|
|
$processor->process($employee, new Post());
|
|
|
|
self::assertNotNull($employee->getEntryDate());
|
|
self::assertSame('2026-04-01', $employee->getEntryDate()->format('Y-m-d'));
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Run test to verify it fails**
|
|
|
|
Run: `php bin/phpunit tests/State/EmployeeWriteProcessorTest.php --filter=testSetsEntryDateOnNewEmployee`
|
|
Expected: FAIL — `entryDate` is null
|
|
|
|
- [ ] **Step 3: Implement — set entryDate in EmployeeWriteProcessor**
|
|
|
|
In `src/State/EmployeeWriteProcessor.php`, inside the `if ($isNew)` block (line 60-71), add **before** `return $result;` (line 71):
|
|
|
|
```php
|
|
$data->setEntryDate($startDate);
|
|
```
|
|
|
|
The full block becomes:
|
|
|
|
```php
|
|
if ($isNew) {
|
|
$startDate = $changeRequest->contractStartDate ?? new DateTimeImmutable('1970-01-01');
|
|
$nature = $changeRequest->contractNature ?? ContractNature::CDI;
|
|
$this->periodManager->ensureContractPeriodExists(
|
|
employee: $data,
|
|
contract: $currentContract,
|
|
startDate: $startDate,
|
|
endDate: $changeRequest->contractEndDate,
|
|
nature: $nature
|
|
);
|
|
|
|
$data->setEntryDate($startDate);
|
|
|
|
return $result;
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 4: Run test to verify it passes**
|
|
|
|
Run: `php bin/phpunit tests/State/EmployeeWriteProcessorTest.php --filter=testSetsEntryDateOnNewEmployee`
|
|
Expected: PASS
|
|
|
|
- [ ] **Step 5: Run all EmployeeWriteProcessor tests**
|
|
|
|
Run: `php bin/phpunit tests/State/EmployeeWriteProcessorTest.php`
|
|
Expected: All tests pass (no regression)
|
|
|
|
- [ ] **Step 6: Commit**
|
|
|
|
```bash
|
|
git add src/State/EmployeeWriteProcessor.php tests/State/EmployeeWriteProcessorTest.php
|
|
git commit -m "feat : remplissage automatique entryDate a la creation employe"
|
|
```
|
|
|
|
---
|
|
|
|
## Chunk 2: Frontend
|
|
|
|
### Task 4: Update frontend DTO and display entry date
|
|
|
|
**Files:**
|
|
- Modify: `frontend/services/dto/employee.ts:14-25`
|
|
- Modify: `frontend/pages/employees/index.vue`
|
|
|
|
- [ ] **Step 1: Add `entryDate` to the Employee DTO**
|
|
|
|
In `frontend/services/dto/employee.ts:24`, add `entryDate` after `displayOrder`:
|
|
|
|
```typescript
|
|
displayOrder?: number
|
|
entryDate?: string | null
|
|
```
|
|
|
|
- [ ] **Step 2: Display entry date in the employee card hover overlay**
|
|
|
|
In `frontend/pages/employees/index.vue`, inside the hover overlay `<div>` (line 49-54), add a new line after the "Site" line (after line 53):
|
|
|
|
```vue
|
|
<p><strong>Entree :</strong> {{ employee.entryDate ? employee.entryDate.split('-').reverse().join('/') : '-' }}</p>
|
|
```
|
|
|
|
This uses string splitting instead of `new Date()` to avoid timezone parsing issues with date-only strings.
|
|
|
|
- [ ] **Step 3: Verify in browser**
|
|
|
|
1. Check the API response for an employee: `GET /api/employees` should include `entryDate` field (confirms backend `employee:read` group works)
|
|
2. Open the employee list page, hover over a card — entry date should appear in the overlay
|
|
3. Create a new employee, verify the entry date shows the contract start date
|
|
4. Existing employees without entry date should show "-"
|
|
|
|
- [ ] **Step 4: Commit**
|
|
|
|
```bash
|
|
git add frontend/services/dto/employee.ts frontend/pages/employees/index.vue
|
|
git commit -m "feat : affichage date d'entree dans la liste employes"
|
|
```
|