3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
6 * Transaction class for galette
10 * Copyright © 2011-2014 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/>.
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2011-2014 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 - 2011-07-31
37 namespace Galette\Entity
;
40 use Laminas\Db\Sql\Expression
;
41 use Galette\Repository\Contributions
;
43 use Galette\Core\History
;
44 use Galette\Core\Login
;
47 * Transaction class for galette
52 * @author Johan Cwiklinski <johan@x-tnd.be>
53 * @copyright 2010-2014 The Galette Team
54 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
55 * @link http://galette.tuxfamily.org
56 * @since Available since 0.7dev - 2010-03-11
62 public const TABLE
= 'transactions';
63 public const PK
= 'trans_id';
68 private $_description;
71 //fields list and their translation
82 * @param Db $zdb Database instance
83 * @param Login $login Login instance
84 * @param null|int|ResultSet $args Either a ResultSet row or its id for to load
85 * a specific transaction, or null to just
88 public function __construct(Db
$zdb, Login
$login, $args = null)
91 $this->login
= $login;
94 * Fields configuration. Each field is an array and must reflect:
100 * I'd prefer a static private variable for this...
101 * But call to the _T function does not seem to be allowed there :/
103 $this->_fields
= array(
105 'label' => null, //not a field in the form
108 'trans_date' => array(
109 'label' => _T("Date:"), //not a field in the form
112 'trans_amount' => array(
113 'label' => _T("Amount:"),
114 'propname' => 'amount'
116 'trans_desc' => array(
117 'label' => _T("Description:"),
118 'propname' => 'description'
120 Adherent
::PK
=> array(
121 'label' => _T("Originator:"),
122 'propname' => 'member'
125 if ($args == null ||
is_int($args)) {
126 $this->_date
= date("Y-m-d");
128 if (is_int($args) && $args > 0) {
131 } elseif (is_object($args)) {
132 $this->loadFromRS($args);
135 $this->loadDynamicFields();
139 * Loads a transaction from its id
141 * @param int $id the identifier for the transaction to load
143 * @return bool true if query succeed, false otherwise
145 public function load($id)
148 $select = $this->zdb
->select(self
::TABLE
);
149 $select->where(self
::PK
. ' = ' . $id);
151 $results = $this->zdb
->execute($select);
152 $result = $results->current();
154 $this->loadFromRS($result);
157 throw new \
Exception();
159 } catch (\Exception
$e) {
161 'Cannot load transaction form id `' . $id . '` | ' .
170 * Remove transaction (and all associated contributions) from database
172 * @param History $hist History
173 * @param boolean $transaction Activate transaction mode (defaults to true)
177 public function remove(History
$hist, $transaction = true)
183 $this->zdb
->connection
->beginTransaction();
186 //remove associated contributions if needeed
187 if ($this->getDispatchedAmount() > 0) {
188 $c = new Contributions($this->zdb
, $this->login
);
189 $clist = $c->getListFromTransaction($this->_id
);
191 foreach ($clist as $cid) {
194 $rem = $c->remove($cids, $hist, false);
197 //remove transaction itself
198 $delete = $this->zdb
->delete(self
::TABLE
);
200 self
::PK
. ' = ' . $this->_id
202 $this->zdb
->execute($delete);
204 $this->dynamicsRemove(true);
207 $this->zdb
->connection
->commit();
209 $emitter->emit('transaction.remove', $this);
211 } catch (\Exception
$e) {
213 $this->zdb
->connection
->rollBack();
216 'An error occurred trying to remove transaction #' .
217 $this->_id
. ' | ' . $e->getMessage(),
225 * Populate object from a resultset row
227 * @param ResultSet $r the resultset row
231 private function loadFromRS($r)
234 $this->_id
= $r->$pk;
235 $this->_date
= $r->trans_date
;
236 $this->_amount
= $r->trans_amount
;
237 $this->_description
= $r->trans_desc
;
238 $adhpk = Adherent
::PK
;
239 $this->_member
= (int)$r->$adhpk;
241 $this->loadDynamicFields();
245 * Check posted values validity
247 * @param array $values All values to check, basically the $_POST array
248 * after sending the form
249 * @param array $required Array of required fields
250 * @param array $disabled Array of disabled fields
254 public function check($values, $required, $disabled)
256 $this->errors
= array();
258 $fields = array_keys($this->_fields
);
259 foreach ($fields as $key) {
260 //first of all, let's sanitize values
261 $key = strtolower($key);
262 $prop = '_' . $this->_fields
[$key]['propname'];
264 if (isset($values[$key])) {
265 $value = trim($values[$key]);
270 // if the field is enabled, check it
271 if (!isset($disabled[$key])) {
272 // now, check validity
278 $d = \DateTime
::createFromFormat(__("Y-m-d"), $value);
280 throw new \
Exception('Incorrect format');
282 $this->$prop = $d->format('Y-m-d');
283 } catch (\Exception
$e) {
285 'Wrong date format. field: ' . $key .
286 ', value: ' . $value . ', expected fmt: ' .
287 __("Y-m-d") . ' | ' . $e->getMessage(),
290 $this->errors
[] = str_replace(
297 $this->_fields
[$key]['label']
299 _T("- Wrong date format (%date_format) for %field!")
304 $this->_member
= (int)$value;
307 $this->_amount
= $value;
308 $value = strtr($value, ',', '.');
309 if (!is_numeric($value)) {
310 $this->errors
[] = _T("- The amount must be an integer!");
314 /** TODO: retrieve field length from database and check that */
315 $this->_description
= $value;
316 if (trim($value) == '') {
317 $this->errors
[] = _T("- Empty transaction description!");
318 } elseif (mb_strlen($value) > 150) {
319 $this->errors
[] = _T("- Transaction description must be 150 characters long maximum.");
327 // missing required fields?
328 foreach ($required as $key => $val) {
330 $prop = '_' . $this->_fields
[$key]['propname'];
331 if (!isset($disabled[$key]) && !isset($this->$prop)) {
332 $this->errors
[] = str_replace(
334 '<a href="#' . $key . '">' . $this->getFieldLabel($key) . '</a>',
335 _T("- Mandatory field %field empty.")
341 if ($this->_id
!= '') {
342 $dispatched = $this->getDispatchedAmount();
343 if ($dispatched > $this->_amount
) {
344 $this->errors
[] = _T("- Sum of all contributions exceed corresponding transaction amount.");
348 $this->dynamicsCheck($values, $required, $disabled);
350 if (count($this->errors
) > 0) {
352 'Some errors has been throwed attempting to edit/store a transaction' .
353 print_r($this->errors
, true),
356 return $this->errors
;
359 'Transaction checked successfully.',
367 * Store the transaction
369 * @param History $hist History
373 public function store(History
$hist)
378 $this->zdb
->connection
->beginTransaction();
380 $fields = $this->getDbFields($this->zdb
);
382 foreach ($fields as $field) {
383 $prop = '_' . $this->_fields
[$field]['propname'];
384 $values[$field] = $this->$prop;
388 if (!isset($this->_id
) ||
$this->_id
== '') {
389 //we're inserting a new transaction
390 unset($values[self
::PK
]);
391 $insert = $this->zdb
->insert(self
::TABLE
);
392 $insert->values($values);
393 $add = $this->zdb
->execute($insert);
394 if ($add->count() > 0) {
395 if ($this->zdb
->isPostgres()) {
396 $this->_id
= $this->zdb
->driver
->getLastGeneratedValue(
397 PREFIX_DB
. 'transactions_id_seq'
400 $this->_id
= $this->zdb
->driver
->getLastGeneratedValue();
405 _T("Transaction added"),
406 Adherent
::getSName($this->zdb
, $this->_member
)
410 $emitter->emit('transaction.add', $this);
412 $hist->add(_T("Fail to add new transaction."));
413 throw new \
Exception(
414 'An error occurred inserting new transaction!'
418 //we're editing an existing transaction
419 $update = $this->zdb
->update(self
::TABLE
);
420 $update->set($values)->where(
421 self
::PK
. '=' . $this->_id
423 $edit = $this->zdb
->execute($update);
424 //edit == 0 does not mean there were an error, but that there
425 //were nothing to change
426 if ($edit->count() > 0) {
428 _T("Transaction updated"),
429 Adherent
::getSName($this->zdb
, $this->_member
)
434 $emitter->emit('transaction.edit', $this);
439 $success = $this->dynamicsStore(true);
442 $this->zdb
->connection
->commit();
444 } catch (\Exception
$e) {
445 $this->zdb
->connection
->rollBack();
447 'Something went wrong :\'( | ' . $e->getMessage() . "\n" .
448 $e->getTraceAsString(),
456 * Retrieve amount that has already been dispatched into contributions
460 public function getDispatchedAmount()
463 $select = $this->zdb
->select(Contribution
::TABLE
);
466 'sum' => new Expression('SUM(montant_cotis)')
468 )->where(self
::PK
. ' = ' . $this->_id
);
470 $results = $this->zdb
->execute($select);
471 $result = $results->current();
472 $dispatched_amount = $result->sum
;
473 return (double)$dispatched_amount;
474 } catch (\Exception
$e) {
476 'An error occurred retrieving dispatched amounts | ' .
484 * Retrieve amount that has not yet been dispatched into contributions
488 public function getMissingAmount()
491 $select = $this->zdb
->select(Contribution
::TABLE
);
494 'sum' => new Expression('SUM(montant_cotis)')
496 )->where(self
::PK
. ' = ' . $this->_id
);
498 $results = $this->zdb
->execute($select);
499 $result = $results->current();
500 $dispatched_amount = $result->sum
;
501 return (double)$this->_amount
- (double)$dispatched_amount;
502 } catch (\Exception
$e) {
504 'An error occurred retrieving missing amounts | ' .
512 * Retrieve fields from database
514 * @param Db $zdb Database instance
518 public function getDbFields(Db
$zdb)
520 $columns = $zdb->getColumns(self
::TABLE
);
522 foreach ($columns as $col) {
523 $fields[] = $col->getName();
529 * Get the relevant CSS class for current transaction
531 * @return string current transaction row class
533 public function getRowClass()
535 return ($this->getMissingAmount() == 0) ?
536 'transaction-normal' : 'transaction-uncomplete';
540 * Global getter method
542 * @param string $name name of the property we want to retrive
544 * @return false|object the called property
546 public function __get($name)
548 $forbidden = array();
550 $rname = '_' . $name;
551 if (!in_array($name, $forbidden) && isset($this->$rname)) {
554 if ($this->$rname != '') {
556 $d = new \
DateTime($this->$rname);
557 return $d->format(__("Y-m-d"));
558 } catch (\Exception
$e) {
559 //oops, we've got a bad date :/
561 'Bad date (' . $this->$rname . ') | ' .
565 return $this->$rname;
570 return $this->$rname;
579 * Global setter method
581 * @param string $name name of the property we want to assign a value to
582 * @param object $value a relevant value for the property
586 public function __set($name, $value)
588 /*$forbidden = array('fields');*/
589 /** TODO: What to do ? :-) */
595 * @param string $field Field name
599 private function getFieldLabel($field)
601 $label = $this->_fields
[$field]['label'];
602 //remove trailing ':' and then nbsp (for french at least)
603 $label = trim(trim($label, ':'), ' ');