Comment: | Merge trunk changes that have been missed, probably because of the trunk on 06/12/2020 [0277842dc6] |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA3-256: |
56201fa5cb86798d3e5a5c8b30723929 |
User & Date: | bohwaz on 2021-01-29 00:54:51 |
Other Links: | branch diff | manifest | tags |
2021-01-29
| ||
02:23 | Common use of modifiers between Smartyer and Brindille check-in: 9ab0478441 user: bohwaz tags: dev | |
00:54 | Merge trunk changes that have been missed, probably because of the trunk on 06/12/2020 [0277842dc6] check-in: 56201fa5cb user: bohwaz tags: dev | |
00:11 | Merge back trunk changes check-in: 587d487631 user: bohwaz tags: dev | |
2021-01-26
| ||
21:17 | Fix overwritten variable by reference check-in: 16cb52d122 user: bohwaz tags: trunk, stable | |
Modified src/VERSION from [05e17b646a] to [528cf42af9].
Modified src/include/data/1.0.0_migration.sql from [ba81ccc806] to [4e16391488].
︙ | ︙ | |||
15 16 17 18 19 20 21 | -------- MIGRATION COMPTA --------- INSERT INTO acc_charts (id, country, code, label) VALUES (1, 'FR', 'PCGA1999', 'Plan comptable associatif 1999'); -- Migration comptes de code comme identifiant à ID unique -- Inversement valeurs actif/passif et produit/charge INSERT INTO acc_accounts (id, id_chart, code, label, position, user) SELECT NULL, 1, id, libelle, | | | | | | | > > | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | -------- MIGRATION COMPTA --------- INSERT INTO acc_charts (id, country, code, label) VALUES (1, 'FR', 'PCGA1999', 'Plan comptable associatif 1999'); -- Migration comptes de code comme identifiant à ID unique -- Inversement valeurs actif/passif et produit/charge INSERT INTO acc_accounts (id, id_chart, code, label, position, user) SELECT NULL, 1, id, libelle, CASE WHEN position = 1 THEN 2 WHEN position = 2 THEN 1 WHEN position = 3 THEN 3 WHEN position = 4 THEN 5 WHEN position = 8 THEN 4 -- Suppression de la position "charge ou produit" qui n'a aucun sens WHEN position = 12 AND id LIKE '6%' THEN 4 WHEN position = 12 AND id LIKE '7%' THEN 5 WHEN position = 12 THEN 0 ELSE 0 END, CASE WHEN plan_comptable = 1 THEN 0 ELSE 1 END FROM compta_comptes; -- Migrations projets vers comptes analytiques INSERT INTO acc_accounts (id_chart, code, label, position, user, type) |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Accounting/Account.php from [055e1ef171] to [cacd2727a4].
︙ | ︙ | |||
209 210 211 212 213 214 215 | unset($columns['change']); } $list = new DynamicList($columns, $tables, $conditions); $list->orderBy('date', false); $list->setCount('COUNT(*)'); $list->setPageSize(null); | | | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | unset($columns['change']); } $list = new DynamicList($columns, $tables, $conditions); $list->orderBy('date', false); $list->setCount('COUNT(*)'); $list->setPageSize(null); $list->setModifier(function (&$row) use (&$sum) { if (property_exists($row, 'sum')) { $sum += isset($row->change) ? $row->change : ($row->credit - $row->debit); $row->sum = $sum; } $row->date = \DateTime::createFromFormat('!Y-m-d', $row->date); }); |
︙ | ︙ | |||
291 292 293 294 295 296 297 | $journal = iterator_to_array($journal); $i = 0; $sum = 0; foreach ($csv as $k => &$line) { try { $date = \DateTime::createFromFormat('!d/m/Y', $line->date); | | | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | $journal = iterator_to_array($journal); $i = 0; $sum = 0; foreach ($csv as $k => &$line) { try { $date = \DateTime::createFromFormat('!d/m/Y', $line->date); $line->amount = (substr($line->amount, 0, 1) == '-' ? -1 : 1) * Utils::moneyToInteger($line->amount); if (!$date) { throw new UserException('Date invalide : ' . $line->date); } $line->date = $date; } |
︙ | ︙ | |||
315 316 317 318 319 320 321 322 | $row = (object) ['csv' => null, 'journal' => $j]; if (isset($j->debit)) { foreach ($csv as &$line) { if (!isset($line->date)) { continue; } if ($j->date->format('Ymd') == $line->date->format('Ymd') | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | $row = (object) ['csv' => null, 'journal' => $j]; if (isset($j->debit)) { foreach ($csv as &$line) { if (!isset($line->date)) { continue; } // Match date, amount and label if ($j->date->format('Ymd') == $line->date->format('Ymd') && ($j->credit * -1 == $line->amount || $j->debit == $line->amount) && strtolower($j->label) == strtolower($line->label)) { $row->csv = $line; $line = null; break; } } } $lines[$id] = $row; } unset($line, $row, $j); // Second round to match only amount and label foreach ($lines as $row) { if ($row->csv || !isset($row->journal->debit)) { continue; } $j = $row->journal; foreach ($csv as &$line) { if (!isset($line->date)) { continue; } if ($j->date->format('Ymd') == $line->date->format('Ymd') && ($j->credit * -1 == $line->amount || $j->debit == $line->amount)) { $row->csv = $line; $line = null; break; } } } unset($j); // Then add CSV lines on the right foreach ($csv as $line) { if (null == $line) { continue; } $id = $line->date->format('Ymd') . '.' . ($i++); $lines[$id] = (object) ['csv' => $line, 'journal' => null]; |
︙ | ︙ | |||
496 497 498 499 500 501 502 | public function chart(): Chart { return Charts::get($this->id_chart); } public function save(): bool { | > | > | 524 525 526 527 528 529 530 531 532 533 534 535 536 | public function chart(): Chart { return Charts::get($this->id_chart); } public function save(): bool { $c = Config::getInstance(); $c->set('last_chart_change', time()); return parent::save(); } } |
Modified src/include/lib/Garradin/Entities/Accounting/Line.php from [0fde672e4f] to [c9978cd52a].
1 2 3 4 5 6 7 8 9 10 11 | <?php namespace Garradin\Entities\Accounting; use Garradin\Entity; use Garradin\ValidationException; use Garradin\Utils; class Line extends Entity { const TABLE = 'acc_transactions_lines'; | > | 1 2 3 4 5 6 7 8 9 10 11 12 | <?php namespace Garradin\Entities\Accounting; use Garradin\DB; use Garradin\Entity; use Garradin\ValidationException; use Garradin\Utils; class Line extends Entity { const TABLE = 'acc_transactions_lines'; |
︙ | ︙ | |||
31 32 33 34 35 36 37 | 'reconciled' => 'int', 'id_analytical' => '?int', ]; protected $_form_rules = [ 'id_account' => 'required|numeric|in_table:acc_accounts,id', 'id_analytical' => 'numeric|in_table:acc_accounts,id', | < < | < > | > > > > > | > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 'reconciled' => 'int', 'id_analytical' => '?int', ]; protected $_form_rules = [ 'id_account' => 'required|numeric|in_table:acc_accounts,id', 'id_analytical' => 'numeric|in_table:acc_accounts,id', 'reference' => 'string|max:200', 'label' => 'string|max:200', ]; public function filterUserValue(string $type, $value, string $key) { if ($key == 'credit' || $key == 'debit') { $value = Utils::moneyToInteger($value); } elseif ($key == 'id_analytical' && $value == 0) { $value = null; } $value = parent::filterUserValue($type, $value, $key); return $value; } public function selfCheck(): void { parent::selfCheck(); $this->assert($this->credit || $this->debit, 'Aucun montant au débit ou au crédit'); $this->assert($this->credit >= 0 && $this->debit >= 0, 'Le montant ne peut être négatif'); $this->assert(($this->credit * $this->debit) === 0 && ($this->credit + $this->debit) > 0, 'Ligne non équilibrée : crédit ou débit doit valoir zéro.'); $this->assert($this->id_transaction, 'Aucun mouvement n\'a été indiqué pour cette ligne.'); $this->assert($this->reconciled === 0 || $this->reconciled === 1); $db = DB::getInstance(); $this->assert($db->firstColumn('SELECT 1 FROM acc_accounts a INNER JOIN acc_transactions t ON t.id = ? INNER JOIN acc_years y ON y.id = t.id_year WHERE a.id = ? AND a.id_chart = y.id_chart;', $this->id_transaction, $this->id_account), 'Le compte sélectionné ne correspond pas à l\'exercice'); } } |
Modified src/include/lib/Garradin/Entities/Accounting/Transaction.php from [df72a69383] to [53d3112cba].
︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 | const TYPE_TRANSFER = 3; const TYPE_DEBT = 4; const TYPE_CREDIT = 5; const STATUS_WAITING = 1; const STATUS_PAID = 2; const STATUS_DEPOSIT = 4; const STATUS_NAMES = [ 1 => 'En attente de règlement', 2 => 'Réglé', 4 => 'Déposé en banque', ]; | > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | const TYPE_TRANSFER = 3; const TYPE_DEBT = 4; const TYPE_CREDIT = 5; const STATUS_WAITING = 1; const STATUS_PAID = 2; const STATUS_DEPOSIT = 4; const STATUS_ERROR = 8; const STATUS_NAMES = [ 1 => 'En attente de règlement', 2 => 'Réglé', 4 => 'Déposé en banque', ]; |
︙ | ︙ | |||
97 98 99 100 101 102 103 104 | switch ($account_type) { case Account::TYPE_REVENUE: return self::TYPE_REVENUE; case Account::TYPE_EXPENSE: return self::TYPE_EXPENSE; case Account::TYPE_THIRD_PARTY: return self::TYPE_DEBT; default: | > > > > | > > > | > | | | > > | > > > | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | switch ($account_type) { case Account::TYPE_REVENUE: return self::TYPE_REVENUE; case Account::TYPE_EXPENSE: return self::TYPE_EXPENSE; case Account::TYPE_THIRD_PARTY: return self::TYPE_DEBT; case Account::TYPE_BANK: case Account::TYPE_CASH: case Account::TYPE_OUTSTANDING: return self::TYPE_TRANSFER; default: return self::TYPE_ADVANCED; } } /** * @param bool $restrict_year Set to TRUE to only return lines linked to the correct chart, or FALSE (deprecated/legacy) to return all lines even if they are linked to accounts in the wrong chart! */ public function getLinesWithAccounts(bool $restrict_year = true) { $restrict = $restrict_year ? 'AND a.id_chart = y.id_chart' : ''; $sql = sprintf('SELECT l.*, a.label AS account_name, a.code AS account_code, b.label AS analytical_name FROM acc_transactions_lines l INNER JOIN acc_accounts a ON a.id = l.id_account %s INNER JOIN acc_transactions t ON t.id = l.id_transaction INNER JOIN acc_years y ON y.id = t.id_year LEFT JOIN acc_accounts b ON b.id = l.id_analytical WHERE l.id_transaction = ? ORDER BY l.id;', $restrict); $em = EntityManager::getInstance(Line::class); return $em->DB()->get($sql, $this->id); } public function getLines($with_accounts = false) { if (null === $this->_lines && $this->exists()) { $em = EntityManager::getInstance(Line::class); $this->_lines = $em->all('SELECT * FROM @TABLE WHERE id_transaction = ? ORDER BY id;', $this->id); |
︙ | ︙ | |||
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | if ($line->id === $id) { return $line; } } return null; } public function getLinesCreditSum() { $sum = 0; foreach ($this->getLines() as $line) { $sum += $line->credit; } return $sum; } public function getTypesAccounts() { if ($this->type == self::TYPE_ADVANCED) { return []; } | > > > > > > > > > > > > > > > > > > > > > > | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | if ($line->id === $id) { return $line; } } return null; } public function getFirstLine() { $lines = $this->getLines(); if (!count($lines)) { return null; } return reset($lines); } public function getLinesCreditSum() { $sum = 0; foreach ($this->getLines() as $line) { $sum += $line->credit; } return $sum; } public function getAnalyticalId(): ?int { $lines = $this->getLines(); if (!count($lines)) { return null; } return current($lines)->id_analytical; } public function getTypesAccounts() { if ($this->type == self::TYPE_ADVANCED) { return []; } |
︙ | ︙ | |||
256 257 258 259 260 261 262 | } return $sum; } public function save(): bool { | | | | < | > > > > > | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | } return $sum; } public function save(): bool { if ($this->validated && empty($this->_modified['validated'])) { throw new ValidationException('Il n\'est pas possible de modifier une écriture qui a été validée'); } $db = DB::getInstance(); if ($db->test(Year::TABLE, 'id = ? AND closed = 1', $this->id_year)) { throw new ValidationException('Il n\'est pas possible de créer ou modifier une écriture dans un exercice clôturé'); } if (!parent::save()) { return false; } foreach ($this->getLines() as $line) { $line->id_transaction = $this->id(); $line->save(); } foreach ($this->_old_lines as $line) { $line->delete(); } // Remove flag if ((self::TYPE_DEBT == $this->type || self::TYPE_CREDIT == $this->type) && $this->_related) { $this->_related->markPaid(); $this->_related->save(); } return true; } public function removeStatus(int $property) { $this->set('status', $this->status & ~$property); } public function addStatus(int $property) { $this->set('status', $this->status | $property); } public function markPaid() { $this->removeStatus(self::STATUS_WAITING); $this->addStatus(self::STATUS_PAID); } public function delete(): bool { if ($this->validated) { throw new ValidationException('Il n\'est pas possible de supprimer une écriture qui a été validée'); } |
︙ | ︙ | |||
394 395 396 397 398 399 400 401 402 403 404 405 406 407 | } $type = $source['type']; $this->importForm($source); if (self::TYPE_ADVANCED == $type) { $lines = Utils::array_transpose($source['lines']); foreach ($lines as $i => $line) { $line['id_account'] = @count($line['account']) ? key($line['account']) : null; if (!$line['id_account']) { throw new ValidationException('Numéro de compte invalide sur la ligne ' . ($i+1)); | > > > > | 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | } $type = $source['type']; $this->importForm($source); if (self::TYPE_ADVANCED == $type) { if (!isset($source['lines']) || !is_array($source['lines'])) { throw new ValidationException('Aucune ligne dans la saisie'); } $lines = Utils::array_transpose($source['lines']); foreach ($lines as $i => $line) { $line['id_account'] = @count($line['account']) ? key($line['account']) : null; if (!$line['id_account']) { throw new ValidationException('Numéro de compte invalide sur la ligne ' . ($i+1)); |
︙ | ︙ | |||
415 416 417 418 419 420 421 | $details = self::getTypesDetails(); if (!array_key_exists($type, $details)) { throw new ValidationException('Type d\'écriture inconnu'); } if (empty($this->_related) && ($type == self::TYPE_DEBT || $type == self::TYPE_CREDIT)) { | | > > > | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | $details = self::getTypesDetails(); if (!array_key_exists($type, $details)) { throw new ValidationException('Type d\'écriture inconnu'); } if (empty($this->_related) && ($type == self::TYPE_DEBT || $type == self::TYPE_CREDIT)) { $this->addStatus(self::STATUS_WAITING); } else { $this->removeStatus(self::STATUS_WAITING); } if (empty($source['amount'])) { throw new UserException('Montant non précisé'); } $amount = $source['amount']; |
︙ | ︙ | |||
442 443 444 445 446 447 448 449 450 451 452 453 454 455 | 'debit' => $debit, 'id_account' => $account, 'id_analytical' => !empty($source['id_analytical']) ? $source['id_analytical'] : null, ]); $this->addLine($line); } } } public function importFromEditForm(?array $source = null): void { if (null === $source) { $source = $_POST; } | > > > | 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | 'debit' => $debit, 'id_account' => $account, 'id_analytical' => !empty($source['id_analytical']) ? $source['id_analytical'] : null, ]); $this->addLine($line); } } // Remove error status when changed $this->removeStatus(self::STATUS_ERROR); } public function importFromEditForm(?array $source = null): void { if (null === $source) { $source = $_POST; } |
︙ | ︙ | |||
479 480 481 482 483 484 485 | } catch (\LogicException $e) { throw new ValidationException('Aucun compte sélectionné pour certaines lignes.'); } $debit = $credit = 0; | | > > | | > > > > | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 | } catch (\LogicException $e) { throw new ValidationException('Aucun compte sélectionné pour certaines lignes.'); } $debit = $credit = 0; foreach ($lines as $k => $line) { $line['id_account'] = @count($line['account']) ? key($line['account']) : null; try { $line = (new Line)->importForm($line); $this->addLine($line); } catch (ValidationException $e) { throw new ValidationException(sprintf('Ligne %d : %s', $k+1, $e->getMessage()), 0, $e); } $debit += $line->debit; $credit += $line->credit; } if ($debit != $credit) { // Add final balance line |
︙ | ︙ | |||
695 696 697 698 699 700 701 | $out->id_account = $line->id_account; break; } return $out; } | | > > > > > | 751 752 753 754 755 756 757 758 759 760 761 762 763 | $out->id_account = $line->id_account; break; } return $out; } public function getTypeName(): string { return self::TYPES_NAMES[$this->type]; } } |
Modified src/include/lib/Garradin/Entities/Accounting/Year.php from [67edc38661] to [42cf3b1884].
︙ | ︙ | |||
36 37 38 39 40 41 42 | ]; public function selfCheck(): void { parent::selfCheck(); $this->assert($this->start_date < $this->end_date, 'La date de fin doit être postérieure à la date de début'); $this->assert($this->closed === 0 || $this->closed === 1); | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | ]; public function selfCheck(): void { parent::selfCheck(); $this->assert($this->start_date < $this->end_date, 'La date de fin doit être postérieure à la date de début'); $this->assert($this->closed === 0 || $this->closed === 1); $db = DB::getInstance(); $this->assert($this->id_chart !== null); if ($this->exists()) { $this->assert( !$db->test(Transaction::TABLE, 'id_year = ? AND date < ?', $this->id(), $this->start_date->format('Y-m-d')), 'Des écritures de cet exercice ont une date antérieure à la date de début de l\'exercice.' ); $this->assert( !$db->test(Transaction::TABLE, 'id_year = ? AND date > ?', $this->id(), $this->end_date->format('Y-m-d')), 'Des écritures de cet exercice ont une date postérieure à la date de fin de l\'exercice.' ); } } public function close(int $user_id): void { if ($this->closed) { throw new \LogicException('Cet exercice est déjà clôturé'); } $this->set('closed', 1); $this->save(); } public function reopen(int $user_id): void { if (!$this->closed) { throw new \LogicException('This year is already open'); } $closing_id = $this->accounts()->getClosingAccountId(); if (!$closing_id) { throw new UserException('Aucun compte n\'est indiqué comme compte de clôture dans le plan comptable'); } $this->set('closed', 0); $this->save(); // Create validated transaction to show that someone has reopened the year $t = new Transaction; $t->import([ 'id_year' => $this->id(), 'label' => sprintf('Exercice réouvert le %s', date('d/m/Y à H:i:s')), 'type' => Transaction::TYPE_ADVANCED, 'date' => $this->end_date->format('d/m/Y'), 'id_creator' => $user_id, 'validated' => 1, ]); $line = new Line; $line->import([ 'debit' => 0, 'credit' => 1, 'id_account' => $closing_id, ]); $t->addLine($line); $line = new Line; $line->import([ 'debit' => 1, 'credit' => 0, 'id_account' => $closing_id, ]); $t->addLine($line); $t->save(); } /** * Splits an accounting year between the current year and another one, at a given date * Any transaction after the given date will be moved to the target year. */ public function split(\DateTime $date, Year $target): void { if ($this->closed) { throw new \LogicException('Cet exercice est déjà clôturé'); } if ($target->closed) { throw new \LogicException('L\'exercice cible est déjà clôturé'); } DB::getInstance()->preparedQuery('UPDATE acc_transactions SET id_year = ? WHERE id_year = ? AND date > ?;', $target->id(), $this->id(), $date->format('Y-m-d')); } public function delete(): bool { // Manual delete of transactions, as there is a voluntary safeguard in SQL: no cascade DB::getInstance()->preparedQuery('DELETE FROM acc_transactions WHERE id_year = ?;', $this->id()); // Clean up files |
︙ | ︙ |
Modified src/include/lib/Garradin/Services/Services.php from [7ad7d0ccb6] to [743242c68f].
︙ | ︙ | |||
51 52 53 54 55 56 57 | return $out; } static public function listWithStats() { $db = DB::getInstance(); | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | return $out; } static public function listWithStats() { $db = DB::getInstance(); $hidden_cats = array_keys(Categories::listHidden()); $condition = sprintf('SELECT COUNT(DISTINCT su.id_user) FROM services_users su INNER JOIN (SELECT id, MAX(date) FROM services_users GROUP BY id_user, id_service) su2 ON su2.id = su.id INNER JOIN membres m ON m.id = su.id_user WHERE su.id_service = s.id AND m.category_id NOT IN (%s)', implode(',', $hidden_cats)); $sql = sprintf('SELECT s.*, |
︙ | ︙ |
Modified src/include/lib/Garradin/Upgrade.php from [55859a3a96] to [7af5640d3e].
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 | Static_Cache::store('upgrade', 'Mise à jour en cours.'); // Créer une sauvegarde automatique $backup_name = (new Sauvegarde)->create('pre-upgrade-' . garradin_version()); try { if (version_compare($v, '1.1.0', '<=')) { // Missing trigger $db->beginSchemaUpdate(); $champs = new Champs($db->firstColumn('SELECT valeur FROM config WHERE cle = \'champs_membres\';')); $db->createFunction('sha1', 'sha1'); $db->import(ROOT . '/include/data/1.1.0_migration.sql'); | > > > > > > > > > > > > > > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | Static_Cache::store('upgrade', 'Mise à jour en cours.'); // Créer une sauvegarde automatique $backup_name = (new Sauvegarde)->create('pre-upgrade-' . garradin_version()); try { if (version_compare($v, '1.0.1', '<')) { // Missing trigger $db->begin(); $db->import(ROOT . '/include/data/1.0.1_migration.sql'); $db->commit(); } if (version_compare($v, '1.0.3', '<')) { // Missing trigger $db->begin(); $db->import(ROOT . '/include/data/1.0.3_migration.sql'); $db->commit(); } if (version_compare($v, '1.1.0', '<=')) { // Missing trigger $db->beginSchemaUpdate(); $champs = new Champs($db->firstColumn('SELECT valeur FROM config WHERE cle = \'champs_membres\';')); $db->createFunction('sha1', 'sha1'); $db->import(ROOT . '/include/data/1.1.0_migration.sql'); |
︙ | ︙ |
Modified src/templates/acc/transactions/details.tpl from [f60d018a35] to [b59c192305].
︙ | ︙ | |||
87 88 89 90 91 92 93 | <dt>Remarques</dt> <dd>{if trim($transaction.notes)}{$transaction.notes|escape|nl2br}{else}-{/if}</dd> <dt>Fichiers joints</dt> {foreach from=$files item="file"} <dd> <aside class="file"> | | | | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | <dt>Remarques</dt> <dd>{if trim($transaction.notes)}{$transaction.notes|escape|nl2br}{else}-{/if}</dd> <dt>Fichiers joints</dt> {foreach from=$files item="file"} <dd> <aside class="file"> <a target="_blank" href="{$file.url}">{$file.name}</a> <small>({$file.type}, {$file.size|format_bytes})</small> {linkbutton shape="download" href=$file->url() target="_blank" label="Télécharger"} {linkbutton shape="delete" href="!acc/transactions/delete_file.php?id=%d&from=%d"|args:$file.id,$transaction.id label="Supprimer"} </aside> </dd> {foreachelse} <dd>-</dd> {/foreach} </dl> |
︙ | ︙ |
Modified src/templates/common/delete_file.tpl from [42a5f97ba7] to [35c6b0709e].
1 2 3 4 | {include file="admin/_head.tpl" title="Supprimer un fichier" current=null} {include file="common/delete_form.tpl" legend="Supprimer ce fichier ?" | | | 1 2 3 4 5 6 7 8 | {include file="admin/_head.tpl" title="Supprimer un fichier" current=null} {include file="common/delete_form.tpl" legend="Supprimer ce fichier ?" warning="Êtes-vous sûr de vouloir supprimer le fichier « %s » ?"|args:$file.name } {include file="admin/_foot.tpl"} |
Modified src/templates/common/search/advanced.tpl from [2ec6ea6165] to [71b8bf21e4].
1 2 3 4 5 | <?php assert(isset($columns)); assert(isset($action_url)); assert(isset($query)); assert(isset($is_admin)); | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?php assert(isset($columns)); assert(isset($action_url)); assert(isset($query)); assert(isset($is_admin)); $sql_disabled = !$is_admin || (!$session->canAccess($session::SECTION_CONFIG, $session::ACCESS_ADMIN) && $is_unprotected); ?> {form_errors} <form method="post" action="{$action_url}" id="queryBuilderForm"> <fieldset> {if $sql_query && !$sql_disabled} <legend>Schéma des tables SQL</legend> <pre class="sql_schema">{foreach from=$schema item="table"}{$table}<br />{/foreach}</pre> <dl> {input type="textarea" name="sql_query" cols="100" rows="7" required=1 label="Requête SQL" help="Si aucune limite n'est précisée, une limite de 100 résultats sera appliquée." default=$sql_query} {if $session->canAccess($session::SECTION_CONFIG, $session::ACCESS_ADMIN)} {input type="checkbox" name="unprotected" value=1 label="Autoriser l'accès à toutes les tables de la base de données" default=$is_unprotected} <dd class="help">Attention : en cochant cette case vous autorisez la requête à lire toutes les données de toutes les tables de la base de données !</dd> {/if} </dl> <p class="submit"> {button type="submit" name="run" label="Exécuter" shape="search" class="main"} <input type="hidden" name="id" value="{$search.id}" /> |
︙ | ︙ |
Modified src/www/admin/acc/accounts/journal.php from [f7ee3c3084] to [da00ba6416].
︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 | if (!$year) { throw new UserException("L'exercice demandé n'existe pas."); } $tpl->assign('year', $year); } // The account has a different chart after changing the current year: // get back to the list of accounts to select a new account! if ($account->id_chart != $year->id_chart) { Utils::redirect(ADMIN_URL . 'acc/accounts/?chart_change'); } | > > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | if (!$year) { throw new UserException("L'exercice demandé n'existe pas."); } $tpl->assign('year', $year); } // The account has a different chart after changing the current year: // get back to the list of accounts to select a new account! if ($account->id_chart != $year->id_chart) { Utils::redirect(ADMIN_URL . 'acc/accounts/?chart_change'); } // The account has a different chart after changing the current year: // get back to the list of accounts to select a new account! if ($account->id_chart != $year->id_chart) { Utils::redirect(ADMIN_URL . 'acc/accounts/?chart_change'); } |
︙ | ︙ |
Modified src/www/admin/acc/charts/accounts/index.php from [5e00b51e32] to [bd5f5966a0].
︙ | ︙ | |||
18 19 20 21 22 23 24 | if (!$chart) { throw new UserException('Aucun plan comptable spécifié'); } $accounts = $chart->accounts(); $tpl->assign('chart', $chart); | | | 18 19 20 21 22 23 24 25 26 | if (!$chart) { throw new UserException('Aucun plan comptable spécifié'); } $accounts = $chart->accounts(); $tpl->assign('chart', $chart); $tpl->assign('accounts_grouped', $accounts->listCommonGrouped(null, true)); $tpl->display('acc/charts/accounts/index.tpl'); |
Modified src/www/admin/acc/charts/accounts/selector.php from [17dde2f21a] to [250e69ae57].
︙ | ︙ | |||
11 12 13 14 15 16 17 | header('X-Frame-Options: SAMEORIGIN', true); $targets = qg('targets'); $chart = qg('chart'); // Cache the page until the charts have changed $hash = sha1($targets . $chart); | | > | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | header('X-Frame-Options: SAMEORIGIN', true); $targets = qg('targets'); $chart = qg('chart'); // Cache the page until the charts have changed $hash = sha1($targets . $chart); $last_change = Config::getInstance()->get('last_chart_change') ?: time(); // Exit if there's no need to reload Utils::HTTPCache($hash, $last_change); if ($chart) { $chart = Charts::get((int)qg('chart')); } elseif (qg('year')) { $year = Years::get((int)qg('year')); |
︙ | ︙ |
Modified src/www/admin/acc/transactions/edit.php from [1c19c2e07b] to [ae476319b4].
︙ | ︙ | |||
39 40 41 42 43 44 45 | if (f('save') && $form->check('acc_edit_' . $transaction->id(), $rules)) { try { $transaction->importFromEditForm(); $transaction->save(); // Append file if (!empty($_FILES['file']['name'])) { | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | if (f('save') && $form->check('acc_edit_' . $transaction->id(), $rules)) { try { $transaction->importFromEditForm(); $transaction->save(); // Append file if (!empty($_FILES['file']['name'])) { File::upload('file', File::CONTEXT_TRANSACTION, $transaction->id()); } // Link members if (null !== f('users') && is_array(f('users'))) { $transaction->updateLinkedUsers(array_keys(f('users'))); } else { |
︙ | ︙ |
Modified src/www/admin/common/search.php from [c7d3eff3a5] to [30c5ed1e7d].
︙ | ︙ | |||
54 55 56 57 58 59 60 | $query->limit = (int) f('limit') ?: $query->limit; } } // Recherche SQL if (f('sql_query')) { // Only admins can run custom queries, others can only run saved queries | | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | $query->limit = (int) f('limit') ?: $query->limit; } } // Recherche SQL if (f('sql_query')) { // Only admins can run custom queries, others can only run saved queries $session->requireAccess($target == 'compta' ? $session::SECTION_ACCOUNTING : $session::SECTION_USERS, $session::ACCESS_ADMIN); $sql_query = f('sql_query'); if ($session->canAccess($session::SECTION_CONFIG, $session::ACCESS_ADMIN)) { $is_unprotected = (bool) f('unprotected'); } else { $is_unprotected = false; } } |
︙ | ︙ |
Modified src/www/admin/membres/ajouter.php from [c9a09edd99] to [39f6545e73].
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | } catch (UserException $e) { $form->addError($e->getMessage()); } } } $tpl->assign('id_field_name', $config->get('champ_identifiant')); $tpl->assign('passphrase', Utils::suggestPassword()); $tpl->assign('champs', $champs->getAll()); $tpl->assign('membres_cats', Categories::listSimple()); $tpl->assign('current_cat', f('category_id') ?: $config->get('categorie_membres')); $tpl->display('admin/membres/ajouter.tpl'); | > > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | } catch (UserException $e) { $form->addError($e->getMessage()); } } } $tpl->assign('id_field_name', $config->get('champ_identifiant')); $tpl->assign('id_field_name', $config->get('champ_identifiant')); $tpl->assign('passphrase', Utils::suggestPassword()); $tpl->assign('champs', $champs->getAll()); $tpl->assign('membres_cats', Categories::listSimple()); $tpl->assign('current_cat', f('category_id') ?: $config->get('categorie_membres')); $tpl->display('admin/membres/ajouter.tpl'); |
Modified src/www/admin/services/save.php from [7a6f7b643f] to [0acb3f484c].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php namespace Garradin; use Garradin\Services\Services; use Garradin\Entities\Services\Service_User; use Garradin\Entities\Accounting\Account; use Garradin\Entities\Accounting\Transaction; require_once __DIR__ . '/_inc.php'; $session->requireAccess($session::SECTION_USERS, $session::ACCESS_WRITE); $count_all = Services::count(); if (!$count_all) { Utils::redirect(ADMIN_URL . 'services/?CREATE'); } | > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php namespace Garradin; use Garradin\Services\Services; use Garradin\Entities\Services\Service_User; use Garradin\Entities\Accounting\Account; use Garradin\Entities\Accounting\Transaction; require_once __DIR__ . '/_inc.php'; $session->requireAccess($session::SECTION_USERS, $session::ACCESS_WRITE); $count_all = Services::count(); if (!$count_all) { Utils::redirect(ADMIN_URL . 'services/?CREATE'); } $count_all = Services::count(); if (!$count_all) { Utils::redirect(ADMIN_URL . 'services/?CREATE'); } |
︙ | ︙ |
Modified src/www/admin/static/styles/03-forms.css from [cb1bad2db4] to [f5a97ac502].
︙ | ︙ |
Modified src/www/admin/static/styles/10-accounting.css from [c404c68c91] to [dc89fc1883].
︙ | ︙ | |||
100 101 102 103 104 105 106 | table.accounts th { font-weight: normal; } table.accounts .account-level-1 th { font-size: 1.6em; } table.accounts .account-level-2 th { padding-left: 1em; font-size: 1.3em; } table.accounts .account-level-3 th { padding-left: 2em; } table.accounts .account-level-4 th { padding-left: 3em; } table.accounts .account-level-5 th { padding-left: 4em; } table.accounts .account-level-6 th { padding-left: 5em; } | < | 100 101 102 103 104 105 106 107 | table.accounts th { font-weight: normal; } table.accounts .account-level-1 th { font-size: 1.6em; } table.accounts .account-level-2 th { padding-left: 1em; font-size: 1.3em; } table.accounts .account-level-3 th { padding-left: 2em; } table.accounts .account-level-4 th { padding-left: 3em; } table.accounts .account-level-5 th { padding-left: 4em; } table.accounts .account-level-6 th { padding-left: 5em; } table.projects tr.title p.help { font-weight: normal; text-align: center; } |