PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / trunk
VikAppointments Services Booking Calendar vtrunk
trunk 1.2.17 1.2.18 1.2.19
vikappointments / admin / controllers / invoice.php
vikappointments / admin / controllers Last commit date
analytics.php 4 years ago apiban.php 4 years ago apilog.php 4 years ago apiplugin.php 4 years ago apiuser.php 4 years ago backup.php 4 years ago calendar.php 4 years ago city.php 4 years ago closure.php 1 month ago configapp.php 4 years ago configcldays.php 2 years ago configcron.php 4 years ago configemp.php 4 years ago configsmsapi.php 4 years ago configuration.php 1 month ago conversion.php 1 year ago country.php 4 years ago coupon.php 4 years ago coupongroup.php 4 years ago cronjob.php 2 years ago cronjoblog.php 4 years ago customer.php 4 months ago customf.php 1 year ago dashboard.php 4 years ago emplocwdays.php 4 years ago employee.php 1 year ago emprates.php 4 years ago export.php 4 years ago exportres.php 4 years ago file.php 4 months ago findreservation.php 1 month ago group.php 4 years ago import.php 4 years ago index.html 4 years ago invoice.php 1 month ago langcustomf.php 4 years ago langemployee.php 4 years ago langgroup.php 4 years ago langmedia.php 4 years ago langoption.php 4 years ago langoptiongroup.php 4 years ago langpackage.php 4 years ago langpackgroup.php 4 years ago langpayment.php 4 years ago langservice.php 4 years ago langstatuscode.php 4 years ago langsubscr.php 4 years ago langtax.php 4 years ago location.php 4 years ago mailtext.php 2 years ago makerecurrence.php 1 month ago media.php 4 years ago multiorder.php 4 years ago option.php 4 months ago optiongroup.php 4 years ago package.php 2 years ago packgroup.php 4 years ago packorder.php 1 year ago payment.php 4 years ago rate.php 4 years ago reportsemp.php 4 years ago reportsser.php 4 years ago reservation.php 1 month ago restriction.php 4 years ago review.php 4 years ago service.php 1 year ago serworkday.php 4 months ago state.php 4 years ago statuscode.php 4 years ago subscription.php 4 years ago subscrorder.php 4 years ago tag.php 4 years ago tax.php 4 years ago usernote.php 4 years ago waitinglist.php 4 years ago webhook.php 4 years ago wizard.php 1 year ago
invoice.php
627 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.controllers.admin');
15 VAPLoader::import('libraries.invoice.factory');
16
17 /**
18 * VikAppointments invoice controller.
19 *
20 * @since 1.7
21 */
22 class VikAppointmentsControllerInvoice extends VAPControllerAdmin
23 {
24 /**
25 * Task used to access the creation page of a new record.
26 *
27 * @return boolean
28 */
29 public function add()
30 {
31 $app = JFactory::getApplication();
32
33 $data = array();
34 $group = $app->input->getString('group');
35 $month = $app->input->getUint('month');
36 $year = $app->input->getUint('year');
37
38 if (!is_null($group))
39 {
40 $data['group'] = $group;
41 }
42
43 if ($month)
44 {
45 $data['month'] = $month;
46 }
47
48 if ($year)
49 {
50 $data['year'] = $year;
51 }
52
53 // unset user state for being recovered again
54 $app->setUserState('vap.invoice.data', $data);
55
56 // check user permissions
57 if (!JFactory::getUser()->authorise('core.create', 'com_vikappointments'))
58 {
59 // back to main list, not authorised to create records
60 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
61 $this->cancel();
62
63 return false;
64 }
65
66 $this->setRedirect('index.php?option=com_vikappointments&view=manageinvoice');
67
68 return true;
69 }
70
71 /**
72 * Task used to access the management page of an existing record.
73 *
74 * @return boolean
75 */
76 public function edit()
77 {
78 $app = JFactory::getApplication();
79
80 // unset user state for being recovered again
81 $app->setUserState('vap.invoice.data', array());
82
83 // check user permissions
84 if (!JFactory::getUser()->authorise('core.edit', 'com_vikappointments'))
85 {
86 // back to main list, not authorised to edit records
87 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
88 $this->cancel();
89
90 return false;
91 }
92
93 $cid = $app->input->getUint('cid', array(0));
94
95 $this->setRedirect('index.php?option=com_vikappointments&view=manageinvoice&cid[]=' . $cid[0]);
96
97 return true;
98 }
99
100 /**
101 * Task used to save the record data set in the request.
102 * After saving, the user is redirected to the creation
103 * page of a new record.
104 *
105 * @return void
106 */
107 public function savenew()
108 {
109 if ($this->save())
110 {
111 $input = JFactory::getApplication()->input;
112
113 $url = 'index.php?option=com_vikappointments&task=invoice.add';
114
115 // recover data from request
116 $group = $input->getString('group');
117 $month = $input->getUint('month');
118 $year = $input->getUint('year');
119
120 if (!is_null($group))
121 {
122 $url .= '&group=' . $group;
123 }
124
125 if ($month)
126 {
127 $url .= '&month=' . $month;
128 }
129
130 if ($year)
131 {
132 $url .= '&year=' . $year;
133 }
134
135 $this->setRedirect($url);
136 }
137 }
138
139 /**
140 * Task used to save the record data set in the request.
141 * After saving, the user is redirected to the management
142 * page of the record that has been saved.
143 *
144 * @param array $data An array of invoice parameters to be used for the generation.
145 * If not specified, the parameters in the request will be used.
146 *
147 * @return boolean
148 */
149 public function save(array $data = array())
150 {
151 $app = JFactory::getApplication();
152 $input = $app->input;
153 $user = JFactory::getUser();
154
155 /**
156 * Added token validation.
157 *
158 * @since 1.7
159 */
160 if (!JSession::checkToken())
161 {
162 // back to main list, missing CSRF-proof token
163 $app->enqueueMessage(JText::translate('JINVALID_TOKEN'), 'error');
164 $this->cancel();
165
166 return false;
167 }
168
169 $args = array();
170
171 if ($data)
172 {
173 $args = $data;
174 }
175 else
176 {
177 $args['id'] = $input->get('id', 0, 'uint');
178 $args['group'] = $input->get('group', 0, 'string');
179 $args['overwrite'] = $input->get('overwrite', 0, 'uint');
180 $args['notify'] = $input->get('notifycust', 0, 'uint');
181 $args['month'] = $input->get('month', 1, 'uint');
182 $args['year'] = $input->get('year', 0, 'uint');
183 $args['cid'] = $input->get('cid', array(), 'uint');
184
185 $args['params'] = array();
186 $args['params']['number'] = $input->get('number', array(), 'string');
187 $args['params']['suffix'] = $input->get('suffix', array(), 'string');
188 $args['params']['datetype'] = $input->get('datetype', 0, 'uint');
189 $args['params']['date'] = $input->get('date', null, 'string');
190 $args['params']['legalinfo'] = $input->get('legalinfo', '', 'string');
191
192 if ($args['params']['date'])
193 {
194 $args['params']['date'] = VAPDateHelper::date2sql($args['params']['date']);
195 }
196
197 // settings
198 $args['constraints']['pageOrientation'] = $input->get('pageorientation', '', 'string');
199 $args['constraints']['pageFormat'] = $input->get('pageformat', '', 'string');
200 $args['constraints']['unit'] = $input->get('unit', '', 'string');
201 $args['constraints']['imageScaleRatio'] = abs($input->get('scale', 100, 'float')) / 100;
202
203 // layout
204 $args['constraints']['font'] = $input->get('font', 'courier', 'string');
205 $args['constraints']['fontSizes'] = $input->get('fontsizes', array(), 'array');
206 $args['constraints']['headerTitle'] = '';
207 $args['constraints']['showFooter'] = $input->get('showfooter', false, 'bool');
208
209 if ($input->getBool('showheader'))
210 {
211 $args['constraints']['headerTitle'] = $input->get('headertitle', '', 'string');
212 }
213
214 // margins
215 $args['constraints']['margins'] = $input->get('margins', array(), 'array');
216 }
217
218 $rule = 'core.' . ($args['id'] > 0 ? 'edit' : 'create');
219
220 // check user permissions
221 if (!$user->authorise($rule, 'com_vikappointments'))
222 {
223 // back to main list, not authorised to create/edit records
224 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
225 $this->cancel();
226
227 return false;
228 }
229
230 // get invoice model
231 $model = $this->getModel();
232
233 // update existing invoice
234 if ($args['id'])
235 {
236 // try to save arguments
237 if (!$model->save($args))
238 {
239 // get string error
240 $error = $model->getError(null, true);
241
242 // display error message
243 $app->enqueueMessage(JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $error), 'error');
244
245 $url = 'index.php?option=com_vikappointments&view=manageinvoice&cid[]=' . $args['id'];
246
247 // redirect to edit page
248 $this->setRedirect($url);
249
250 return false;
251 }
252
253 // display generic successful message
254 $app->enqueueMessage(JText::plural('VAPINVGENERATEDMSG', 1));
255
256 if ($model->isNotified())
257 {
258 // invoice notified, display message
259 $app->enqueueMessage(JText::plural('VAPINVMAILSENT', 1));
260 }
261 }
262 // invoices mass creation
263 else
264 {
265 // mass-save the matching records
266 $result = $model->saveMass($args);
267
268 if ($result['generated'])
269 {
270 // display number of generated invoices
271 $app->enqueueMessage(JText::plural('VAPINVGENERATEDMSG', $result['generated']));
272
273 if ($result['notified'])
274 {
275 // display number of notified customers
276 $app->enqueueMessage(JText::plural('VAPINVMAILSENT', $result['notified']));
277 }
278 }
279 else
280 {
281 // no generated invoices
282 $app->enqueueMessage(JText::translate('VAPNOINVOICESGENERATED'), 'warning');
283
284 // save invoice data to keep changed settings
285 $model->createGenerator($args)->save();
286 }
287 }
288
289 // always redirect to invoices list when generating the invoices
290 $this->cancel();
291
292 return true;
293 }
294
295 /**
296 * Generates an invoice for the specified reservations.
297 *
298 * @return boolean
299 */
300 public function generate()
301 {
302 $input = JFactory::getApplication()->input;
303
304 // create array with required attributes
305 $data = array();
306 $data['id'] = 0;
307 $data['cid'] = $input->get('cid', array(), 'uint');
308 $data['group'] = $input->get('group', 'appointments', 'string');
309 $data['notify'] = $input->get('notifycust', 0, 'uint');
310
311 // generate invoice
312 return $this->save($data);
313 }
314
315 /**
316 * Deletes a list of records set in the request.
317 *
318 * @return boolean
319 */
320 public function delete()
321 {
322 $app = JFactory::getApplication();
323 $cid = $app->input->get('cid', array(), 'uint');
324
325 /**
326 * Added token validation.
327 * Both GET and POST are supported.
328 *
329 * @since 1.7
330 */
331 if (!JSession::checkToken() && !JSession::checkToken('get'))
332 {
333 // back to main list, missing CSRF-proof token
334 $app->enqueueMessage(JText::translate('JINVALID_TOKEN'), 'error');
335 $this->cancel();
336
337 return false;
338 }
339
340 // check user permissions
341 if (!JFactory::getUser()->authorise('core.delete', 'com_vikappointments'))
342 {
343 // back to main list, not authorised to delete records
344 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
345 $this->cancel();
346
347 return false;
348 }
349
350 // delete selected records
351 $this->getModel()->delete($cid);
352
353 // back to main list
354 $this->cancel();
355
356 return true;
357 }
358
359 /**
360 * Redirects the users to the main records list.
361 *
362 * @return void
363 */
364 public function cancel()
365 {
366 $input = JFactory::getApplication()->input;
367
368 $group = $input->get('group', null, 'string');
369 $year = $input->get('year', 0, 'uint');
370 $month = $input->get('month', 0, 'uint');
371
372 $url = 'index.php?option=com_vikappointments&view=invoices';
373
374 if (!is_null($group))
375 {
376 $url .= '&group=' . $group;
377 }
378
379 if ($year)
380 {
381 $url .= '&year=' . $year;
382 }
383
384 if ($month)
385 {
386 $url .= '&month=' . $month;
387 }
388
389 $this->setRedirect($url);
390 }
391
392 /**
393 * Downloads one or more selected invoices.
394 * In case of single selection, the invoice will be
395 * directly downloaded in PDF format. Otherwise a
396 * ZIP archive will be given.
397 *
398 * @return void
399 */
400 public function download()
401 {
402 $app = JFactory::getApplication();
403 $ids = $app->input->get('cid', array(), 'uint');
404
405 /**
406 * Added token validation.
407 * Both GET and POST are supported.
408 *
409 * @since 1.7
410 */
411 if (!JSession::checkToken() && !JSession::checkToken('get'))
412 {
413 // back to main list, missing CSRF-proof token
414 $app->enqueueMessage(JText::translate('JINVALID_TOKEN'), 'error');
415 $this->cancel();
416
417 return false;
418 }
419
420 // get invoice model
421 $model = $this->getModel();
422
423 // get path to download
424 $path = $model->download($ids);
425
426 if (!$path)
427 {
428 // retrieve fetched error message
429 $error = $model->getError();
430
431 if ($error)
432 {
433 // raise error
434 $app->enqueueMessage($error, 'error');
435 }
436 else
437 {
438 // no error fetched, probably the list of IDs was empty
439 $app->enqueueMessage(JText::translate('JGLOBAL_NO_MATCHING_RESULTS'), 'warning');
440 }
441
442 // back to main list
443 $this->cancel();
444
445 // do not go ahead
446 return true;
447 }
448
449 $unlink = false;
450
451 $app->setHeader('Content-Disposition', 'attachment; filename=' . basename($path));
452 $app->setHeader('Content-Length', filesize($path));
453
454 // check if we have a PDF file or a ZIP archive
455 if (preg_match("/\.pdf$/", $path))
456 {
457 // download PDF file
458 $app->setHeader('Content-Type', 'application/pdf');
459 }
460 else
461 {
462 $app->setHeader('Content-Type', 'application/zip');
463 $unlink = true;
464 }
465
466 $app->sendHeaders();
467
468 // use fopen to properly download large files
469 $handle = fopen($path, 'rb');
470
471 // read 1MB per cycle
472 $chunk_size = 1024 * 1024;
473
474 while (!feof($handle))
475 {
476 echo fread($handle, $chunk_size);
477 ob_flush();
478 flush();
479 }
480
481 fclose($handle);
482
483 if ($unlink)
484 {
485 // delete package once its contents have been buffered
486 unlink($path);
487 }
488
489 // break process to complete download
490 $app->close();
491 }
492
493 /**
494 * Loads via AJAX the remaining invoices.
495 *
496 * @return void
497 */
498 public function ajaxload()
499 {
500 $app = JFactory::getApplication();
501 $input = $app->input;
502 $dbo = JFactory::getDbo();
503
504 /**
505 * Added token validation.
506 *
507 * @since 1.7
508 */
509 if (!JSession::checkToken())
510 {
511 // missing CSRF-proof token
512 UIErrorFactory::raiseError(403, JText::translate('JINVALID_TOKEN'));
513 }
514
515 $lim0 = $input->getUint('start_limit');
516 $lim = $input->getUint('limit');
517
518 $filters = array();
519 $filters['year'] = $app->getUserStateFromRequest('vap.invoices.year', 'year', 0, 'uint');
520 $filters['month'] = $app->getUserStateFromRequest('vap.invoices.month', 'month', 1, 'uint');
521 $filters['group'] = $input->getString('group', 'appointments');
522 $filters['keysearch'] = $input->getString('keysearch');
523
524 // get invoices
525 $invoices = array();
526 $not_all = false;
527 $max_lim = 0;
528
529 // create start date, adjusted to local timezone
530 $start = JFactory::getDate("{$filters['year']}-{$filters['month']}-1 00:00:00", JFactory::getUser()->getTimezone());
531
532 // create end date, cover the whole month
533 $end = clone $start;
534 $end->modify('+1 month');
535
536 $q = $dbo->getQuery(true)
537 ->select('SQL_CALC_FOUND_ROWS *')
538 ->from($dbo->qn('#__vikappointments_invoice'))
539 ->where($dbo->qn('inv_date') . ' >= ' . $dbo->q($start))
540 ->where($dbo->qn('inv_date') . ' < ' . $dbo->q($end))
541 ->where($dbo->qn('group') . ' = ' . $dbo->q($filters['group']))
542 ->order(array(
543 $dbo->qn('inv_date') . ' ASC',
544 $dbo->qn('id_order') . ' ASC',
545 ));
546
547 if ($filters['keysearch'])
548 {
549 $q->andWhere(array(
550 $dbo->qn('file') . ' LIKE ' . $dbo->q("%{$filters['keysearch']}%"),
551 $dbo->qn('inv_number') . ' = ' . $dbo->q($filters['keysearch']),
552 ), 'OR');
553 }
554
555 // create hook for query manipulation
556 $event = 'onBeforeListQueryInvoices';
557
558 // Create a dummy object to simulate the behavior of a view.
559 // Not the best solution but does its job, at least until
560 // List models will be implemented...
561 $view = new stdClass;
562 $view->filters = $filters;
563 $view->ordering = 'inv_date';
564 $view->orderingDir = 'asc';
565
566 /**
567 * Replicate same hook used by the view in order to keep the
568 * custom filters also while loading the records via AJAX.
569 *
570 * @since 1.7
571 */
572 VAPFactory::getEventDispatcher()->trigger($event, array(&$q, $view));
573
574 $dbo->setQuery($q, $lim0, $lim);
575 $invoices = $dbo->loadAssocList();
576
577 if ($invoices)
578 {
579 $not_all = true;
580
581 $dbo->setQuery('SELECT FOUND_ROWS();');
582 if (($max_lim = $dbo->loadResult()) <= $lim0 + count($invoices))
583 {
584 $not_all = false;
585 }
586 }
587
588 $invoices_html = array();
589
590 $cont = $lim0;
591
592 $invoiceLayout = new JLayoutFile('blocks.invoice');
593
594 // get invoice handler
595 $handler = VAPInvoiceFactory::getInvoice(null, $filters['group']);
596 // register invoice URI
597 $folder = $handler->getInvoiceFolderURI();
598
599 foreach ($invoices as $inv)
600 {
601 $cont++;
602
603 $data = array(
604 'id' => $inv['id'],
605 'number' => $inv['inv_number'],
606 'file' => $folder . $inv['file'],
607 );
608
609 $invoices_html[] = $invoiceLayout->render($data);
610 }
611
612 if (!$invoices_html)
613 {
614 $invoices_html[] = VAPApplication::getInstance()->alert(JText::translate('JGLOBAL_NO_MATCHING_RESULTS'));
615 }
616
617 $result = array(
618 'count' => $cont,
619 'invoices' => $invoices_html,
620 'more' => $not_all,
621 'max' => $max_lim,
622 );
623
624 $this->sendJSON($result);
625 }
626 }
627