PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
← All changes | app/Services/ItineraryPdfBuilder.php +37 -8 3.0.8 → 3.0.16 View file →
@@ -75,15 +75,32 @@
75 75 $bookingDate = $createdAt !== '' ? date_i18n(get_option('date_format'), strtotime($createdAt)) : '';
76 76 $travelRaw = (string) ($source['travel_date'] ?? '');
77 77 $travelDate = $travelRaw !== '' ? date_i18n(get_option('date_format'), strtotime($travelRaw)) : '';
78 78
79 + // Departure / return date. The booking already stores the real
80 + // trip end (`end_date`, written at booking time from the selected
81 + // departure or start + duration), so prefer that authoritative
82 + // value rather than re-deriving one — this is what keeps the
83 + // travel timeline showing the ACTUAL booked end date instead of
84 + // repeating the arrival date. Only when no stored end_date exists
85 + // do we derive it, and then the SAME way
86 + // BookingRepository::calculateEndDate does: start + (duration_days
87 + // - 1). A 5-day trip therefore ends on day 5, not day 6 — the old
88 + // "+ duration_days" was off by one and implied an extra night.
79 89 $returnDate = '';
80 - if ($travelRaw !== '' && $trip && !empty($trip->duration_days)) {
81 - $returnDate = date_i18n(
82 - get_option('date_format'),
83 - strtotime($travelRaw . ' +' . (int) $trip->duration_days . ' days')
90 + $endRaw = (string) ($source['end_date'] ?? '');
91 + if ($endRaw === '' && $travelRaw !== '' && $trip && (int) ($trip->duration_days ?? 0) > 1) {
92 + $endRaw = date(
93 + 'Y-m-d',
94 + strtotime($travelRaw . ' +' . ((int) $trip->duration_days - 1) . ' days')
84 95 );
85 96 }
97 + // Expose a return date only when it genuinely differs from the
98 + // arrival date. Single-day bookings store end_date == start_date,
99 + // so the timeline renders one item instead of two identical dates.
100 + if ($endRaw !== '' && $endRaw !== $travelRaw) {
101 + $returnDate = date_i18n(get_option('date_format'), strtotime($endRaw));
102 + }
86 103
87 104 $statusRaw = strtolower((string) ($source['booking_status'] ?? $source['status'] ?? ''));
88 105 $statusClass = in_array($statusRaw, ['confirmed', 'completed', 'success'], true)
89 106 ? 'confirmed'
@@ -104,8 +121,9 @@
104 121
105 122 $templateData = [
106 123 'company_name' => SettingsService::get('company_name', get_bloginfo('name')),
107 124 'company_address' => SettingsService::get('company_address', ''),
125 + 'company_address_lines' => \Yatra\Helpers\FormatHelper::companyAddressLines(),
108 126 'company_email' => SettingsService::get('company_email', get_option('admin_email')),
109 127 'company_phone' => SettingsService::get('company_phone', ''),
110 128 'customer_name' => $customerName,
111 129 'customer_email' => (string) ($source['contact_email'] ?? $source['customer_email'] ?? ''),
@@ -116,9 +134,15 @@
116 134 'trip_title' => $trip ? ((string) ($trip->title ?? $tripFallbackTitle)) : $tripFallbackTitle,
117 135 'trip_description'=> $trip ? (string) ($trip->description ?? $trip->content ?? '') : '',
118 136 // Duration is duration_days/duration_nights (no `duration` column).
119 137 'trip_duration' => $trip
120 - ? yatra_format_duration((int) ($trip->duration_days ?? 0), isset($trip->duration_nights) ? (int) $trip->duration_nights : null)
138 + ? yatra_format_duration(
139 + (int) ($trip->duration_days ?? 0),
140 + isset($trip->duration_nights) ? (int) $trip->duration_nights : null,
141 + // Hour-based day tours: "8 hours" instead of "1 day". Absent
142 + // or NULL on every day-based trip, which keeps its wording.
143 + (int) ($trip->duration_hours ?? 0)
144 + )
121 145 : '',
122 146 'trip_difficulty' => $trip ? (string) ($trip->difficulty_name ?? '') : '',
123 147 'trip_highlights' => $trip ? ($trip->highlights ?? $trip->trip_highlights ?? '') : '',
124 148 'trip_includes' => $trip ? ($trip->includes ?? $trip->trip_includes ?? '') : '',
@@ -127,11 +151,11 @@
127 151 'destination' => $trip ? (string) ($trip->destination ?? '') : (string) ($source['destination'] ?? ''),
128 152 'travel_date' => $travelDate,
129 153 'return_date' => $returnDate,
130 154 'currency_symbol' => FormatHelper::getCurrencySymbol($currency),
131 - 'total_amount' => number_format((float) ($source['total_amount'] ?? 0), 2),
132 - 'amount_paid' => number_format((float) ($source['amount_paid'] ?? 0), 2),
133 - 'amount_due' => number_format((float) ($source['amount_due'] ?? 0), 2),
155 + 'total_amount' => yatra_format_price((float) ($source['total_amount'] ?? 0), $currency, false),
156 + 'amount_paid' => yatra_format_price((float) ($source['amount_paid'] ?? 0), $currency, false),
157 + 'amount_due' => yatra_format_price((float) ($source['amount_due'] ?? 0), $currency, false),
134 158 'traveler_count' => (int) ($source['travelers_count'] ?? $source['traveler_count'] ?? $source['travelers'] ?? 1),
135 159 'itinerary_days' => $itineraryDays,
136 160 // Real trip-specific Important Information fields, same
137 161 // ones the single-trip page surfaces in its "Important
@@ -177,8 +201,13 @@
177 201 'trip_id' => $payment->trip_id ?? 0,
178 202 'booking_id' => $payment->booking_id ?? 0,
179 203 'created_at' => $payment->created_at ?? null,
180 204 'travel_date' => $payment->travel_date ?? null,
205 + // Actual booked trip range — used for the arrival/departure
206 + // timeline. Null-safe: build() falls back to a correct
207 + // derivation when the joined record doesn't carry these.
208 + 'start_date' => $payment->start_date ?? null,
209 + 'end_date' => $payment->end_date ?? null,
181 210 // Use the BOOKING status, not the payment status. Payment
182 211 // status can be "completed" while the booking itself is
183 212 // still "pending" admin confirmation — the itinerary
184 213 // header should reflect the booking, not the transaction.