]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Controllers/Crud/ContributionsController.php
Fix missing namespace
[galette.git] / galette / lib / Galette / Controllers / Crud / ContributionsController.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Galette contributions controller
7 *
8 * PHP version 5
9 *
10 * Copyright © 2020 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * @category Controllers
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2020 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.tuxfamily.org
34 * @since Available since 0.9.4dev - 2020-05-08
35 */
36
37 namespace Galette\Controllers\Crud;
38
39 use Galette\Controllers\CrudController;
40 use Slim\Http\Request;
41 use Slim\Http\Response;
42 use Galette\Entity\Adherent;
43 use Galette\Entity\Contribution;
44 use Galette\Entity\Transaction;
45 use Galette\Repository\Contributions;
46 use Galette\Repository\Transactions;
47 use Galette\Repository\Members;
48 use Galette\Entity\ContributionsTypes;
49 use Galette\Core\GaletteMail;
50 use Galette\Entity\Texts;
51 use Galette\IO\PdfMembersCards;
52 use Galette\Repository\PaymentTypes;
53 use Analog\Analog;
54
55 /**
56 * Galette contributions controller
57 *
58 * @category Controllers
59 * @name ContributionsController
60 * @package Galette
61 * @author Johan Cwiklinski <johan@x-tnd.be>
62 * @copyright 2020 The Galette Team
63 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
64 * @link http://galette.tuxfamily.org
65 * @since Available since 0.9.4dev - 2020-05-02
66 */
67
68 class ContributionsController extends CrudController
69 {
70 // CRUD - Create
71
72 /**
73 * Add/Edit page
74 *
75 * Only a few things changes in add and edit pages,
76 * boths methods will use this common one.
77 *
78 * @param Request $request PSR Request
79 * @param Response $response PSR Response
80 * @param array $args Request arguments
81 * @param Contribution $contrib Contribution instance
82 *
83 * @return Response
84 */
85 public function addEditPage(
86 Request $request,
87 Response $response,
88 array $args,
89 Contribution $contrib
90 ): Response {
91 // contribution types
92 $ct = new ContributionsTypes($this->zdb);
93 $contributions_types = $ct->getList($args['type'] === 'fee');
94
95 $disabled = array();
96
97 if (!is_int($contrib->id)) {
98 // initialiser la structure contribution à vide (nouvelle contribution)
99 $contribution['duree_mois_cotis'] = $this->preferences->pref_membership_ext;
100 }
101
102 // template variable declaration
103 $title = null;
104 if ($args['type'] === 'fee') {
105 $title = _T("Membership fee");
106 } else {
107 $title = _T("Donation");
108 }
109
110 if ($contrib->id != '') {
111 $title .= ' (' . _T("modification") . ')';
112 } else {
113 $title .= ' (' . _T("creation") . ')';
114 }
115
116 // required fields
117 $required = [
118 'id_type_cotis' => 1,
119 'id_adh' => 1,
120 'date_enreg' => 1,
121 'date_debut_cotis' => 1,
122 'date_fin_cotis' => $contrib->isCotis(),
123 'montant_cotis' => $contrib->isCotis() ? 1 : 0
124 ];
125
126 $params = [
127 'page_title' => $title,
128 'required' => $required,
129 'disabled' => $disabled,
130 'contribution' => $contrib,
131 'adh_selected' => $contrib->member,
132 'type' => $args['type']
133 ];
134
135 // contribution types
136 $params['type_cotis_options'] = $contributions_types;
137
138 // members
139 $m = new Members();
140 $members = $m->getSelectizedMembers(
141 $this->zdb,
142 isset($contrib) && $contrib->member > 0 ? $contrib->member : null
143 );
144
145 $params['members'] = [
146 'filters' => $m->getFilters(),
147 'count' => $m->getCount()
148 ];
149
150 if (count($members)) {
151 $params['members']['list'] = $members;
152 }
153
154 $ext_membership = '';
155 if (isset($contrib) && $contrib->isCotis() || !isset($contrib) && $args['type'] === 'fee') {
156 $ext_membership = $this->preferences->pref_membership_ext;
157 }
158 $params['pref_membership_ext'] = $ext_membership;
159 $params['autocomplete'] = true;
160
161 // display page
162 $this->view->render(
163 $response,
164 'ajouter_contribution.tpl',
165 $params
166 );
167 return $response;
168 }
169
170 /**
171 * Add page
172 *
173 * @param Request $request PSR Request
174 * @param Response $response PSR Response
175 * @param array $args Request arguments
176 *
177 * @return Response
178 */
179 public function add(Request $request, Response $response, array $args = []): Response
180 {
181 if ($this->session->contribution !== null) {
182 $contrib = $this->session->contribution;
183 $this->session->contribution = null;
184 } else {
185 $get = $request->getQueryParams();
186
187 $ct = new ContributionsTypes($this->zdb);
188 $contributions_types = $ct->getList($args['type'] === 'fee');
189
190 $cparams = ['type' => array_keys($contributions_types)[0]];
191
192 //member id
193 if (isset($get[Adherent::PK]) && $get[Adherent::PK] > 0) {
194 $cparams['adh'] = (int)$get[Adherent::PK];
195 }
196
197 //transaction id
198 if (isset($get[Transaction::PK]) && $get[Transaction::PK] > 0) {
199 $cparams['trans'] = $get[Transaction::PK];
200 }
201
202 $contrib = new Contribution(
203 $this->zdb,
204 $this->login,
205 (count($cparams) > 0 ? $cparams : null)
206 );
207
208 if (isset($cparams['adh'])) {
209 $contrib->member = $cparams['adh'];
210 }
211
212 if (isset($get['montant_cotis']) && $get['montant_cotis'] > 0) {
213 $contrib->amount = $get['montant_cotis'];
214 }
215 }
216
217 return $this->addEditPage($request, $response, $args, $contrib);
218 }
219
220 /**
221 * Add action
222 *
223 * @param Request $request PSR Request
224 * @param Response $response PSR Response
225 * @param array $args Request arguments
226 *
227 * @return Response
228 */
229 public function doAdd(Request $request, Response $response, array $args = []): Response
230 {
231 $args['action'] = 'add';
232 return $this->store($request, $response, $args);
233 }
234
235 // /CRUD - Create
236 // CRUD - Read
237
238 /**
239 * List page
240 *
241 * @param Request $request PSR Request
242 * @param Response $response PSR Response
243 * @param array $args Request arguments
244 *
245 * @return Response
246 */
247 public function list(Request $request, Response $response, array $args = []): Response
248 {
249 $ajax = false;
250 if (
251 $request->isXhr()
252 || isset($request->getQueryParams()['ajax'])
253 && $request->getQueryParams()['ajax'] == 'true'
254 ) {
255 $ajax = true;
256 }
257 $get = $request->getQueryParams();
258
259 $option = $args['option'] ?? null;
260 $value = $args['value'] ?? null;
261 $raw_type = null;
262
263 switch ($args['type']) {
264 case 'transactions':
265 $raw_type = 'transactions';
266 break;
267 case 'contributions':
268 $raw_type = 'contributions';
269 break;
270 }
271
272 $filter_name = 'filter_' . $raw_type;
273
274 if (isset($this->session->$filter_name) && $ajax === false) {
275 $filters = $this->session->$filter_name;
276 } else {
277 $filter_class = '\\Galette\\Filters\\' . ucwords($raw_type . 'List');
278 $filters = new $filter_class();
279 }
280
281 //member id
282 if (isset($get[Adherent::PK]) && $get[Adherent::PK] > 0) {
283 $filters->filtre_cotis_adh = (int)$get[Adherent::PK];
284 }
285
286 $filters->filtre_transactions = false;
287 if (isset($request->getQueryParams()['max_amount'])) {
288 $filters->filtre_transactions = true;
289 $filters->max_amount = (int)$request->getQueryParams()['max_amount'];
290 }
291
292 if ($option !== null) {
293 switch ($option) {
294 case 'page':
295 $filters->current_page = (int)$value;
296 break;
297 case 'order':
298 $filters->orderby = $value;
299 break;
300 case 'member':
301 if (
302 ($this->login->isAdmin()
303 || $this->login->isStaff())
304 ) {
305 if ($value == 'all') {
306 $filters->filtre_cotis_adh = null;
307 } else {
308 $filters->filtre_cotis_adh = $value;
309 }
310 }
311 break;
312 }
313 }
314
315 if (!$this->login->isAdmin() && !$this->login->isStaff()) {
316 $filters->filtre_cotis_adh = $this->login->id;
317 }
318
319 $class = '\\Galette\\Repository\\' . ucwords($raw_type);
320 $contrib = new $class($this->zdb, $this->login, $filters);
321 $contribs_list = $contrib->getList(true);
322
323 //store filters into session
324 if ($ajax === false) {
325 $this->session->$filter_name = $filters;
326 }
327
328 //assign pagination variables to the template and add pagination links
329 $filters->setSmartyPagination($this->router, $this->view->getSmarty());
330
331 $tpl_vars = [
332 'page_title' => $raw_type === 'contributions' ?
333 _T("Contributions management") : _T("Transactions management"),
334 'contribs' => $contrib,
335 'list' => $contribs_list,
336 'nb' => $contrib->getCount(),
337 'filters' => $filters,
338 'mode' => ($ajax === true ? 'ajax' : 'std')
339 ];
340
341 if ($filters->filtre_cotis_adh != null) {
342 $member = new Adherent($this->zdb);
343 $member->load($filters->filtre_cotis_adh);
344 $tpl_vars['member'] = $member;
345 }
346
347 // display page
348 $this->view->render(
349 $response,
350 'gestion_' . $raw_type . '.tpl',
351 $tpl_vars
352 );
353 return $response;
354 }
355
356 /**
357 * Filtering
358 *
359 * @param Request $request PSR Request
360 * @param Response $response PSR Response
361 * @param array $args Request arguments
362 *
363 * @return Response
364 */
365 public function filter(Request $request, Response $response, array $args = []): Response
366 {
367 $raw_type = null;
368 switch ($args['type']) {
369 case 'transactions':
370 $raw_type = 'transactions';
371 break;
372 case 'contributions':
373 $raw_type = 'contributions';
374 break;
375 }
376
377 $type = 'filter_' . $raw_type;
378 $post = $request->getParsedBody();
379 $error_detected = [];
380
381 if ($this->session->$type !== null) {
382 $filters = $this->session->$type;
383 } else {
384 $filter_class = '\\Galette\\Filters\\' . ucwords($raw_type) . 'List';
385 $filters = new $filter_class();
386 }
387
388 if (isset($post['clear_filter'])) {
389 $filters->reinit();
390 } else {
391 if (isset($post['max_amount'])) {
392 $filters->max_amount = null;
393 }
394
395 if (
396 (isset($post['nbshow']) && is_numeric($post['nbshow']))
397 ) {
398 $filters->show = $post['nbshow'];
399 }
400
401 if (isset($post['end_date_filter']) || isset($post['start_date_filter'])) {
402 try {
403 if (isset($post['start_date_filter'])) {
404 $filters->start_date_filter = $post['start_date_filter'];
405 }
406 if (isset($post['end_date_filter'])) {
407 $filters->end_date_filter = $post['end_date_filter'];
408 }
409 } catch (\Exception $e) {
410 $error_detected[] = $e->getMessage();
411 }
412 }
413
414 if (isset($post['payment_type_filter'])) {
415 $ptf = (int)$post['payment_type_filter'];
416 $ptypes = new PaymentTypes(
417 $this->zdb,
418 $this->preferences,
419 $this->login
420 );
421 $ptlist = $ptypes->getList();
422 if (isset($ptlist[$ptf])) {
423 $filters->payment_type_filter = $ptf;
424 } elseif ($ptf == -1) {
425 $filters->payment_type_filter = null;
426 } else {
427 $error_detected[] = _T("- Unknown payment type!");
428 }
429 }
430 }
431
432 $this->session->$type = $filters;
433
434 if (count($error_detected) > 0) {
435 //report errors
436 foreach ($error_detected as $error) {
437 $this->flash->addMessage(
438 'error_detected',
439 $error
440 );
441 }
442 }
443
444 return $response
445 ->withStatus(301)
446 ->withHeader('Location', $this->router->pathFor('contributions', ['type' => $raw_type]));
447 }
448
449 // /CRUD - Read
450 // CRUD - Update
451
452 /**
453 * Edit page
454 *
455 * @param Request $request PSR Request
456 * @param Response $response PSR Response
457 * @param array $args Request arguments
458 *
459 * @return Response
460 */
461 public function edit(Request $request, Response $response, array $args = []): Response
462 {
463 if ($this->session->contribution !== null) {
464 $contrib = $this->session->contribution;
465 $this->session->contribution = null;
466 } else {
467 $contrib = new Contribution($this->zdb, $this->login, (int)$args['id']);
468 if ($contrib->id == '') {
469 //not possible to load contribution, exit
470 $this->flash->addMessage(
471 'error_detected',
472 str_replace(
473 '%id',
474 $args['id'],
475 _T("Unable to load contribution #%id!")
476 )
477 );
478 return $response
479 ->withStatus(301)
480 ->withHeader('Location', $this->router->pathFor(
481 'contributions',
482 ['type' => 'contributions']
483 ));
484 }
485 }
486
487 return $this->addEditPage($request, $response, $args, $contrib);
488 }
489
490 /**
491 * Edit action
492 *
493 * @param Request $request PSR Request
494 * @param Response $response PSR Response
495 * @param array $args Request arguments
496 *
497 * @return Response
498 */
499 public function doEdit(Request $request, Response $response, array $args = []): Response
500 {
501 $args['action'] = 'edit';
502 return $this->store($request, $response, $args);
503 }
504
505 /**
506 * Store contribution (new or existing)
507 *
508 * @param Request $request PSR Request
509 * @param Response $response PSR Response
510 * @param array $args Request arguments
511 *
512 * @return Response
513 */
514 public function store(Request $request, Response $response, array $args = []): Response
515 {
516 $post = $request->getParsedBody();
517 $action = $args['action'];
518
519 if ($action == 'edit' && isset($post['btnreload'])) {
520 $redirect_url = $this->router->pathFor($action . 'Contribution', $args);
521 $redirect_url .= '?' . Adherent::PK . '=' . $post[Adherent::PK] . '&' .
522 ContributionsTypes::PK . '=' . $post[ContributionsTypes::PK] . '&' .
523 'montant_cotis=' . $post['montant_cotis'];
524 return $response
525 ->withStatus(301)
526 ->withHeader('Location', $redirect_url);
527 }
528
529 $error_detected = [];
530 $redirect_url = null;
531
532 $id_cotis = null;
533 if (isset($args['id'])) {
534 $id_cotis = $args['id'];
535 }
536
537 if ($this->session->contribution !== null) {
538 $contrib = $this->session->contribution;
539 $this->session->contribution = null;
540 } else {
541 if ($id_cotis === null) {
542 $contrib = new Contribution($this->zdb, $this->login);
543 } else {
544 $contrib = new Contribution($this->zdb, $this->login, (int)$id_cotis);
545 }
546 }
547
548 // flagging required fields for first step only
549 $required = [
550 'id_type_cotis' => 1,
551 'id_adh' => 1,
552 'date_enreg' => 1,
553 'montant_cotis' => 1, //TODO: not always required, see #196
554 'date_debut_cotis' => 1,
555 'date_fin_cotis' => ($args['type'] === 'fee')
556 ];
557 $disabled = [];
558
559 // regular fields
560 $valid = $contrib->check($post, $required, $disabled);
561 if ($valid !== true) {
562 $error_detected = array_merge($error_detected, $valid);
563 }
564
565 if (count($error_detected) == 0) {
566 //all goes well, we can proceed
567 $new = false;
568 if ($contrib->id == '') {
569 $new = true;
570 }
571
572 if (count($error_detected) == 0) {
573 // send email to member
574 if (isset($post['mail_confirm']) && $post['mail_confirm'] == '1') {
575 $contrib->setSendmail(); //flag to send creation email
576 }
577
578 $store = $contrib->store();
579 if ($store === true) {
580 $this->flash->addMessage(
581 'success_detected',
582 _T('Contribution has been successfully stored')
583 );
584 } else {
585 //something went wrong :'(
586 $error_detected[] = _T("An error occurred while storing the contribution.");
587 }
588 }
589 }
590
591 if (count($error_detected) == 0) {
592 $this->session->contribution = null;
593 if ($contrib->isTransactionPart() && $contrib->transaction->getMissingAmount() > 0) {
594 //new contribution
595 $redirect_url = $this->router->pathFor(
596 'addContribution',
597 [
598 'type' => $post['contrib_type']
599 ]
600 ) . '?' . Transaction::PK . '=' . $contrib->transaction->id .
601 '&' . Adherent::PK . '=' . $contrib->member;
602 } else {
603 //contributions list for member
604 $redirect_url = $this->router->pathFor(
605 'contributions',
606 [
607 'type' => 'contributions'
608 ]
609 ) . '?' . Adherent::PK . '=' . $contrib->member;
610 }
611 } else {
612 //something went wrong.
613 //store entity in session
614 $this->session->contribution = $contrib;
615 $redirect_url = $this->router->pathFor($args['action'] . 'Contribution', $args);
616
617 //report errors
618 foreach ($error_detected as $error) {
619 $this->flash->addMessage(
620 'error_detected',
621 $error
622 );
623 }
624 }
625
626 //redirect to calling action
627 return $response
628 ->withStatus(301)
629 ->withHeader('Location', $redirect_url);
630 }
631
632 // /CRUD - Update
633 // CRUD - Delete
634
635 /**
636 * Get redirection URI
637 *
638 * @param array $args Route arguments
639 *
640 * @return string
641 */
642 public function redirectUri(array $args = [])
643 {
644 return $this->router->pathFor('contributions', ['type' => $args['type']]);
645 }
646
647 /**
648 * Get form URI
649 *
650 * @param array $args Route arguments
651 *
652 * @return string
653 */
654 public function formUri(array $args = [])
655 {
656 return $this->router->pathFor(
657 'doRemoveContribution',
658 $args
659 );
660 }
661
662 /**
663 * Get confirmation removal page title
664 *
665 * @param array $args Route arguments
666 *
667 * @return string
668 */
669 public function confirmRemoveTitle(array $args = [])
670 {
671 $raw_type = null;
672
673 switch ($args['type']) {
674 case 'transactions':
675 $raw_type = 'transactions';
676 break;
677 case 'contributions':
678 $raw_type = 'contributions';
679 break;
680 }
681
682 if (isset($args['ids'])) {
683 return sprintf(
684 _T('Remove %1$s %2$s'),
685 count($args['ids']),
686 ($raw_type === 'contributions') ? _T('contributions') : _T('transactions')
687 );
688 } else {
689 return sprintf(
690 _T('Remove %1$s #%2$s'),
691 ($raw_type === 'contributions') ? _T('contribution') : _T('transaction'),
692 $args['id']
693 );
694 }
695 }
696
697 /**
698 * Remove object
699 *
700 * @param array $args Route arguments
701 * @param array $post POST values
702 *
703 * @return boolean
704 */
705 protected function doDelete(array $args, array $post)
706 {
707 $raw_type = null;
708 switch ($args['type']) {
709 case 'transactions':
710 $raw_type = 'transactions';
711 break;
712 case 'contributions':
713 $raw_type = 'contributions';
714 break;
715 }
716
717 $class = '\\Galette\Repository\\' . ucwords($raw_type);
718 $contribs = new $class($this->zdb, $this->login);
719 $rm = $contribs->remove($args['ids'] ?? $args['id'], $this->history);
720 return $rm;
721 }
722
723 // /CRUD - Delete
724 // /CRUD
725 }