Files
myep/docs/PICKUP_PRICING_IMPLEMENTATION.md
T

7.7 KiB

Pickup Pricing Implementation

Overview

This document describes the implementation of pricing display for pickup choice labels in the MyEP Next Booking system. The implementation extends the existing service pricing pattern to include pickup locations, with special handling for negative prices as discounts.

Implementation Details

Enhanced Pricing Support

The pickup pricing implementation follows the established pattern used for other bookable services while adding specific support for discount pricing:

New Method: formatPickupLabelWithPrice()

private function formatPickupLabelWithPrice(?Pickup $pickup): string
{
    if (null === $pickup) {
        return '';
    }

    $label = $this->formatPickupLabel($pickup);

    // Handle zero prices (no display)
    if (null === $pickup->price || 0.0 === $pickup->price) {
        return $label;
    }

    // Handle negative prices (discounts)
    if ($pickup->price < 0) {
        return sprintf('%s (-%s€ Rabatt)', $label, number_format(abs($pickup->price), 2, ',', '.'));
    }

    // Handle positive prices (costs)
    return sprintf('%s (€%s)', $label, number_format($pickup->price, 2, ',', '.'));
}

Updated Field Options

Both outbound and inbound pickup fields now use the enhanced pricing formatter:

// Outbound Pickup
$this->fieldOptionProviders['pickupOutbound'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
    'label' => 'Zustieg Hinfahrt',
    'choices' => $bookingDto->travel->pickupsTo,
    'choice_label' => fn (?Pickup $pickup) => $this->formatPickupLabelWithPrice($pickup),
    'choice_value' => 'id',
    'expanded' => false,
    'multiple' => false,
    'required' => true,
    'placeholder' => 'Zustieg auswählen',
];

// Inbound Pickup
$this->fieldOptionProviders['pickupInbound'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
    'label' => 'Ausstieg Rückfahrt',
    'choices' => $bookingDto->travel->pickupsFro,
    'choice_label' => fn (?Pickup $pickup) => $this->formatPickupLabelWithPrice($pickup),
    'choice_value' => 'id',
    'expanded' => false,
    'multiple' => false,
    'required' => true,
    'placeholder' => 'Ausstieg auswählen',
];

Service Pricing Consistency

The implementation also extends the existing formatServiceLabelWithPrice() method to handle negative service prices consistently:

private function formatServiceLabelWithPrice(?Service $service): string
{
    if (null === $service) {
        return '';
    }

    // Handle zero prices (no display)
    if (null === $service->price || 0.0 === $service->price) {
        return $service->label;
    }

    // Handle negative prices (discounts)
    if ($service->price < 0) {
        return sprintf('%s (-%s€ Rabatt)', $service->label, number_format(abs($service->price), 2, ',', '.'));
    }

    // Handle positive prices (costs)
    return sprintf('%s (€%s)', $service->label, number_format($service->price, 2, ',', '.'));
}

Pricing Display Examples

Pickup Locations

Positive Pricing (Additional Cost):

  • "München Hbf (€15,00)"
  • "Nürnberg Zentral (€25,00)"
  • "Augsburg Bahnhof (€12,50)"

Zero Pricing (No Additional Cost):

  • "Standardzustieg"
  • "Hauptbahnhof"
  • "Zentrum"

Negative Pricing (Discount):

  • "Nahverkehr (-5,00€ Rabatt)"
  • "Sammelstelle (-10,00€ Rabatt)"
  • "Gruppentarif (-15,00€ Rabatt)"

Services (Updated Consistency)

Positive Pricing:

  • "Skikurs Anfänger (€25,00)"
  • "Versicherung (€15,00)"
  • "5-Tage Skipass (€120,00)"

Zero Pricing:

  • "Vollpension"
  • "Grundausstattung"
  • "Standardleistung"

Negative Pricing (Discounts):

  • "Frühbucher-Bonus (-15,00€ Rabatt)"
  • "Stammgast-Vorteil (-5,00€ Rabatt)"
  • "Gruppen-Rabatt (-20,00€ Rabatt)"

Technical Features

German Number Formatting

All pricing uses German locale formatting:

  • Decimal separator: Comma (,)
  • Thousands separator: Period (.)
  • Currency symbol: Euro ()

Null Safety

The implementation handles all edge cases:

  • null pickup objects return empty string
  • null prices treated as zero (no display)
  • Proper type checking for price comparisons

Performance Considerations

  • Lightweight formatting methods with minimal overhead
  • Reuses existing formatPickupLabel() logic
  • No additional database queries or API calls
  • Efficient string formatting with sprintf()

Integration Points

Form System Integration

The pricing display integrates seamlessly with:

  • Conditional Field States: Pickup fields show/hide based on transportation selection
  • HTMX Updates: Real-time pricing updates when selections change
  • Field Handlers: ParticipantPickupOutboundFieldHandler and ParticipantPickupInboundFieldHandler
  • Form Validation: Maintains existing validation rules

Pricing Calculation System

Pickup pricing integrates with the broader pricing system:

  • BookingService: Pickup costs included in total calculations
  • Pricing Summary: Pickup selections reflected in booking summary
  • Real-time Updates: HTMX updates include pickup pricing changes

Business Logic

Discount Handling

Negative pickup prices represent business discounts:

  • Volume Discounts: Lower prices for group pickups
  • Location Incentives: Discounts for convenient pickup locations
  • Promotional Offers: Special pricing for certain routes
  • Loyalty Programs: Reduced costs for repeat customers

Zero Price Logic

Zero-priced pickups indicate:

  • Included Services: No additional cost for standard pickups
  • Base Package: Pickup included in base travel price
  • Promotional Free: Temporarily free pickup locations

Files Modified

  1. src/Form/Service/ParticipantFieldOptionsProvider.php

    • Added formatPickupLabelWithPrice() method
    • Enhanced formatServiceLabelWithPrice() with discount handling
    • Updated pickup field option providers
  2. docs/PRICING_DISPLAY_IMPLEMENTATION.md

    • Updated service label formatting examples
    • Added pickup services to affected service types
    • Enhanced pricing logic documentation
  3. myep-next-booking/CLAUDE.md

    • Added pricing display standards section
    • Updated development guidelines for pricing

Testing Considerations

Manual Testing Scenarios

  1. Positive Pickup Pricing: Select pickup with additional cost
  2. Zero Pickup Pricing: Select free pickup location
  3. Negative Pickup Pricing: Select discounted pickup location
  4. Mixed Scenarios: Combine different pickup price types
  5. HTMX Integration: Verify real-time pricing updates

Test Data Requirements

  • Pickup objects with positive, zero, and negative prices
  • Various German number formatting scenarios
  • Edge cases with null values and empty strings

Future Enhancements

Potential Improvements

  • Currency Selection: Support for multiple currencies
  • Dynamic Pricing: Time-based or demand-based pricing
  • Bulk Discounts: Automatic discounts for group bookings
  • Regional Pricing: Location-based price variations

Integration Opportunities

  • Payment Gateway: Direct integration with pricing calculations
  • Analytics: Track pickup selection patterns and pricing impact
  • Reporting: Detailed pickup pricing reports
  • API Extensions: Expose pickup pricing via REST API

Implementation Status: Completed
Last Updated: January 2025
Files Modified: 3
Testing: Manual verification required
Documentation: Updated and comprehensive

This implementation successfully extends the pricing display system to include pickup locations while maintaining consistency with existing service pricing patterns and handling the unique business requirement for discount pricing display.