6f9d19bda3
Auto Tag Develop / tag (push) Successful in 7s
## Résumé Ajoute un bouton **Exporter** (admin uniquement) à droite du titre « Heures » qui génère un **PDF d'une journée**, regroupé par site, reprenant les colonnes de la vue Jour **sans la colonne « Valider »**. - Drawer : champ date (préremplit la date affichée) + cases à cocher des sites (préselectionnées sur le filtre courant). - Portée identique à l'écran : non-conducteurs, sous contrat à la date, sites cochés (lignes vides incluses). - Jour/Nuit/Total incluent le crédit d'absence et le crédit virtuel férié. ## Implémentation - Back : `WorkHourDayExport` (ApiResource) + `WorkHourDayExportProvider`, endpoint `GET /work-hours/day-export?workDate=&siteIds=` (ROLE_ADMIN). - Calcul des cellules mutualisé via `YearlyHoursExportBuilder::buildDayRowsForEmployees` (source unique de vérité). - Gabarit `templates/work-hour-day-export/print.html.twig` (A4 portrait compact). - Front : `HoursDayExportDrawer.vue` + câblage dans `pages/hours.vue`. - Docs : `doc/hours-day-export.md`, `documentation-content.ts`, `CLAUDE.md`. ## Tests - Test unitaire `YearlyHoursDayRowsTest` ajouté. - Suite complète verte : 173 tests, 359 assertions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #24 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
114 lines
4.2 KiB
PHP
114 lines
4.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Service\WorkHours;
|
|
|
|
use App\Entity\Contract;
|
|
use App\Entity\Employee;
|
|
use App\Entity\WorkHour;
|
|
use App\Repository\Contract\AbsenceReadRepositoryInterface;
|
|
use App\Repository\Contract\WorkHourReadRepositoryInterface;
|
|
use App\Service\Contracts\EmployeeContractResolver;
|
|
use App\Service\PublicHolidayServiceInterface;
|
|
use App\Service\WorkHours\AbsenceSegmentsResolver;
|
|
use App\Service\WorkHours\DailyReferenceMinutesResolver;
|
|
use App\Service\WorkHours\HolidayVirtualHoursResolver;
|
|
use App\Service\WorkHours\WorkedHoursCreditPolicy;
|
|
use App\Service\WorkHours\YearlyHoursExportBuilder;
|
|
use DateTimeImmutable;
|
|
use PHPUnit\Framework\TestCase;
|
|
use ReflectionProperty;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
final class YearlyHoursDayRowsTest extends TestCase
|
|
{
|
|
public function testTimeContractRowComputesHoursAndExcludesNoContract(): void
|
|
{
|
|
$date = new DateTimeImmutable('2026-06-08'); // lundi
|
|
|
|
$contract = new Contract();
|
|
$contract->setName('35h');
|
|
$contract->setTrackingMode(Contract::TRACKING_TIME);
|
|
$contract->setWeeklyHours(35);
|
|
|
|
$withContract = new Employee();
|
|
$withContract->setFirstName('Jean')->setLastName('Dupont');
|
|
$this->setEmployeeId($withContract, 1);
|
|
|
|
$noContract = new Employee();
|
|
$noContract->setFirstName('Paul')->setLastName('Martin');
|
|
$this->setEmployeeId($noContract, 2);
|
|
|
|
$workHour = new WorkHour();
|
|
$workHour->setEmployee($withContract)
|
|
->setWorkDate($date)
|
|
->setMorningFrom('08:00')->setMorningTo('12:00')
|
|
->setAfternoonFrom('13:00')->setAfternoonTo('17:00')
|
|
;
|
|
|
|
$workHourRepo = $this->createStub(WorkHourReadRepositoryInterface::class);
|
|
$workHourRepo->method('findByDateRangeAndEmployees')->willReturn([$workHour]);
|
|
|
|
$absenceRepo = $this->createStub(AbsenceReadRepositoryInterface::class);
|
|
$absenceRepo->method('findForPrint')->willReturn([]);
|
|
|
|
$contractResolver = $this->createStub(EmployeeContractResolver::class);
|
|
$contractResolver->method('resolveForEmployeesAndDays')->willReturn([
|
|
1 => ['2026-06-08' => $contract],
|
|
2 => ['2026-06-08' => null],
|
|
]);
|
|
$contractResolver->method('resolveIsDriverForEmployeesAndDays')->willReturn([
|
|
1 => ['2026-06-08' => false],
|
|
2 => ['2026-06-08' => false],
|
|
]);
|
|
$contractResolver->method('resolveWorkDaysMinutesForEmployeesAndDays')->willReturn([
|
|
1 => ['2026-06-08' => null],
|
|
2 => ['2026-06-08' => null],
|
|
]);
|
|
|
|
$holidayService = $this->createStub(PublicHolidayServiceInterface::class);
|
|
$holidayService->method('getHolidaysDayByYears')->willReturn([]);
|
|
|
|
// No holiday on this Monday → virtual credit resolves to 0 via the real resolver.
|
|
$virtualResolver = new HolidayVirtualHoursResolver(
|
|
new DailyReferenceMinutesResolver(),
|
|
$holidayService,
|
|
$contractResolver,
|
|
);
|
|
|
|
$builder = new YearlyHoursExportBuilder(
|
|
$workHourRepo,
|
|
$absenceRepo,
|
|
$contractResolver,
|
|
new AbsenceSegmentsResolver(),
|
|
new WorkedHoursCreditPolicy($contractResolver, new DailyReferenceMinutesResolver()),
|
|
$holidayService,
|
|
$virtualResolver,
|
|
);
|
|
|
|
$rows = $builder->buildDayRowsForEmployees([$withContract, $noContract], $date);
|
|
|
|
self::assertCount(1, $rows);
|
|
self::assertSame(1, $rows[0]['employeeId']);
|
|
self::assertSame('Dupont Jean', $rows[0]['employeeName']);
|
|
self::assertSame('08:00', $rows[0]['morningFrom']);
|
|
self::assertSame('17:00', $rows[0]['afternoonTo']);
|
|
self::assertSame('8h', $rows[0]['total']);
|
|
self::assertSame('8h', $rows[0]['dayHours']);
|
|
self::assertSame('', $rows[0]['nightHours']);
|
|
self::assertNull($rows[0]['statut']);
|
|
self::assertNull($rows[0]['statutColor']);
|
|
self::assertFalse($rows[0]['isWeekend']);
|
|
}
|
|
|
|
private function setEmployeeId(Employee $employee, int $id): void
|
|
{
|
|
$ref = new ReflectionProperty(Employee::class, 'id');
|
|
$ref->setAccessible(true);
|
|
$ref->setValue($employee, $id);
|
|
}
|
|
}
|