'Y-m-d H:i'])] public ?\DateTimeImmutable $time = null; #[Groups(['api:single', 'snapshot'])] public ?float $price = null; #[Groups(['api:single', 'snapshot'])] public ?float $priceOutbound = null; #[Groups(['api:single', 'snapshot'])] public ?float $priceInbound = null; /** @var list */ #[Groups(['api:booking'])] public array $mapping = []; /** * Gets the formatted label for the pickup location without pricing. * * Creates user-friendly labels following the pattern: * - Primary format: "City (Street)" if street is available * - Fallback format: "City" if no street information * * @return string The formatted pickup location label */ public function getLabel(): string { $label = $this->city ?? ''; if (null !== $this->street && '' !== trim($this->street)) { $label .= ' ('.$this->street.')'; } return $label; } /** * Gets the formatted label with pricing information. * * Formats the pickup label with price display: * - Zero/null price: Just the label without price suffix * - Positive price: "Label (€X,XX)" as surcharge * - Negative price: "Label (-X,XX€ Rabatt)" as discount * * @return string The formatted pickup label with pricing */ public function getLabelWithPrice(): string { $label = $this->getLabel(); if (null === $this->price) { return $label; } if (0.0 === $this->price) { return sprintf('%s (inkl.)', $label); } if ($this->price < 0) { return sprintf('%s (-%s€ Rabatt)', $label, number_format(abs($this->price), 2, ',', '.')); } return sprintf('%s (€%s)', $label, number_format($this->price, 2, ',', '.')); } /** * Gets the formatted label with context-aware pricing based on transportation directions. * * Calculates the effective price based on which directions use bus transportation: * - Both directions BUS: shows combined price (outbound + inbound) * - Only outbound BUS: shows outbound price only * - Only inbound BUS: shows inbound price only * - Falls back to legacy price property when split pricing data is unavailable * * @param bool $hasOutboundBus Whether outbound transportation is bus * @param bool $hasInboundBus Whether inbound transportation is bus * * @return string The formatted pickup label with contextual pricing */ public function getLabelWithContextualPrice(bool $hasOutboundBus, bool $hasInboundBus): string { $label = $this->getLabel(); $effectivePrice = $this->calculateEffectivePrice($hasOutboundBus, $hasInboundBus); if (null === $effectivePrice) { return $label; } if (0.0 === $effectivePrice) { return sprintf('%s (inkl.)', $label); } if ($effectivePrice < 0) { return sprintf('%s (-%s€ Rabatt)', $label, number_format(abs($effectivePrice), 2, ',', '.')); } return sprintf('%s (€%s)', $label, number_format($effectivePrice, 2, ',', '.')); } /** * Calculates the effective pickup price based on transportation directions. * * Uses split pricing (priceOutbound/priceInbound) when available, falling back * to the legacy price property for backward compatibility with older data. * * @param bool $hasOutboundBus Whether outbound transportation is bus * @param bool $hasInboundBus Whether inbound transportation is bus * * @return float|null The calculated effective price or null if no applicable price */ public function calculateEffectivePrice(bool $hasOutboundBus, bool $hasInboundBus): ?float { // Neither direction is bus - no pickup price applies if (false === $hasOutboundBus && false === $hasInboundBus) { return null; } $effectivePrice = 0.0; $hasSplitPricing = null !== $this->priceOutbound || null !== $this->priceInbound; if ($hasOutboundBus) { if ($hasSplitPricing) { $effectivePrice += $this->priceOutbound ?? 0.0; } else { // Fallback to legacy price for backward compatibility $effectivePrice += $this->price ?? 0.0; } } if ($hasInboundBus && null !== $this->priceInbound) { $effectivePrice += $this->priceInbound; } return $effectivePrice; } }