]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Repository/Contributions.php
2f90ee98e51257caddb55836c244f02c86c0e9f2
[galette.git] / galette / lib / Galette / Repository / Contributions.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Contributions class
7 *
8 * PHP version 5
9 *
10 * Copyright © 2010-2023 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 Repository
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2010-2023 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.7dev - 2010-03-11
35 */
36
37 namespace Galette\Repository;
38
39 use ArrayObject;
40 use Throwable;
41 use Analog\Analog;
42 use Laminas\Db\Sql\Expression;
43 use Galette\Core\Db;
44 use Galette\Core\Login;
45 use Galette\Core\History;
46 use Galette\Entity\Contribution;
47 use Galette\Entity\Adherent;
48 use Galette\Entity\Transaction;
49 use Galette\Entity\ContributionsTypes;
50 use Galette\Filters\ContributionsList;
51 use Laminas\Db\Sql\Select;
52
53 /**
54 * Contributions class for galette
55 *
56 * @name Contributions
57 * @category Repository
58 * @package Galette
59 *
60 * @author Johan Cwiklinski <johan@x-tnd.be>
61 * @copyright 2009-2023 The Galette Team
62 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
63 * @link http://galette.tuxfamily.org
64 */
65 class Contributions
66 {
67 public const TABLE = Contribution::TABLE;
68 public const PK = Contribution::PK;
69
70 private $filters = false;
71 private $count = null;
72
73 private $zdb;
74 private $login;
75 private $sum;
76 private $current_selection;
77
78 /**
79 * Default constructor
80 *
81 * @param Db $zdb Database
82 * @param Login $login Login
83 * @param ContributionsList $filters Filtering
84 */
85 public function __construct(Db $zdb, Login $login, $filters = null)
86 {
87 $this->zdb = $zdb;
88 $this->login = $login;
89
90 if ($filters === null) {
91 $this->filters = new ContributionsList();
92 } else {
93 $this->filters = $filters;
94 }
95 }
96
97 /**
98 * Get contributions list for a specific transaction
99 *
100 * @param int $trans_id Transaction identifier
101 *
102 * @return Contribution[]|ArrayObject
103 */
104 public function getListFromTransaction($trans_id)
105 {
106 $this->filters->from_transaction = $trans_id;
107 return $this->getList(true);
108 }
109
110 /**
111 * Get contributions list for a specific transaction
112 *
113 * @param array $ids an array of members id that has been selected
114 * @param bool $as_contrib return the results as an array of
115 * @param array $fields field(s) name(s) to get. Should be a string or
116 * an array. If null, all fields will be
117 * returned
118 * @param boolean $count true if we want to count members
119 *
120 * @return Contribution[]|ArrayObject[]|false
121 */
122 public function getArrayList(array $ids, bool $as_contrib = false, array $fields = null, bool $count = true)
123 {
124 if (count($ids) < 1) {
125 Analog::log('No contribution selected.', Analog::INFO);
126 return false;
127 }
128
129 $this->current_selection = $ids;
130 $list = $this->getList($as_contrib, $fields, $count);
131 $array_list = [];
132 foreach ($list as $entry) {
133 $array_list[] = $entry;
134 }
135 return $array_list;
136 }
137
138 /**
139 * Get contributions list
140 *
141 * @param bool $as_contrib return the results as an array of
142 * Contribution object.
143 * @param array $fields field(s) name(s) to get. Should be a string or
144 * an array. If null, all fields will be
145 * returned
146 * @param boolean $count true if we want to count members
147 *
148 * @return Contribution[]|ArrayObject
149 */
150 public function getList($as_contrib = false, $fields = null, $count = true)
151 {
152 try {
153 $select = $this->buildSelect($fields, $count);
154
155 $this->filters->setLimits($select);
156
157 $contributions = array();
158 $results = $this->zdb->execute($select);
159 if ($as_contrib) {
160 foreach ($results as $row) {
161 $contributions[] = new Contribution($this->zdb, $this->login, $row);
162 }
163 } else {
164 $contributions = $results;
165 }
166 return $contributions;
167 } catch (Throwable $e) {
168 Analog::log(
169 'Cannot list contributions | ' . $e->getMessage(),
170 Analog::WARNING
171 );
172 throw $e;
173 }
174 }
175
176 /**
177 * Builds the SELECT statement
178 *
179 * @param array $fields fields list to retrieve
180 * @param bool $count true if we want to count members
181 * (not applicable from static calls), defaults to false
182 *
183 * @return string SELECT statement
184 */
185 private function buildSelect($fields, $count = false)
186 {
187 try {
188 $fieldsList = ($fields != null)
189 ? ((!is_array($fields) || count($fields) < 1) ? (array)'*'
190 : implode(', ', $fields)) : (array)'*';
191
192 $select = $this->zdb->select(self::TABLE, 'a');
193 $select->columns($fieldsList);
194
195 $select->join(
196 array('p' => PREFIX_DB . Adherent::TABLE),
197 'a.' . Adherent::PK . '= p.' . Adherent::PK,
198 array()
199 );
200
201 $this->buildWhereClause($select);
202 $select->order(self::buildOrderClause());
203
204 $this->calculateSum($select);
205
206 if ($count) {
207 $this->proceedCount($select);
208 }
209
210 return $select;
211 } catch (Throwable $e) {
212 Analog::log(
213 'Cannot build SELECT clause for contributions | ' . $e->getMessage(),
214 Analog::WARNING
215 );
216 throw $e;
217 }
218 }
219
220 /**
221 * Count contributions from the query
222 *
223 * @param Select $select Original select
224 *
225 * @return void
226 */
227 private function proceedCount(Select $select)
228 {
229 try {
230 $countSelect = clone $select;
231 $countSelect->reset($countSelect::COLUMNS);
232 $countSelect->reset($countSelect::JOINS);
233 $countSelect->reset($countSelect::ORDER);
234 $countSelect->columns(
235 array(
236 self::PK => new Expression('COUNT(' . self::PK . ')')
237 )
238 );
239
240 $results = $this->zdb->execute($countSelect);
241 $result = $results->current();
242
243 $k = self::PK;
244 $this->count = $result->$k;
245
246 if ($this->count > 0) {
247 $this->filters->setCounter($this->count);
248 }
249 } catch (Throwable $e) {
250 Analog::log(
251 'Cannot count contributions | ' . $e->getMessage(),
252 Analog::WARNING
253 );
254 throw $e;
255 }
256 }
257
258 /**
259 * Calculate sum of all selected contributions
260 *
261 * @param Select $select Original select
262 *
263 * @return void
264 */
265 private function calculateSum(Select $select)
266 {
267 try {
268 $sumSelect = clone $select;
269 $sumSelect->reset($sumSelect::COLUMNS);
270 $sumSelect->reset($sumSelect::JOINS);
271 $sumSelect->reset($sumSelect::ORDER);
272 $sumSelect->columns(
273 array(
274 'contribsum' => new Expression('SUM(montant_cotis)')
275 )
276 );
277
278 $results = $this->zdb->execute($sumSelect);
279 $result = $results->current();
280 if ($result->contribsum) {
281 $this->sum = round($result->contribsum, 2);
282 }
283 } catch (Throwable $e) {
284 Analog::log(
285 'Cannot calculate contributions sum | ' . $e->getMessage(),
286 Analog::WARNING
287 );
288 throw $e;
289 }
290 }
291
292 /**
293 * Builds the order clause
294 *
295 * @return array SQL ORDER clauses
296 */
297 private function buildOrderClause()
298 {
299 $order = array();
300
301 switch ($this->filters->orderby) {
302 case ContributionsList::ORDERBY_ID:
303 $order[] = Contribution::PK . ' ' . $this->filters->ordered;
304 break;
305 case ContributionsList::ORDERBY_DATE:
306 $order[] = 'date_enreg ' . $this->filters->ordered;
307 break;
308 case ContributionsList::ORDERBY_BEGIN_DATE:
309 $order[] = 'date_debut_cotis ' . $this->filters->ordered;
310 break;
311 case ContributionsList::ORDERBY_END_DATE:
312 $order[] = 'date_fin_cotis ' . $this->filters->ordered;
313 break;
314 case ContributionsList::ORDERBY_MEMBER:
315 $order[] = 'nom_adh ' . $this->filters->ordered;
316 $order[] = 'prenom_adh ' . $this->filters->ordered;
317 break;
318 case ContributionsList::ORDERBY_TYPE:
319 $order[] = ContributionsTypes::PK;
320 break;
321 case ContributionsList::ORDERBY_AMOUNT:
322 $order[] = 'montant_cotis ' . $this->filters->ordered;
323 break;
324 /*
325 Hum... I really do not know how to sort a query with a value that
326 is calculated code side :/
327 case ContributionsList::ORDERBY_DURATION:
328 break;*/
329 case ContributionsList::ORDERBY_PAYMENT_TYPE:
330 $order[] = 'type_paiement_cotis ' . $this->filters->ordered;
331 break;
332 default:
333 $order[] = $this->filters->orderby . ' ' . $this->filters->ordered;
334 break;
335 }
336
337 return $order;
338 }
339
340 /**
341 * Builds where clause, for filtering on simple list mode
342 *
343 * @param Select $select Original select
344 *
345 * @return void
346 */
347 private function buildWhereClause(Select $select)
348 {
349 $field = 'date_debut_cotis';
350
351 switch ($this->filters->date_field) {
352 case ContributionsList::DATE_RECORD:
353 $field = 'date_enreg';
354 break;
355 case ContributionsList::DATE_END:
356 $field = 'date_fin_cotis';
357 break;
358 case ContributionsList::DATE_BEGIN:
359 default:
360 $field = 'date_debut_cotis';
361 break;
362 }
363
364 if (isset($this->current_selection)) {
365 $select->where->in('a.' . self::PK, $this->current_selection);
366 }
367
368 try {
369 if ($this->filters->start_date_filter != null) {
370 $d = new \DateTime($this->filters->rstart_date_filter);
371 $select->where->greaterThanOrEqualTo(
372 $field,
373 $d->format('Y-m-d')
374 );
375 }
376
377 if ($this->filters->end_date_filter != null) {
378 $d = new \DateTime($this->filters->rend_date_filter);
379 $select->where->lessThanOrEqualTo(
380 $field,
381 $d->format('Y-m-d')
382 );
383 }
384
385 if ($this->filters->payment_type_filter !== null) {
386 $select->where->equalTo(
387 'type_paiement_cotis',
388 $this->filters->payment_type_filter
389 );
390 }
391
392 if ($this->filters->from_transaction !== false) {
393 $select->where->equalTo(
394 Transaction::PK,
395 $this->filters->from_transaction
396 );
397 }
398
399 if ($this->filters->max_amount !== null && is_int($this->filters->max_amount)) {
400 $select->where(
401 '(montant_cotis <= ' . $this->filters->max_amount .
402 ' OR montant_cotis IS NULL)'
403 );
404 }
405
406 $member_clause = null;
407 if ($this->filters->filtre_cotis_adh != null) {
408 $member_clause = [$this->filters->filtre_cotis_adh];
409 if (!$this->login->isAdmin() && !$this->login->isStaff() && $this->filters->filtre_cotis_adh != $this->login->id) {
410 $member = new Adherent(
411 $this->zdb,
412 (int)$this->filters->filtre_cotis_adh,
413 [
414 'picture' => false,
415 'groups' => false,
416 'dues' => false,
417 'parent' => true
418 ]
419 );
420 if (
421 !$member->hasParent() ||
422 $member->hasParent() && $member->parent->id != $this->login->id
423 ) {
424 Analog::log(
425 'Trying to display contributions for member #' . $member->id .
426 ' without appropriate ACLs',
427 Analog::WARNING
428 );
429 $this->filters->filtre_cotis_adh = $this->login->id;
430 $member_clause = [$this->login->id];
431 }
432 }
433 } elseif ($this->filters->filtre_cotis_children !== false) {
434 $member_clause = [$this->login->id];
435 $member = new Adherent(
436 $this->zdb,
437 (int)$this->filters->filtre_cotis_children,
438 [
439 'picture' => false,
440 'groups' => false,
441 'dues' => false,
442 'children' => true
443 ]
444 );
445 foreach ($member->children as $child) {
446 $member_clause[] = $child->id;
447 }
448 } elseif (!$this->login->isAdmin() && !$this->login->isStaff()) {
449 //non staff members can only view their own contributions
450 $member_clause = $this->login->id;
451 }
452
453 if ($member_clause !== null) {
454 $select->where(
455 array(
456 'a.' . Adherent::PK => $member_clause
457 )
458 );
459 }
460
461 if ($this->filters->filtre_transactions === true) {
462 $select->where('a.trans_id IS NULL');
463 }
464 } catch (Throwable $e) {
465 Analog::log(
466 __METHOD__ . ' | ' . $e->getMessage(),
467 Analog::WARNING
468 );
469 throw $e;
470 }
471 }
472
473 /**
474 * Get count for current query
475 *
476 * @return int
477 */
478 public function getCount()
479 {
480 return $this->count;
481 }
482
483 /**
484 * Get sum
485 *
486 * @return int
487 */
488 public function getSum()
489 {
490 return $this->sum;
491 }
492
493 /**
494 * Remove specified contributions
495 *
496 * @param integer|array $ids Contributions identifiers to delete
497 * @param History $hist History
498 * @param boolean $transaction True to begin a database transaction
499 *
500 * @return boolean
501 */
502 public function remove($ids, History $hist, $transaction = true)
503 {
504 $list = array();
505 if (is_numeric($ids)) {
506 //we've got only one identifier
507 $list[] = $ids;
508 } else {
509 $list = $ids;
510 }
511
512 if (is_array($list)) {
513 try {
514 if ($transaction) {
515 $this->zdb->connection->beginTransaction();
516 }
517 $select = $this->zdb->select(self::TABLE);
518 $select->where->in(self::PK, $list);
519 $contributions = $this->zdb->execute($select);
520 foreach ($contributions as $contribution) {
521 $c = new Contribution($this->zdb, $this->login, $contribution);
522 $res = $c->remove(false);
523 if ($res === false) {
524 throw new \Exception();
525 }
526 }
527 if ($transaction) {
528 $this->zdb->connection->commit();
529 }
530 $hist->add(
531 str_replace(
532 '%list',
533 print_r($list, true),
534 _T("Contributions deleted (%list)")
535 )
536 );
537 return true;
538 } catch (Throwable $e) {
539 if ($transaction) {
540 $this->zdb->connection->rollBack();
541 }
542 Analog::log(
543 'An error occurred trying to remove contributions | ' .
544 $e->getMessage(),
545 Analog::ERROR
546 );
547 throw $e;
548 }
549 } else {
550 //not numeric and not an array: incorrect.
551 Analog::log(
552 'Asking to remove contribution, but without providing an array or a single numeric value.',
553 Analog::WARNING
554 );
555 return false;
556 }
557 }
558 }