| @@ -66,20 +66,20 @@ | ||
| 66 | 66 | // ===================== |
| 67 | 67 | // BOOKINGS ROUTES |
| 68 | 68 | // ===================== |
| 69 | 69 | |
| 70 | - // List bookings | |
| 70 | + // List bookings — view cap. | |
| 71 | 71 | register_rest_route($this->namespace, '/bookings', [ |
| 72 | 72 | 'methods' => 'GET', |
| 73 | 73 | 'callback' => [$this, 'getBookings'], |
| 74 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 74 | + 'permission_callback' => [$this, 'checkCanView'], | |
| 75 | 75 | ]); |
| 76 | 76 | |
| 77 | - // Get single booking | |
| 77 | + // Get single booking — view cap. | |
| 78 | 78 | register_rest_route($this->namespace, '/bookings/(?P<id>\d+)', [ |
| 79 | 79 | 'methods' => 'GET', |
| 80 | 80 | 'callback' => [$this, 'getBooking'], |
| 81 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 81 | + 'permission_callback' => [$this, 'checkCanView'], | |
| 82 | 82 | 'args' => [ |
| 83 | 83 | 'id' => [ |
| 84 | 84 | 'required' => true, |
| 85 | 85 | 'type' => 'integer', |
| @@ -87,66 +87,75 @@ | ||
| 87 | 87 | ], |
| 88 | 88 | ], |
| 89 | 89 | ]); |
| 90 | 90 | |
| 91 | - // Create booking | |
| 91 | + // Create booking — create cap. | |
| 92 | 92 | register_rest_route($this->namespace, '/bookings', [ |
| 93 | 93 | 'methods' => 'POST', |
| 94 | 94 | 'callback' => [$this, 'createBooking'], |
| 95 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 95 | + 'permission_callback' => [$this, 'checkCanCreate'], | |
| 96 | 96 | ]); |
| 97 | 97 | |
| 98 | - // Update booking | |
| 98 | + // Update booking — edit cap. | |
| 99 | 99 | register_rest_route($this->namespace, '/bookings/(?P<id>\d+)', [ |
| 100 | 100 | 'methods' => 'PUT', |
| 101 | 101 | 'callback' => [$this, 'updateBooking'], |
| 102 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 102 | + 'permission_callback' => [$this, 'checkCanEdit'], | |
| 103 | 103 | ]); |
| 104 | 104 | |
| 105 | - // Delete booking | |
| 105 | + // Delete booking — critical-sensitivity delete cap. Only | |
| 106 | + // Owner role gets this by default. | |
| 106 | 107 | register_rest_route($this->namespace, '/bookings/(?P<id>\d+)', [ |
| 107 | 108 | 'methods' => 'DELETE', |
| 108 | 109 | 'callback' => [$this, 'deleteBooking'], |
| 109 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 110 | + 'permission_callback' => [$this, 'checkCanDelete'], | |
| 110 | 111 | ]); |
| 111 | 112 | |
| 112 | - // Update booking status | |
| 113 | + // Update booking status — dedicated change-status cap so | |
| 114 | + // Front Desk (who has this cap but NOT edit) can flip | |
| 115 | + // confirmed → checked-in without being able to mutate other | |
| 116 | + // fields. | |
| 113 | 117 | register_rest_route($this->namespace, '/bookings/(?P<id>\d+)/status', [ |
| 114 | 118 | 'methods' => 'PUT', |
| 115 | 119 | 'callback' => [$this, 'updateBookingStatus'], |
| 116 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 120 | + 'permission_callback' => [$this, 'checkCanChangeStatus'], | |
| 117 | 121 | ]); |
| 118 | 122 | |
| 119 | - // Get booking statistics | |
| 123 | + // Get booking statistics — view cap (aggregates only). | |
| 120 | 124 | register_rest_route($this->namespace, '/bookings/stats', [ |
| 121 | 125 | 'methods' => 'GET', |
| 122 | 126 | 'callback' => [$this, 'getBookingStats'], |
| 123 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 127 | + 'permission_callback' => [$this, 'checkCanView'], | |
| 124 | 128 | ]); |
| 125 | 129 | |
| 126 | - // Send booking email | |
| 130 | + // Send booking email — edit cap. Sending a transactional | |
| 131 | + // re-confirmation is a write-side operation against the | |
| 132 | + // customer's record. | |
| 127 | 133 | register_rest_route($this->namespace, '/bookings/(?P<id>\d+)/send-email', [ |
| 128 | 134 | 'methods' => 'POST', |
| 129 | 135 | 'callback' => [$this, 'sendBookingEmail'], |
| 130 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 136 | + 'permission_callback' => [$this, 'checkCanEdit'], | |
| 131 | 137 | ]); |
| 132 | 138 | |
| 133 | 139 | // ===================== |
| 134 | 140 | // PAYMENTS ROUTES |
| 135 | 141 | // ===================== |
| 136 | - | |
| 137 | - // Get booking payments | |
| 142 | + | |
| 143 | + // Get booking payments — view cap. | |
| 138 | 144 | register_rest_route($this->namespace, '/bookings/(?P<id>\d+)/payments', [ |
| 139 | 145 | 'methods' => 'GET', |
| 140 | 146 | 'callback' => [$this, 'getBookingPayments'], |
| 141 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 147 | + 'permission_callback' => [$this, 'checkCanView'], | |
| 142 | 148 | ]); |
| 143 | 149 | |
| 144 | - // Add payment to booking | |
| 150 | + // Add payment to booking — edit cap (modifies the booking's | |
| 151 | + // payment state). Refunds + payment deletion live on the | |
| 152 | + // dedicated PaymentController with their own high-sensitivity | |
| 153 | + // caps. | |
| 145 | 154 | register_rest_route($this->namespace, '/bookings/(?P<id>\d+)/payments', [ |
| 146 | 155 | 'methods' => 'POST', |
| 147 | 156 | 'callback' => [$this, 'addPayment'], |
| 148 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 157 | + 'permission_callback' => [$this, 'checkCanEdit'], | |
| 149 | 158 | ]); |
| 150 | 159 | |
| 151 | 160 | // NOTE: Payment CRUD operations moved to PaymentController |
| 152 | 161 | // This keeps BookingsController focused on booking operations only |
| @@ -153,20 +162,21 @@ | ||
| 153 | 162 | |
| 154 | 163 | // ===================== |
| 155 | 164 | // TRAVELERS ROUTES |
| 156 | 165 | // ===================== |
| 157 | - | |
| 166 | + | |
| 167 | + // Travelers list — view cap. | |
| 158 | 168 | register_rest_route($this->namespace, '/travelers', [ |
| 159 | 169 | 'methods' => 'GET', |
| 160 | 170 | 'callback' => [$this, 'getTravelers'], |
| 161 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 171 | + 'permission_callback' => [$this, 'checkCanView'], | |
| 162 | 172 | ]); |
| 163 | 173 | |
| 164 | - // Traveler bulk actions | |
| 174 | + // Traveler bulk actions — edit cap. | |
| 165 | 175 | register_rest_route($this->namespace, '/travelers/bulk', [ |
| 166 | 176 | 'methods' => 'PUT', |
| 167 | 177 | 'callback' => [$this, 'bulkTravelers'], |
| 168 | - 'permission_callback' => [$this, 'checkAdminPermission'], | |
| 178 | + 'permission_callback' => [$this, 'checkCanEdit'], | |
| 169 | 179 | ]); |
| 170 | 180 | |
| 171 | 181 | // Download travel voucher for a booking |
| 172 | 182 | register_rest_route($this->namespace, '/bookings/(?P<id>\d+)/voucher', [ |
| @@ -183,17 +193,53 @@ | ||
| 183 | 193 | ]); |
| 184 | 194 | } |
| 185 | 195 | |
| 186 | 196 | /** |
| 187 | - * Check admin permission | |
| 197 | + * Granular permission checks — one per operation. WP administrators | |
| 198 | + * pass every cap via the Team module's admin-fallback filter | |
| 199 | + * (priority 7 / 8), so an explicit `manage_options` check isn't | |
| 200 | + * needed at this layer — the cap covers it. | |
| 188 | 201 | */ |
| 202 | + public function checkCanView(): bool | |
| 203 | + { | |
| 204 | + return current_user_can('yatra_view_bookings'); | |
| 205 | + } | |
| 206 | + | |
| 207 | + public function checkCanCreate(): bool | |
| 208 | + { | |
| 209 | + return current_user_can('yatra_create_bookings'); | |
| 210 | + } | |
| 211 | + | |
| 212 | + public function checkCanEdit(): bool | |
| 213 | + { | |
| 214 | + return current_user_can('yatra_edit_bookings'); | |
| 215 | + } | |
| 216 | + | |
| 217 | + public function checkCanDelete(): bool | |
| 218 | + { | |
| 219 | + // Critical-sensitivity cap. By default only the Owner role | |
| 220 | + // holds this — Manager, Sales Agent, Front Desk, etc. cannot | |
| 221 | + // delete bookings even when they can edit them. | |
| 222 | + return current_user_can('yatra_delete_bookings'); | |
| 223 | + } | |
| 224 | + | |
| 225 | + public function checkCanChangeStatus(): bool | |
| 226 | + { | |
| 227 | + // Separate from edit — Front Desk has this without the | |
| 228 | + // broader edit cap so they can confirm/check-in bookings | |
| 229 | + // without being able to mutate other fields. | |
| 230 | + return current_user_can('yatra_change_booking_status'); | |
| 231 | + } | |
| 232 | + | |
| 233 | + /** | |
| 234 | + * @deprecated Kept for any external code (snippet, integration) | |
| 235 | + * that referenced the old method name. Routes to the view-only | |
| 236 | + * cap — safer than the old `view OR manage_options` shorthand, | |
| 237 | + * and admin users still pass via the admin-fallback layer. | |
| 238 | + */ | |
| 189 | 239 | public function checkAdminPermission(): bool |
| 190 | 240 | { |
| 191 | - // Allow custom booking capability or fallback to manage_options | |
| 192 | - if (current_user_can('yatra_view_bookings')) { | |
| 193 | - return true; | |
| 194 | - } | |
| 195 | - return current_user_can('manage_options'); | |
| 241 | + return $this->checkCanView(); | |
| 196 | 242 | } |
| 197 | 243 | |
| 198 | 244 | // ========================================================================= |
| 199 | 245 | // BOOKING ENDPOINTS |
| @@ -213,8 +259,11 @@ | ||
| 213 | 259 | 'trip_id' => (int) $request->get_param('trip_id'), |
| 214 | 260 | 'search' => $request->get_param('search') ?: '', |
| 215 | 261 | 'date_from' => $request->get_param('date_from') ?: '', |
| 216 | 262 | 'date_to' => $request->get_param('date_to') ?: '', |
| 263 | + // Column sorting from the table headers — whitelisted in the repository. | |
| 264 | + 'orderby' => $request->get_param('orderby') ?: '', | |
| 265 | + 'order' => $request->get_param('order') ?: '', | |
| 217 | 266 | ]; |
| 218 | 267 | |
| 219 | 268 | // Delegate to service |
| 220 | 269 | $result = $this->bookingService->getBookings($filters); |
| @@ -313,9 +362,9 @@ | ||
| 313 | 362 | return $this->error_response($result['message'] ?? 'Failed to update booking', 400); |
| 314 | 363 | } |
| 315 | 364 | |
| 316 | 365 | Logger::info("Booking updated successfully", ['booking_id' => $id]); |
| 317 | - return $this->success_response($result['data']); | |
| 366 | + return $this->success_response($result['data'] ?? null); | |
| 318 | 367 | |
| 319 | 368 | } catch (\Exception $e) { |
| 320 | 369 | Logger::error("Failed to update booking", ['booking_id' => $id ?? 0, 'data' => $data ?? [], 'error' => $e->getMessage()]); |
| 321 | 370 | return $this->handle_exception($e); |
| @@ -732,11 +781,21 @@ | ||
| 732 | 781 | $bookingDate = !empty($createdAt) ? date_i18n(get_option('date_format'), strtotime((string) $createdAt)) : ''; |
| 733 | 782 | $travelDateRaw = $booking['travel_date'] ?? ''; |
| 734 | 783 | $travelDate = !empty($travelDateRaw) ? date_i18n(get_option('date_format'), strtotime((string) $travelDateRaw)) : ''; |
| 735 | 784 | |
| 785 | + // Return date. Prefer the booking's STORED end_date — the actual booked | |
| 786 | + // return (accounts for a flexible window or a trip duration changed after | |
| 787 | + // booking). Fall back to the trip duration only when no end is stored: | |
| 788 | + // duration_days is INCLUSIVE, so the return is travel_date + (days - 1) | |
| 789 | + // (matches BookingRepository::calculateEndDate; a bare "+ duration_days" | |
| 790 | + // was one day too far and implied an extra night — see ItineraryPdfBuilder). | |
| 736 | 791 | $returnDate = ''; |
| 737 | - if (!empty($travelDateRaw) && $trip && !empty($trip->duration)) { | |
| 738 | - $returnTimestamp = strtotime((string) $travelDateRaw . ' +' . (int) $trip->duration . ' days'); | |
| 792 | + $storedEnd = (string) ($booking['end_date'] ?? ''); | |
| 793 | + if ($storedEnd !== '' && ($travelDateRaw === '' || $storedEnd >= $travelDateRaw)) { | |
| 794 | + $returnDate = date_i18n(get_option('date_format'), strtotime($storedEnd)); | |
| 795 | + } elseif (!empty($travelDateRaw) && $trip && !empty($trip->duration_days)) { | |
| 796 | + $returnOffset = max(0, (int) $trip->duration_days - 1); | |
| 797 | + $returnTimestamp = strtotime((string) $travelDateRaw . ' +' . $returnOffset . ' days'); | |
| 739 | 798 | $returnDate = date_i18n(get_option('date_format'), $returnTimestamp); |
| 740 | 799 | } |
| 741 | 800 | |
| 742 | 801 | $statusRaw = (string) ($booking['booking_status'] ?? $booking['status'] ?? ''); |
| @@ -749,12 +808,14 @@ | ||
| 749 | 808 | |
| 750 | 809 | $templateData = [ |
| 751 | 810 | 'company_name' => $companyName, |
| 752 | 811 | 'company_address' => $companyAddress, |
| 812 | + 'company_address_lines' => \Yatra\Helpers\FormatHelper::companyAddressLines(), | |
| 753 | 813 | 'company_email' => $companyEmail, |
| 754 | 814 | 'company_phone' => $companyPhone, |
| 755 | 815 | 'customer_name' => $customerName, |
| 756 | 816 | 'customer_email' => (string) ($booking['contact_email'] ?? $booking['customer_email'] ?? ''), |
| 817 | + 'customer_address_lines' => FormatHelper::customerAddressLines($booking), | |
| 757 | 818 | 'booking_ref' => $bookingRef, |
| 758 | 819 | 'booking_date' => $bookingDate, |
| 759 | 820 | 'booking_status' => ucfirst($statusRaw ?: 'pending'), |
| 760 | 821 | 'status_class' => in_array(strtolower($statusRaw), ['confirmed', 'completed', 'success'], true) ? 'confirmed' : |
| @@ -759,9 +820,19 @@ | ||
| 759 | 820 | 'booking_status' => ucfirst($statusRaw ?: 'pending'), |
| 760 | 821 | 'status_class' => in_array(strtolower($statusRaw), ['confirmed', 'completed', 'success'], true) ? 'confirmed' : |
| 761 | 822 | (in_array(strtolower($statusRaw), ['cancelled'], true) ? 'cancelled' : 'pending'), |
| 762 | 823 | 'trip_title' => $trip ? ($trip->title ?? $booking['trip_title'] ?? __('Trip Booking', 'yatra')) : ($booking['trip_title'] ?? __('Trip Booking', 'yatra')), |
| 763 | - 'trip_duration' => $trip && $trip->duration ? sprintf(__('%d days', 'yatra'), (int) $trip->duration) : '', | |
| 824 | + // Trip duration comes from duration_days/duration_nights (there is no | |
| 825 | + // `duration` column — accessing it caused a blank value + PHP notice). | |
| 826 | + 'trip_duration' => $trip | |
| 827 | + ? yatra_format_duration( | |
| 828 | + (int) ($trip->duration_days ?? 0), | |
| 829 | + isset($trip->duration_nights) ? (int) $trip->duration_nights : null, | |
| 830 | + // Hour-based day tours: "8 hours" instead of "1 day". Absent | |
| 831 | + // or NULL on every day-based trip, which keeps its wording. | |
| 832 | + (int) ($trip->duration_hours ?? 0) | |
| 833 | + ) | |
| 834 | + : '', | |
| 764 | 835 | 'trip_difficulty' => $trip ? ($trip->difficulty_name ?? '') : '', |
| 765 | 836 | 'departure_location' => $trip ? ($trip->departure_location ?? '') : '', |
| 766 | 837 | 'destination' => $trip ? ($trip->destination ?? '') : '', |
| 767 | 838 | 'travel_date' => $travelDate, |
| @@ -766,11 +837,11 @@ | ||
| 766 | 837 | 'destination' => $trip ? ($trip->destination ?? '') : '', |
| 767 | 838 | 'travel_date' => $travelDate, |
| 768 | 839 | 'return_date' => $returnDate, |
| 769 | 840 | 'currency_symbol' => $currencySymbol, |
| 770 | - 'total_amount' => number_format((float) ($booking['total_amount'] ?? 0), 2), | |
| 771 | - 'amount_paid' => number_format((float) ($booking['amount_paid'] ?? 0), 2), | |
| 772 | - 'amount_due' => number_format((float) ($booking['amount_due'] ?? 0), 2), | |
| 841 | + 'total_amount' => yatra_format_price((float) ($booking['total_amount'] ?? 0), $currency, false), | |
| 842 | + 'amount_paid' => yatra_format_price((float) ($booking['amount_paid'] ?? 0), $currency, false), | |
| 843 | + 'amount_due' => yatra_format_price((float) ($booking['amount_due'] ?? 0), $currency, false), | |
| 773 | 844 | 'traveler_count' => (int) ($booking['travelers_count'] ?? $booking['travelers'] ?? 1), |
| 774 | 845 | ]; |
| 775 | 846 | |
| 776 | 847 | $pdfService = new PdfService(); |
| @@ -806,43 +877,10 @@ | ||
| 806 | 877 | * @param array<string,mixed> $booking From BookingService::getBooking() |
| 807 | 878 | */ |
| 808 | 879 | private function renderItineraryFromBookingData(array $booking, bool $isPreview) |
| 809 | 880 | { |
| 810 | - $tripRepository = new TripRepository(); | |
| 811 | - $trip = null; | |
| 812 | - $tripId = (int) ($booking['trip_id'] ?? 0); | |
| 813 | - if ($tripId > 0) { | |
| 814 | - $trip = $tripRepository->find($tripId); | |
| 815 | - } | |
| 816 | - | |
| 817 | - $companyName = SettingsService::get('company_name', get_bloginfo('name')); | |
| 818 | - $companyAddress = SettingsService::get('company_address', ''); | |
| 819 | - $companyEmail = SettingsService::get('company_email', get_option('admin_email')); | |
| 820 | - $companyPhone = SettingsService::get('company_phone', ''); | |
| 821 | - $currency = SettingsService::getCurrency(); | |
| 822 | - $currencySymbol = FormatHelper::getCurrencySymbol($currency); | |
| 823 | - | |
| 824 | - $createdAt = $booking['created_at'] ?? $booking['booking_date'] ?? ''; | |
| 825 | - $bookingDate = !empty($createdAt) ? date_i18n(get_option('date_format'), strtotime((string) $createdAt)) : ''; | |
| 826 | - $travelDateRaw = $booking['travel_date'] ?? ''; | |
| 827 | - $travelDate = !empty($travelDateRaw) ? date_i18n(get_option('date_format'), strtotime((string) $travelDateRaw)) : ''; | |
| 828 | - | |
| 829 | - $returnDate = ''; | |
| 830 | - if (!empty($travelDateRaw) && $trip && !empty($trip->duration)) { | |
| 831 | - $returnTimestamp = strtotime((string) $travelDateRaw . ' +' . (int) $trip->duration . ' days'); | |
| 832 | - $returnDate = date_i18n(get_option('date_format'), $returnTimestamp); | |
| 833 | - } | |
| 834 | - | |
| 835 | - $statusRaw = (string) ($booking['booking_status'] ?? $booking['status'] ?? ''); | |
| 836 | - $bookingId = (int) ($booking['id'] ?? 0); | |
| 837 | - $bookingRef = 'YTR-' . strtoupper(str_pad((string) $bookingId, 8, '0', STR_PAD_LEFT)); | |
| 838 | - | |
| 839 | - $customerName = trim( | |
| 840 | - (string) ($booking['contact_first_name'] ?? '') . ' ' . (string) ($booking['contact_last_name'] ?? '') | |
| 841 | - ) ?: (string) ($booking['customer_name'] ?? __('Customer', 'yatra')); | |
| 842 | - | |
| 843 | - $pdfService = new PdfService(); | |
| 844 | - if (!$pdfService->isAvailable()) { | |
| 881 | + $builder = new \Yatra\Services\ItineraryPdfBuilder(); | |
| 882 | + if (!$builder->pdfService()->isAvailable()) { | |
| 845 | 883 | return new WP_Error( |
| 846 | 884 | 'pdf_engine_missing', |
| 847 | 885 | __('Itinerary PDF generator is not installed. Please run composer install to install dompdf/dompdf.', 'yatra'), |
| 848 | 886 | ['status' => 500] |
| @@ -848,46 +886,20 @@ | ||
| 848 | 886 | ['status' => 500] |
| 849 | 887 | ); |
| 850 | 888 | } |
| 851 | 889 | |
| 890 | + $bookingId = (int) ($booking['id'] ?? 0); | |
| 891 | + $bookingRef = $bookingId > 0 | |
| 892 | + ? 'YTR-' . strtoupper(str_pad((string) $bookingId, 8, '0', STR_PAD_LEFT)) | |
| 893 | + : 'PENDING'; | |
| 852 | 894 | $filename = 'Travel-Itinerary-' . $bookingRef . '.pdf'; |
| 853 | 895 | |
| 854 | - $templateData = [ | |
| 855 | - 'company_name' => $companyName, | |
| 856 | - 'company_address' => $companyAddress, | |
| 857 | - 'company_email' => $companyEmail, | |
| 858 | - 'company_phone' => $companyPhone, | |
| 859 | - 'customer_name' => $customerName, | |
| 860 | - 'customer_email' => (string) ($booking['contact_email'] ?? $booking['customer_email'] ?? ''), | |
| 861 | - 'booking_ref' => $bookingRef, | |
| 862 | - 'booking_date' => $bookingDate, | |
| 863 | - 'booking_status' => ucfirst($statusRaw ?: 'pending'), | |
| 864 | - 'status_class' => in_array(strtolower($statusRaw), ['confirmed', 'completed', 'success'], true) ? 'confirmed' : | |
| 865 | - (in_array(strtolower($statusRaw), ['cancelled'], true) ? 'cancelled' : 'pending'), | |
| 866 | - 'trip_title' => $trip ? ($trip->title ?? $booking['trip_title'] ?? __('Trip Booking', 'yatra')) : ($booking['trip_title'] ?? __('Trip Booking', 'yatra')), | |
| 867 | - 'trip_description' => $trip ? ($trip->description ?? $trip->content ?? '') : '', | |
| 868 | - 'trip_duration' => $trip && $trip->duration ? sprintf(__('%d days', 'yatra'), (int) $trip->duration) : '', | |
| 869 | - 'trip_difficulty' => $trip ? ($trip->difficulty_name ?? '') : '', | |
| 870 | - 'trip_highlights' => $trip ? ($trip->highlights ?? $trip->trip_highlights ?? '') : '', | |
| 871 | - 'trip_includes' => $trip ? ($trip->includes ?? $trip->trip_includes ?? '') : '', | |
| 872 | - 'trip_excludes' => $trip ? ($trip->excludes ?? $trip->trip_excludes ?? '') : '', | |
| 873 | - 'departure_location' => $trip ? ($trip->departure_location ?? '') : '', | |
| 874 | - 'destination' => $trip ? ($trip->destination ?? '') : '', | |
| 875 | - 'travel_date' => $travelDate, | |
| 876 | - 'return_date' => $returnDate, | |
| 877 | - 'currency_symbol' => $currencySymbol, | |
| 878 | - 'total_amount' => number_format((float) ($booking['total_amount'] ?? 0), 2), | |
| 879 | - 'amount_paid' => number_format((float) ($booking['amount_paid'] ?? 0), 2), | |
| 880 | - 'amount_due' => number_format((float) ($booking['amount_due'] ?? 0), 2), | |
| 881 | - 'traveler_count' => (int) ($booking['travelers_count'] ?? $booking['travelers'] ?? 1), | |
| 882 | - ]; | |
| 896 | + // The builder accepts the booking array shape directly — just | |
| 897 | + // forward `id` as `booking_id` so the reference resolves the | |
| 898 | + // same as the legacy code, and let it normalise everything else. | |
| 899 | + $source = $booking + ['booking_id' => $bookingId]; | |
| 900 | + $pdfBinary = $builder->build($source); | |
| 883 | 901 | |
| 884 | - $pdfBinary = $pdfService->renderTemplateToPdfSafely('pdf/itinerary.php', $templateData, [ | |
| 885 | - 'paper' => 'A4', | |
| 886 | - 'orientation' => 'portrait', | |
| 887 | - 'default_font' => 'DejaVu Sans', | |
| 888 | - ]); | |
| 889 | - | |
| 890 | 902 | if ($isPreview) { |
| 891 | 903 | return new WP_REST_Response([ |
| 892 | 904 | 'success' => true, |
| 893 | 905 | 'pdf_data' => base64_encode($pdfBinary), |
| @@ -894,8 +906,8 @@ | ||
| 894 | 906 | 'filename' => $filename, |
| 895 | 907 | ]); |
| 896 | 908 | } |
| 897 | 909 | |
| 898 | - $pdfService->outputPdfDownload($pdfBinary, $filename); | |
| 910 | + $builder->pdfService()->outputPdfDownload($pdfBinary, $filename); | |
| 899 | 911 | exit; |
| 900 | 912 | } |
| 901 | 913 | } |