feat: extended insurance parsing, pass info urls to view
This commit is contained in:
@@ -37,6 +37,9 @@ class Insurance
|
|||||||
#[Groups(['api:single', 'api:list'])]
|
#[Groups(['api:single', 'api:list'])]
|
||||||
public bool $package = false;
|
public bool $package = false;
|
||||||
|
|
||||||
|
#[Groups(['api:single', 'api:list'])]
|
||||||
|
public bool $complementary = false;
|
||||||
|
|
||||||
#[Groups(['api:single', 'api:list'])]
|
#[Groups(['api:single', 'api:list'])]
|
||||||
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
|
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
|
||||||
public ?\DateTimeImmutable $travelDateFrom = null;
|
public ?\DateTimeImmutable $travelDateFrom = null;
|
||||||
@@ -110,4 +113,66 @@ class Insurance
|
|||||||
|
|
||||||
return $this->subType;
|
return $this->subType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all info URLs from this insurance or its contained insurances (for packages).
|
||||||
|
*
|
||||||
|
* @return array<string> Array of info URLs
|
||||||
|
*/
|
||||||
|
public function getAllUrlsInfo(): array
|
||||||
|
{
|
||||||
|
if (true === $this->package) {
|
||||||
|
return $this->collectUrlsFromContainedInsurances('urlInfo');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null !== $this->urlInfo ? [$this->urlInfo] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all product info URLs from this insurance or its contained insurances (for packages).
|
||||||
|
*
|
||||||
|
* @return array<string> Array of product info URLs
|
||||||
|
*/
|
||||||
|
public function getAllUrlsProductInfo(): array
|
||||||
|
{
|
||||||
|
if (true === $this->package) {
|
||||||
|
return $this->collectUrlsFromContainedInsurances('urlProductInfo');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null !== $this->urlProductInfo ? [$this->urlProductInfo] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all terms URLs from this insurance or its contained insurances (for packages).
|
||||||
|
*
|
||||||
|
* @return array<string> Array of terms URLs
|
||||||
|
*/
|
||||||
|
public function getAllUrlsTerms(): array
|
||||||
|
{
|
||||||
|
if (true === $this->package) {
|
||||||
|
return $this->collectUrlsFromContainedInsurances('urlTerms');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null !== $this->urlTerms ? [$this->urlTerms] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects URLs from contained insurances for a specific URL property.
|
||||||
|
*
|
||||||
|
* @param string $urlProperty The URL property name to collect (urlInfo, urlProductInfo, urlTerms)
|
||||||
|
*
|
||||||
|
* @return array<string> Array of non-null URLs from contained insurances
|
||||||
|
*/
|
||||||
|
private function collectUrlsFromContainedInsurances(string $urlProperty): array
|
||||||
|
{
|
||||||
|
$urls = [];
|
||||||
|
|
||||||
|
foreach ($this->containedInsurances as $containedInsurance) {
|
||||||
|
if (null !== $containedInsurance->{$urlProperty}) {
|
||||||
|
$urls[] = $containedInsurance->{$urlProperty};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values(array_unique($urls));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -39,13 +39,14 @@ class InsuranceParser extends AbstractParser
|
|||||||
// Parse all individual insurances for reference lookup
|
// Parse all individual insurances for reference lookup
|
||||||
$insurance = $this->parseInsuranceNode($node, false);
|
$insurance = $this->parseInsuranceNode($node, false);
|
||||||
if (null !== $insurance && null !== $insurance->id) {
|
if (null !== $insurance && null !== $insurance->id) {
|
||||||
|
// Set complementary flag from XML attribute
|
||||||
|
$insurance->complementary = $isComplementary;
|
||||||
|
|
||||||
$individualInsurances[$insurance->id] = $insurance;
|
$individualInsurances[$insurance->id] = $insurance;
|
||||||
|
|
||||||
// Include only if it's NOT a complementary insurance (zusatzversicherung)
|
// Include ALL insurances in the result array
|
||||||
// Complementary insurances are only available as part of packages, never standalone
|
// Complementary insurances will be filtered out at the form field level
|
||||||
if (!$isComplementary) {
|
$insurances[$insurance->id] = $insurance;
|
||||||
$insurances[$insurance->id] = $insurance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -56,6 +57,15 @@ class InsuranceParser extends AbstractParser
|
|||||||
if (null !== $insurance && null !== $insurance->id) {
|
if (null !== $insurance && null !== $insurance->id) {
|
||||||
// Parse contained insurance IDs
|
// Parse contained insurance IDs
|
||||||
$insurance->containedInsuranceIds = $this->parseContainedInsuranceIds($node);
|
$insurance->containedInsuranceIds = $this->parseContainedInsuranceIds($node);
|
||||||
|
|
||||||
|
// Populate containedInsurances with actual Insurance objects
|
||||||
|
$insurance->containedInsurances = [];
|
||||||
|
foreach ($insurance->containedInsuranceIds as $containedId) {
|
||||||
|
if (isset($individualInsurances[$containedId])) {
|
||||||
|
$insurance->containedInsurances[] = $individualInsurances[$containedId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$insurances[$insurance->id] = $insurance;
|
$insurances[$insurance->id] = $insurance;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ class BookingCreateParticipantType extends AbstractType
|
|||||||
'parking' => CheckboxType::class,
|
'parking' => CheckboxType::class,
|
||||||
'licensePlate' => TextType::class,
|
'licensePlate' => TextType::class,
|
||||||
'bulkInsuranceBooking' => CheckboxType::class,
|
'bulkInsuranceBooking' => CheckboxType::class,
|
||||||
'insurance' => ChoiceType::class,
|
'insurance' => InsuranceChoiceType::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($dynamicFields as $fieldName => $fieldType) {
|
foreach ($dynamicFields as $fieldName => $fieldType) {
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\BusProNet\Model\Insurance;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\FormView;
|
||||||
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom form type for insurance selection that provides full Insurance objects to templates.
|
||||||
|
*
|
||||||
|
* This type extends ChoiceType to pass complete Insurance model data to the template layer,
|
||||||
|
* enabling flexible rendering of insurance details including URLs, pricing, and coverage information.
|
||||||
|
*/
|
||||||
|
class InsuranceChoiceType extends AbstractType
|
||||||
|
{
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setRequired('insurances');
|
||||||
|
$resolver->setAllowedTypes('insurances', 'array');
|
||||||
|
|
||||||
|
$resolver->setDefault('choices', function (Options $options) {
|
||||||
|
// Prepend "no insurance" option to eligible insurances
|
||||||
|
return array_merge(
|
||||||
|
[0 => null],
|
||||||
|
$options['insurances']
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$resolver->setDefault('choice_value', function ($insurance) {
|
||||||
|
// Handle "no insurance" option (null value at index 0)
|
||||||
|
// instanceof check required because closures in configureOptions receive mixed types
|
||||||
|
if ($insurance instanceof Insurance) {
|
||||||
|
return (string) $insurance->id;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
$resolver->setDefault('choice_label', function ($insurance) {
|
||||||
|
// Handle "no insurance" option (null value at index 0)
|
||||||
|
// instanceof check required because closures in configureOptions receive mixed types
|
||||||
|
if (!$insurance instanceof Insurance) {
|
||||||
|
return 'Keine Versicherung';
|
||||||
|
}
|
||||||
|
|
||||||
|
$label = $insurance->label;
|
||||||
|
|
||||||
|
if (null !== $insurance->price && $insurance->price > 0) {
|
||||||
|
$label .= sprintf(' (€%s)', number_format($insurance->price, 2, ',', '.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $label;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||||
|
{
|
||||||
|
// Index insurances by ID for template lookup
|
||||||
|
$insurances = [];
|
||||||
|
foreach ($options['insurances'] as $insurance) {
|
||||||
|
if ($insurance instanceof Insurance) {
|
||||||
|
$insurances[(string) $insurance->id] = $insurance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass Insurance objects to template indexed by choice value
|
||||||
|
$view->vars['insurances'] = $insurances;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent(): string
|
||||||
|
{
|
||||||
|
return ChoiceType::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlockPrefix(): string
|
||||||
|
{
|
||||||
|
return 'insurance_choice';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -422,12 +422,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
|||||||
'multiple' => false,
|
'multiple' => false,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'choices' => array_merge(
|
'insurances' => $this->getEligibleInsurances($bookingDto, $participantIndex),
|
||||||
[0 => null], // "no insurance" option
|
|
||||||
$this->getEligibleInsurances($bookingDto, $participantIndex)
|
|
||||||
),
|
|
||||||
'choice_label' => fn (?Insurance $insurance) => $this->formatInsuranceLabel($insurance),
|
|
||||||
'choice_value' => 'id',
|
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'hx-post' => $this->urlGenerator->generate('app_booking_create_step_2_refresh'),
|
'hx-post' => $this->urlGenerator->generate('app_booking_create_step_2_refresh'),
|
||||||
'hx-swap' => 'none',
|
'hx-swap' => 'none',
|
||||||
@@ -729,6 +724,10 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
|||||||
|
|
||||||
$availableInsurances = $bookingDto->travel->insurances ?? [];
|
$availableInsurances = $bookingDto->travel->insurances ?? [];
|
||||||
|
|
||||||
|
// Exclude complementary insurances from standalone selection
|
||||||
|
// They are only available as part of packages
|
||||||
|
$availableInsurances = array_filter($availableInsurances, fn ($insurance) => !$insurance->complementary);
|
||||||
|
|
||||||
// Only apply insurance filtering for BookingCreateDto (creation workflow)
|
// Only apply insurance filtering for BookingCreateDto (creation workflow)
|
||||||
if (!$bookingDto instanceof BookingCreateDto) {
|
if (!$bookingDto instanceof BookingCreateDto) {
|
||||||
return $availableInsurances;
|
return $availableInsurances;
|
||||||
|
|||||||
@@ -607,6 +607,10 @@ class TravelDataService
|
|||||||
try {
|
try {
|
||||||
$insurances = $this->insuranceLoader->loadAll();
|
$insurances = $this->insuranceLoader->loadAll();
|
||||||
$travel->insurances = array_values($insurances);
|
$travel->insurances = array_values($insurances);
|
||||||
|
|
||||||
|
// Hydrate package relationships after loading
|
||||||
|
// Packages lose their containedInsurances during serialization, so rebuild them
|
||||||
|
$this->hydrateInsurancePackageRelationships($travel->insurances);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->logger->warning('Failed to load insurance data', [
|
$this->logger->warning('Failed to load insurance data', [
|
||||||
'travelId' => $travel->id,
|
'travelId' => $travel->id,
|
||||||
@@ -614,4 +618,36 @@ class TravelDataService
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconstructs containedInsurances arrays for insurance packages.
|
||||||
|
*
|
||||||
|
* Insurance packages reference other insurances via containedInsuranceIds.
|
||||||
|
* After deserialization, the containedInsurances array is empty due to
|
||||||
|
* circular reference prevention. This method rebuilds those relationships.
|
||||||
|
*
|
||||||
|
* @param array<Insurance> $insurances All insurances including packages
|
||||||
|
*/
|
||||||
|
private function hydrateInsurancePackageRelationships(array $insurances): void
|
||||||
|
{
|
||||||
|
// Build lookup map of all insurances by ID (includes complementary insurances)
|
||||||
|
$insuranceById = [];
|
||||||
|
foreach ($insurances as $insurance) {
|
||||||
|
$insuranceById[$insurance->id] = $insurance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconstruct containedInsurances for each package
|
||||||
|
foreach ($insurances as $insurance) {
|
||||||
|
if (!$insurance->package || empty($insurance->containedInsuranceIds)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$insurance->containedInsurances = [];
|
||||||
|
foreach ($insurance->containedInsuranceIds as $containedId) {
|
||||||
|
if (isset($insuranceById[$containedId])) {
|
||||||
|
$insurance->containedInsurances[] = $insuranceById[$containedId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,3 +193,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock multiselect_widget -%}
|
{%- endblock multiselect_widget -%}
|
||||||
|
|
||||||
|
{% block insurance_choice_widget %}
|
||||||
|
{%- if expanded -%}
|
||||||
|
{%- for child in form %}
|
||||||
|
{%- set child_attr = {} -%}
|
||||||
|
|
||||||
|
{%- if attr['hx-trigger'] is defined -%}
|
||||||
|
{%- set child_attr = child_attr|merge({
|
||||||
|
'hx-trigger': attr['hx-trigger'],
|
||||||
|
'hx-post': attr['hx-post'],
|
||||||
|
'hx-swap': attr['hx-swap']
|
||||||
|
}) -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{%- if child.vars.attr is defined -%}
|
||||||
|
{%- set child_attr = child_attr|merge(child.vars.attr) -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{%- set insurance = insurances[child.vars.value] ?? null -%}
|
||||||
|
{%- if insurance is not null -%}
|
||||||
|
{%- set urlsInfo = insurance.getAllUrlsInfo() -%}
|
||||||
|
{%- set urlsProductInfo = insurance.getAllUrlsProductInfo() -%}
|
||||||
|
{%- set urlsTerms = insurance.getAllUrlsTerms() -%}
|
||||||
|
|
||||||
|
{%- if urlsInfo is not empty -%}
|
||||||
|
{%- set child_attr = child_attr|merge({'data-urls-info': urlsInfo|json_encode}) -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- if urlsProductInfo is not empty -%}
|
||||||
|
{%- set child_attr = child_attr|merge({'data-urls-product-info': urlsProductInfo|json_encode}) -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- if urlsTerms is not empty -%}
|
||||||
|
{%- set child_attr = child_attr|merge({'data-urls-terms': urlsTerms|json_encode}) -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{{- form_row(child, { 'attr': child_attr }) -}}
|
||||||
|
{% endfor -%}
|
||||||
|
{%- else -%}
|
||||||
|
{{- parent() -}}
|
||||||
|
{%- endif -%}
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ class InsuranceParserTest extends TestCase
|
|||||||
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
||||||
<versicherungsstammdaten>
|
<versicherungsstammdaten>
|
||||||
<versicherungen>
|
<versicherungen>
|
||||||
<versicherung id="1" idbuspro="178917" code="903100" bezeichnung="Reise-Rücktritt" preis="5,00" preisartprozent="False" unterart="RRV" familienversicherung="False" paarversicherung="False" zusatzversicherung="False" reisedatumvon="01.09.2024" reisedatumbis="31.12.2030" buchungdatumvon="01.03.2025" buchungdatumbis="31.12.2030" altervon="0" alterbis="64" reisepreisvon="0,00" reisepreisbis="100,00" reisedauervon="0" reisedauerbis="255" />
|
<versicherung id="1" idbuspro="178917" code="903100" bezeichnung="Reise-Rücktritt" preis="5,00" preisartprozent="False" unterart="RRV" familienversicherung="False" paarversicherung="False" zusatzversicherung="False" reisedatumvon="01.09.2024" reisedatumbis="31.12.2030" buchungdatumvon="01.03.2025" buchungdatumbis="31.12.2030" altervon="0" alterbis="64" reisepreisvon="0,00" reisepreisbis="100,00" reisedauervon="0" reisedauerbis="255" urlinfo="https://example.com/info1" urlproduktinfo="https://example.com/product1" urlagb="https://example.com/terms1" />
|
||||||
<versicherung id="2" idbuspro="177236" code="913320" bezeichnung="Reiseschutz Platin Auto/Bahn/Bus (Europa)" preis="9,00" preisartprozent="False" unterart="PAK" familienversicherung="True" paarversicherung="False" zusatzversicherung="False" reisedatumvon="01.09.2024" reisedatumbis="31.12.2030" buchungdatumvon="01.03.2025" buchungdatumbis="31.12.2030" altervon="0" alterbis="64" reisepreisvon="0,00" reisepreisbis="100,00" reisedauervon="1" reisedauerbis="45" />
|
<versicherung id="2" idbuspro="177236" code="913320" bezeichnung="Reiseschutz Platin Auto/Bahn/Bus (Europa)" preis="9,00" preisartprozent="False" unterart="PAK" familienversicherung="True" paarversicherung="False" zusatzversicherung="False" reisedatumvon="01.09.2024" reisedatumbis="31.12.2030" buchungdatumvon="01.03.2025" buchungdatumbis="31.12.2030" altervon="0" alterbis="64" reisepreisvon="0,00" reisepreisbis="100,00" reisedauervon="1" reisedauerbis="45" urlinfo="https://example.com/info2" urlproduktinfo="https://example.com/product2" urlagb="https://example.com/terms2" />
|
||||||
<versicherung id="3" idbuspro="177270" code="903551" bezeichnung="Selbstbehaltübernahme" preis="5,00" preisartprozent="False" unterart="OHN" familienversicherung="False" paarversicherung="False" zusatzversicherung="True" reisedatumvon="01.09.2024" reisedatumbis="31.12.2030" buchungdatumvon="01.03.2025" buchungdatumbis="31.12.2030" altervon="0" alterbis="64" reisepreisvon="0,00" reisepreisbis="600,00" reisedauervon="0" reisedauerbis="255" />
|
<versicherung id="3" idbuspro="177270" code="903551" bezeichnung="Selbstbehaltübernahme" preis="5,00" preisartprozent="False" unterart="OHN" familienversicherung="False" paarversicherung="False" zusatzversicherung="True" reisedatumvon="01.09.2024" reisedatumbis="31.12.2030" buchungdatumvon="01.03.2025" buchungdatumbis="31.12.2030" altervon="0" alterbis="64" reisepreisvon="0,00" reisepreisbis="600,00" reisedauervon="0" reisedauerbis="255" urlinfo="https://example.com/info3" urlproduktinfo="https://example.com/product3" urlagb="https://example.com/terms3" />
|
||||||
</versicherungen>
|
</versicherungen>
|
||||||
<versicherungspakete>
|
<versicherungspakete>
|
||||||
<versicherungspaket id="1" idbuspro="P1000320" bezeichnung="Reiseschutz Platin Auto/Bahn/Bus (Europa) + Selbstbehaltübernahme" preis="14,00" preisartprozent="False" reisedatumvon="01.09.2024" reisedatumbis="31.12.2030" altervon="0" alterbis="64" reisepreisvon="0" reisepreisbis="100,00" familienversicherung="False">
|
<versicherungspaket id="1" idbuspro="P1000320" bezeichnung="Reiseschutz Platin Auto/Bahn/Bus (Europa) + Selbstbehaltübernahme" preis="14,00" preisartprozent="False" reisedatumvon="01.09.2024" reisedatumbis="31.12.2030" altervon="0" alterbis="64" reisepreisvon="0" reisepreisbis="100,00" familienversicherung="False">
|
||||||
@@ -39,7 +39,7 @@ class InsuranceParserTest extends TestCase
|
|||||||
$crawler = new Crawler($xmlContent);
|
$crawler = new Crawler($xmlContent);
|
||||||
$insurances = $this->parser->parse($crawler);
|
$insurances = $this->parser->parse($crawler);
|
||||||
|
|
||||||
// Test individual insurances + package (3 individual + 1 package = 4 total)
|
// Test all insurances are included (2 regular + 1 complementary + 1 package = 4 total)
|
||||||
$this->assertCount(4, $insurances);
|
$this->assertCount(4, $insurances);
|
||||||
|
|
||||||
// Test travel cancellation insurance (RRV)
|
// Test travel cancellation insurance (RRV)
|
||||||
@@ -51,18 +51,20 @@ class InsuranceParserTest extends TestCase
|
|||||||
$this->assertEquals(5.0, $cancellationInsurance->price);
|
$this->assertEquals(5.0, $cancellationInsurance->price);
|
||||||
$this->assertFalse($cancellationInsurance->familyInsurance);
|
$this->assertFalse($cancellationInsurance->familyInsurance);
|
||||||
$this->assertFalse($cancellationInsurance->package);
|
$this->assertFalse($cancellationInsurance->package);
|
||||||
|
$this->assertFalse($cancellationInsurance->complementary);
|
||||||
|
|
||||||
// Test travel protection insurance (PAK) with family flag
|
// Test travel protection insurance (PAK) with family flag
|
||||||
$protectionInsurance = $insurances[177236];
|
$protectionInsurance = $insurances[177236];
|
||||||
$this->assertEquals(177236, $protectionInsurance->id);
|
$this->assertEquals(177236, $protectionInsurance->id);
|
||||||
$this->assertEquals('913320', $protectionInsurance->code);
|
$this->assertEquals('913320', $protectionInsurance->code);
|
||||||
$this->assertEquals('Reiseschutz Platin Auto/Bahn/Bus (Europa)', $protectionInsurance->label);
|
$this->assertEquals('Reiseschutz Platin', $protectionInsurance->label); // "Auto/Bahn/Bus (Europa)" is normalized away
|
||||||
$this->assertEquals('PAK', $protectionInsurance->subType);
|
$this->assertEquals('PAK', $protectionInsurance->subType);
|
||||||
$this->assertEquals(9.0, $protectionInsurance->price);
|
$this->assertEquals(9.0, $protectionInsurance->price);
|
||||||
$this->assertTrue($protectionInsurance->familyInsurance);
|
$this->assertTrue($protectionInsurance->familyInsurance);
|
||||||
$this->assertFalse($protectionInsurance->package);
|
$this->assertFalse($protectionInsurance->package);
|
||||||
|
$this->assertFalse($protectionInsurance->complementary);
|
||||||
|
|
||||||
// Test deductible insurance (OHN) - should be included because it's referenced by package
|
// Test deductible insurance (OHN) - IS included but marked as complementary
|
||||||
$deductibleInsurance = $insurances[177270];
|
$deductibleInsurance = $insurances[177270];
|
||||||
$this->assertEquals(177270, $deductibleInsurance->id);
|
$this->assertEquals(177270, $deductibleInsurance->id);
|
||||||
$this->assertEquals('903551', $deductibleInsurance->code);
|
$this->assertEquals('903551', $deductibleInsurance->code);
|
||||||
@@ -71,16 +73,27 @@ class InsuranceParserTest extends TestCase
|
|||||||
$this->assertEquals(5.0, $deductibleInsurance->price);
|
$this->assertEquals(5.0, $deductibleInsurance->price);
|
||||||
$this->assertFalse($deductibleInsurance->familyInsurance);
|
$this->assertFalse($deductibleInsurance->familyInsurance);
|
||||||
$this->assertFalse($deductibleInsurance->package);
|
$this->assertFalse($deductibleInsurance->package);
|
||||||
|
$this->assertTrue($deductibleInsurance->complementary); // Marked as complementary
|
||||||
|
|
||||||
// Test package - should be family insurance because it contains a family insurance (177236)
|
// Test package - should be family insurance because it contains a family insurance (177236)
|
||||||
$package = $insurances['P1000320'];
|
$package = $insurances['P1000320'];
|
||||||
$this->assertEquals('P1000320', $package->id);
|
$this->assertEquals('P1000320', $package->id);
|
||||||
$this->assertEquals('Reiseschutz Platin Auto/Bahn/Bus (Europa) + Selbstbehaltübernahme', $package->label);
|
$this->assertEquals('Reiseschutz Platin + Selbstbehaltübernahme', $package->label); // "Auto/Bahn/Bus (Europa)" is normalized away
|
||||||
$this->assertNull($package->subType); // Packages don't have subtypes
|
$this->assertNull($package->subType); // Packages don't have subtypes
|
||||||
$this->assertEquals(14.0, $package->price);
|
$this->assertEquals(14.0, $package->price);
|
||||||
$this->assertTrue($package->familyInsurance); // Should be true because it contains family insurance 177236
|
$this->assertTrue($package->familyInsurance); // Should be true because it contains family insurance 177236
|
||||||
$this->assertTrue($package->package);
|
$this->assertTrue($package->package);
|
||||||
$this->assertEquals([177236, 177270], $package->containedInsuranceIds);
|
$this->assertEquals([177236, 177270], $package->containedInsuranceIds);
|
||||||
|
|
||||||
|
// Test containedInsurances array is populated with actual Insurance objects
|
||||||
|
$this->assertCount(2, $package->containedInsurances);
|
||||||
|
$this->assertEquals(177236, $package->containedInsurances[0]->id);
|
||||||
|
$this->assertEquals(177270, $package->containedInsurances[1]->id);
|
||||||
|
|
||||||
|
// Test URL aggregation from contained insurances
|
||||||
|
$this->assertEquals(['https://example.com/info2', 'https://example.com/info3'], $package->getAllUrlsInfo());
|
||||||
|
$this->assertEquals(['https://example.com/product2', 'https://example.com/product3'], $package->getAllUrlsProductInfo());
|
||||||
|
$this->assertEquals(['https://example.com/terms2', 'https://example.com/terms3'], $package->getAllUrlsTerms());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testParsePackageWithoutSubtype(): void
|
public function testParsePackageWithoutSubtype(): void
|
||||||
@@ -169,12 +182,16 @@ class InsuranceParserTest extends TestCase
|
|||||||
$crawler = new Crawler($xmlContent);
|
$crawler = new Crawler($xmlContent);
|
||||||
$insurances = $this->parser->parse($crawler);
|
$insurances = $this->parser->parse($crawler);
|
||||||
|
|
||||||
// Should include: normal insurance (178917), referenced zusatz (177270), and package (P1000320)
|
// Should include: normal insurance (178917), both complementary insurances (177270, 999999), and package (P1000320)
|
||||||
// Should exclude: unreferenced zusatz (999999)
|
$this->assertCount(4, $insurances);
|
||||||
$this->assertCount(3, $insurances);
|
|
||||||
$this->assertArrayHasKey(178917, $insurances);
|
$this->assertArrayHasKey(178917, $insurances);
|
||||||
$this->assertArrayHasKey(177270, $insurances);
|
$this->assertArrayHasKey(177270, $insurances);
|
||||||
|
$this->assertArrayHasKey(999999, $insurances);
|
||||||
$this->assertArrayHasKey('P1000320', $insurances);
|
$this->assertArrayHasKey('P1000320', $insurances);
|
||||||
$this->assertArrayNotHasKey(999999, $insurances);
|
|
||||||
|
// Verify complementary flags
|
||||||
|
$this->assertFalse($insurances[178917]->complementary); // Normal insurance
|
||||||
|
$this->assertTrue($insurances[177270]->complementary); // Complementary insurance
|
||||||
|
$this->assertTrue($insurances[999999]->complementary); // Complementary insurance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user