| @@ -1,8 +1,22 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | $companyName = (string) ($company_name ?? ''); |
| 4 | +if ($companyName === '' && function_exists('yatra_get_brand_name')) { | |
| 5 | + $companyName = yatra_get_brand_name(); | |
| 6 | +} | |
| 4 | 7 | $companyAddress = (string) ($company_address ?? ''); |
| 8 | +// Settings keep the business address in separate fields (street, city, state, | |
| 9 | +// postcode, country); documents only ever printed the street, so everything after | |
| 10 | +// it was missing and the address looked cut off at its last line. Prefer the | |
| 11 | +// composed lines, falling back to the raw street for any caller not passing them. | |
| 12 | +$companyAddressLines = (isset($company_address_lines) && is_array($company_address_lines)) | |
| 13 | + ? array_values(array_filter(array_map('strval', $company_address_lines), static function ($line) { | |
| 14 | + return trim($line) !== ''; | |
| 15 | + })) | |
| 16 | + : array_values(array_filter(preg_split('/\R/', $companyAddress) ?: [], static function ($line) { | |
| 17 | + return trim($line) !== ''; | |
| 18 | + })); | |
| 5 | 19 | $companyEmail = (string) ($company_email ?? ''); |
| 6 | 20 | $companyPhone = (string) ($company_phone ?? ''); |
| 7 | 21 | |
| 8 | 22 | $customerName = (string) ($customer_name ?? ''); |
| @@ -7,8 +21,16 @@ | ||
| 7 | 21 | |
| 8 | 22 | $customerName = (string) ($customer_name ?? ''); |
| 9 | 23 | $customerEmail = (string) ($customer_email ?? ''); |
| 10 | 24 | |
| 25 | +// The customer's postal address, one entry per line. Absent on older callers, | |
| 26 | +// so an empty list simply renders nothing. | |
| 27 | +$customerAddressLines = (isset($customer_address_lines) && is_array($customer_address_lines)) | |
| 28 | + ? array_filter(array_map('strval', $customer_address_lines), static function ($line) { | |
| 29 | + return trim($line) !== ''; | |
| 30 | + }) | |
| 31 | + : []; | |
| 32 | + | |
| 11 | 33 | $paymentRef = (string) ($payment_ref ?? ''); |
| 12 | 34 | $paymentDate = (string) ($payment_date ?? ''); |
| 13 | 35 | $paymentStatus = (string) ($payment_status ?? ''); |
| 14 | 36 | $statusClass = (string) ($status_class ?? 'pending'); |
| @@ -23,23 +45,38 @@ | ||
| 23 | 45 | $bookingTotal = (string) ($booking_total ?? '0.00'); |
| 24 | 46 | $amountPaid = (string) ($amount_paid ?? '0.00'); |
| 25 | 47 | $amountDue = (string) ($amount_due ?? '0.00'); |
| 26 | 48 | |
| 49 | +// Structured payment instructions (e.g. bank-transfer details) provided by a | |
| 50 | +// gateway via the yatra_invoice_payment_instructions filter. Shape: | |
| 51 | +// ['title' => string, 'rows' => [['label'=>.., 'value'=>..], ...], 'note' => string] | |
| 52 | +$paymentInstructions = (isset($payment_instructions) && is_array($payment_instructions)) ? $payment_instructions : []; | |
| 53 | + | |
| 54 | + | |
| 55 | +// Optional logo + header colour supplied by the White Label module. | |
| 56 | +// Unbranded sites keep this document's original header colour and show no logo. | |
| 57 | +$pdfBranding = function_exists('yatra_get_pdf_branding') | |
| 58 | + ? yatra_get_pdf_branding('#1e40af') | |
| 59 | + : ['logo_url' => '', 'header_color' => '#1e40af']; | |
| 60 | +$brandLogoUrl = (string) ($pdfBranding['logo_url'] ?? ''); | |
| 61 | +$brandHeaderColor = (string) ($pdfBranding['header_color'] ?? '#1e40af'); | |
| 62 | + | |
| 27 | 63 | ?><!DOCTYPE html> |
| 28 | 64 | <html lang="en"> |
| 29 | 65 | <head> |
| 30 | 66 | <meta charset="UTF-8"> |
| 31 | 67 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 32 | - <title>Invoice - <?php echo htmlspecialchars($paymentRef, ENT_QUOTES, 'UTF-8'); ?></title> | |
| 68 | + <title><?php echo esc_html__('Invoice', 'yatra'); ?> - <?php echo htmlspecialchars($bookingRef !== '' ? $bookingRef : $paymentRef, ENT_QUOTES, 'UTF-8'); ?></title> | |
| 33 | 69 | <style> |
| 34 | 70 | @page { size: A4 portrait; margin: 0mm; } |
| 35 | 71 | * { margin: 0; padding: 0; box-sizing: border-box; } |
| 36 | - body { margin: 0; padding: 0; font-family: "DejaVu Sans", sans-serif; font-size: 12px; color: #111; } | |
| 72 | + body { margin: 0; padding: 0; font-family: "Noto Sans Devanagari", "Noto Sans Arabic", "Noto Sans CJK", "DejaVu Sans", sans-serif; font-size: 12px; color: #111; } | |
| 37 | 73 | .content { padding: 12mm 12mm 14mm 12mm; } |
| 38 | 74 | |
| 39 | 75 | .header { width: 100%; border-collapse: collapse; } |
| 40 | 76 | .header td { vertical-align: top; } |
| 41 | - .brand { background: #1e40af; color: #fff; padding: 6mm 6mm; } | |
| 77 | + .brand { background: <?php echo htmlspecialchars($brandHeaderColor, ENT_QUOTES, 'UTF-8'); ?>; color: #fff; padding: 6mm 6mm; } | |
| 78 | + .brand-logo { max-height: 18mm; max-width: 60mm; margin-bottom: 3mm; } | |
| 42 | 79 | .brand h1 { font-size: 18px; font-weight: 700; margin: 0; } |
| 43 | 80 | .brand p { font-size: 11px; margin-top: 4px; } |
| 44 | 81 | |
| 45 | 82 | .panel { width: 100%; border: 1px solid #e5e7eb; border-collapse: collapse; margin-top: 6mm; margin-bottom: 6mm; } |
| @@ -54,8 +91,9 @@ | ||
| 54 | 91 | .details .v-wrap { white-space: normal; } |
| 55 | 92 | |
| 56 | 93 | .badge { display: inline-block; padding: 2px 10px; font-size: 10px; border-radius: 12px; border: 1px solid #e5e7eb; margin-left: 6px; vertical-align: middle; line-height: 1.4; } |
| 57 | 94 | .badge-paid { background: #d1fae5; color: #065f46; border-color: #a7f3d0; } |
| 95 | + .badge-partial { background: #dbeafe; color: #1e40af; border-color: #bfdbfe; } | |
| 58 | 96 | .badge-pending { background: #fef3c7; color: #92400e; border-color: #fde68a; } |
| 59 | 97 | |
| 60 | 98 | .items { width: 100%; border-collapse: collapse; margin-top: 4mm; } |
| 61 | 99 | .items th { text-align: left; font-size: 10px; text-transform: uppercase; color: #6b7280; background: #f9fafb; border-bottom: 1px solid #e5e7eb; padding: 10px 12px; } |
| @@ -72,8 +110,11 @@ | ||
| 72 | 110 | <body> |
| 73 | 111 | <table class="header"> |
| 74 | 112 | <tr> |
| 75 | 113 | <td class="brand"> |
| 114 | + <?php if ($brandLogoUrl !== ''): ?> | |
| 115 | + <img class="brand-logo" src="<?php echo htmlspecialchars($brandLogoUrl, ENT_QUOTES, 'UTF-8'); ?>" alt=""> | |
| 116 | + <?php endif; ?> | |
| 76 | 117 | <h1><?php echo htmlspecialchars($companyName, ENT_QUOTES, 'UTF-8'); ?></h1> |
| 77 | 118 | <p><?php esc_html_e('Payment Invoice', 'yatra'); ?></p> |
| 78 | 119 | </td> |
| 79 | 120 | </tr> |
| @@ -84,8 +125,11 @@ | ||
| 84 | 125 | <tr> |
| 85 | 126 | <td style="width: 40%;"> |
| 86 | 127 | <h3><?php esc_html_e('Invoice To', 'yatra'); ?></h3> |
| 87 | 128 | <div><strong><?php echo htmlspecialchars($customerName, ENT_QUOTES, 'UTF-8'); ?></strong></div> |
| 129 | + <?php foreach ($customerAddressLines as $addressLine): ?> | |
| 130 | + <div class="muted"><?php echo htmlspecialchars($addressLine, ENT_QUOTES, 'UTF-8'); ?></div> | |
| 131 | + <?php endforeach; ?> | |
| 88 | 132 | <div class="muted"><?php echo htmlspecialchars($customerEmail, ENT_QUOTES, 'UTF-8'); ?></div> |
| 89 | 133 | </td> |
| 90 | 134 | <td style="width: 30%;"> |
| 91 | 135 | <h3><?php esc_html_e('Invoice Details', 'yatra'); ?></h3> |
| @@ -91,9 +135,13 @@ | ||
| 91 | 135 | <h3><?php esc_html_e('Invoice Details', 'yatra'); ?></h3> |
| 92 | 136 | <table class="details"> |
| 93 | 137 | <tr> |
| 94 | 138 | <td class="k"><?php esc_html_e('Invoice #:', 'yatra'); ?></td> |
| 95 | - <td class="v-wrap"><?php echo htmlspecialchars($paymentRef, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 139 | + <?php // Always the booking reference — consistent whether the invoice is | |
| 140 | + // downloaded from the confirmation page or after a payment. Previously | |
| 141 | + // this used $paymentRef, which the post-payment path set to the payment | |
| 142 | + // number (PAY-xxxxxx), so the invoice number changed per download. ?> | |
| 143 | + <td class="v-wrap"><?php echo htmlspecialchars($bookingRef !== '' ? $bookingRef : $paymentRef, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 96 | 144 | </tr> |
| 97 | 145 | <tr> |
| 98 | 146 | <td class="k"><?php esc_html_e('Date:', 'yatra'); ?></td> |
| 99 | 147 | <td class="v"><?php echo htmlspecialchars($paymentDate, ENT_QUOTES, 'UTF-8'); ?></td> |
| @@ -106,11 +154,11 @@ | ||
| 106 | 154 | </td> |
| 107 | 155 | <td style="width: 30%;"> |
| 108 | 156 | <h3><?php esc_html_e('Company', 'yatra'); ?></h3> |
| 109 | 157 | <div><?php echo htmlspecialchars($companyName, ENT_QUOTES, 'UTF-8'); ?></div> |
| 110 | - <?php if ($companyAddress !== ''): ?> | |
| 111 | - <div class="muted"><?php echo htmlspecialchars($companyAddress, ENT_QUOTES, 'UTF-8'); ?></div> | |
| 112 | - <?php endif; ?> | |
| 158 | + <?php foreach ($companyAddressLines as $companyAddressLine): ?> | |
| 159 | + <div class="muted"><?php echo htmlspecialchars($companyAddressLine, ENT_QUOTES, 'UTF-8'); ?></div> | |
| 160 | + <?php endforeach; ?> | |
| 113 | 161 | <?php if ($companyEmail !== ''): ?> |
| 114 | 162 | <div class="muted"><?php echo htmlspecialchars($companyEmail, ENT_QUOTES, 'UTF-8'); ?></div> |
| 115 | 163 | <?php endif; ?> |
| 116 | 164 | <?php if ($companyPhone !== ''): ?> |
| @@ -136,9 +184,9 @@ | ||
| 136 | 184 | <div class="muted"><?php esc_html_e('Payment via', 'yatra'); ?> <?php echo htmlspecialchars($paymentMethod, ENT_QUOTES, 'UTF-8'); ?></div> |
| 137 | 185 | </td> |
| 138 | 186 | <td><?php echo htmlspecialchars($bookingRef, ENT_QUOTES, 'UTF-8'); ?></td> |
| 139 | 187 | <td><?php echo htmlspecialchars($travelDate, ENT_QUOTES, 'UTF-8'); ?></td> |
| 140 | - <td class="right"><strong><?php echo $currencySymbol . htmlspecialchars($amount, ENT_QUOTES, 'UTF-8'); ?></strong></td> | |
| 188 | + <td class="right"><strong><?php echo htmlspecialchars($amount, ENT_QUOTES, 'UTF-8'); ?></strong></td> | |
| 141 | 189 | </tr> |
| 142 | 190 | </tbody> |
| 143 | 191 | </table> |
| 144 | 192 | |
| @@ -145,33 +193,63 @@ | ||
| 145 | 193 | <table class="totals"> |
| 146 | 194 | <?php if (!empty($tax_breakdown)): ?> |
| 147 | 195 | <tr> |
| 148 | 196 | <td class="label"><?php esc_html_e('Subtotal', 'yatra'); ?></td> |
| 149 | - <td class="value"><?php echo $currencySymbol . htmlspecialchars($subtotal, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 197 | + <td class="value"><?php echo htmlspecialchars($subtotal, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 150 | 198 | </tr> |
| 151 | 199 | <?php foreach ($tax_breakdown as $tax): ?> |
| 152 | 200 | <tr> |
| 153 | 201 | <td class="label"><?php echo htmlspecialchars($tax['name'], ENT_QUOTES, 'UTF-8'); ?> (<?php echo htmlspecialchars($tax['rate'], ENT_QUOTES, 'UTF-8'); ?>%)</td> |
| 154 | - <td class="value"><?php echo $currencySymbol . htmlspecialchars($tax['amount'], ENT_QUOTES, 'UTF-8'); ?></td> | |
| 202 | + <td class="value"><?php echo htmlspecialchars($tax['amount'], ENT_QUOTES, 'UTF-8'); ?></td> | |
| 155 | 203 | </tr> |
| 156 | 204 | <?php endforeach; ?> |
| 157 | 205 | <?php endif; ?> |
| 158 | 206 | <tr> |
| 159 | 207 | <td class="label"><?php esc_html_e('Booking Total', 'yatra'); ?></td> |
| 160 | - <td class="value"><?php echo $currencySymbol . htmlspecialchars($bookingTotal, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 208 | + <td class="value"><?php echo htmlspecialchars($bookingTotal, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 161 | 209 | </tr> |
| 162 | 210 | <tr> |
| 163 | 211 | <td class="label"><?php esc_html_e('Total Paid', 'yatra'); ?></td> |
| 164 | - <td class="value"><?php echo $currencySymbol . htmlspecialchars($amountPaid, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 212 | + <td class="value"><?php echo htmlspecialchars($amountPaid, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 165 | 213 | </tr> |
| 166 | 214 | <tr> |
| 167 | 215 | <td class="label"><?php esc_html_e('Balance Due', 'yatra'); ?></td> |
| 168 | - <td class="value"><?php echo $currencySymbol . htmlspecialchars($amountDue, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 216 | + <td class="value"><?php echo htmlspecialchars($amountDue, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 169 | 217 | </tr> |
| 170 | 218 | <tr> |
| 171 | 219 | <td class="label grand"><?php esc_html_e('This Payment', 'yatra'); ?></td> |
| 172 | - <td class="value grand"><?php echo $currencySymbol . htmlspecialchars($amount, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 220 | + <td class="value grand"><?php echo htmlspecialchars($amount, ENT_QUOTES, 'UTF-8'); ?></td> | |
| 173 | 221 | </tr> |
| 174 | 222 | </table> |
| 223 | + | |
| 224 | + <?php if (!empty($paymentInstructions['rows']) && is_array($paymentInstructions['rows'])): ?> | |
| 225 | + <table class="panel" style="margin-top: 6mm;"> | |
| 226 | + <tr> | |
| 227 | + <td> | |
| 228 | + <h3><?php echo htmlspecialchars((string) ($paymentInstructions['title'] ?? __('Payment Instructions', 'yatra')), ENT_QUOTES, 'UTF-8'); ?></h3> | |
| 229 | + <table class="details"> | |
| 230 | + <?php foreach ($paymentInstructions['rows'] as $piRow): ?> | |
| 231 | + <?php if (!is_array($piRow)) { continue; } ?> | |
| 232 | + <tr> | |
| 233 | + <?php | |
| 234 | + // A gateway may supply its label with or without a | |
| 235 | + // trailing colon (Bank Transfer reuses the colon-suffixed | |
| 236 | + // strings from the confirmation page so a single | |
| 237 | + // translation covers both). Normalise so neither form | |
| 238 | + // renders "Label::". | |
| 239 | + $piLabel = rtrim(trim((string) ($piRow['label'] ?? '')), ':'); | |
| 240 | + ?> | |
| 241 | + <td class="k" style="width: 40mm;"><?php echo htmlspecialchars($piLabel, ENT_QUOTES, 'UTF-8'); ?>:</td> | |
| 242 | + <td class="v-wrap"><?php echo htmlspecialchars((string) ($piRow['value'] ?? ''), ENT_QUOTES, 'UTF-8'); ?></td> | |
| 243 | + </tr> | |
| 244 | + <?php endforeach; ?> | |
| 245 | + </table> | |
| 246 | + <?php if (!empty($paymentInstructions['note'])): ?> | |
| 247 | + <p class="muted" style="margin-top: 4mm;"><?php echo htmlspecialchars((string) $paymentInstructions['note'], ENT_QUOTES, 'UTF-8'); ?></p> | |
| 248 | + <?php endif; ?> | |
| 249 | + </td> | |
| 250 | + </tr> | |
| 251 | + </table> | |
| 252 | + <?php endif; ?> | |
| 175 | 253 | </div> |
| 176 | 254 | </body> |
| 177 | 255 | </html> |