Files
SIRH/docs/superpowers/plans/2026-03-12-employee-entry-date.md
2026-03-12 12:30:13 +01:00

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"
```