11 KiB
Pricing Display Implementation Plan
Overview
Implement comprehensive pricing display functionality that shows costs both inline in form options and in a detailed sidebar summary with real-time updates via HTMX.
Goals
- Inline pricing in form options (rooms, services) so users see costs while selecting
- Sidebar pricing summary with itemized breakdown and grand total
- Real-time updates via existing HTMX integration
- Professional UX with clear pricing integrated into the booking flow
Implementation Strategy
1. Core Pricing Service (src/Service/BookingPriceCalculatorService.php)
Purpose: Central service for all pricing calculations
Key Methods:
calculateRoomPricing(BookingCreateDto $bookingDto): arraycalculateServicePricing(BookingCreateDto $bookingDto): arraycalculateGrandTotal(BookingCreateDto $bookingDto): floatgetPricingBreakdown(BookingCreateDto $bookingDto): array
Room Pricing Logic:
// For each selected room: quantity × room.price
foreach ($bookingDto->getSelectedRooms() as $roomSelection) {
$room = $bookingDto->travel->rooms[$roomSelection->roomId];
$totalPrice = $roomSelection->quantity * $room->price;
}
Service Pricing Logic:
// For each participant's selected services
foreach ($bookingDto->getParticipants() as $participant) {
// Handle different service types (single vs multiple selection)
$serviceTotal += $participant->skiPass?->price ?? 0;
$serviceTotal += array_sum(array_map(fn($s) => $s->price, $participant->courses));
// etc.
}
2. Enhanced Form Field Options with Pricing ✅ COMPLETED
A. Room Selection Forms ✅ COMPLETED
File: src/Form/RoomSelectType.php
Enhancement: Choice labels include pricing information
'choice_label' => function (Room $room) {
$priceText = $room->price ? sprintf(' (€%.2f pro Nacht)', $room->price) : '';
return $room->label . $priceText;
}
B. Service Selection Forms ✅ COMPLETED
File: src/Form/Service/ParticipantFieldOptionsProvider.php
Enhancement: Service labels include pricing via formatServiceLabelWithPrice() method
- Zero-priced services display without price suffix (e.g., "Vollpension" instead of "Vollpension (€0,00)")
- Priced services show with formatted price (e.g., "Halbpension (€45,00)")
- All services consistently show quantity prefix (e.g., "1x", "2x")
Service Label Formatting Logic:
private function formatServiceLabelWithPrice(Service $service): string
{
$label = $service->label;
// Add price only if service has a cost
if ($service->price > 0) {
$label .= sprintf(' (€%.2f)', $service->price);
}
return $label;
}
Affected Service Types:
- Courses:
"Skikurs Anfänger (€25,00)" - Additional Services:
"Versicherung (€15,00)" - Ski Pass:
"5-Tage Skipass (€120,00)" - Rentals:
"Ski-Set (€30,00)" - Board:
"Halbpension (€45,00)"or"Vollpension"(if €0,00)
3. Enhanced Unified Booking Summary ✅ COMPLETED
A. Unified Summary Component ✅ COMPLETED
File: templates/booking/_summary.html.twig
The pricing summary has been integrated into the main booking summary, creating a single cohesive sticky sidebar that displays:
Travel Information Section:
- Travel details (destination, dates, duration)
- Room selections with quantities
- Participant count summary
Pricing Summary Section (integrated):
- Room pricing breakdown with quantities (e.g., "2x Doppelzimmer")
- Service selections by participant with pricing
- Grand total calculation
Key UX Improvements:
- Single sticky summary box (eliminated separate pricing sidebar)
- Clean, unified layout with consistent typography
- Service labels show quantity and pricing appropriately
- Zero-priced services display without price suffix
- Real-time updates via HTMX for all pricing changes
B. Deprecated Separate Pricing Template ✅ COMPLETED
File: templates/booking/_pricing_summary.html.twig - REMOVED
The separate pricing summary template was removed in favor of the integrated approach within the main summary template for better UX.
4. Service Layer Integration ✅ COMPLETED
A. Enhanced BookingService ✅ COMPLETED
File: src/Service/BookingService.php
Implemented Pricing Methods:
public function calculateServiceTotal(BookingCreateDto $bookingDto): float
{
// Groups services by type and calculates total pricing
$serviceGroups = $this->getServiceGroups($bookingDto);
return array_sum(array_column($serviceGroups, 'groupTotal'));
}
private function getServiceGroups(BookingCreateDto $bookingDto): array
{
// Groups selected services and calculates totals for pricing display
// Fixed bug: Uses 'groupTotal' instead of incorrect 'totalPrice' key
}
Key Bug Fix: The calculateServiceTotal() method was corrected to use groupTotal instead of totalPrice from the grouped array structure, ensuring selected services are properly included in the grand total calculation.
5. Controller Integration ✅ COMPLETED
A. Step 1 Controller Updates ✅ COMPLETED
File: src/Controller/Booking/CreateStep1Controller.php
Enhancement: Integrated pricing calculations into existing summary methods
// In index() method - pricing data included in summary
$summary = $this->bookingService->getRoomSummaryAndParticipantCount($bookingCreateDto);
// Pricing calculated and passed to template
// In roomSummary() method - HTMX endpoint includes pricing updates
$summary = $this->bookingService->getRoomSummaryAndParticipantCount($bookingCreateDto);
return $this->render('booking/_summary.html.twig', [
// existing data with integrated pricing
]);
B. Step 2 Controller Updates ✅ COMPLETED
File: src/Controller/Booking/CreateStep2Controller.php
Enhancement: Service pricing calculations integrated into form refresh
// In refresh() method - service selections update pricing in real-time
$summary = $this->bookingService->getRoomSummaryAndParticipantCount($bookingCreateDto);
// Service total calculation includes selected service pricing
C. HTMX Service Field Integration ✅ COMPLETED
Critical Fix: Service field HTMX triggers were moved from container elements to individual form inputs (checkboxes/radios) to ensure real-time updates work properly for expanded choice fields.
6. Error Handling & Edge Cases ✅ COMPLETED
Price Data Handling ✅ IMPLEMENTED:
- Zero prices handled gracefully (services show without price suffix)
- All prices rounded to 2 decimal places
- German number formatting implemented (comma as decimal separator)
- Null price handling via conditional display logic
Service Quantity Logic ✅ IMPLEMENTED:
- Single services (skiPass): price × 1 per participant
- Multiple services (courses, rentals): sum of all selected service prices per participant
- Service quantity consistently displayed (1x, 2x) in labels
- Field handlers store complete Service objects (not just IDs) for pricing access
Field Handler Data Fix ✅ COMPLETED:
- All service field handlers updated to store Service objects instead of service IDs
- Enables pricing calculator to access service price data
- Resolves issue where selected services were ignored in total calculations
Implementation Progress
✅ Completed
- Created implementation plan documentation
- Enhanced BookingService with pricing calculation methods (
calculateServiceTotal,getServiceGroups) - Updated ParticipantFieldOptionsProvider to add prices to service labels with smart formatting
- Updated RoomSelectType to add prices to room labels
- Integrated pricing into main summary template (unified approach)
- Updated controllers to include pricing data in summary calculations
- Fixed HTMX integration for service field real-time updates
- Fixed field handler data storage (Service objects vs IDs)
- Fixed pricing calculation bug (groupTotal vs totalPrice key)
- Implemented smart service label formatting (zero-price handling)
- Added consistent quantity display for all services
- Completed comprehensive testing of pricing display and HTMX integration
✅ Additional Improvements Completed
- Unified booking summary layout (eliminated separate pricing sidebar)
- Enhanced UX with sticky summary positioning
- Fixed service field HTMX triggers for expanded choice types
- Resolved linter warnings with proper type annotations
- Streamlined service label formatting logic
Technical Considerations
Performance
- All pricing calculations done in-memory (no database queries)
- Calculations triggered only on form changes via HTMX
- Efficient array operations for service aggregation
HTMX Integration ✅ COMPLETED
- Uses existing
#booking-summarytarget for seamless updates - Fixed service field triggers: HTMX attributes moved to individual form inputs for expanded choice fields
- Real-time pricing updates work reliably for all service selections
- Maintains current real-time update behavior with improved reliability
Styling
- CSS classes for pricing components:
.pricing-summary- Overall container.pricing-section- Room/service sections.pricing-item- Individual line items.pricing-total- Grand total display
Accessibility
- Proper semantic HTML structure
- ARIA labels for pricing information
- Screen reader friendly number formatting
Testing Strategy
Unit Tests
- Test pricing calculations with various room/service combinations
- Test edge cases (null prices, zero quantities)
- Test German number formatting
Integration Tests
- Test HTMX updates with pricing changes
- Test form submission with pricing data
- Test step navigation with pricing persistence
Manual Testing Scenarios
- Select rooms and verify inline pricing appears
- Change room quantities and verify total updates
- Add/remove services and verify pricing updates
- Navigate between steps and verify pricing persistence
- Test with services that have null/zero prices
Future Enhancements
Potential Additions
- Discounts/Promotions: Add discount calculation logic
- Currency Selection: Support multiple currencies
- Price History: Track pricing changes during booking session
- Export Pricing: Add pricing breakdown to booking confirmations
- Tax Calculations: Add VAT/tax breakdown if needed
Performance Optimizations
- Cache frequently calculated pricing data
- Optimize service aggregation algorithms
- Consider lazy loading for complex pricing scenarios
Last Updated: 2025-09-02
Status: ✅ Implementation completed successfully
Completed Features: Inline pricing, unified summary, HTMX integration, service field fixes, and comprehensive UX improvements