PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / site / models / packagesconfirm.php
vikappointments / site / models Last commit date
allorders.php 2 days ago calendarweek.php 2 days ago cart.php 2 days ago confirmapp.php 2 days ago empaccountstat.php 2 days ago empattachser.php 2 days ago empcoupons.php 2 days ago empcustfields.php 2 days ago empeditcoupon.php 2 days ago empeditcustfield.php 2 days ago empeditlocation.php 2 days ago empeditpay.php 2 days ago empeditprofile.php 2 days ago empeditservice.php 2 days ago empeditwdays.php 2 days ago emplocations.php 2 days ago emplocwdays.php 2 days ago emplogin.php 2 days ago employeesearch.php 2 days ago employeeslist.php 2 days ago empmanres.php 2 days ago emppaylist.php 2 days ago empserviceslist.php 2 days ago empsettingsman.php 2 days ago empsubscrcart.php 2 days ago empsubscrhistory.php 2 days ago empsubscrorder.php 2 days ago empwdays.php 2 days ago index.html 2 days ago packages.php 2 days ago packagescart.php 2 days ago packagesconfirm.php 2 days ago packorders.php 2 days ago servicesearch.php 2 days ago serviceslist.php 2 days ago subscrcart.php 2 days ago subscrhistory.php 2 days ago subscrpayment.php 2 days ago
packagesconfirm.php
466 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 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 VAPLoader::import('libraries.mvc.model');
15
16 /**
17 * VikAppointments packages confirmation view model.
18 *
19 * @since 1.7
20 */
21 class VikAppointmentsModelPackagesconfirm extends JModelVAP
22 {
23 /**
24 * Completes the booking process by saving the purchased packages.
25 *
26 * @param array $data An array containing some booking options.
27 *
28 * @return mixed The landing page URL on success, false otherwise.
29 */
30 public function save($data)
31 {
32 $dispatcher = VAPFactory::getEventDispatcher();
33
34 // get cart model
35 $model = JModelVAP::getInstance('packagescart');
36 // get cart instance
37 $cart = $model->getCart();
38
39 ////////////////////////////////////////////////////////////
40 ////////////////////// INITIALIZATION //////////////////////
41 ////////////////////////////////////////////////////////////
42
43 if ($cart->isEmpty())
44 {
45 // cart is empty
46 $this->setError(JText::translate('VAPCARTEMPTYERR'));
47 return false;
48 }
49
50 $user = JFactory::getUser();
51
52 if ($user->guest)
53 {
54 // the user must be logged in
55 $this->setError(JText::translate('VAPPACKLOGINREQERR'));
56 return false;
57 }
58
59 try
60 {
61 /**
62 * Trigger event to manipulate the cart instance.
63 *
64 * @param mixed $cart The cart instance.
65 *
66 * @return void
67 *
68 * @since 1.7
69 * @since 1.7.8 It is now possible to throw exceptions to abort the saving process.
70 */
71 $dispatcher->trigger('onInitSavePackagesOrder', [$cart]);
72 }
73 catch (Exception $e)
74 {
75 $this->setError($e->getMessage());
76 return false;
77 }
78
79 ////////////////////////////////////////////////////////////
80 //////////////////// FETCH CUSTOM FIELDS ///////////////////
81 ////////////////////////////////////////////////////////////
82
83 // prepare order array
84 $order = array();
85
86 // register current language tag
87 $order['langtag'] = JFactory::getLanguage()->getTag();
88
89 // import custom fields requestor and loader (as dependency)
90 VAPLoader::import('libraries.customfields.requestor');
91
92 // get relevant custom fields only
93 $_cf = VAPCustomFieldsLoader::getInstance()
94 ->setLanguageFilter($order['langtag'])
95 ->noSeparator()
96 ->onPage('confirm')
97 ->fetch();
98
99 try
100 {
101 // load custom fields from request
102 $order['custom_f'] = VAPCustomFieldsRequestor::loadForm($_cf, $tmp, $strict = true);
103 }
104 catch (Exception $e)
105 {
106 // catch exception and register it as error message
107 $this->setError($e->getMessage());
108 return false;
109 }
110
111 // merge custom fields and uploaded files
112 $order['custom_f'] = array_merge($order['custom_f'], $tmp['uploads']);
113
114 /**
115 * Trigger event to manipulate the custom fields array and the
116 * billing information of the customer, extrapolated from the rules
117 * of the custom fields.
118 *
119 * @param array &$fields The custom fields values.
120 * @param array &$args The billing array.
121 *
122 * @return void
123 *
124 * @since 1.7
125 */
126 $dispatcher->trigger('onPrepareFieldsSavePackagesOrder', array(&$order['custom_f'], &$tmp));
127
128 // register data fetched by the custom fields so that the package order
129 // model is able to use them for saving purposes
130 $order['fields_data'] = $tmp;
131
132 if (empty($order['fields_data']['purchaser_nominative']))
133 {
134 // use name of the currently logged-in user
135 $order['fields_data']['purchaser_nominative'] = $user->name;
136 }
137
138 if (empty($order['fields_data']['purchaser_mail']))
139 {
140 // use e-mail of the currently logged-in user
141 $order['fields_data']['purchaser_mail'] = $user->email;
142 }
143
144 ////////////////////////////////////////////////////////////
145 ///////////////////// VALIDATE PAYMENT /////////////////////
146 ////////////////////////////////////////////////////////////
147
148 $payment = null;
149
150 if ($cart->getTotalGross() > 0)
151 {
152 // load supported payments
153 $payments = VikAppointments::getPayments('packages');
154
155 if (!isset($data['id_payment']))
156 {
157 $data['id_payment'] = 0;
158 }
159
160 // unset payment charge
161 $order['payment_charge'] = 0;
162 $order['payment_tax'] = 0;
163
164 /**
165 * Trigger event to manipulate the selected payment gateway.
166 *
167 * @param integer &$id_payment The ID of the selected payment.
168 * @param array &$payments The list of the available payments.
169 *
170 * @return void
171 *
172 * @since 1.7
173 */
174 $dispatcher->trigger('onSwitchPaymentSavePackagesOrder', array(&$data['id_payment'], &$payments));
175
176 if ($payments)
177 {
178 // search for the selected gateway
179 $payments = array_filter($payments, function($gateway) use ($data)
180 {
181 return $gateway['id'] == $data['id_payment'];
182 });
183
184 // take the first payment found
185 $payment = array_shift($payments);
186
187 if (!$payment)
188 {
189 // invalid payment
190 $this->setError(JText::translate('VAPERRINVPAYMENT'));
191 return false;
192 }
193
194 // register payment ID
195 $order['id_payment'] = $payment['id'];
196
197 if ($payment['charge'] > 0)
198 {
199 VAPLoader::import('libraries.tax.factory');
200
201 $options = array();
202 $options['subject'] = 'payment';
203 $options['order'] = $order;
204 // $options['id_user'] = $user->id;
205
206 // calculate payment taxes
207 $charge = VAPTaxFactory::calculate($payment['id'], $payment['charge'], $options);
208
209 // set payment charge
210 $order['payment_charge'] = $charge->net;
211 $order['payment_tax'] = $charge->tax;
212 }
213 else if ($payment['charge'] < 0)
214 {
215 // register payment charge within the cart as discount
216 $cart->setDiscount(new VAPCartDiscount('payment', $payment['charge']));
217 }
218
219 // auto-confirm orders according to the configuration of
220 // the payment, otherwise force PENDING status to let the
221 // customers be able to start a transaction
222 if ($payment['setconfirmed'])
223 {
224 // auto-confirm order
225 $order['status'] = JHtml::fetch('vaphtml.status.confirmed', 'packages', 'code');
226 }
227 }
228 }
229
230 ////////////////////////////////////////////////////////////
231 ///////////////////// FETCH TOTAL COSTS ////////////////////
232 ////////////////////////////////////////////////////////////
233
234 /**
235 * Trigger event to manipulate the total cost before it is going to be calculated.
236 *
237 * The prices of the cart are strictly related to the taxes and to the discounts.
238 * For this reason it is not possible to change the total cost and the user credit
239 * at runtime. Any surcharge/discount have to be applied by using the apposite
240 * methods provided by the cart objects.
241 *
242 * @param VAPCartPackages $cart The cart instance.
243 * @param JUser $user The instance of the current user.
244 * @param array $order The order details (@since 1.7.8).
245 *
246 * @return void
247 *
248 * @since 1.7
249 */
250 $dispatcher->trigger('onBeforeCalculateTotalSavePackagesOrder', array($cart, $user, $order));
251
252 // set up order totals
253 $order['total_cost'] = $cart->getTotalGross();
254 $order['total_net'] = $cart->getTotalNet();
255 $order['total_tax'] = $cart->getTotalTax();
256 $order['discount'] = $cart->getTotalDiscount();
257
258 // increase total cost by the payment charge
259 if (!empty($order['payment_charge']))
260 {
261 $order['total_cost'] += $order['payment_charge'] + $order['payment_tax'];
262 $order['total_tax'] += $order['payment_tax'];
263 }
264
265 ////////////////////////////////////////////////////////////
266 /////////////////////// ORDER STATUS ///////////////////////
267 ////////////////////////////////////////////////////////////
268
269 if (empty($order['status']))
270 {
271 // auto-confirm in case of no cost
272 $status = $order['total_cost'] > 0 ? 'pending' : 'confirmed';
273
274 // status not yet specified, use pending
275 $order['status'] = JHtml::fetch('vaphtml.status.' . $status, 'packages', 'code');
276 }
277
278 $order['status_comment'] = null;
279
280 /**
281 * Trigger event to manipulate the order status at runtime.
282 *
283 * @param string &$status The currently fetched order status.
284 * @param string &$comment An optional status comment to be used.
285 *
286 * @return void
287 *
288 * @since 1.7
289 */
290 $dispatcher->trigger('onFetchStatusSavePackagesOrder', array(&$order['status'], &$order['status_comment']));
291
292 // check whether the status has been immediately confirmed and we have an empty comment
293 if (empty($order['status_comment']) && JHtml::fetch('vaphtml.status.isconfirmed', 'packages', $order['status']))
294 {
295 if ($order['total_cost'] == 0)
296 {
297 // no cost, automatically confirmed
298 $order['status_comment'] = 'VAP_STATUS_CONFIRMED_AS_NO_COST';
299 }
300 else if (!$payment)
301 {
302 // no configured payments
303 $order['status_comment'] = 'VAP_STATUS_CONFIRMED_AS_NO_PAYMENT';
304 }
305 else
306 {
307 // auto-approved through the configuration of the payment
308 $order['status_comment'] = 'VAP_STATUS_CONFIRMED_RESULT_OF_PAYMENT';
309 }
310 }
311
312 ////////////////////////////////////////////////////////////
313 ///////////////////// FETCH COUPON CODE ////////////////////
314 ////////////////////////////////////////////////////////////
315
316 // check whether the coupon code was set
317 $coupon = $cart->getDiscount('coupon');
318
319 if ($coupon)
320 {
321 // assign coupon code to the order
322 $order['coupon'] = (array) $coupon->get('couponData');
323 // redeem coupon code
324 VikAppointments::couponUsed($order['coupon']);
325 }
326
327 ////////////////////////////////////////////////////////////
328 ///////////////////// USER REGISTRATION ////////////////////
329 ////////////////////////////////////////////////////////////
330
331 // create customer data
332 $customer = array(
333 'id' => 0,
334 'jid' => $user->id,
335 'fields' => $order['custom_f'],
336 );
337
338 // inject fetched billing details
339 $customer = array_merge($customer, $order['fields_data']);
340
341 // get customer model
342 $customerModel = JModelVAP::getInstance('customer');
343
344 // insert/update customer
345 if ($id_user = $customerModel->save($customer))
346 {
347 // assign order to saved customer
348 $order['id_user'] = $id_user;
349 }
350
351 ////////////////////////////////////////////////////////////
352 ///////////////////// FETCH ORDER ITEMS ////////////////////
353 ////////////////////////////////////////////////////////////
354
355 $itemsTotals = $cart->getTotalsPerItem();
356
357 $order['items'] = array();
358
359 foreach ($cart->getPackagesList() as $i => $p)
360 {
361 $item = array();
362
363 // register package details
364 $item['id_package'] = $p->getID();
365 $item['price'] = $p->getPrice();
366 $item['quantity'] = $p->getQuantity();
367 $item['num_app'] = $p->getNumberAppointments() * $p->getQuantity();
368
369 // register package totals
370 $item['net'] = $itemsTotals[$i]->net;
371 $item['tax'] = $itemsTotals[$i]->tax;
372 $item['gross'] = $itemsTotals[$i]->gross;
373 $item['discount'] = $itemsTotals[$i]->discount;
374
375 // register package tax breakdown
376 $item['tax_breakdown'] = json_encode($itemsTotals[$i]->breakdown);
377
378 /**
379 * Check whether the purchased package has an expiration threshold.
380 *
381 * @since 1.7.4
382 */
383 $validity = (int) JModelVAP::getInstance('package')->getItem($p->getID(), $blank = true)->validity;
384
385 if ($validity)
386 {
387 // the package can be redeemed until {$validity} days since now
388 $item['validthru'] = JFactory::getDate('+' . $validity . ' days')->toSql();
389 }
390
391 // register package
392 $order['items'][] = $item;
393 }
394
395 ////////////////////////////////////////////////////////////
396 //////////////////// SAVE ORDER DETAILS ////////////////////
397 ////////////////////////////////////////////////////////////
398
399 $ordnum = $ordkey = null;
400
401 // get package order model
402 $orderModel = JModelVAP::getInstance('packorder');
403
404 // save the order
405 if (!$orderModel->save($order))
406 {
407 // an error occurred while trying to save the order
408 $error = $orderModel->getError();
409 // propagate the error found or use a generic one
410 $this->setError($error ? $error : JText::translate('VAPSUBSCRINSERTERR'));
411 return false;
412 }
413
414 // get order saved data
415 $orderData = $orderModel->getData();
416
417 // use order number/key pair of saved order
418 $ordnum = $orderData['id'];
419 $ordkey = $orderData['sid'];
420
421 // empty cart on success
422 $cart->emptyCart();
423 $cart->store();
424
425 ////////////////////////////////////////////////////////////
426 ////////////////////// NOTIFICATIONS ///////////////////////
427 ////////////////////////////////////////////////////////////
428
429 $mailOptions = array();
430 // validate e-mail rules before sending
431 $mailOptions['check'] = true;
432
433 // send e-mail notification to the customer
434 $orderModel->sendEmailNotification($ordnum, $mailOptions);
435
436 // send e-mail notification to the administrator(s)
437 $mailOptions['client'] = 'packadmin';
438 $orderModel->sendEmailNotification($ordnum, $mailOptions);
439
440 $redirect_url = "index.php?option=com_vikappointments&view=packagesorder&ordnum={$ordnum}&ordkey={$ordkey}";
441
442 if (!empty($data['itemid']))
443 {
444 $redirect_url .= "&Itemid={$data['itemid']}";
445 }
446
447 /**
448 * Trigger event to manipulate the redirect URL after completing
449 * the packages purchase process.
450 *
451 * Use VAPOrderFactory::getPackages($ordnum) to access the order details.
452 *
453 * @param string &$url The redirect URL (plain).
454 * @param integer $order The order id.
455 *
456 * @return void
457 *
458 * @since 1.7
459 */
460 $dispatcher->trigger('onRedirectPackagesOrder', array(&$redirect_url, $ordnum));
461
462 // rewrite landing page
463 return JRoute::rewrite($redirect_url, false);
464 }
465 }
466