# Address Fields Implementation **Date:** 2025-10-06 **Status:** ✅ TESTED SUCCESSFULLY (2025-10-06) ## Overview Added participant address collection to the booking create flow. Address is mandatory for the applicant (first participant) and optional for others. ## Implementation ### 1. Models **ParticipantDto** (`src/Form/Model/ParticipantDto.php`): - Added `Address $address` property - Made email field required (`@Assert\NotBlank`) - Added constructor to initialize `Address` object - Removed `@Assert\NotNull` constraint (validation handled by ParticipantValidator) **Address** (`src/BusProNet/Model/Address.php`): - Already had `toPayload()` method for XML generation - Properties: street, postCode, city, district, country - No changes needed (existing model worked perfectly) ### 2. Forms **AddressType** (`src/Form/AddressType.php`) - NEW: ```php class AddressType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('street', TextType::class, [...]) ->add('postCode', TextType::class, [...]) ->add('city', TextType::class, [...]) ->add('country', CountryType::class, [ 'property' => 'country', 'preferred_choices' => ['DE', 'AT', 'CH'], ]); } } ``` **BookingParticipantType** (`src/Form/BookingParticipantType.php`): - Added address field after mobile field - `required => 0 === $participantIndex` (mandatory for applicant) - Added 'address' to `$baseFields` array for rebuild handling ### 3. Validation **ParticipantValidator** (`src/Validator/Constraints/ParticipantValidator.php`): - Added `assertApplicantAddressValid()` method - Validates street, postCode, city, country for applicant only - Uses `$participant->isApplicant()` to check if validation should run - Error messages: "Bitte angeben" for each missing field ### 4. Payload Generation **BookingDataProcessor** (`src/BusProNet/DataProcessor/BookingDataProcessor.php`): **Applicant section** (lines 451-454): ```php // Add address for applicant if (null !== $firstParticipant->address) { $payload['anmelder']['anschrift'] = $firstParticipant->address->toPayload(); } ``` **Participant list** (lines 481-488): ```php // Add address (always include structure, even if empty) $participantData['anschrift'] = $participant->address?->toPayload() ?? [ 'strasse' => null, 'plz' => null, 'ort' => null, 'ortsteil' => null, 'land' => null, ]; ``` **XML Output:** ```xml Gablonzer Straße 32 53359 Rheinbach D Gablonzer Straße 32 53359 Rheinbach D ``` ## Bug Fixes (Same Session) ### 1. Room Quantity Fix **Issue:** Room `@anzahl` was set to participant count instead of room quantity **Fix:** Use `roomSelections[].quantity` from step 1 (lines 576-603) ```php $roomQuantities = []; foreach ($bookingDto->roomSelections as $selection) { if ($selection->quantity > 0) { $roomQuantities[$selection->roomId] = $selection->quantity; } } $quantity = $roomQuantities[$roomId] ?? 1; ``` ### 2. Insurance Selection for Dependent Participants **Issue:** `ParticipantBulkInsuranceFieldHandler` cleared independent selections on every form submit **Fix:** Added `wasBulkInsurancePreviouslyEnabled()` check (lines 90, 144-176) ```php if (false === $isBulkEnabled && $this->wasBulkInsurancePreviouslyEnabled($bookingDto)) { $this->clearDependentParticipantsInsurance($bookingDto); } ``` ## Files Modified 1. `src/Form/Model/ParticipantDto.php` - Added address property, constructor, required email 2. `src/Form/AddressType.php` - NEW form type 3. `src/Form/BookingParticipantType.php` - Added address field 4. `src/Validator/Constraints/ParticipantValidator.php` - Added address validation 5. `src/BusProNet/DataProcessor/BookingDataProcessor.php` - Address in payload, room quantity fix, `isApplicant()` usage 6. `src/Form/Service/ParticipantBulkInsuranceFieldHandler.php` - Insurance clearing fix ## Testing ### Manual Testing Checklist: - [x] Applicant address required (all fields) - ✅ PASSED - [x] Dependent participant address optional - ✅ PASSED - [x] Country dropdown shows DE, AT, CH first - ✅ PASSED - [x] Address appears in XML for applicant - ✅ PASSED - [x] Address appears in XML for all participants (empty if not provided) - ✅ PASSED - [x] Email is mandatory for all participants - ✅ PASSED - [x] Insurance selection works for dependent participants - ✅ PASSED - [x] Room quantity matches selection from step 1 - ✅ PASSED ### Expected XML: - Applicant: Full address in `` - All participants: Address structure in `` (may be empty) - Room quantity: Matches quantity selected in step 1, not participant count ## Notes - Address object initialized in ParticipantDto constructor - Uses existing `Address::toPayload()` method for XML generation - CountryType provides nationality dropdown with German country codes - Validation runs via existing ParticipantValidator constraint - Template integration happens automatically via Symfony form system --- **Implementation Status:** ✅ TESTED SUCCESSFULLY **Code Quality:** ✅ PHP-CS-Fixer validated **Test Date:** 2025-10-06 ## Test Results **Test Date:** 2025-10-06 **Environment:** DDEV sandbox with BusProNet API **Validated Features:** - ✅ Applicant address validation (all fields mandatory) - ✅ Dependent participant address optional - ✅ Country dropdown with preferred choices (DE, AT, CH) - ✅ Address correctly included in XML payload for applicant - ✅ Address structure included for all participants (empty nodes for optional) - ✅ Email mandatory validation working for all participants - ✅ Mobile mandatory validation working for applicant only **Validated Bug Fixes:** - ✅ Room quantity using correct value from step 1 selections - ✅ Insurance selection working correctly for dependent participants - ✅ Pickup locations included with correct quantities **Result:** All address field requirements working correctly in production-like environment.