PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 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 All 36 releases
vikbooking / admin / helpers / src / mealplan / manager.php

manager.php in VikBooking Hotel Booking Engine & PMS trunk, at admin/helpers/src/mealplan/manager.php

489 lines 12.8 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 Alessio Gaggii - E4J s.r.l.
6 * @copyright Copyright (C) 2023 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 * Meal plan manager.
16 *
17 * @since 1.16.1 (J) - 1.6.1 (WP)
18 */
19 final class VBOMealplanManager extends JObject
20 {
21 /**
22 * The singleton instance of the class.
23 *
24 * @var VBOMealplanManager
25 */
26 private static $instance = null;
27
28 /**
29 * @var array
30 */
31 private $meal_plans = [];
32
33 /**
34 * Proxy to construct the object.
35 *
36 * @param array|object $data optional data to bind.
37 * @param boolean $anew true for forcing a new instance.
38 *
39 * @return self
40 */
41 public static function getInstance($data = [], $anew = false)
42 {
43 if (is_null(static::$instance) || $anew) {
44 static::$instance = new static($data);
45 }
46
47 return static::$instance;
48 }
49
50 /**
51 * Class constructor.
52 *
53 * @param array|object $data optional data to bind.
54 */
55 public function __construct($data)
56 {
57 // make sure to load the back-end language definitions
58 if (VikBooking::isSite()) {
59 $lang = JFactory::getLanguage();
60 $lang_tag = $lang->getTag();
61 if (VBOPlatformDetection::isWordPress()) {
62 $lang->load('com_vikbooking', VIKBOOKING_LANG, $lang_tag, true);
63 $lang->attachHandler(VIKBOOKING_LIBRARIES . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR . 'admin.php', 'vikbooking');
64 } else {
65 $lang->load('com_vikbooking', JPATH_ADMINISTRATOR, $lang_tag, true);
66 $lang->load('joomla', JPATH_ADMINISTRATOR, $lang_tag, true);
67 }
68 }
69
70 // set up the various meal plans
71 $this->setup();
72
73 parent::__construct($data);
74 }
75
76 /**
77 * Tells whether the specified rate plan includes the given meal plan enum code.
78 *
79 * @param array|int $rplan either the rate plan record or rate plan ID.
80 * @param string $meal_enum the meal plan enum identifier.
81 *
82 * @return bool
83 */
84 public function ratePlanMealIncluded($rplan, $meal_enum)
85 {
86 if (is_scalar($rplan)) {
87 $rplan = VikBooking::getPriceInfo($rplan);
88 }
89
90 if (!is_array($rplan) || !$rplan) {
91 return false;
92 }
93
94 // for BC we check the dedicated flag to breakfast included
95 if (!strcasecmp($meal_enum, 'breakfast') && !empty($rplan['breakfast_included'])) {
96 return true;
97 }
98
99 if (empty($rplan['meal_plans'])) {
100 return false;
101 }
102
103 if (is_string($rplan['meal_plans'])) {
104 return (stripos($rplan['meal_plans'], $meal_enum) !== false);
105 }
106
107 if (is_array($rplan['meal_plans'])) {
108 return in_array($meal_enum, $rplan['meal_plans']);
109 }
110
111 return false;
112 }
113
114 /**
115 * Returns an associative list of meal plans included in the room rate plan.
116 *
117 * @param array|int $rplan either the rate plan record or rate plan ID.
118 *
119 * @return array
120 */
121 public function ratePlanIncludedMeals($rplan)
122 {
123 if (is_scalar($rplan)) {
124 $rplan = VikBooking::getPriceInfo($rplan);
125 }
126
127 if (!is_array($rplan) || !$rplan) {
128 return [];
129 }
130
131 $included_meals = [];
132
133 // for BC we check the dedicated flag to breakfast included
134 if (!empty($rplan['breakfast_included'])) {
135 $included_meals['breakfast'] = $this->meal_plans['breakfast'];
136 }
137
138 if (empty($rplan['meal_plans'])) {
139 return $included_meals;
140 }
141
142 if (is_string($rplan['meal_plans'])) {
143 // we support JSON encoded strings as well as comma-separated strings
144 $included = json_decode($rplan['meal_plans'], true);
145 if (is_array($included)) {
146 $rplan['meal_plans'] = $included;
147 } else {
148 $rplan['meal_plans'] = explode(',', $rplan['meal_plans']);
149 $rplan['meal_plans'] = array_map(function($mn) {
150 return trim($mn);
151 }, $rplan['meal_plans']);
152 }
153 }
154
155 if (!is_array($rplan['meal_plans']) || !$rplan['meal_plans']) {
156 return $included_meals;
157 }
158
159 foreach ($rplan['meal_plans'] as $meal_enum) {
160 if (isset($this->meal_plans[$meal_enum])) {
161 $included_meals[$meal_enum] = $this->meal_plans[$meal_enum];
162 }
163 }
164
165 return $included_meals;
166 }
167
168 /**
169 * Returns an associative list of meal plans included in OTA reservation record.
170 * For those OTA bookings downloaded by older versions of VCM, this method attempts
171 * to find the meals included in the reservation by parsing the "Meal_plan" string.
172 * Alternatively, it relies on VCM to check the Content/Product/Listing API data.
173 *
174 * @param array $res the OTA reservation record.
175 * @param array $room_res the optional room reservation record.
176 *
177 * @return array list of included meal enum values calculated on the fly.
178 */
179 public function otaDataIncludedMeals(array $res, array $room_res = [])
180 {
181 $included_meals = [];
182
183 // make sure this is actually an OTA reservation downloaded by VCM
184 if (empty($res['idorderota']) || empty($res['channel']) || empty($res['custdata'])) {
185 return $included_meals;
186 }
187
188 // this usually works for Booking.com reservations
189 foreach ($this->meal_plans as $meal_enum => $meal_name) {
190 $meal_regexp = preg_quote($meal_name);
191 if (preg_match("/meal_plan:([a-z0-9 ,\.])*{$meal_regexp}/i", $res['custdata'], $matches)) {
192 if ($matches && $matches[0]) {
193 /**
194 * Make sure the statement is not "Enjoy a convenient {meal} at the property for NN per person, per night."
195 * or "Breakfast costs US$12 per person per night." (same for "Lunch costs" and "Dinner costs").
196 *
197 * @since 1.16.2 (J) - 1.6.2 (WP)
198 */
199 if (preg_match("/enjoy|convenient/i", $matches[0])) {
200 // the meal plan is not included, it is just available at the property
201 continue;
202 } elseif (stripos($res['custdata'], "{$matches[0]} cost") !== false) {
203 // the meal plan is not included, it has a cost
204 continue;
205 }
206 }
207 $included_meals[$meal_enum] = $meal_name;
208 }
209 }
210
211 // let VCM detect any included meal plan for this OTA reservation
212 if (!$included_meals && class_exists('VCMOtaRateplan')) {
213 // this method will rely on the information stored through Content/Product/Listing APIs of the involved OTA
214 $included_meals = VCMOtaRateplan::getInstance(['meal_plans' => $this->meal_plans])->findIncludedMeals($res, $room_res);
215 }
216
217 return $included_meals;
218 }
219
220 /**
221 * Tells if a meal plan is included in the OTA reservation or room data.
222 *
223 * @param array $res the OTA reservation record.
224 * @param array $room_res the optional room reservation record.
225 * @param string $meal_enum the meal plan enum identifier.
226 *
227 * @return bool
228 */
229 public function otaDataMealIncluded(array $res, array $room_res, $meal_enum)
230 {
231 $included_meals = $this->otaDataIncludedMeals($res, $room_res);
232
233 return isset($included_meals[$meal_enum]);
234 }
235
236 /**
237 * Tells whether the given room reservation record includes the given meal plan enum code.
238 *
239 * @param array $room_res the room reservation record.
240 * @param string $meal_enum the meal plan enum identifier.
241 *
242 * @return bool
243 */
244 public function roomRateMealIncluded($room_res, $meal_enum)
245 {
246 if (!is_array($room_res) || empty($room_res['meals'])) {
247 return false;
248 }
249
250 if (is_string($room_res['meals'])) {
251 return (stripos($room_res['meals'], $meal_enum) !== false);
252 }
253
254 if (is_array($room_res['meals'])) {
255 return in_array($meal_enum, $room_res['meals']);
256 }
257
258 return false;
259 }
260
261 /**
262 * Returns an associative list of meal plans included in the room reservation record.
263 *
264 * @param array $room_res the room reservation record.
265 *
266 * @return array
267 */
268 public function roomRateIncludedMeals($room_res)
269 {
270 if (!is_array($room_res) || empty($room_res['meals'])) {
271 return [];
272 }
273
274 $meals_included = $room_res['meals'];
275
276 if (is_string($meals_included)) {
277 // we support JSON encoded strings as well as comma-separated strings
278 $included = json_decode($meals_included, true);
279 if (is_array($included)) {
280 $meals_included = $included;
281 } else {
282 $meals_included = explode(',', $meals_included);
283 $meals_included = array_map(function($mn) {
284 return trim($mn);
285 }, $meals_included);
286 }
287 }
288
289 if (!is_array($meals_included) || !$meals_included) {
290 return [];
291 }
292
293 $valid_enums = [];
294 foreach ($this->meal_plans as $meal_enum => $meal_name) {
295 if (isset($meals_included[$meal_enum]) || in_array($meal_enum, $meals_included)) {
296 $valid_enums[$meal_enum] = $meal_name;
297 }
298 }
299
300 return $valid_enums;
301 }
302
303 /**
304 * Given one or more meal plan enum code, tries to convert them to translated strings.
305 *
306 * @param array|string $meal_enum the meal plan enum identifier(s).
307 *
308 * @return string
309 */
310 public function sayMealPlanEnum($meal_enum)
311 {
312 if (!$meal_enum) {
313 return '';
314 }
315
316 $plans_included = [];
317 if (!is_array($meal_enum)) {
318 $meal_enum = [$meal_enum];
319 }
320
321 foreach ($meal_enum as $enum) {
322 if (is_string($enum) && isset($this->meal_plans[$enum])) {
323 $plans_included[] = $this->meal_plans[$enum];
324 }
325 }
326
327 if (!$plans_included) {
328 return '';
329 }
330
331 return implode(', ', array_unique($plans_included));
332 }
333
334 /**
335 * This method is mainly used by VCM when storing new reservations onto VBO. Given
336 * a raw list of meals included in the OTA room tariff, builds the string to be stored.
337 * The information about the OTA meals is usually fetched from the rooms mapping information
338 * when receiving the whole reservation payload including the OTA room and rate plan IDs.
339 * In case the OTA rate plan was mapped with a "meal_plans" property, that value will be
340 * passed along to this method for parsing the OTA enum values transmitted by the E4jConnect servers.
341 *
342 * @param string|array $ota_meals the raw meals included fetched from the OTA rate plan.
343 *
344 * @return string the meals value to be stored onto the room reservation record.
345 */
346 public function buildOTAMealPlans($ota_meals)
347 {
348 $store_meals = '';
349
350 if (empty($ota_meals)) {
351 return $store_meals;
352 }
353
354 // convert the OTA meals into an array of enums or raw names
355 if (is_string($ota_meals)) {
356 // we usually have comma-separated strings
357 $included_meals = explode(',', $ota_meals);
358 if ($included_meals && !empty($included_meals[0])) {
359 // we've got something to parse
360 $ota_meals = array_map(function($mn) {
361 return trim($mn);
362 }, $included_meals);
363 } else {
364 // attempt to decode a JSON string
365 $ota_meals = json_decode($ota_meals, true);
366 }
367 }
368
369 if (!is_array($ota_meals) || !$ota_meals) {
370 return $store_meals;
371 }
372
373 $valid_enums = [];
374 $known_enums = array_keys($this->meal_plans);
375
376 foreach ($ota_meals as $ota_meal) {
377 foreach ($known_enums as $known_enum) {
378 if ($ota_meal && !strcasecmp($ota_meal, $known_enum)) {
379 // valid meal found
380 $valid_enums[] = $known_enum;
381 // parse the next one, if any
382 break;
383 }
384 }
385 }
386
387 if ($valid_enums) {
388 // some valid OTA meals were found
389 $store_meals = json_encode(array_unique($valid_enums));
390 }
391
392 return $store_meals;
393 }
394
395 /**
396 * Returns the supported meal plans.
397 *
398 * @return array
399 */
400 public function getPlans()
401 {
402 return $this->meal_plans;
403 }
404
405 /**
406 * Returns an associative list of meal plans and short name identifier (B, L, D).
407 *
408 * @return array
409 */
410 public function getShortMealPlans()
411 {
412 return [
413 'breakfast' => JText::translate('VBO_SHORTM_BREAKFAST'),
414 'lunch' => JText::translate('VBO_SHORTM_LUNCH'),
415 'dinner' => JText::translate('VBO_SHORTM_DINNER'),
416 ];
417 }
418
419 /**
420 * Finds the ID and name of the rate plan from the given tariff ID.
421 *
422 * @param int $idtar the ID of the tariff.
423 *
424 * @return array list of rate plan name (or an empty string) and ID.
425 */
426 public function getPriceData($idtar)
427 {
428 if (empty($idtar)) {
429 return [JText::translate('VBOROOMCUSTRATEPLAN'), 0];
430 }
431
432 $dbo = JFactory::getDbo();
433
434 $q = "SELECT `p`.`id`, `p`.`name` FROM `#__vikbooking_prices` AS `p`
435 LEFT JOIN `#__vikbooking_dispcost` AS `t` ON `p`.`id`=`t`.`idprice` WHERE `t`.`id`=" . (int)$idtar;
436 $dbo->setQuery($q, 0, 1);
437 $price_record = $dbo->loadAssoc();
438
439 if ($price_record) {
440 return [$price_record['name'], $price_record['id']];
441 }
442
443 return ['', 0];
444 }
445
446 /**
447 * Prepares the various meal plans supported.
448 *
449 * @return void
450 */
451 private function setup()
452 {
453 $config = VBOFactory::getConfig();
454
455 $def_meals = $this->getDefaultMealPlans();
456
457 $meals = $config->getArray('meal_plans', []);
458 if (!$meals) {
459 $meals = $def_meals;
460 $config->set('meal_plans', $meals);
461 }
462
463 // map translations at runtime
464 foreach ($meals as $meal_enum => &$meal_name) {
465 $meal_name = $def_meals[$meal_enum];
466 }
467
468 // unset last reference
469 unset($meal_name);
470
471 // set updated map
472 $this->meal_plans = $meals;
473 }
474
475 /**
476 * Returns an associative list of default meal plans.
477 *
478 * @return array
479 */
480 private function getDefaultMealPlans()
481 {
482 return [
483 'breakfast' => JText::translate('VBO_MEAL_BREAKFAST'),
484 'lunch' => JText::translate('VBO_MEAL_LUNCH'),
485 'dinner' => JText::translate('VBO_MEAL_DINNER'),
486 ];
487 }
488 }
489