8.2 KiB
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
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
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 afterdisplayOrder) -
Step 1: Add the column, getter, and setter to Employee entity
Add after the displayOrder property (line 58):
#[ORM\Column(type: 'date_immutable', nullable: true)]
#[Groups(['employee:read'])]
private ?\DateTimeImmutable $entryDate = null;
Add getter and setter after setDisplayOrder() (after line 167):
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
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:
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):
$data->setEntryDate($startDate);
The full block becomes:
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
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
entryDateto the Employee DTO
In frontend/services/dto/employee.ts:24, add entryDate after displayOrder:
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):
<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
- Check the API response for an employee:
GET /api/employeesshould includeentryDatefield (confirms backendemployee:readgroup works) - Open the employee list page, hover over a card — entry date should appear in the overlay
- Create a new employee, verify the entry date shows the contract start date
- Existing employees without entry date should show "-"
- Step 4: Commit
git add frontend/services/dto/employee.ts frontend/pages/employees/index.vue
git commit -m "feat : affichage date d'entree dans la liste employes"