]> git.agnieray.net Git - galette.git/blob - galette/includes/routes/contributions.routes.php
Fix new member admin recipients; closes #1344
[galette.git] / galette / includes / routes / contributions.routes.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Contributions related routes
7 *
8 * PHP version 5
9 *
10 * Copyright © 2014-2016 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 Routes
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2014-2016 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 * @version SVN: $Id$
34 * @link http://galette.tuxfamily.org
35 * @since 0.8.2dev 2014-11-27
36 */
37
38 use Galette\Entity\Contribution;
39 use Galette\Repository\Contributions;
40 use Galette\Entity\Transaction;
41 use Galette\Repository\Transactions;
42 use Galette\Repository\Members;
43 use Galette\Entity\Adherent;
44 use Galette\Entity\ContributionsTypes;
45 use Galette\Core\GaletteMail;
46 use Galette\Entity\Texts;
47 use Galette\IO\PdfContribution;
48 use Galette\Repository\PaymentTypes;
49
50 $app->get(
51 '/{type:transactions|contributions}[/{option:page|order|member}/{value:\d+|all}]',
52 function ($request, $response, $args) {
53 $ajax = false;
54 if ($request->isXhr()
55 || isset($request->getQueryParams()['ajax'])
56 && $request->getQueryParams()['ajax'] == 'true'
57 ) {
58 $ajax = true;
59 }
60 $get = $request->getQueryParams();
61
62 $option = null;
63 if (isset($args['option'])) {
64 $option = $args['option'];
65 }
66 $value = null;
67 if (isset($args['value'])) {
68 $value = $args['value'];
69 }
70
71 $raw_type = null;
72 switch ($args['type']) {
73 case 'transactions':
74 $raw_type = 'transactions';
75 break;
76 case 'contributions':
77 $raw_type = 'contributions';
78 break;
79 }
80
81 $filter_name = 'filter_' . $raw_type;
82
83 if (isset($this->session->$filter_name) && $ajax === false) {
84 $filters = $this->session->$filter_name;
85 } else {
86 $filter_class = '\\Galette\\Filters\\' . ucwords($raw_type . 'List');
87 $filters = new $filter_class();
88 }
89
90 //member id
91 $id_adh = null;
92 if (isset($get[Adherent::PK]) && $get[Adherent::PK] > 0) {
93 $id_adh = (int)$get[Adherent::PK];
94 $filters->filtre_cotis_adh = $id_adh;
95 }
96
97 $max_amount = null;
98 if (isset($request->getQueryParams()['max_amount'])) {
99 $filters->filtre_transactions = true;
100 $filters->max_amount = (int)$request->getQueryParams()['max_amount'];
101 } else {
102 $filters->filtre_transactions = false;
103 $filters->max_amount = null;
104 }
105
106 if ($option !== null) {
107 switch ($option) {
108 case 'page':
109 $filters->current_page = (int)$value;
110 break;
111 case 'order':
112 $filters->orderby = $value;
113 break;
114 case 'member':
115 if (($this->login->isAdmin()
116 || $this->login->isStaff())
117 ) {
118 if ($value == 'all') {
119 $filters->filtre_cotis_adh = null;
120 } else {
121 $filters->filtre_cotis_adh = $value;
122 }
123 }
124 break;
125 }
126 }
127
128 if (!$this->login->isAdmin() && !$this->login->isStaff()) {
129 $filters->filtre_cotis_adh = $this->login->id;
130 }
131
132 $class = '\\Galette\\Repository\\' . ucwords($raw_type);
133 $contrib = new $class($this->zdb, $this->login, $filters);
134 $contribs_list = $contrib->getList(true);
135
136 //store filters into session
137 if ($ajax === false) {
138 $this->session->$filter_name = $filters;
139 }
140
141 //assign pagination variables to the template and add pagination links
142 $filters->setSmartyPagination($this->router, $this->view->getSmarty());
143
144 $tpl_vars = [
145 'page_title' => $raw_type === 'contributions' ?
146 _T("Contributions management") :
147 _T("Transactions management"),
148 'require_dialog' => true,
149 'require_calendar' => true,
150 'contribs' => $contrib,
151 'list' => $contribs_list,
152 'nb' => $contrib->getCount(),
153 'filters' => $filters,
154 'mode' => ($ajax === true ? 'ajax' : 'std')
155 ];
156
157 if ($filters->filtre_cotis_adh != null) {
158 $member = new Adherent($this->zdb);
159 $member->load($filters->filtre_cotis_adh);
160 $tpl_vars['member'] = $member;
161 }
162
163 // display page
164 $this->view->render(
165 $response,
166 'gestion_' . $raw_type . '.tpl',
167 $tpl_vars
168 );
169 return $response;
170 }
171 )->setName('contributions')->add($authenticate);
172
173 $app->post(
174 '/{type:contributions|transactions}/filter',
175 function ($request, $response, $args) {
176 $raw_type = null;
177 switch ($args['type']) {
178 case 'transactions':
179 $raw_type = 'transactions';
180 break;
181 case 'contributions':
182 $raw_type = 'contributions';
183 break;
184 }
185
186 $type = 'filter_' . $raw_type;
187 $post = $request->getParsedBody();
188 $error_detected = [];
189
190 if ($this->session->$type !== null) {
191 $filters = $this->session->$type;
192 } else {
193 $filter_class = '\\Galette\\Filters\\' . ucwords($raw_type) . 'List';
194 $filters = new $filter_class();
195 }
196
197 if (isset($post['clear_filter'])) {
198 $filters->reinit();
199 } else {
200 if (isset($post['max_amount'])) {
201 $filters->max_amount = null;
202 }
203
204 if ((isset($post['nbshow']) && is_numeric($post['nbshow']))
205 ) {
206 $filters->show = $post['nbshow'];
207 }
208
209 if (isset($post['end_date_filter']) || isset($post['start_date_filter'])) {
210 try {
211 if (isset($post['start_date_filter'])) {
212 $field = _T("start date filter");
213 $filters->start_date_filter = $post['start_date_filter'];
214 }
215 if (isset($post['end_date_filter'])) {
216 $field = _T("end date filter");
217 $filters->end_date_filter = $post['end_date_filter'];
218 }
219 } catch (Exception $e) {
220 $error_detected[] = $e->getMessage();
221 }
222 }
223
224 if (isset($post['payment_type_filter'])) {
225 $ptf = (int)$post['payment_type_filter'];
226 $ptypes = new PaymentTypes(
227 $this->zdb,
228 $this->preferences,
229 $this->login
230 );
231 $ptlist = $ptypes->getList();
232 if (isset($ptlist[$ptf])) {
233 $filters->payment_type_filter = $ptf;
234 } elseif ($ptf == -1) {
235 $filters->payment_type_filter = null;
236 } else {
237 $error_detected[] = _T("- Unknown payment type!");
238 }
239 }
240 }
241
242 $this->session->$type = $filters;
243
244 if (count($error_detected) > 0) {
245 //report errors
246 foreach ($error_detected as $error) {
247 $this->flash->addMessage(
248 'error_detected',
249 $error
250 );
251 }
252 }
253
254 return $response
255 ->withStatus(301)
256 ->withHeader('Location', $this->router->pathFor('contributions', ['type' => $raw_type]));
257 }
258 )->setName(
259 'payments_filter'
260 )->add($authenticate);
261
262 $app->get(
263 '/contribution/{type:fee|donation}/{action:add|edit}[/{id:\d+}]',
264 function ($request, $response, $args) {
265 $action = $args['action'];
266 $get = $request->getQueryParams();
267 $id_cotis = null;
268 $id_adh = null;
269 if (isset($args['id'])) {
270 $id_cotis = $args['id'];
271 }
272
273 if ($action === 'edit' && $id_cotis === null) {
274 throw new \RuntimeException(
275 _T("Contribution ID cannot ben null calling edit route!")
276 );
277 } elseif ($action === 'add' && $id_cotis !== null) {
278 return $response
279 ->withStatus(301)
280 ->withHeader(
281 'Location',
282 $this->router->pathFor(
283 'contribution',
284 [
285 'type' => $args['type'],
286 'action' => 'add'
287 ]
288 )
289 );
290 }
291
292 // contribution types
293 $ct = new ContributionsTypes($this->zdb);
294 $contributions_types = $ct->getList($args['type'] === 'fee');
295
296 if ($this->session->contribution !== null) {
297 $contrib = $this->session->contribution;
298 $this->session->contribution = null;
299 } else {
300 if ($args['action'] === 'edit') {
301 $contrib = new Contribution($this->zdb, $this->login, (int)$id_cotis);
302 $id_adh = $contrib->member;
303 if ($contrib->id == '') {
304 //not possible to load contribution, exit
305 $this->flash->addMessage(
306 'error_detected',
307 str_replace(
308 '%id',
309 $id_cotis,
310 _T("Unable to load contribution #%id!")
311 )
312 );
313 return $response
314 ->withStatus(301)
315 ->withHeader('Location', $this->router->pathFor(
316 'contributions',
317 ['type' => 'contributions']
318 ));
319 }
320 } else {
321 $id_type_cotis = 0;
322 if (isset($get[ContributionsTypes::PK])
323 && $get[ContributionsTypes::PK]
324 && $action === 'add'
325 ) {
326 $id_type_cotis = $get[ContributionsTypes::PK];
327 }
328 $cparams = ['type' => array_keys($contributions_types)[0]];
329
330 //member id
331 $id_adh = null;
332 if (isset($get[Adherent::PK]) && $get[Adherent::PK] > 0 && $action === 'add') {
333 $id_adh = (int)$get[Adherent::PK];
334 $cparams['adh'] = $id_adh;
335 }
336
337 //transaction id
338 $trans_id = null;
339 if (isset($get[Transaction::PK]) && $get[Transaction::PK] > 0) {
340 $cparams['trans'] = $get[Transaction::PK];
341 }
342
343 $contrib = new Contribution(
344 $this->zdb,
345 $this->login,
346 (count($cparams) > 0 ? $cparams : null)
347 );
348
349 if (isset($get['montant_cotis']) && $get['montant_cotis'] > 0 && $action === 'add') {
350 $contrib->amount = $get['montant_cotis'];
351 }
352
353 if ($contrib->isTransactionPart()) {
354 $id_adh = $contrib->member;
355 }
356 }
357 }
358
359 $disabled = array();
360
361 if (!is_int($contrib->id)) {
362 // initialiser la structure contribution à vide (nouvelle contribution)
363 $contribution['duree_mois_cotis'] = $this->preferences->pref_membership_ext;
364 }
365
366 // template variable declaration
367 $title = null;
368 if ($args['type'] === 'fee') {
369 $title = _T("Membership fee");
370 } else {
371 $title = _T("Donation");
372 }
373
374 if ($contrib->id != '') {
375 $title .= ' (' . _T("modification") . ')';
376 } else {
377 $title .= ' (' . _T("creation") . ')';
378 }
379
380 // required fields
381 $required = [
382 'id_type_cotis' => 1,
383 'id_adh' => 1,
384 'date_enreg' => 1,
385 'date_debut_cotis' => 1,
386 'date_fin_cotis' => $contrib->isCotis(),
387 'montant_cotis' => $contrib->isCotis() ? 1 : 0
388 ];
389
390 $params = [
391 'page_title' => $title,
392 'required' => $required,
393 'disabled' => $disabled,
394 'contribution' => $contrib,
395 'adh_selected' => $id_adh,
396 'require_calendar' => true,
397 'type' => $args['type']
398 ];
399
400 // contribution types
401 $params['type_cotis_options'] = $contributions_types;
402
403 // members
404 $members = [];
405 $m = new Members();
406 $required_fields = array(
407 'id_adh',
408 'nom_adh',
409 'prenom_adh'
410 );
411 $list_members = $m->getList(false, $required_fields, true);
412
413 if (count($list_members) > 0) {
414 foreach ($list_members as $member) {
415 $pk = Adherent::PK;
416 $sname = mb_strtoupper($member->nom_adh, 'UTF-8') .
417 ' ' . ucwords(mb_strtolower($member->prenom_adh, 'UTF-8')) .
418 ' (' . $member->id_adh . ')';
419 $members[$member->$pk] = $sname;
420 }
421 }
422
423 $params['members'] = [
424 'filters' => $m->getFilters(),
425 'count' => $m->getCount()
426 ];
427
428 //check if current attached member is part of the list
429 if (isset($contrib)
430 && $contrib->member > 0
431 && !isset($members[$contrib->member])
432 ) {
433 $members[$contrib->member] = Adherent::getSName($this->zdb, $contrib->member, true);
434 }
435
436 if (count($members)) {
437 $params['members']['list'] = $members;
438 }
439
440 $ext_membership = '';
441 if (isset($contrib) && $contrib->isCotis() || !isset($contrib) && $args['type'] === 'fee') {
442 $ext_membership = $this->preferences->pref_membership_ext;
443 }
444 $params['pref_membership_ext'] = $ext_membership;
445 $params['autocomplete'] = true;
446
447 // display page
448 $this->view->render(
449 $response,
450 'ajouter_contribution.tpl',
451 $params
452 );
453 return $response;
454 }
455 )->setName('contribution')->add($authenticate);
456
457 $app->post(
458 '/contribution/{type:fee|donation}/{action:add|edit}[/{id:\d+}]',
459 function ($request, $response, $args) {
460 $post = $request->getParsedBody();
461
462 if (isset($post['btnreload'])) {
463 $redirect_url = $this->router->pathFor('contribution', $args);
464 $redirect_url .= '?' . Adherent::PK . '=' . $post[Adherent::PK] . '&' .
465 ContributionsTypes::PK . '=' . $post[ContributionsTypes::PK] . '&' .
466 'montant_cotis=' . $post['montant_cotis'];
467 return $response
468 ->withStatus(301)
469 ->withHeader('Location', $redirect_url);
470 }
471
472 $error_detected = [];
473 $warning_detected = [];
474 $redirect_url = null;
475
476 $action = $args['action'];
477 $id_cotis = null;
478 if (isset($args['id'])) {
479 $id_cotis = $args['id'];
480 }
481
482 $id_adh = $post['id_adh'];
483
484 if ($action === 'edit' && $id_cotis === null) {
485 throw new \RuntimeException(
486 _T("Contribution ID cannot ben null calling edit route!")
487 );
488 } elseif ($action === 'add' && $id_cotis !== null) {
489 throw new \RuntimeException(
490 _T("Contribution ID must be null calling add route!")
491 );
492 }
493
494 if ($this->session->contribution !== null) {
495 $contrib = $this->session->contribution;
496 $this->session->contribution = null;
497 } else {
498 if ($id_cotis === null) {
499 $contrib = new Contribution($this->zdb, $this->login);
500 } else {
501 $contrib = new Contribution($this->zdb, $this->login, (int)$id_cotis);
502 }
503 }
504
505 // flagging required fields for first step only
506 $required = [
507 'id_type_cotis' => 1,
508 'id_adh' => 1,
509 'date_enreg' => 1,
510 'montant_cotis' => 1, //TODO: not always required, see #196
511 'date_debut_cotis' => 1,
512 'date_fin_cotis' => ($args['type'] === 'fee')
513 ];
514 $disabled = [];
515
516 // regular fields
517 $valid = $contrib->check($post, $required, $disabled);
518 if ($valid !== true) {
519 $error_detected = array_merge($error_detected, $valid);
520 }
521
522 if (count($error_detected) == 0) {
523 //all goes well, we can proceed
524 $new = false;
525 if ($contrib->id == '') {
526 $new = true;
527 }
528
529 if (count($error_detected) == 0) {
530 $store = $contrib->store();
531 if ($store === true) {
532 //contribution has been stored :)
533 if ($new) {
534 //if an external script has been configured, we call it
535 if ($this->preferences->pref_new_contrib_script) {
536 $es = new Galette\IO\ExternalScript($this->preferences);
537 $res = $contrib->executePostScript($es);
538
539 if ($res !== true) {
540 //send admin a mail with all details
541 if ($this->preferences->pref_mail_method > GaletteMail::METHOD_DISABLED) {
542 $mail = new GaletteMail($this->preferences);
543 $mail->setSubject(
544 _T("Post contribution script failed")
545 );
546
547 $recipients = [];
548 foreach ($this->preferences->vpref_email_newadh as $pref_email) {
549 $recipients[$pref_email] = $pref_email;
550 }
551 $mail->setRecipients($recipients);
552
553 $message = _T("The configured post contribution script has failed.");
554 $message .= "\n" . _T("You can find contribution information and script output below.");
555 $message .= "\n\n";
556 $message .= $res;
557
558 $mail->setMessage($message);
559 $sent = $mail->send();
560
561 if (!$sent) {
562 $txt = preg_replace(
563 array('/%name/', '/%email/'),
564 array($adh->sname, $adh->getEmail()),
565 _T("A problem happened while sending to admin post contribution notification for user %name (%email) contribution")
566 );
567 $this->history->add($txt);
568 $error_detected[] = $txt;
569 //Mails are disabled... We log (not safe, but)...
570 Analog::log(
571 'Post contribution script has failed. Here was the data: ' .
572 "\n" . print_r($res, true),
573 Analog::ERROR
574 );
575 }
576 } else {
577 //Mails are disabled... We log (not safe, but)...
578 Analog::log(
579 'Post contribution script has failed. Here was the data: ' .
580 "\n" . print_r($res, true),
581 Analog::ERROR
582 );
583 }
584 }
585 }
586 }
587 } else {
588 //something went wrong :'(
589 $error_detected[] = _T("An error occurred while storing the contribution.");
590 }
591 }
592 }
593
594 if (count($error_detected) == 0) {
595 // Get member informations
596 $adh = new Adherent($this->zdb);
597 $adh->load($contrib->member);
598
599 if ($this->preferences->pref_mail_method > GaletteMail::METHOD_DISABLED) {
600 $texts = new Texts(
601 $this->texts_fields,
602 $this->preferences,
603 $this->router,
604 array(
605 'name_adh' => custom_html_entity_decode($adh->sname),
606 'firstname_adh' => custom_html_entity_decode($adh->surname),
607 'lastname_adh' => custom_html_entity_decode($adh->name),
608 'mail_adh' => custom_html_entity_decode($adh->getEmail()),
609 'login_adh' => custom_html_entity_decode($adh->login),
610 'deadline' => custom_html_entity_decode($contrib->end_date),
611 'contrib_info' => custom_html_entity_decode($contrib->info),
612 'contrib_amount' => custom_html_entity_decode($contrib->amount),
613 'contrib_type' => custom_html_entity_decode($contrib->type->libelle)
614 )
615 );
616 if ($new && isset($_POST['mail_confirm'])
617 && $_POST['mail_confirm'] == '1'
618 ) {
619 if (GaletteMail::isValidEmail($adh->getEmail())) {
620 $text = 'contrib';
621 if (!$contrib->isCotis()) {
622 $text = 'donation';
623 }
624 $mtxt = $texts->getTexts($text, $adh->language);
625
626 $mail = new GaletteMail($this->preferences);
627 $mail->setSubject($texts->getSubject());
628 $mail->setRecipients(
629 array(
630 $adh->getEmail() => $adh->sname
631 )
632 );
633
634 $mail->setMessage($texts->getBody());
635 $sent = $mail->send();
636
637 if ($sent) {
638 $this->history->add(
639 preg_replace(
640 array('/%name/', '/%email/'),
641 array($adh->sname, $adh->getEmail()),
642 _T("Mail sent to user %name (%email)")
643 )
644 );
645 } else {
646 $txt = preg_replace(
647 array('/%name/', '/%email/'),
648 array($adh->sname, $adh->getEmail()),
649 _T("A problem happened while sending contribution receipt to user %name (%email)")
650 );
651 $this->history->add($txt);
652 $error_detected[] = $txt;
653 }
654 } else {
655 $txt = preg_replace(
656 array('/%name/', '/%email/'),
657 array($adh->sname, $adh->getEmail()),
658 _T("Trying to send a mail to a member (%name) with an invalid address: %email")
659 );
660 $this->history->add($txt);
661 $warning_detected[] = $txt;
662 }
663 }
664
665 // Sent email to admin if pref checked
666 if ($new && $this->preferences->pref_bool_mailadh) {
667 // Get email text in database
668 $text = 'newcont';
669 if (!$contrib->isCotis()) {
670 $text = 'newdonation';
671 }
672 $mtxt = $texts->getTexts($text, $this->preferences->pref_lang);
673
674 $mail = new GaletteMail($this->preferences);
675 $mail->setSubject($texts->getSubject());
676
677 $recipients = [];
678 foreach ($this->preferences->vpref_email_newadh as $pref_email) {
679 $recipients[$pref_email] = $pref_email;
680 }
681 $mail->setRecipients($recipients);
682
683 $mail->setMessage($texts->getBody());
684 $sent = $mail->send();
685
686 if ($sent) {
687 $this->history->add(
688 preg_replace(
689 array('/%name/', '/%email/'),
690 array($adh->sname, $adh->getEmail()),
691 _T("Mail sent to admin for user %name (%email)")
692 )
693 );
694 } else {
695 $txt = preg_replace(
696 array('/%name/', '/%email/'),
697 array($adh->sname, $adh->getEmail()),
698 _T("A problem happened while sending to admin notification for user %name (%email) contribution")
699 );
700 $this->history->add($txt);
701 $error_detected[] = $txt;
702 }
703 }
704 }
705
706 if (count($warning_detected) > 0) {
707 foreach ($warning_detected as $warning) {
708 $this->flash->addMessage(
709 'warning_detected',
710 $warning
711 );
712 }
713 }
714
715 if (count($error_detected) == 0) {
716 if ($contrib->isTransactionPart() && $contrib->transaction->getMissingAmount() > 0) {
717 $redirect_url = $this->router->pathFor(
718 'contribution',
719 [
720 'action' => 'add',
721 'type' => $post['contrib_type']
722 ]
723 ) . '?' . Transaction::PK . '=' . $contrib->transaction->id .
724 '&' . Adherent::PK . '=' . $contrib->member;
725 } else {
726 $redirect_url = $this->router->pathFor(
727 'contributions',
728 [
729 'type' => 'contributions'
730 ]
731 ) . '?' . Adherent::PK . '=' . $contrib->member;
732 }
733 }
734 }
735
736 /* TODO: remove */
737 if (!isset($contribution['duree_mois_cotis'])
738 || $contribution['duree_mois_cotis'] == ''
739 ) {
740 // On error restore entered value or default to display the form again
741 if (isset($_POST['duree_mois_cotis'])
742 && $_POST['duree_mois_cotis'] != ''
743 ) {
744 $contribution['duree_mois_cotis'] = $_POST['duree_mois_cotis'];
745 } else {
746 $contribution['duree_mois_cotis'] = $this->preferences->pref_membership_ext;
747 }
748 }
749
750 if (count($error_detected) > 0) {
751 //something went wrong.
752 //store entity in session
753 $this->session->contribution = $contrib;
754 $redirect_url = $this->router->pathFor('contribution', $args);
755
756 //report errors
757 foreach ($error_detected as $error) {
758 $this->flash->addMessage(
759 'error_detected',
760 $error
761 );
762 }
763 } else {
764 $this->session->contribution = null;
765 if ($redirect_url === null) {
766 $redirect_url = $this->router->pathFor('contributions', ['type' => $args['type']]);
767 }
768 }
769
770 //redirect to calling action
771 return $response
772 ->withStatus(301)
773 ->withHeader('Location', $redirect_url);
774 }
775 )->setName('contribution')->add($authenticate);
776
777 $app->get(
778 '/transaction/{action:add|edit}[/{id:\d+}]',
779 function ($request, $response, $args) {
780 $trans = null;
781
782 if ($this->session->transaction !== null) {
783 $trans = $this->session->transaction;
784 $this->session->transaction = null;
785 } else {
786 $trans = new Transaction($this->zdb, $this->login);
787 }
788
789 $action = $args['action'];
790 $trans_id = null;
791 if (isset($args['id'])) {
792 $trans_id = $args['id'];
793 }
794
795 if ($action === 'edit' && $trans_id === null) {
796 throw new \RuntimeException(
797 _T("Transaction ID cannot ben null calling edit route!")
798 );
799 } elseif ($action === 'add' && $trans_id !== null) {
800 return $response
801 ->withStatus(301)
802 ->withHeader('Location', $this->router->pathFor('transaction', ['action' => 'add']));
803 }
804
805 $transaction['trans_id'] = $trans_id;
806 $transaction['trans_amount'] = get_numeric_form_value("trans_amount", '');
807 $transaction['trans_date'] = get_form_value("trans_date", '');
808 $transaction['trans_desc'] = get_form_value("trans_desc", '');
809 $transaction['id_adh'] = get_numeric_form_value("id_adh", '');
810
811 // flagging required fields
812 $required = array(
813 'trans_amount' => 1,
814 'trans_date' => 1,
815 'trans_desc' => 1,
816 'id_adh' => 1
817 );
818 $disabled = array();
819
820 if ($action === 'edit') {
821 // initialize transactions structure with database values
822 $trans->load($trans_id);
823 if ($trans->id == '') {
824 //not possible to load transaction, exit
825 throw new \RuntimeException('Transaction does not exists!');
826 }
827 }
828
829 // template variable declaration
830 $title = _T("Transaction");
831 if ($action === 'edit') {
832 $title .= ' (' . _T("modification") . ')';
833 } else {
834 $title .= ' (' . _T("creation") . ')';
835 }
836
837 $params = [
838 'page_title' => $title,
839 'required' => $required,
840 'data' => $transaction, //TODO: remove
841 'transaction' => $trans,
842 'require_calendar' => true,
843 'require_dialog' => true
844 ];
845
846 if ($trans->id != '') {
847 $contribs = new Contributions($this->zdb, $this->login);
848 $params['contribs'] = $contribs->getListFromTransaction($trans->id);
849 }
850
851 // members
852 $members = [];
853 $m = new Members();
854 $required_fields = array(
855 'id_adh',
856 'nom_adh',
857 'prenom_adh'
858 );
859 $list_members = $m->getList(false, $required_fields, true);
860
861 if (count($list_members) > 0) {
862 foreach ($list_members as $member) {
863 $pk = Adherent::PK;
864 $sname = mb_strtoupper($member->nom_adh, 'UTF-8') .
865 ' ' . ucwords(mb_strtolower($member->prenom_adh, 'UTF-8')) .
866 ' (' . $member->id_adh . ')';
867 $members[$member->$pk] = $sname;
868 }
869 }
870
871 $params['members'] = [
872 'filters' => $m->getFilters(),
873 'count' => $m->getCount()
874 ];
875 $params['autocomplete'] = true;
876
877 //check if current attached member is part of the list
878 if (isset($trans) && $trans->member > 0) {
879 if (!isset($members[$trans->member])) {
880 $members =
881 [$trans->member => Adherent::getSName($this->zdb, $trans->member, true)] +
882 $members
883 ;
884 }
885 }
886
887 if (count($members)) {
888 $params['members']['list'] = $members;
889 }
890
891 // display page
892 $this->view->render(
893 $response,
894 'ajouter_transaction.tpl',
895 $params
896 );
897 return $response;
898 }
899 )->setName('transaction')->add($authenticate);
900
901 $app->get(
902 '/transaction/{id}/attach/{cid}',
903 function ($request, $response, $args) {
904 if (!Contribution::setTransactionPart($this->zdb, $args['id'], $args['cid'])) {
905 $this->flash->addMessage(
906 'error_detected',
907 _T("Unable to attach contribution to transaction")
908 );
909 } else {
910 $this->flash->addMessage(
911 'success_detected',
912 _T("Contribution has been successfully attached to current transaction")
913 );
914 }
915
916 return $response
917 ->withStatus(301)
918 ->withHeader('Location', $this->router->pathFor(
919 'transaction',
920 ['action' => 'edit', 'id' => $args['id']]
921 ));
922 }
923 )->setName('attach_contribution')->add($authenticate);
924
925 $app->get(
926 '/transaction/{id}/detach/{cid}',
927 function ($request, $response, $args) {
928 if (!Contribution::unsetTransactionPart($this->zdb, $this->login, $args['id'], $args['cid'])) {
929 $this->flash->addMessage(
930 'error_detected',
931 _T("Unable to detach contribution from transaction")
932 );
933 } else {
934 $this->flash->addMessage(
935 'success_detected',
936 _T("Contribution has been successfully detached from current transaction")
937 );
938 }
939
940 return $response
941 ->withStatus(301)
942 ->withHeader('Location', $this->router->pathFor(
943 'transaction',
944 ['action' => 'edit', 'id' => $args['id']]
945 ));
946 }
947 )->setName('detach_contribution')->add($authenticate);
948
949 $app->post(
950 '/transaction/{action:add|edit}[/{id:\d+}]',
951 function ($request, $response, $args) {
952 $post = $request->getParsedBody();
953 $trans = new Transaction($this->zdb, $this->login);
954
955 $action = $args['action'];
956 $trans_id = null;
957 if (isset($args['id'])) {
958 $trans_id = $args['id'];
959 }
960
961 if ($action === 'edit' && $trans_id === null) {
962 throw new \RuntimeException(
963 _T("Transaction ID cannot ben null calling edit route!")
964 );
965 } elseif ($action === 'add' && $trans_id !== null) {
966 throw new \RuntimeException(
967 _T("Transaction ID cannot ben set while adding!")
968 );
969 }
970
971 $transaction['trans_id'] = $trans_id;
972 $transaction['trans_amount'] = $post['trans_amount'];
973 $transaction['trans_date'] = $post['trans_date'];
974 $transaction['trans_desc'] = $post['trans_desc'];
975 $transaction['id_adh'] = $post['id_adh'];
976
977 // flagging required fields
978 $required = array(
979 'trans_amount' => 1,
980 'trans_date' => 1,
981 'trans_desc' => 1,
982 'id_adh' => 1
983 );
984 $disabled = array();
985
986 if ($action === 'edit') {
987 // initialize transactions structure with database values
988 $trans->load($trans_id);
989 if ($trans->id == '') {
990 //not possible to load transaction, exit
991 throw new \RuntimeException('Transaction does not exists!');
992 }
993 }
994
995 $error_detected = [];
996 // regular fields
997 $valid = $trans->check($_POST, $required, $disabled);
998 if ($valid !== true) {
999 $error_detected = array_merge($error_detected, $valid);
1000 }
1001
1002 if (count($error_detected) == 0) {
1003 //all goes well, we can proceed
1004 $new = false;
1005 if ($trans->id == '') {
1006 $new = true;
1007 }
1008
1009 $store = $trans->store($this->history);
1010 if ($store === true) {
1011 //transaction has been stored :)
1012 if ($new) {
1013 $transaction['trans_id'] = $trans->id;
1014 }
1015 } else {
1016 //something went wrong :'(
1017 $error_detected[] = _T("An error occurred while storing the transaction.");
1018 }
1019 }
1020
1021 if (count($error_detected) == 0) {
1022 if ($trans->getMissingAmount() > 0) {
1023 $rparams = [
1024 'action' => 'add',
1025 'type' => $post['contrib_type']
1026 ];
1027
1028 if (isset($trans->member)) {
1029 $rparams['id_adh'] = $trans->member;
1030 }
1031
1032 return $response
1033 ->withStatus(301)
1034 ->withHeader(
1035 'Location',
1036 $this->router->pathFor(
1037 'contribution',
1038 $rparams
1039 ) . '?' . Transaction::PK . '=' . $trans->id .
1040 '&' . Adherent::PK . '=' . $trans->member
1041 );
1042 } else {
1043 //report success
1044 $this->flash->addMessage(
1045 'success_detected',
1046 _T("Transaction has been successfully stored")
1047 );
1048
1049 //get back to transactions list
1050 return $response
1051 ->withStatus(301)
1052 ->withHeader(
1053 'Location',
1054 $this->router->pathFor('contributions', ['type' => 'transactions'])
1055 );
1056 }
1057 } else {
1058 //something went wrong.
1059 //store entity in session
1060 $this->session->transaction = $trans;
1061
1062 //report errors
1063 foreach ($error_detected as $error) {
1064 $this->flash->addMessage(
1065 'error_detected',
1066 $error
1067 );
1068 }
1069
1070 //redirect to calling action
1071 return $response
1072 ->withStatus(301)
1073 ->withHeader('Location', $this->router->pathFor('transaction', $args));
1074 }
1075
1076 return $response
1077 ->withStatus(301)
1078 ->withHeader('Location', $this->router->pathFor('contributions', ['type' => 'transactions']));
1079 }
1080 )->setName('doEditTransaction')->add($authenticate);
1081
1082 $app->get(
1083 '/{type:contributions|transactions}/remove' .'/{id:\d+}',
1084 function ($request, $response, $args) {
1085 $raw_type = null;
1086 switch ($args['type']) {
1087 case 'transactions':
1088 $raw_type = 'transactions';
1089 break;
1090 case 'contributions':
1091 $raw_type = 'contributions';
1092 break;
1093 }
1094
1095 $data = [
1096 'id' => $args['id'],
1097 'redirect_uri' => $this->router->pathFor('contributions', ['type' => $args['type']])
1098 ];
1099
1100 // display page
1101 $this->view->render(
1102 $response,
1103 'confirm_removal.tpl',
1104 array(
1105 'type' => ($raw_type === 'contributions') ? _T('Contributions') : _T('Transactions'),
1106 'mode' => $request->isXhr() ? 'ajax' : '',
1107 'page_title' => sprintf(
1108 _T('Remove %1$s #%2$s'),
1109 ($raw_type === 'contributions') ? _T('contribution') : _T('transaction'),
1110 $args['id']
1111 ),
1112 'form_url' => $this->router->pathFor(
1113 'doRemoveContribution',
1114 ['id' => $args['id'], 'type' => $args['type']]
1115 ),
1116 'cancel_uri' => $data['redirect_uri'],
1117 'data' => $data
1118 )
1119 );
1120 return $response;
1121 }
1122 )->setName('removeContribution')->add($authenticate);
1123
1124 $app->post(
1125 '/{type:contributions|transactions}/batch/remove',
1126 function ($request, $response, $args) {
1127 $post = $request->getParsedBody();
1128
1129 $raw_type = null;
1130 switch ($args['type']) {
1131 case 'transactions':
1132 $raw_type = 'transactions';
1133 break;
1134 case 'contributions':
1135 $raw_type = 'contributions';
1136 break;
1137 }
1138
1139 $data = [
1140 'id' => $post['contrib_sel'],
1141 'redirect_uri' => $this->router->pathFor('contributions', ['type' => $args['type']])
1142 ];
1143
1144 // display page
1145 $this->view->render(
1146 $response,
1147 'confirm_removal.tpl',
1148 array(
1149 'type' => ($raw_type === 'contributions') ? _T('Contributions') : _T('Transactions'),
1150 'mode' => $request->isXhr() ? 'ajax' : '',
1151 'page_title' => sprintf(
1152 _T('Remove %1$s'),
1153 ($raw_type === 'contributions') ? _T('contributions') : _T('transactions')
1154 ),
1155 'message' => str_replace(
1156 '%count',
1157 count($data['id']),
1158 ($raw_type === 'contributions' ?
1159 _T('You are about to remove %count contributions.') :
1160 _T('You are about to remove %count transactions.'))
1161 ),
1162 'form_url' => $this->router->pathFor(
1163 'doRemoveContribution',
1164 ['type' => $args['type']]
1165 ),
1166 'cancel_uri' => $data['redirect_uri'],
1167 'data' => $data
1168 )
1169 );
1170 return $response;
1171 }
1172 )->setName('removeContributions')->add($authenticate);
1173
1174 $app->post(
1175 '/{type:contributions|transactions}/remove[/{id}]',
1176 function ($request, $response, $args) {
1177 $ids = null;
1178 $post = $request->getParsedBody();
1179 $ajax = isset($post['ajax']) && $post['ajax'] === 'true';
1180 $success = false;
1181
1182 if (!is_array($post['id'])) {
1183 $ids = (array)$post['id'];
1184 } else {
1185 $ids = $post['id'];
1186 }
1187
1188 $raw_type = null;
1189 switch ($args['type']) {
1190 case 'transactions':
1191 $raw_type = 'transactions';
1192 break;
1193 case 'contributions':
1194 $raw_type = 'contributions';
1195 break;
1196 }
1197
1198 $uri = isset($post['redirect_uri']) ?
1199 $post['redirect_uri'] :
1200 $this->router->pathFor('slash');
1201
1202 if (!isset($post['confirm'])) {
1203 $this->flash->addMessage(
1204 'error_detected',
1205 _T("Removal has not been confirmed!")
1206 );
1207 } else {
1208 $class = '\\Galette\Repository\\' . ucwords($raw_type);
1209 $contribs = new $class($this->zdb, $this->login);
1210 $rm = $contribs->remove($ids, $this->history);
1211 if ($rm) {
1212 $msg = null;
1213 if ($raw_type === 'contributions') {
1214 $msg = _T("Contributions(s) has been removed!");
1215 } else {
1216 $msg = _T("Transactions(s) has been removed!");
1217 }
1218 $this->flash->addMessage(
1219 'success_detected',
1220 $msg
1221 );
1222 $success = true;
1223 } else {
1224 $msg = null;
1225 if ($raw_type === 'contributions') {
1226 $msg = _T("An error occurred trying to remove contributions(s) :(");
1227 } else {
1228 $msg = _T("An error occurred trying to remove transaction(s) :(");
1229 }
1230 $this->flash->addMessage(
1231 'error_detected',
1232 $msg
1233 );
1234 }
1235 }
1236
1237 if (!$ajax) {
1238 return $response
1239 ->withStatus(301)
1240 ->withHeader('Location', $uri);
1241 } else {
1242 return $response->withJson(
1243 [
1244 'success' => $success
1245 ]
1246 );
1247 }
1248 }
1249 )->setName('doRemoveContribution')->add($authenticate);
1250
1251 //Contribution PDF
1252 $app->get(
1253 '/contribution/print/{id:\d+}',
1254 function ($request, $response, $args) {
1255 $contribution = new Contribution($this->zdb, $this->login, (int)$args['id']);
1256 $pdf = new PdfContribution($contribution, $this->zdb, $this->preferences);
1257 $pdf->download();
1258 }
1259 )->setName('printContribution')->add($authenticate);