244 lines
7.7 KiB
Markdown
244 lines
7.7 KiB
Markdown
# 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()`
|
|
|
|
```php
|
|
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:
|
|
|
|
```php
|
|
// 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:
|
|
|
|
```php
|
|
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. |