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\Contributions
;
47 use Galette\Repository\Transactions
;
48 use Galette\Repository\Members
;
49 use Galette\Entity\ContributionsTypes
;
50 use Galette\Core\GaletteMail
;
51 use Galette\IO\PdfMembersCards
;
52 use Galette\Repository\PaymentTypes
;
56 * Galette contributions controller
58 * @category Controllers
59 * @name ContributionsController
61 * @author Johan Cwiklinski <johan@x-tnd.be>
62 * @copyright 2020-2021 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
68 class ContributionsController
extends CrudController
75 * Only a few things changes in add and edit pages,
76 * boths methods will use this common one.
78 * @param Request $request PSR Request
79 * @param Response $response PSR Response
80 * @param string $type Contribution type
81 * @param Contribution $contrib Contribution instance
85 public function addEditPage(
92 $ct = new ContributionsTypes($this->zdb
);
93 $contributions_types = $ct->getList($type === 'fee');
97 if (!is_int($contrib->id
)) {
98 // initialiser la structure contribution à vide (nouvelle contribution)
99 $contribution['duree_mois_cotis'] = $this->preferences
->pref_membership_ext
;
102 // template variable declaration
104 if ($type === 'fee') {
105 $title = _T("Membership fee");
107 $title = _T("Donation");
110 if ($contrib->id
!= '') {
111 $title .= ' (' . _T("modification") . ')';
113 $title .= ' (' . _T("creation") . ')';
118 'id_type_cotis' => 1,
121 'date_debut_cotis' => 1,
122 'date_fin_cotis' => $contrib->isCotis(),
123 'montant_cotis' => $contrib->isCotis() ?
1 : 0
127 'page_title' => $title,
128 'required' => $required,
129 'disabled' => $disabled,
130 'contribution' => $contrib,
131 'adh_selected' => $contrib->member
,
135 // contribution types
136 $params['type_cotis_options'] = $contributions_types;
140 $members = $m->getSelectizedMembers(
142 isset($contrib) && $contrib->member
> 0 ?
$contrib->member
: null
145 $params['members'] = [
146 'filters' => $m->getFilters(),
147 'count' => $m->getCount()
150 if (count($members)) {
151 $params['members']['list'] = $members;
154 $ext_membership = '';
155 if (isset($contrib) && $contrib->isCotis() ||
!isset($contrib) && $type === 'fee') {
156 $ext_membership = $this->preferences
->pref_membership_ext
;
158 $params['pref_membership_ext'] = $ext_membership;
159 $params['autocomplete'] = true;
164 'ajouter_contribution.tpl',
173 * @param Request $request PSR Request
174 * @param Response $response PSR Response
175 * @param string $type Contribution type
179 public function add(Request
$request, Response
$response, string $type = null): Response
181 if ($this->session
->contribution
!== null) {
182 $contrib = $this->session
->contribution
;
183 $this->session
->contribution
= null;
185 $get = $request->getQueryParams();
187 $ct = new ContributionsTypes($this->zdb
);
188 $contributions_types = $ct->getList($type === 'fee');
190 $cparams = ['type' => array_keys($contributions_types)[0]];
193 if (isset($get[Adherent
::PK
]) && $get[Adherent
::PK
] > 0) {
194 $cparams['adh'] = (int)$get[Adherent
::PK
];
198 if (isset($get[Transaction
::PK
]) && $get[Transaction
::PK
] > 0) {
199 $cparams['trans'] = $get[Transaction
::PK
];
202 $contrib = new Contribution(
205 (count($cparams) > 0 ?
$cparams : null)
208 if (isset($cparams['adh'])) {
209 $contrib->member
= $cparams['adh'];
212 if (isset($get['montant_cotis']) && $get['montant_cotis'] > 0) {
213 $contrib->amount
= $get['montant_cotis'];
217 return $this->addEditPage($request, $response, $type, $contrib);
223 * @param Request $request PSR Request
224 * @param Response $response PSR Response
225 * @param string $type Contribution type
229 public function doAdd(Request
$request, Response
$response, string $type = null): Response
231 return $this->store($request, $response, 'add', $type);
240 * @param Request $request PSR Request
241 * @param Response $response PSR Response
242 * @param string $option One of 'page' or 'order'
243 * @param string|integer $value Value of the option
244 * @param string $type One of 'transactions' or 'contributions'
248 public function list(Request
$request, Response
$response, $option = null, $value = null, $type = null): Response
251 $get = $request->getQueryParams();
255 ||
isset($get['ajax'])
256 && $get['ajax'] == 'true'
265 $raw_type = 'transactions';
267 case 'contributions':
268 $raw_type = 'contributions';
272 $filter_name = 'filter_' . $raw_type;
274 if (isset($this->session
->$filter_name) && $ajax === false) {
275 $filters = $this->session
->$filter_name;
277 $filter_class = '\\Galette\\Filters\\' . ucwords($raw_type . 'List');
278 $filters = new $filter_class();
282 if (isset($get[Adherent
::PK
]) && $get[Adherent
::PK
] > 0) {
283 $filters->filtre_cotis_adh
= (int)$get[Adherent
::PK
];
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'];
292 if ($option !== null) {
295 $filters->current_page
= (int)$value;
298 $filters->orderby
= $value;
302 ($this->login
->isAdmin()
303 ||
$this->login
->isStaff())
305 if ($value == 'all') {
306 $filters->filtre_cotis_adh
= null;
308 $filters->filtre_cotis_adh
= $value;
315 if (!$this->login
->isAdmin() && !$this->login
->isStaff()) {
316 $filters->filtre_cotis_adh
= $this->login
->id
;
319 $class = '\\Galette\\Repository\\' . ucwords($raw_type);
320 $contrib = new $class($this->zdb
, $this->login
, $filters);
321 $contribs_list = $contrib->getList(true);
323 //store filters into session
324 if ($ajax === false) {
325 $this->session
->$filter_name = $filters;
328 //assign pagination variables to the template and add pagination links
329 $filters->setSmartyPagination($this->router
, $this->view
->getSmarty());
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')
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;
350 'gestion_' . $raw_type . '.tpl',
359 * @param Request $request PSR Request
360 * @param Response $response PSR Response
361 * @param string $type Contribution type
365 public function filter(Request
$request, Response
$response, string $type = null): Response
370 $raw_type = 'transactions';
372 case 'contributions':
373 $raw_type = 'contributions';
377 $type = 'filter_' . $raw_type;
378 $post = $request->getParsedBody();
379 $error_detected = [];
381 if ($this->session
->$type !== null) {
382 $filters = $this->session
->$type;
384 $filter_class = '\\Galette\\Filters\\' . ucwords($raw_type) . 'List';
385 $filters = new $filter_class();
388 if (isset($post['clear_filter'])) {
391 if (isset($post['max_amount'])) {
392 $filters->max_amount
= null;
396 (isset($post['nbshow']) && is_numeric($post['nbshow']))
398 $filters->show
= $post['nbshow'];
401 if (isset($post['end_date_filter']) ||
isset($post['start_date_filter'])) {
403 if (isset($post['start_date_filter'])) {
404 $filters->start_date_filter
= $post['start_date_filter'];
406 if (isset($post['end_date_filter'])) {
407 $filters->end_date_filter
= $post['end_date_filter'];
409 } catch (Throwable
$e) {
410 $error_detected[] = $e->getMessage();
414 if (isset($post['payment_type_filter'])) {
415 $ptf = (int)$post['payment_type_filter'];
416 $ptypes = new PaymentTypes(
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;
427 $error_detected[] = _T("- Unknown payment type!");
432 $this->session
->$type = $filters;
434 if (count($error_detected) > 0) {
436 foreach ($error_detected as $error) {
437 $this->flash
->addMessage(
446 ->withHeader('Location', $this->router
->pathFor('contributions', ['type' => $raw_type]));
455 * @param Request $request PSR Request
456 * @param Response $response PSR Response
457 * @param int $id Contribution id
458 * @param string $type Contribution type
462 public function edit(Request
$request, Response
$response, int $id, string $type = null): Response
464 if ($this->session
->contribution
!== null) {
465 $contrib = $this->session
->contribution
;
466 $this->session
->contribution
= null;
468 $contrib = new Contribution($this->zdb
, $this->login
, $id);
469 if ($contrib->id
== '') {
470 //not possible to load contribution, exit
471 $this->flash
->addMessage(
476 _T("Unable to load contribution #%id!")
481 ->withHeader('Location', $this->router
->pathFor(
483 ['type' => 'contributions']
488 return $this->addEditPage($request, $response, $type, $contrib);
494 * @param Request $request PSR Request
495 * @param Response $response PSR Response
496 * @param integer $id Contribution id
497 * @param string $type Contribution type
501 public function doEdit(Request
$request, Response
$response, int $id, string $type = null): Response
503 return $this->store($request, $response, 'edit', $type, $id);
507 * Store contribution (new or existing)
509 * @param Request $request PSR Request
510 * @param Response $response PSR Response
511 * @param string $action Action ('edit' or 'add')
512 * @param string $type Contribution type
513 * @param integer $id Contribution id
517 public function store(Request
$request, Response
$response, $action, string $type, $id = null): Response
519 $post = $request->getParsedBody();
528 if ($action == 'edit' && isset($post['btnreload'])) {
529 $redirect_url = $this->router
->pathFor($action . 'Contribution', $args);
530 $redirect_url .= '?' . Adherent
::PK
. '=' . $post[Adherent
::PK
] . '&' .
531 ContributionsTypes
::PK
. '=' . $post[ContributionsTypes
::PK
] . '&' .
532 'montant_cotis=' . $post['montant_cotis'];
535 ->withHeader('Location', $redirect_url);
538 $error_detected = [];
539 $redirect_url = null;
541 if ($this->session
->contribution
!== null) {
542 $contrib = $this->session
->contribution
;
543 $this->session
->contribution
= null;
546 $contrib = new Contribution($this->zdb
, $this->login
);
548 $contrib = new Contribution($this->zdb
, $this->login
, $id);
552 // flagging required fields for first step only
554 'id_type_cotis' => 1,
557 'montant_cotis' => 1, //TODO: not always required, see #196
558 'date_debut_cotis' => 1,
559 'date_fin_cotis' => ($type === 'fee')
564 $valid = $contrib->check($post, $required, $disabled);
565 if ($valid !== true) {
566 $error_detected = array_merge($error_detected, $valid);
569 if (count($error_detected) == 0) {
570 //all goes well, we can proceed
571 if (count($error_detected) == 0) {
572 // send email to member
573 if (isset($post['mail_confirm']) && $post['mail_confirm'] == '1') {
574 $contrib->setSendmail(); //flag to send creation email
577 $store = $contrib->store();
578 if ($store === true) {
579 $this->flash
->addMessage(
581 _T('Contribution has been successfully stored')
584 //something went wrong :'(
585 $error_detected[] = _T("An error occurred while storing the contribution.");
590 if (count($error_detected) === 0) {
591 $files_res = $contrib->handleFiles($_FILES);
592 if (is_array($files_res)) {
593 $error_detected = array_merge($error_detected, $files_res);
597 if (count($error_detected) == 0) {
598 $this->session
->contribution
= null;
599 if ($contrib->isTransactionPart() && $contrib->transaction
->getMissingAmount() > 0) {
601 $redirect_url = $this->router
->pathFor(
604 'type' => $post['contrib_type']
606 ) . '?' . Transaction
::PK
. '=' . $contrib->transaction
->id
.
607 '&' . Adherent
::PK
. '=' . $contrib->member
;
609 //contributions list for member
610 $redirect_url = $this->router
->pathFor(
613 'type' => 'contributions'
615 ) . '?' . Adherent
::PK
. '=' . $contrib->member
;
618 //something went wrong.
619 //store entity in session
620 $this->session
->contribution
= $contrib;
621 $redirect_url = $this->router
->pathFor($action . 'Contribution', $args);
624 foreach ($error_detected as $error) {
625 $this->flash
->addMessage(
632 //redirect to calling action
635 ->withHeader('Location', $redirect_url);
642 * Get redirection URI
644 * @param array $args Route arguments
648 public function redirectUri(array $args)
650 return $this->router
->pathFor('contributions', ['type' => $args['type']]);
656 * @param array $args Route arguments
660 public function formUri(array $args)
662 return $this->router
->pathFor(
663 'doRemoveContribution',
669 * Get confirmation removal page title
671 * @param array $args Route arguments
675 public function confirmRemoveTitle(array $args)
679 switch ($args['type']) {
681 $raw_type = 'transactions';
683 case 'contributions':
684 $raw_type = 'contributions';
688 if (isset($args['ids'])) {
690 _T('Remove %1$s %2$s'),
692 ($raw_type === 'contributions') ?
_T('contributions') : _T('transactions')
696 _T('Remove %1$s #%2$s'),
697 ($raw_type === 'contributions') ?
_T('contribution') : _T('transaction'),
706 * @param array $args Route arguments
707 * @param array $post POST values
711 protected function doDelete(array $args, array $post)
714 switch ($args['type']) {
716 $raw_type = 'transactions';
718 case 'contributions':
719 $raw_type = 'contributions';
723 $class = '\\Galette\Repository\\' . ucwords($raw_type);
724 $contribs = new $class($this->zdb
, $this->login
);
725 $rm = $contribs->remove($args['ids'] ??
$args['id'], $this->history
);