3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
6 * Galette contributions controller
10 * Copyright © 2020-2021 The Galette Team
12 * This file is part of Galette (http://galette.tuxfamily.org).
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.
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.
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/>.
27 * @category Controllers
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2020-2021 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
37 namespace Galette\Controllers\Crud
;
40 use Galette\Controllers\CrudController
;
41 use Slim\Http\Request
;
42 use Slim\Http\Response
;
43 use Galette\Entity\Adherent
;
44 use Galette\Entity\Contribution
;
45 use Galette\Entity\Transaction
;
46 use Galette\Repository\Members
;
47 use Galette\Entity\ContributionsTypes
;
48 use Galette\Repository\PaymentTypes
;
51 * Galette contributions controller
53 * @category Controllers
54 * @name ContributionsController
56 * @author Johan Cwiklinski <johan@x-tnd.be>
57 * @copyright 2020-2021 The Galette Team
58 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
59 * @link http://galette.tuxfamily.org
60 * @since Available since 0.9.4dev - 2020-05-02
63 class ContributionsController
extends CrudController
70 * Only a few things changes in add and edit pages,
71 * boths methods will use this common one.
73 * @param Request $request PSR Request
74 * @param Response $response PSR Response
75 * @param string $type Contribution type
76 * @param Contribution $contrib Contribution instance
80 public function addEditPage(
87 $ct = new ContributionsTypes($this->zdb
);
88 $contributions_types = $ct->getList($type === 'fee');
90 // template variable declaration
92 if ($type === 'fee') {
93 $title = _T("Membership fee");
95 $title = _T("Donation");
98 if ($contrib->id
!= '') {
99 $title .= ' (' . _T("modification") . ')';
101 $title .= ' (' . _T("creation") . ')';
105 'page_title' => $title,
106 'required' => $contrib->getRequired(),
107 'contribution' => $contrib,
108 'adh_selected' => $contrib->member
,
112 // contribution types
113 $params['type_cotis_options'] = $contributions_types;
117 $members = $m->getSelectizedMembers(
119 $contrib->member
> 0 ?
$contrib->member
: null
122 $params['members'] = [
123 'filters' => $m->getFilters(),
124 'count' => $m->getCount()
127 if (count($members)) {
128 $params['members']['list'] = $members;
131 $ext_membership = '';
132 if ($contrib->isCotis() ||
!isset($contrib) && $type === 'fee') {
133 $ext_membership = $this->preferences
->pref_membership_ext
;
135 $params['pref_membership_ext'] = $ext_membership;
136 $params['autocomplete'] = true;
141 'ajouter_contribution.tpl',
150 * @param Request $request PSR Request
151 * @param Response $response PSR Response
152 * @param string|null $type Contribution type
156 public function add(Request
$request, Response
$response, string $type = null): Response
158 if ($this->session
->contribution
!== null) {
159 $contrib = $this->session
->contribution
;
160 $this->session
->contribution
= null;
162 $get = $request->getQueryParams();
164 $ct = new ContributionsTypes($this->zdb
);
165 $contributions_types = $ct->getList($type === 'fee');
167 $cparams = ['type' => array_keys($contributions_types)[0]];
170 if (isset($get[Adherent
::PK
]) && $get[Adherent
::PK
] > 0) {
171 $cparams['adh'] = (int)$get[Adherent
::PK
];
175 if (isset($get[Transaction
::PK
]) && $get[Transaction
::PK
] > 0) {
176 $cparams['trans'] = $get[Transaction
::PK
];
179 $contrib = new Contribution(
182 (count($cparams) > 0 ?
$cparams : null)
185 if (isset($cparams['adh'])) {
186 $contrib->member
= $cparams['adh'];
189 if (isset($get['montant_cotis']) && $get['montant_cotis'] > 0) {
190 $contrib->amount
= $get['montant_cotis'];
194 return $this->addEditPage($request, $response, $type, $contrib);
200 * @param Request $request PSR Request
201 * @param Response $response PSR Response
202 * @param string $type Contribution type
206 public function doAdd(Request
$request, Response
$response, string $type = null): Response
208 return $this->store($request, $response, 'add', $type);
212 * Choose contribution type to mass add contribution
214 * @param Request $request PSR Request
215 * @param Response $response PSR Response
219 public function massAddChooseType(Request
$request, Response
$response): Response
221 $filters = $this->session
->filter_members
;
223 'id' => $filters->selected
,
224 'redirect_uri' => $this->router
->pathFor('members')
230 'mass_choose_type.tpl',
232 'mode' => $request->isXhr() ?
'ajax' : '',
233 'page_title' => str_replace(
236 _T('Mass add contribution on %count members')
239 'form_url' => $this->router
->pathFor('massAddContributions'),
240 'cancel_uri' => $this->router
->pathFor('members')
247 * Massive change page
249 * @param Request $request PSR Request
250 * @param Response $response PSR Response
254 public function massAddContributions(Request
$request, Response
$response): Response
256 $post = $request->getParsedBody();
257 $filters = $this->session
->filter_members
;
258 $contribution = new Contribution($this->zdb
, $this->login
);
260 $type = $post['type'];
262 'id' => $filters->selected
,
263 'redirect_uri' => $this->router
->pathFor('members'),
267 // contribution types
268 $ct = new ContributionsTypes($this->zdb
);
269 $contributions_types = $ct->getList($type === 'fee');
274 'mass_add_contribution.tpl',
276 'mode' => $request->isXhr() ?
'ajax' : '',
277 'page_title' => str_replace(
280 _T('Mass add contribution on %count members')
282 'form_url' => $this->router
->pathFor('doMassAddContributions'),
283 'cancel_uri' => $this->router
->pathFor('members'),
285 'contribution' => $contribution,
287 'require_mass' => true,
288 'required' => $contribution->getRequired(),
289 'type_cotis_options' => $contributions_types
296 * Do massive contribution add
298 * @param Request $request PSR Request
299 * @param Response $response PSR Response
303 public function doMassAddContributions(Request
$request, Response
$response): Response
305 $post = $request->getParsedBody();
306 $members_ids = $post['id'];
309 $error_detected = [];
311 // flagging required fields for first step only
316 foreach ($members_ids as $member_id) {
317 $post[Adherent
::PK
] = (int)$member_id;
318 $contrib = new Contribution($this->zdb
, $this->login
);
321 $valid = $contrib->check($post, $contrib->getRequired(), $disabled);
322 if ($valid !== true) {
323 $error_detected = array_merge($error_detected, $valid);
326 //all goes well, we can proceed
327 if (count($error_detected) == 0) {
328 $store = $contrib->store();
329 if ($store === true) {
331 $files_res = $contrib->handleFiles($_FILES);
332 if (is_array($files_res)) {
333 $error_detected = array_merge($error_detected, $files_res);
341 if (count($error_detected) == 0) {
342 $redirect_url = $this->router
->pathFor('members');
344 //something went wrong.
345 //store entity in session
346 $redirect_url = $this->router
->pathFor('massAddContributions');
348 foreach ($error_detected as $error) {
349 $this->flash
->addMessage(
356 //redirect to calling action
359 ->withHeader('Location', $redirect_url);
368 * @param Request $request PSR Request
369 * @param Response $response PSR Response
370 * @param string $option One of 'page' or 'order'
371 * @param string|integer $value Value of the option
372 * @param string $type One of 'transactions' or 'contributions'
376 public function list(Request
$request, Response
$response, $option = null, $value = null, $type = null): Response
379 $get = $request->getQueryParams();
383 ||
isset($get['ajax'])
384 && $get['ajax'] == 'true'
393 $raw_type = 'transactions';
395 case 'contributions':
396 $raw_type = 'contributions';
400 $filter_name = 'filter_' . $raw_type;
402 if (isset($this->session
->$filter_name) && $ajax === false) {
403 $filters = $this->session
->$filter_name;
405 $filter_class = '\\Galette\\Filters\\' . ucwords($raw_type . 'List');
406 $filters = new $filter_class();
410 if (isset($get[Adherent
::PK
]) && $get[Adherent
::PK
] > 0) {
411 $filters->filtre_cotis_adh
= (int)$get[Adherent
::PK
];
414 $filters->filtre_transactions
= false;
415 if (isset($request->getQueryParams()['max_amount'])) {
416 $filters->filtre_transactions
= true;
417 $filters->max_amount
= (int)$request->getQueryParams()['max_amount'];
420 if ($option !== null) {
423 $filters->current_page
= (int)$value;
426 $filters->orderby
= $value;
430 ($this->login
->isAdmin()
431 ||
$this->login
->isStaff())
433 if ($value == 'all') {
434 $filters->filtre_cotis_adh
= null;
436 $filters->filtre_cotis_adh
= $value;
443 if (!$this->login
->isAdmin() && !$this->login
->isStaff()) {
444 $filters->filtre_cotis_adh
= $this->login
->id
;
447 $class = '\\Galette\\Repository\\' . ucwords($raw_type);
448 $contrib = new $class($this->zdb
, $this->login
, $filters);
449 $contribs_list = $contrib->getList(true);
451 //store filters into session
452 if ($ajax === false) {
453 $this->session
->$filter_name = $filters;
456 //assign pagination variables to the template and add pagination links
457 $filters->setSmartyPagination($this->router
, $this->view
->getSmarty());
460 'page_title' => $raw_type === 'contributions' ?
461 _T("Contributions management") : _T("Transactions management"),
462 'contribs' => $contrib,
463 'list' => $contribs_list,
464 'nb' => $contrib->getCount(),
465 'filters' => $filters,
466 'mode' => ($ajax === true ?
'ajax' : 'std')
469 if ($filters->filtre_cotis_adh
!= null) {
470 $member = new Adherent($this->zdb
);
471 $member->load($filters->filtre_cotis_adh
);
472 $tpl_vars['member'] = $member;
478 'gestion_' . $raw_type . '.tpl',
487 * @param Request $request PSR Request
488 * @param Response $response PSR Response
489 * @param string $type Contribution type
493 public function filter(Request
$request, Response
$response, string $type = null): Response
498 $raw_type = 'transactions';
500 case 'contributions':
501 $raw_type = 'contributions';
505 $type = 'filter_' . $raw_type;
506 $post = $request->getParsedBody();
507 $error_detected = [];
509 if ($this->session
->$type !== null) {
510 $filters = $this->session
->$type;
512 $filter_class = '\\Galette\\Filters\\' . ucwords($raw_type) . 'List';
513 $filters = new $filter_class();
516 if (isset($post['clear_filter'])) {
519 if (isset($post['max_amount'])) {
520 $filters->max_amount
= null;
524 (isset($post['nbshow']) && is_numeric($post['nbshow']))
526 $filters->show
= $post['nbshow'];
529 if (isset($post['end_date_filter']) ||
isset($post['start_date_filter'])) {
531 if (isset($post['start_date_filter'])) {
532 $filters->start_date_filter
= $post['start_date_filter'];
534 if (isset($post['end_date_filter'])) {
535 $filters->end_date_filter
= $post['end_date_filter'];
537 } catch (Throwable
$e) {
538 $error_detected[] = $e->getMessage();
542 if (isset($post['payment_type_filter'])) {
543 $ptf = (int)$post['payment_type_filter'];
544 $ptypes = new PaymentTypes(
549 $ptlist = $ptypes->getList();
550 if (isset($ptlist[$ptf])) {
551 $filters->payment_type_filter
= $ptf;
552 } elseif ($ptf == -1) {
553 $filters->payment_type_filter
= null;
555 $error_detected[] = _T("- Unknown payment type!");
560 $this->session
->$type = $filters;
562 if (count($error_detected) > 0) {
564 foreach ($error_detected as $error) {
565 $this->flash
->addMessage(
574 ->withHeader('Location', $this->router
->pathFor('contributions', ['type' => $raw_type]));
583 * @param Request $request PSR Request
584 * @param Response $response PSR Response
585 * @param int $id Contribution id
586 * @param string $type Contribution type
590 public function edit(Request
$request, Response
$response, int $id, string $type = null): Response
592 if ($this->session
->contribution
!== null) {
593 $contrib = $this->session
->contribution
;
594 $this->session
->contribution
= null;
596 $contrib = new Contribution($this->zdb
, $this->login
, $id);
597 if ($contrib->id
== '') {
598 //not possible to load contribution, exit
599 $this->flash
->addMessage(
604 _T("Unable to load contribution #%id!")
609 ->withHeader('Location', $this->router
->pathFor(
611 ['type' => 'contributions']
616 return $this->addEditPage($request, $response, $type, $contrib);
622 * @param Request $request PSR Request
623 * @param Response $response PSR Response
624 * @param integer $id Contribution id
625 * @param string $type Contribution type
629 public function doEdit(Request
$request, Response
$response, int $id, string $type = null): Response
631 return $this->store($request, $response, 'edit', $type, $id);
635 * Store contribution (new or existing)
637 * @param Request $request PSR Request
638 * @param Response $response PSR Response
639 * @param string $action Action ('edit' or 'add')
640 * @param string $type Contribution type
641 * @param integer $id Contribution id
645 public function store(Request
$request, Response
$response, $action, string $type, $id = null): Response
647 $post = $request->getParsedBody();
656 if ($action == 'edit' && isset($post['btnreload'])) {
657 $redirect_url = $this->router
->pathFor($action . 'Contribution', $args);
658 $redirect_url .= '?' . Adherent
::PK
. '=' . $post[Adherent
::PK
] . '&' .
659 ContributionsTypes
::PK
. '=' . $post[ContributionsTypes
::PK
] . '&' .
660 'montant_cotis=' . $post['montant_cotis'];
663 ->withHeader('Location', $redirect_url);
666 $error_detected = [];
667 $redirect_url = null;
669 if ($this->session
->contribution
!== null) {
670 $contrib = $this->session
->contribution
;
671 $this->session
->contribution
= null;
674 $contrib = new Contribution($this->zdb
, $this->login
);
676 $contrib = new Contribution($this->zdb
, $this->login
, $id);
683 $valid = $contrib->check($post, $contrib->getRequired(), $disabled);
684 if ($valid !== true) {
685 $error_detected = array_merge($error_detected, $valid);
688 if (count($error_detected) == 0) {
689 //all goes well, we can proceed
690 if (count($error_detected) == 0) {
691 // send email to member
692 if (isset($post['mail_confirm']) && $post['mail_confirm'] == '1') {
693 $contrib->setSendmail(); //flag to send creation email
696 $store = $contrib->store();
697 if ($store === true) {
698 $this->flash
->addMessage(
700 _T('Contribution has been successfully stored')
703 //something went wrong :'(
704 $error_detected[] = _T("An error occurred while storing the contribution.");
709 if (count($error_detected) === 0) {
710 $files_res = $contrib->handleFiles($_FILES);
711 if (is_array($files_res)) {
712 $error_detected = array_merge($error_detected, $files_res);
716 if (count($error_detected) == 0) {
717 $this->session
->contribution
= null;
718 if ($contrib->isTransactionPart() && $contrib->transaction
->getMissingAmount() > 0) {
720 $redirect_url = $this->router
->pathFor(
723 'type' => $post['contrib_type']
725 ) . '?' . Transaction
::PK
. '=' . $contrib->transaction
->id
.
726 '&' . Adherent
::PK
. '=' . $contrib->member
;
728 //contributions list for member
729 $redirect_url = $this->router
->pathFor(
732 'type' => 'contributions'
734 ) . '?' . Adherent
::PK
. '=' . $contrib->member
;
737 //something went wrong.
738 //store entity in session
739 $this->session
->contribution
= $contrib;
740 $redirect_url = $this->router
->pathFor($action . 'Contribution', $args);
743 foreach ($error_detected as $error) {
744 $this->flash
->addMessage(
751 //redirect to calling action
754 ->withHeader('Location', $redirect_url);
761 * Get redirection URI
763 * @param array $args Route arguments
767 public function redirectUri(array $args)
769 return $this->router
->pathFor('contributions', ['type' => $args['type']]);
775 * @param array $args Route arguments
779 public function formUri(array $args)
781 return $this->router
->pathFor(
782 'doRemoveContribution',
788 * Get confirmation removal page title
790 * @param array $args Route arguments
794 public function confirmRemoveTitle(array $args)
798 switch ($args['type']) {
800 $raw_type = 'transactions';
802 case 'contributions':
803 $raw_type = 'contributions';
807 if (isset($args['ids'])) {
809 _T('Remove %1$s %2$s'),
811 ($raw_type === 'contributions') ?
_T('contributions') : _T('transactions')
815 _T('Remove %1$s #%2$s'),
816 ($raw_type === 'contributions') ?
_T('contribution') : _T('transaction'),
825 * @param array $args Route arguments
826 * @param array $post POST values
830 protected function doDelete(array $args, array $post)
833 switch ($args['type']) {
835 $raw_type = 'transactions';
837 case 'contributions':
838 $raw_type = 'contributions';
842 $class = '\\Galette\Repository\\' . ucwords($raw_type);
843 $contribs = new $class($this->zdb
, $this->login
);
844 $rm = $contribs->remove($args['ids'] ??
$args['id'], $this->history
);