feat: feedback export to excel

addresses #869dv9k9z
This commit is contained in:
2026-09-01 10:20:31 +02:00
parent 2f747fc22f
commit 496a707a16
7 changed files with 835 additions and 79 deletions
@@ -0,0 +1,122 @@
<?php
namespace App\Tests\Service\Export;
use App\Config\SeasonCalendar;
use App\Entity\Feedback;
use App\Entity\FeedbackSet;
use App\Entity\Teamer;
use App\Entity\User;
use App\Service\Export\FeedbackExporter;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class FeedbackExporterTest extends WebTestCase
{
public function testExportsOneSheetPerFeedbackSetWithExpandedQuestions(): void
{
$projectDir = static::getContainer()->getParameter('kernel.project_dir');
/** @var SeasonCalendar $seasonCalendar */
$seasonCalendar = static::getContainer()->get(SeasonCalendar::class);
$exporter = new FeedbackExporter($seasonCalendar);
$author = (new User())->setFirstName('Carmen')->setLastName('Bär');
$teamerMueller = (new Teamer())->setFirstName('Mara')->setLastName('Müller');
$teamerSchmidt = (new Teamer())->setFirstName('Timo')->setLastName('Schmidt');
$guideSet = (new FeedbackSet())->setName('Reiseleiter-Feedback');
$careSet = (new FeedbackSet())->setName('Betreuer-Feedback');
// Müller, season 2024/25, guide set - full question list.
$first = $this->makeFeedback($author, $teamerMueller, $guideSet, new \DateTimeImmutable('2024-12-01'), [
['rating' => 'Pünktlichkeit', 'mark' => 5],
['rating' => 'Freundlichkeit', 'mark' => 4],
]);
$first->setComment('Alles bestens');
// Müller again, same season, guide set - extra question the first row lacks.
$second = $this->makeFeedback($author, $teamerMueller, $guideSet, new \DateTimeImmutable('2025-02-20'), [
['rating' => 'Pünktlichkeit', 'mark' => 3],
['rating' => 'Freundlichkeit', 'mark' => 4],
['rating' => 'Sprachkenntnisse', 'mark' => 2],
]);
// Schmidt, care set, no assignment date -> season falls back to a dash.
$third = $this->makeFeedback($author, $teamerSchmidt, $careSet, null, [
['rating' => 'Organisation', 'mark' => 4],
]);
$spreadsheet = $exporter->export([$first, $second, $third]);
self::assertSame(2, $spreadsheet->getSheetCount());
self::assertSame('Reiseleiter-Feedback', $spreadsheet->getSheet(0)->getTitle());
self::assertSame('Betreuer-Feedback', $spreadsheet->getSheet(1)->getTitle());
$guideSheet = $spreadsheet->getSheet(0);
$header = $guideSheet->rangeToArray('A1:Q1')[0];
self::assertSame('Name', $header[0]);
self::assertSame('Saison', $header[2]);
// Fixed block is 11 columns (A-K); questions follow in first-seen order.
self::assertSame('Pünktlichkeit', $header[11]);
self::assertSame('Freundlichkeit', $header[12]);
self::assertSame('Sprachkenntnisse', $header[13]);
self::assertSame('Ø Bewertung', $header[14]);
self::assertSame('Kommentar (öffentlich)', $header[15]);
self::assertSame('Kommentar (intern)', $header[16]);
// Row 2 = first Müller feedback: mark present, missing question is blank.
self::assertSame('Müller', $guideSheet->getCell('A2')->getValue());
self::assertSame('Saison 2024/25', $guideSheet->getCell('C2')->getValue());
self::assertSame(5, $guideSheet->getCell('L2')->getValue());
self::assertSame(4, $guideSheet->getCell('M2')->getValue());
self::assertNull($guideSheet->getCell('N2')->getValue());
self::assertSame(4.5, $guideSheet->getCell('O2')->getValue());
self::assertSame('Alles bestens', $guideSheet->getCell('P2')->getValue());
// Row 3 = second Müller feedback: the third question now carries a mark.
self::assertSame(2, $guideSheet->getCell('N3')->getValue());
$careSheet = $spreadsheet->getSheet(1);
self::assertSame('—', $careSheet->getCell('C2')->getValue());
self::assertSame('Organisation', $careSheet->rangeToArray('A1:O1')[0][11]);
(new Xlsx($spreadsheet))->save($projectDir.'/temp/feedback-export.xlsx');
}
/**
* @param list<array{rating: string, mark: int}> $ratings
*/
private function makeFeedback(
User $author,
Teamer $teamer,
FeedbackSet $set,
?\DateTimeImmutable $dateFrom,
array $ratings,
): Feedback {
$feedback = (new Feedback($author))
->setStatus(Feedback::STATUS_PUBLISHED)
->setTeamer($teamer)
->setFeedbackSet($set)
->setDestinationName('Skireise')
->setHotel('Unterkunft')
->setHotelCode('ABC123')
->setJobProfileName('Allrounder')
->setRatings($ratings)
;
$feedback->setCreatedAt(new \DateTimeImmutable('2025-03-01'));
$feedback->setAverageRating();
if (null !== $dateFrom) {
$feedback
->setAssignmentDateFrom($dateFrom)
->setAssignmentDateTo($dateFrom->modify('+2 months'))
;
}
return $feedback;
}
}