PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 All 35 releases
vikbooking / admin / controllers / quote.php

quote.php in VikBooking Hotel Booking Engine & PMS trunk, at admin/controllers/quote.php

711 lines 26.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2026 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * VikBooking quote (quotation) controller (admin).
16 *
17 * @since 1.18.8 (J) - 1.8.8 (WP)
18 */
19 class VikBookingControllerQuote extends JControllerAdmin
20 {
21 /**
22 * AJAX endpoint to save a new quotation.
23 *
24 * @return void
25 */
26 public function save()
27 {
28 $app = JFactory::getApplication();
29
30 if (!JSession::checkToken()) {
31 // missing CSRF-proof token
32 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
33 }
34
35 // gather request values
36 $quote = $app->input->get('quote', [], 'array');
37 $sessionId = $app->input->getUInt('session_id', 0);
38
39 if (empty($quote['customer']['id']) && empty($quote['customer']['first_name'])) {
40 // missing customer data
41 VBOHttpDocument::getInstance($app)->close(400, 'Missing customer information.');
42 }
43
44 // filter out invalid booking solutions
45 $quote['solutions'] = array_values(array_filter((array) ($quote['solutions'] ?? []), function($solution) {
46 if (empty($solution['checkin']) || empty($solution['checkout']) || empty($solution['rooms']) || !is_array($solution['rooms'])) {
47 // missing stay dates or booking rooms
48 return false;
49 }
50
51 foreach ($solution['rooms'] as $roomSol) {
52 if (empty($roomSol['id'])) {
53 // missing room ID
54 return false;
55 }
56 if ((empty($roomSol['id_price']) || empty($roomSol['room_cost'])) && empty($roomSol['cust_cost'])) {
57 // missing room rate, either a rate plan or a custom rate
58 return false;
59 }
60 if (empty($roomSol['adults']) && empty($roomSol['children'])) {
61 // invalid guest party
62 return false;
63 }
64 }
65
66 return true;
67 }));
68
69 if (empty($quote['solutions'])) {
70 // missing booking solutions
71 VBOHttpDocument::getInstance($app)->close(400, 'No valid booking solutions.');
72 }
73
74 // access customer model
75 $customerModel = VBOMvcModel::getInstance('customer');
76
77 // build customer information
78 $customerInfo = [
79 'first_name' => $quote['customer']['first_name'],
80 'last_name' => ($quote['customer']['last_name'] ?? '') ?: '(Quote)',
81 'email' => $quote['customer']['email'] ?? null,
82 'phone' => $quote['customer']['phone'] ?? null,
83 'country' => $quote['customer']['country'] ?? null,
84 ];
85
86 if (!empty($quote['customer']['id'])) {
87 // make sure the given customer exists
88 $customer = $customerModel->getItem((int) $quote['customer']['id']);
89 if (!$customer) {
90 // abort on error
91 VBOHttpDocument::getInstance($app)->close(404, 'Customer ID not found.');
92 }
93 // assign customer ID and information
94 $customerId = $customer->id;
95 $customerInfo = (array) $customer;
96 } else {
97 // save new customer or update existing
98 $customerId = $customerModel->save($customerInfo);
99 }
100
101 // access quote model
102 $quoteModel = VBOMvcModel::getInstance('quote');
103
104 // save quote record
105 $quoteId = $quoteModel->save([
106 'name' => ($quote['name'] ?? '') ?: date('Y-m-d H:i:s'),
107 'subject' => $quote['message']['subject'] ?? null,
108 'message' => $quote['message']['content'] ?? null,
109 'notes' => $quote['message']['notes'] ?? null,
110 'valid_until' => $quote['validity'] ?? null,
111 'idcustomer' => $customerId ?: null,
112 'first_name' => $quote['customer']['first_name'],
113 'last_name' => $quote['customer']['last_name'] ?? null,
114 'email' => $quote['customer']['email'] ?? null,
115 'phone' => $quote['customer']['phone'] ?? null,
116 'country_3_code' => $quote['customer']['country'] ?? null,
117 'preferred' => !empty($quote['message']['preferred']) ? 1 : 0,
118 'sent' => !empty($quote['message']['send']) ? 1 : 0,
119 ]);
120
121 if (!$quoteId) {
122 // raise an error
123 VBOHttpDocument::getInstance($app)->close(500, sprintf('Could not save quote record: %s.', (string) ($quoteModel->getError() ?: '---')));
124 }
125
126 foreach ($quote['solutions'] as $index => $solution) {
127 // access a new instance of the reservation model
128 $resModel = VBOModelReservation::getInstance([
129 'status' => 'standby',
130 'type' => 'quote',
131 'checkin' => strtotime($solution['checkin']),
132 'checkout' => strtotime($solution['checkout']),
133 'num_rooms' => count($solution['rooms']),
134 'adults' => array_sum(array_column($solution['rooms'], 'adults')),
135 'children' => array_sum(array_column($solution['rooms'], 'children')),
136 'id_payment' => $quote['id_payment'] ?? null,
137 'lock_until' => $quote['validity'] ?? null,
138 'dont_lock' => empty($quote['validity']),
139 'idquote' => $quoteId,
140 ], true);
141
142 // set booking customer
143 $resModel->setCustomer([
144 'id' => $customerId,
145 'first_name' => $customerInfo['first_name'],
146 'last_name' => $customerInfo['last_name'],
147 'email' => $customerInfo['email'],
148 'country' => $customerInfo['country'],
149 'phone' => $customerInfo['phone'],
150 ]);
151
152 // scan all booking solution rooms
153 $roomSolutions = [];
154 foreach ($solution['rooms'] as $roomSolution) {
155 // check if we have a custom cancellation policy ID to apply
156 $custCancPolicyId = null;
157 if (!empty($roomSolution['cust_cost']) && !empty($roomSolution['id_price'])) {
158 // apply the selected rate plan for the custom cancellation policy
159 $custCancPolicyId = $roomSolution['id_price'];
160 }
161
162 // push booking solution room
163 $roomSolutions[] = [
164 'id' => $roomSolution['id'],
165 'adults' => $roomSolution['adults'] ?? null,
166 'children' => $roomSolution['children'] ?? null,
167 'id_price' => $roomSolution['id_price'] ?? null,
168 'room_cost' => $roomSolution['room_cost'] ?? null,
169 'cust_cost' => $roomSolution['cust_cost'] ?? null,
170 'id_tax' => $roomSolution['id_tax'] ?? null,
171 'cust_cpolicy_id' => $custCancPolicyId,
172 'options' => $roomSolution['options'] ?? null,
173 'extras' => $roomSolution['extras'] ?? null,
174 ];
175 }
176
177 // set all booking rooms
178 $resModel->setRooms($roomSolutions);
179
180 // store the reservation
181 $resModel->create();
182
183 // get the new booking ID
184 $res_id = $resModel->getNewBookingID();
185
186 if (!$res_id) {
187 // delete quote
188 $quoteModel->delete($quoteId);
189
190 // abort on error
191 VBOHttpDocument::getInstance($app)->close(500, sprintf('Error saving booking solution #%d: %s', ($index + 1), (string) ($resModel->getError() ?: '---')));
192 }
193 }
194
195 // check if the quote should be sent via email
196 $sendResultErr = null;
197 if (!empty($quote['message']['send'])) {
198 // send the quotation to the guest via email
199 try {
200 $quoteModel->sendEmail($quoteId);
201 } catch (Exception $e) {
202 // catch the error message
203 $sendResultErr = $e->getMessage();
204 }
205 }
206
207 // check if the quote was originated from a chat session
208 if ($sessionId) {
209 try {
210 // link the newly generated quote to the initial chat session
211 (new VBOChatSessionModel)->setMetadata($sessionId, 'quote_id', $quoteId);
212 } catch (Exception $e) {
213 // do nothing on error
214 }
215 }
216
217 // send the response to output
218 VBOHttpDocument::getInstance($app)->json([
219 'success' => true,
220 'idquote' => $quoteId,
221 'sending_error' => $sendResultErr,
222 ]);
223 }
224
225 /**
226 * AJAX endpoint to update a quotation.
227 *
228 * @return void
229 */
230 public function update()
231 {
232 $app = JFactory::getApplication();
233
234 if (!JSession::checkToken()) {
235 // missing CSRF-proof token
236 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
237 }
238
239 // gather request values
240 $quote = $app->input->get('quote', [], 'array');
241
242 if (empty($quote['id'])) {
243 VBOHttpDocument::getInstance($app)->close(400, 'Missing quote ID to update.');
244 }
245
246 // access quote model
247 $quoteModel = VBOMvcModel::getInstance('quote');
248
249 // get requested quote record
250 $quoteRecord = $quoteModel->getItem($quote['id']);
251
252 if (!$quoteRecord) {
253 VBOHttpDocument::getInstance($app)->close(404, 'Quote record not found.');
254 }
255
256 // update record
257 $result = $quoteModel->save([
258 'id' => $quoteRecord->id,
259 'name' => $quote['name'] ?? null,
260 'subject' => $quote['message']['subject'] ?? null,
261 'message' => $quote['message']['content'] ?? null,
262 'notes' => $quote['message']['notes'] ?? null,
263 'valid_until' => $quote['validity'] ?? null,
264 'idcustomer' => $quote['customer']['id'] ?? null,
265 'first_name' => $quote['customer']['first_name'] ?? null,
266 'last_name' => $quote['customer']['last_name'] ?? null,
267 'email' => $quote['customer']['email'] ?? null,
268 'phone' => $quote['customer']['phone'] ?? null,
269 'country_3_code' => $quote['customer']['country'] ?? null,
270 'preferred' => !empty($quote['message']['preferred']) ? 1 : 0,
271 ]);
272
273 if (!$result) {
274 // abort
275 VBOHttpDocument::getInstance($app)->close(500, 'Could not update quote record.');
276 }
277
278 // check if we have a different validity date for the quote
279 if (!empty($quote['validity']) && JFactory::getDate($quote['validity'], $app->get('offset'))->toSql() != $quoteRecord->valid_until) {
280 // validity date has changed, booking solutions must be updated
281 $quoteData = $quoteModel->loadBookingRecords(0, 0, [
282 'filters' => [
283 'id' => $quoteRecord->id,
284 ],
285 ])[0] ?? null;
286
287 if (!$quoteData) {
288 // abort
289 VBOHttpDocument::getInstance($app)->close(500, 'Quote record missing booking solutions.');
290 }
291
292 // extract confirmed booking solutions
293 $confirmedBookings = array_filter($quoteData->solutions ?? [], function($solution) {
294 return ($solution->status ?? '') == 'confirmed';
295 });
296
297 // extract pending booking solutions
298 $pendingBookings = array_filter($quoteData->solutions ?? [], function($solution) {
299 return ($solution->status ?? '') == 'standby';
300 });
301
302 // make sure the quote still needs to be confirmed
303 if (!$confirmedBookings && $pendingBookings) {
304 // determine the timestamp to lock until
305 $lockUntilTs = strtotime($quote['validity']);
306
307 // check whether the CM may require to update previously locked records
308 if (method_exists('VCMRequestAvailability', 'updateReleaseDate')) {
309 // let the CM update the release date of the involved and unconfirmed booking IDs, if needed
310 VCMRequestAvailability::getInstance()->updateReleaseDate(array_column($pendingBookings, 'id'), $lockUntilTs);
311 }
312
313 // access temp-lock model
314 $tmpLockModel = VBOMvcModel::getInstance('tmplock');
315
316 // scan all pending reservations
317 foreach ($pendingBookings as $pendingBooking) {
318 // delete any previously locked record for this booking
319 $tmpLockModel->deleteFromBooking($pendingBooking->id);
320 // scan all booking rooms
321 foreach ($pendingBooking->rooms as $pendingRoomBooking) {
322 // lock records temporarily until new validity date
323 $tmpLockModel->save([
324 'idroom' => $pendingRoomBooking->id,
325 'checkin' => $pendingBooking->checkin,
326 'checkout' => $pendingBooking->checkout,
327 'until' => $lockUntilTs,
328 'realback' => $pendingBooking->checkout,
329 'idorder' => $pendingBooking->id,
330 ]);
331 }
332 if (VikBooking::vcmAutoUpdate() > 0) {
333 // silently trigger an availability update request for this pending booking
334 $vcm_obj = VikBooking::getVcmInvoker();
335 $vcm_obj->setOids([$pendingBooking->id])->setSyncType('new');
336 // ignore the update result
337 $sync_result = $vcm_obj->doSync();
338 }
339 }
340 }
341 }
342
343 // send the response to output
344 VBOHttpDocument::getInstance($app)->json([
345 'success' => true,
346 ]);
347 }
348
349 /**
350 * AJAX endpoint to delete a quotation.
351 *
352 * @return void
353 */
354 public function delete()
355 {
356 $app = JFactory::getApplication();
357
358 if (!JSession::checkToken()) {
359 // missing CSRF-proof token
360 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
361 }
362
363 // gather quotation ID
364 $quoteId = $app->input->getUInt('quote_id', 0);
365
366 if (empty($quoteId)) {
367 VBOHttpDocument::getInstance($app)->close(400, 'Missing quote ID to delete.');
368 }
369
370 // access quote model
371 $quoteModel = VBOMvcModel::getInstance('quote');
372
373 // get requested quote data
374 $quoteData = $quoteModel->loadBookingRecords(0, 0, [
375 'filters' => [
376 'id' => $quoteId,
377 ],
378 ])[0] ?? null;
379
380 if (!$quoteData) {
381 VBOHttpDocument::getInstance($app)->close(404, 'Quote record not found.');
382 }
383
384 // extract confirmed booking solutions
385 $confirmedBookings = array_filter($quoteData->solutions ?? [], function($solution) {
386 return ($solution->status ?? '') == 'confirmed';
387 });
388
389 // extract pending booking solutions
390 $pendingBookings = array_filter($quoteData->solutions ?? [], function($solution) {
391 return ($solution->status ?? '') == 'standby';
392 });
393
394 if ($confirmedBookings) {
395 // do not proceed
396 VBOHttpDocument::getInstance($app)->close(400, 'Cannot delete quote record because one of its booking solutions is confirmed. Delete reservations first.');
397 }
398
399 // access reservation model
400 $resModel = VBOModelReservation::getInstance();
401
402 // scan all pending bookings involved, if any
403 foreach ($pendingBookings as $pendingBooking) {
404 // set reservation status to cancelled
405 $resModel->delete([
406 'booking_id' => $pendingBooking->id,
407 'cancellation_reason' => sprintf('Quote #%d cancelled.', $quoteData->id),
408 ]);
409 }
410
411 // delete quote record
412 $result = $quoteModel->delete($quoteData->id);
413
414 if (!$result) {
415 // raise error
416 VBOHttpDocument::getInstance($app)->close(500, 'Deleting requested quote record failed.');
417 }
418
419 // send the response to output
420 VBOHttpDocument::getInstance($app)->json([
421 'success' => true,
422 ]);
423 }
424
425 /**
426 * AJAX endpoint to send a quotation via email.
427 *
428 * @return void
429 */
430 public function sendMail()
431 {
432 $app = JFactory::getApplication();
433
434 if (!JSession::checkToken()) {
435 // missing CSRF-proof token
436 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
437 }
438
439 // gather quote ID
440 $quoteId = $app->input->getUInt('quote_id', 0);
441
442 if (!$quoteId) {
443 // missing quotation ID
444 VBOHttpDocument::getInstance($app)->close(400, 'Missing quotation ID.');
445 }
446
447 // access quote model
448 $quoteModel = VBOMvcModel::getInstance('quote');
449
450 try {
451 // send the quotation via email
452 $quoteModel->sendEmail($quoteId);
453 } catch (Exception $e) {
454 // propagate the error
455 VBOHttpDocument::getInstance($app)->close($e->getCode() ?: 500, $e->getMessage());
456 }
457
458 // send the response to output
459 VBOHttpDocument::getInstance($app)->json([
460 'success' => true,
461 ]);
462 }
463
464 /**
465 * AJAX endpoint to send a quotation via messaging account (i.e. WhatsApp).
466 *
467 * @return void
468 */
469 public function sendMessaging()
470 {
471 $app = JFactory::getApplication();
472
473 if (!JSession::checkToken()) {
474 // missing CSRF-proof token
475 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
476 }
477
478 // gather quote ID and template configuration data
479 $quoteId = $app->input->getUInt('quote_id', 0);
480 $accountId = $app->input->getString('account_id', '');
481 $phoneId = $app->input->getString('phone_id', '');
482 $configId = $app->input->getUInt('config_id', 0);
483
484 if (!$quoteId) {
485 // missing quotation ID
486 VBOHttpDocument::getInstance($app)->close(400, 'Missing quotation ID.');
487 }
488
489 if (empty($accountId) || empty($phoneId)) {
490 // missing account data
491 VBOHttpDocument::getInstance($app)->close(400, 'Missing messaging account information.');
492 }
493
494 // access quote model
495 $quoteModel = VBOMvcModel::getInstance('quote');
496
497 // get current quote and related booking record(s)
498 $quoteData = $quoteModel->loadBookingRecords(0, 0, [
499 'filters' => [
500 'id' => $quoteId,
501 ],
502 ])[0] ?? null;
503
504 if (!$quoteData) {
505 // quotation not found
506 VBOHttpDocument::getInstance($app)->close(404, 'Quotation not found.');
507 }
508
509 // access booking registry through the first quote booking solution
510 try {
511 $booking = VBOBookingRegistry::getInstance(['id' => $quoteData->solutions[0]->id ?? null]);
512 } catch (Exception $e) {
513 // propagate error
514 VBOHttpDocument::getInstance($app)->close($e->getCode() ?: 404, 'Could not load quote booking solution.');
515 }
516
517 // get recipient (guest) phone number
518 $phoneNumber = ($quoteData->phone ?? null) ?: $booking->getPhoneNumber();
519 if (!$phoneNumber) {
520 // unable to proceed without a recipient
521 VBOHttpDocument::getInstance($app)->close(400, 'Missing guest recipient phone number.');
522 }
523
524 if (!class_exists('VCMMessagingAccountsModel')) {
525 // unsupported request
526 VBOHttpDocument::getInstance($app)->close(400, 'Missing Channel Manager with messaging accounts.');
527 }
528
529 // access messaging accounts model
530 $maModel = VCMMessagingAccountsModel::getInstance();
531
532 // fetch requested messaging account record
533 $accountRecord = $maModel->getItem([
534 'account_id' => $accountId,
535 'phone_id' => $phoneId,
536 ]);
537
538 if (!$accountRecord || empty($accountRecord->settings['configurations'][$configId])) {
539 VBOHttpDocument::getInstance($app)->close(404, 'Could not find the requested messaging account.');
540 }
541
542 // load channel details
543 $channelDetails = VikChannelManager::getChannel($accountRecord->idchannel);
544 if (!$channelDetails) {
545 VBOHttpDocument::getInstance($app)->close(404, 'Messaging channel not found.');
546 }
547
548 // register booking messaging account provider
549 $maModel->setBookingAccountProvider($booking, $accountRecord);
550
551 // access chat handler first
552 $chatHandler = VikBooking::getVcmChatInstance($booking->getID(), $channelDetails['name']);
553
554 if (!$chatHandler) {
555 VBOHttpDocument::getInstance($app)->close(500, 'Could not invoke proper chat handler.');
556 }
557
558 // build chat message object
559 $chatMessage = new VCMChatMessage(
560 // no message content
561 '',
562 // no attachments
563 [],
564 // data to bind
565 [
566 'recipient' => $phoneNumber,
567 'account' => $accountRecord,
568 'message_template' => $configId,
569 'quote_data' => $quoteData,
570 'mark_previous_replied' => true,
571 ]
572 );
573
574 // inject thread ID with recipient phone number
575 $chatMessage->set('idthread', $phoneNumber);
576
577 try {
578 // send message
579 if (!$chatHandler->send($chatMessage)) {
580 throw new Exception($chatHandler->getError() ?: 'Could not send the message template to recipient phone number.', 500);
581 }
582 } catch (Exception $e) {
583 // propagate error
584 VBOHttpDocument::getInstance($app)->close($e->getCode() ?: 500, $e->getMessage());
585 }
586
587 // flag record and "sent"
588 $quoteModel->save([
589 'id' => $quoteId,
590 'sent' => 1,
591 ]);
592
593 // send the response to output
594 VBOHttpDocument::getInstance($app)->json([
595 'success' => true,
596 'recipient' => $phoneNumber,
597 ]);
598 }
599
600 /**
601 * AJAX endpoint to toggle or update the preferred status of a quotation.
602 *
603 * @return void
604 */
605 public function togglePreferred()
606 {
607 $app = JFactory::getApplication();
608
609 if (!JSession::checkToken()) {
610 // missing CSRF-proof token
611 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
612 }
613
614 // gather quote ID and optional preferred status
615 $quoteId = $app->input->getUInt('quote_id', 0);
616 $status = $app->input->get('status', null);
617
618 if (!$quoteId) {
619 // missing quotation ID
620 VBOHttpDocument::getInstance($app)->close(400, 'Missing quotation ID.');
621 }
622
623 // access quote model
624 $quoteModel = VBOMvcModel::getInstance('quote');
625
626 // get current record
627 $quoteRecord = $quoteModel->getItem($quoteId);
628
629 if (!$quoteRecord) {
630 // quotation not found
631 VBOHttpDocument::getInstance($app)->close(404, 'Quotation not found.');
632 }
633
634 // determine new status
635 $preferredStatus = $quoteRecord->preferred ? 0 : 1;
636 if ($status !== null) {
637 $preferredStatus = (int) boolval($status);
638 }
639
640 // update record
641 $result = $quoteModel->save([
642 'id' => $quoteRecord->id,
643 'preferred' => $preferredStatus,
644 ]);
645
646 if (!$result) {
647 // abort
648 VBOHttpDocument::getInstance($app)->close(500, 'Could not update quote record.');
649 }
650
651 // send the response to output
652 VBOHttpDocument::getInstance($app)->json([
653 'success' => true,
654 ]);
655 }
656
657 /**
658 * AJAX endpoint to get a list of messaging template configurations for a given quote.
659 *
660 * @return void
661 */
662 public function getMessagingConfigurations()
663 {
664 $app = JFactory::getApplication();
665
666 if (!JSession::checkToken()) {
667 // missing CSRF-proof token
668 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
669 }
670
671 // gather quote ID
672 $quoteId = $app->input->getUInt('quote_id', 0);
673
674 if (!$quoteId) {
675 // missing quotation ID
676 VBOHttpDocument::getInstance($app)->close(400, 'Missing quotation ID.');
677 }
678
679 // access quote model
680 $quoteModel = VBOMvcModel::getInstance('quote');
681
682 // get current record
683 $quoteRecord = $quoteModel->getItem($quoteId);
684
685 if (!$quoteRecord) {
686 // quotation not found
687 VBOHttpDocument::getInstance($app)->close(404, 'Quotation not found.');
688 }
689
690 if (!class_exists('VCMMessagingAccountsModel')) {
691 // unsupported feature
692 VBOHttpDocument::getInstance($app)->close(400, 'Missing required Channel Manager and Messaging services.');
693 }
694
695 // load messaging account configurations data by injecting the current quote
696 $configurationsData = VCMMessagingAccountsModel::getInstance()->getConfigurationsData(
697 new VCMMessagingTemplateDecoratorQuote($quoteId)
698 );
699
700 if (!$configurationsData) {
701 // abort
702 VBOHttpDocument::getInstance($app)->close(400, 'Missing configuration for Channel Manager messaging services. No messaging templates found.');
703 }
704
705 // send the response to output
706 VBOHttpDocument::getInstance($app)->json([
707 'data' => $configurationsData,
708 ]);
709 }
710 }
711