Overview
Comment: | Re-merge missing stuff |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA3-256: |
0688a410c3a393ea6841227ee41422c2 |
User & Date: | bohwaz on 2021-10-05 02:28:07 |
Other Links: | branch diff | manifest | tags |
Context
2021-10-05
| ||
02:58 | Rename plugin fields check-in: d65ebca7db user: bohwaz tags: dev | |
02:28 | Re-merge missing stuff check-in: 0688a410c3 user: bohwaz tags: dev | |
02:17 | Merge missing commits, fix upgrade check-in: 701cb83b3b user: bohwaz tags: dev | |
00:47 | Implement generic signals for entities check-in: 84133f87d4 user: bohwaz tags: trunk, stable | |
Changes
Modified src/Makefile from [7364cd9885] to [7915fce3ed].
︙ | ︙ | |||
10 11 12 13 14 15 16 | rm -rf "include/lib/KD2" unzip "${TMP_KD2}/kd2.zip" -d include/lib rm -rf ${TMP_KD2} wget -O "include/lib/Parsedown.php" "https://raw.githubusercontent.com/erusev/parsedown/1.7.x/Parsedown.php" | < < < | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | rm -rf "include/lib/KD2" unzip "${TMP_KD2}/kd2.zip" -d include/lib rm -rf ${TMP_KD2} wget -O "include/lib/Parsedown.php" "https://raw.githubusercontent.com/erusev/parsedown/1.7.x/Parsedown.php" dev-server: php -S localhost:8082 -t www www/_route.php test: find . -name '*.php' -print0 | xargs -0 -n1 php -l > /dev/null phpstan: |
︙ | ︙ |
Modified src/include/data/1.0.0_migration.sql from [4e16391488] to [931bc31a61].
︙ | ︙ | |||
106 107 108 109 110 111 112 | UPDATE acc_accounts SET type = 5, description = (SELECT description FROM compta_categories WHERE compte = acc_accounts.code) WHERE id IN (SELECT a.id FROM acc_accounts a INNER JOIN compta_categories c ON c.compte = a.code AND c.type = -1 AND c.compte NOT LIKE '4%' AND a.position = 4); -- Tiers UPDATE acc_accounts SET type = 4, description = (SELECT description FROM compta_categories WHERE compte = acc_accounts.code) WHERE id IN (SELECT a.id FROM acc_accounts a INNER JOIN compta_categories c ON c.compte = a.code AND c.type = -1 AND c.compte LIKE '4%'); | < < < < > | 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 | UPDATE acc_accounts SET type = 5, description = (SELECT description FROM compta_categories WHERE compte = acc_accounts.code) WHERE id IN (SELECT a.id FROM acc_accounts a INNER JOIN compta_categories c ON c.compte = a.code AND c.type = -1 AND c.compte NOT LIKE '4%' AND a.position = 4); -- Tiers UPDATE acc_accounts SET type = 4, description = (SELECT description FROM compta_categories WHERE compte = acc_accounts.code) WHERE id IN (SELECT a.id FROM acc_accounts a INNER JOIN compta_categories c ON c.compte = a.code AND c.type = -1 AND c.compte LIKE '4%'); -- Recopie des exercices, mais la date de fin ne peut être nulle INSERT INTO acc_years (id, label, start_date, end_date, closed, id_chart) SELECT id, libelle, debut, CASE WHEN fin IS NULL THEN date(debut, '+1 year') ELSE fin END, cloture, 1 FROM compta_exercices; -- Recopie des catégories, on supprime la colonne id_cotisation_obligatoire INSERT INTO membres_categories SELECT id, nom, droit_wiki, droit_membres, droit_compta, droit_inscription, droit_connexion, droit_config, cacher FROM membres_categories_old; DROP TABLE membres_categories_old; -- Transfert des rapprochements UPDATE acc_transactions_lines SET reconciled = 1 WHERE id_transaction IN (SELECT id_operation FROM compta_rapprochement); --------- MIGRATION COTISATIONS ---------- -- A edge-case where the end date is after the start date, let's fix it… UPDATE cotisations SET fin = debut WHERE fin < debut; UPDATE cotisations SET duree = NULL WHERE duree = 0; INSERT INTO services SELECT id, intitule, description, duree, debut, fin FROM cotisations; INSERT INTO services_fees (id, label, amount, id_service, id_account, id_year) SELECT id, intitule, CASE WHEN montant IS NOT NULL THEN CAST(montant*100 AS integer) ELSE NULL END, id, (SELECT id FROM acc_accounts WHERE code = (SELECT compte FROM compta_categories WHERE id = id_categorie_compta)), (SELECT MAX(id) FROM acc_years WHERE closed = 0) |
︙ | ︙ | |||
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | INSERT INTO services_reminders SELECT * FROM rappels; INSERT INTO services_reminders_sent SELECT id, id_membre, id_cotisation, CASE WHEN id_rappel IS NULL THEN (SELECT MAX(id) FROM rappels) ELSE id_rappel END, date FROM rappels_envoyes WHERE id_rappel IS NOT NULL GROUP BY id_membre, id_cotisation, id_rappel; DROP TABLE cotisations; DROP TABLE cotisations_membres; DROP TABLE rappels; DROP TABLE rappels_envoyes; -- Suppression inutilisées DROP TABLE compta_rapprochement; DROP TABLE compta_journal; DROP TABLE compta_categories; DROP TABLE compta_comptes; DROP TABLE compta_exercices; DROP TABLE membres_operations_old; DROP TABLE compta_projets; DROP TABLE compta_comptes_bancaires; DROP TABLE compta_moyens_paiement; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 153 154 155 156 157 158 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 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 | INSERT INTO services_reminders SELECT * FROM rappels; INSERT INTO services_reminders_sent SELECT id, id_membre, id_cotisation, CASE WHEN id_rappel IS NULL THEN (SELECT MAX(id) FROM rappels) ELSE id_rappel END, date FROM rappels_envoyes WHERE id_rappel IS NOT NULL GROUP BY id_membre, id_cotisation, id_rappel; -- Recopie des opérations par membre, mais le nom a changé pour acc_transactions_users, et il faut valider l'existence du membre ET du service INSERT INTO acc_transactions_users SELECT a.* FROM membres_operations_old a INNER JOIN membres b ON b.id = a.id_membre INNER JOIN services_users c ON c.id = a.id_cotisation; DROP TABLE cotisations; DROP TABLE cotisations_membres; DROP TABLE rappels; DROP TABLE rappels_envoyes; -- Suppression inutilisées DROP TABLE compta_rapprochement; DROP TABLE compta_journal; DROP TABLE compta_categories; DROP TABLE compta_comptes; DROP TABLE compta_exercices; DROP TABLE membres_operations_old; DROP TABLE compta_projets; DROP TABLE compta_comptes_bancaires; DROP TABLE compta_moyens_paiement; INSERT INTO acc_charts (country, code, label) VALUES ('FR', 'PCA2018', 'Plan comptable associatif 2018'); CREATE TEMP TABLE tmp_accounts (code,label,description,position,type); .import charts/fr_2018.csv tmp_accounts INSERT INTO acc_accounts (id_chart, code, label, description, position, type) SELECT (SELECT id FROM acc_charts WHERE code = 'PCA2018'), code, label, description, CASE position WHEN 'Actif' THEN 1 WHEN 'Passif' THEN 2 WHEN 'Actif ou passif' THEN 3 WHEN 'Charge' THEN 4 WHEN 'Produit' THEN 5 ELSE 0 END, CASE type WHEN 'Banque' THEN 1 WHEN 'Caisse' THEN 2 WHEN 'Attente d''encaissement' THEN 3 WHEN 'Tiers' THEN 4 WHEN 'Dépenses' THEN 5 WHEN 'Recettes' THEN 6 WHEN 'Analytique' THEN 7 WHEN 'Bénévolat' THEN 8 WHEN 'Ouverture' THEN 9 WHEN 'Clôture' THEN 10 WHEN 'Résultat excédentaire' THEN 11 WHEN 'Résultat déficitaire' THEN 12 ELSE 0 END FROM tmp_accounts; DROP TABLE tmp_accounts; |
Modified src/include/lib/Garradin/Entities/Accounting/Transaction.php from [f45c5b736f] to [7faa864717].
︙ | ︙ | |||
87 88 89 90 91 92 93 94 95 96 97 98 99 100 | 'reference' => 'string|max:200', 'date' => 'required|date_format:d/m/Y', ]; protected $_lines; protected $_old_lines = []; /** * @var Transaction */ protected $_related; static public function getTypeFromAccountType(int $account_type) { | > > | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 'reference' => 'string|max:200', 'date' => 'required|date_format:d/m/Y', ]; protected $_lines; protected $_old_lines = []; protected $_accounts = []; /** * @var Transaction */ protected $_related; static public function getTypeFromAccountType(int $account_type) { |
︙ | ︙ | |||
113 114 115 116 117 118 119 | 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! */ | | | > > | | | | | | | | | > > > > > > | > > > > | > > | | > > > > > > > > > > > > > > > > > > > > | | 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 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | 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): array { $db = EntityManager::getInstance(Line::class)->DB(); if (null === $this->_lines || $restrict_year === false) { $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 = %d ORDER BY l.id;', $restrict, $this->id()); return $db->get($sql); } else { // Merge data from accounts with lines $accounts = []; $lines_with_accounts = []; foreach ($this->getLines() as $line) { if (!array_key_exists($line->id_account, $this->_accounts)) { $accounts[] = $line->id_account; } if ($line->id_analytical && !array_key_exists($line->id_analytical, $this->_accounts)) { $accounts[] = $line->id_analytical; } } if (count($accounts)) { $sql = sprintf('SELECT id, label, code FROM acc_accounts WHERE %s;', $db->where('id', 'IN', $accounts)); $this->_accounts += $db->getGrouped($sql); } foreach ($this->getLines() as $line) { $account = [ 'account_code' => $this->_accounts[$line->id_account]->code, 'account_name' => $this->_accounts[$line->id_account]->label, 'analytical_name' => $line->id_analytical ? $this->_accounts[$line->id_analytical]->label : null, ]; $lines_with_accounts[] = (object) ($line->asArray() + $account); } return $lines_with_accounts; } } public function getLines(): array { 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); } elseif (null === $this->_lines) { $this->_lines = []; |
︙ | ︙ | |||
250 251 252 253 254 255 256 257 258 259 260 261 262 263 | $type = $this->getTypesDetails()[$this->type]; return [ $type->accounts[0]->position == 'credit' ? $credit : $debit, $type->accounts[1]->position == 'credit' ? $credit : $debit, ]; } /* public function getHash() { if (!$this->id_year) { throw new \LogicException('Il n\'est pas possible de hasher un mouvement qui n\'est pas associé à un exercice'); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 286 287 288 289 290 291 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 | $type = $this->getTypesDetails()[$this->type]; return [ $type->accounts[0]->position == 'credit' ? $credit : $debit, $type->accounts[1]->position == 'credit' ? $credit : $debit, ]; } /** * Creates a new Transaction entity (not saved) from an existing one, * trying to adapt to a different chart if possible * @param int $id * @param Year $year Target year * @return Transaction */ public function duplicate(Year $year): Transaction { $new = new Transaction; $copy = ['type', 'status', 'label', 'notes', 'reference', 'date']; foreach ($copy as $field) { $new->$field = $this->$field; } $copy = ['credit', 'debit', 'id_account', 'label', 'reference']; $lines = DB::getInstance()->get('SELECT l.credit, l.debit, l.label, l.reference, b.id AS id_account FROM acc_transactions_lines l INNER JOIN acc_accounts a ON a.id = l.id_account LEFT JOIN acc_accounts b ON b.code = a.code AND b.id_chart = ? WHERE l.id_transaction = ?;', $year->chart()->id, $this->id() ); foreach ($lines as $l) { $line = new Line; foreach ($copy as $field) { $line->$field = $l->$field; } $new->addLine($line); } return $new; } /* public function getHash() { if (!$this->id_year) { throw new \LogicException('Il n\'est pas possible de hasher un mouvement qui n\'est pas associé à un exercice'); } |
︙ | ︙ | |||
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 | 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) { | > > > | > | | 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 | 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'); } $exists = $this->exists(); $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) { if ($line->exists()) { $line->delete(); } } // Remove flag if (!$exists && $this->_related) { $this->_related->markPaid(); $this->_related->save(); } return true; } |
︙ | ︙ | |||
462 463 464 465 466 467 468 | else { $details = self::getTypesDetails(); if (!array_key_exists($type, $details)) { throw new ValidationException('Type d\'écriture inconnu'); } | | | | | | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 | else { $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->set('type', self::TYPE_ADVANCED); } elseif (!$this->exists() && ($type == self::TYPE_DEBT || $type == self::TYPE_CREDIT)) { $this->addStatus(self::STATUS_WAITING); } if (empty($source['amount'])) { throw new UserException('Montant non précisé'); } $amount = $source['amount']; |
︙ | ︙ |
Modified src/include/lib/Garradin/UserTemplate/UserTemplate.php from [af2cae2f01] to [67a24ca43b].
︙ | ︙ | |||
174 175 176 177 178 179 180 | public function fetch(): string { ob_start(); $this->display(); return ob_get_clean(); } | | > > > > > > > > > > > | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | public function fetch(): string { ob_start(); $this->display(); return ob_get_clean(); } public function displayPDF(?string $filename = null): void { header('Content-type: application/pdf'); if ($filename) { header(sprintf('Content-Disposition: attachment; filename="%s"', Utils::safeFileName($filename))); } Utils::streamPDF($this->fetch()); } } |
Modified src/include/lib/Garradin/Web/Render/EncryptedSkriv.php from [d0d2a69554] to [42fbb7e0fc].
1 2 3 4 5 6 7 8 | <?php namespace Garradin\Web\Render; use Garradin\Entities\Files\File; use Garradin\Template; use const Garradin\ADMIN_URL; | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php namespace Garradin\Web\Render; use Garradin\Entities\Files\File; use Garradin\Template; use const Garradin\ADMIN_URL; class EncryptedSkriv extends AbstractRender { public function render(?string $content = null): string { $tpl = Template::getInstance(); $file = $this->file; $content = $content ?? $file->fetch(); $tpl->assign('admin_url', ADMIN_URL); $tpl->assign(compact('file', 'content')); return $tpl->fetch('common/files/_file_render_encrypted.tpl'); } } |
Modified src/include/lib/Garradin/Web/Render/Skriv.php from [0ea4ef11c0] to [ce5e528272].
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace Garradin\Web\Render; use Garradin\Entities\Files\File; use Garradin\Plugin; use Garradin\UserTemplate\CommonModifiers; use KD2\SkrivLite; use const Garradin\{ADMIN_URL, WWW_URL}; | | < < | | < > | | | > | | | | < > | < | | < | > > > > > > > | | | 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 26 27 28 29 30 31 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 73 74 75 76 77 | <?php namespace Garradin\Web\Render; use Garradin\Entities\Files\File; use Garradin\Plugin; use Garradin\UserTemplate\CommonModifiers; use KD2\SkrivLite; use const Garradin\{ADMIN_URL, WWW_URL}; class Skriv extends AbstractRender { protected $skriv; public function __construct(?File $file = null, ?string $user_prefix = null) { parent::__construct($file, $user_prefix); $this->skriv = new SkrivLite; $this->skriv->registerExtension('file', [$this, 'SkrivFile']); $this->skriv->registerExtension('fichier', [$this, 'SkrivFile']); $this->skriv->registerExtension('image', [$this, 'SkrivImage']); // Enregistrer d'autres extensions éventuellement Plugin::fireSignal('skriv.init', ['skriv' => $this->skriv]); } public function render(?string $content = null): string { $skriv =& $this->skriv; $str = $content ?? $this->file->fetch(); $str = preg_replace_callback('/#file:\[([^\]\h]+)\]/', function ($match) { return $this->resolveAttachment($match[1]); }, $str); $str = $skriv->render($str); $str = CommonModifiers::typo($str); $str = preg_replace_callback(';<a href="((?!https?://|\w+:).+)">;i', function ($matches) { return sprintf('<a href="%s" target="_parent">', $this->resolveLink($matches[1])); }, $str); return sprintf('<div class="web-content">%s</div>', $str); } public function callExtension(array $match) { $method = new \ReflectionMethod($this->skriv, '_callExtension'); $method->setAccessible(true); return $method->invoke($this->skriv, $match); } /** * Callback utilisé pour l'extension <<file>> dans le wiki-texte * @param array $args Arguments passés à l'extension * @param string $content Contenu éventuel (en mode bloc) * @param SkrivLite $skriv Objet SkrivLite */ public function SkrivFile(array $args, ?string $content, SkrivLite $skriv): string { $name = $args[0] ?? null; $caption = $args[1] ?? null; if (!$name || null === $this->current_path) { return $skriv->parseError('/!\ Tag file : aucun nom de fichier indiqué.'); } if (empty($caption)) { $caption = $name; |
︙ | ︙ | |||
84 85 86 87 88 89 90 | /** * Callback utilisé pour l'extension <<image>> dans le wiki-texte * @param array $args Arguments passés à l'extension * @param string $content Contenu éventuel (en mode bloc) * @param SkrivLite $skriv Objet SkrivLite */ | | | > > | | 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 | /** * Callback utilisé pour l'extension <<image>> dans le wiki-texte * @param array $args Arguments passés à l'extension * @param string $content Contenu éventuel (en mode bloc) * @param SkrivLite $skriv Objet SkrivLite */ public function SkrivImage(array $args, ?string $content, SkrivLite $skriv): string { static $align_replace = ['gauche' => 'left', 'droite' => 'right', 'centre' => 'center']; $name = $args[0] ?? null; $align = $args[1] ?? null; $caption = $args[2] ?? null; $align = strtr($align, $align_replace); if (!$name || null === $this->current_path) { return $skriv->parseError('/!\ Tag image : aucun nom de fichier indiqué.'); } $url = $this->resolveAttachment($name); $thumb_url = sprintf('%s?%dpx', $url, $align == 'center' ? 500 : 200); |
︙ | ︙ |
Modified src/include/lib/Garradin/Web/Skeleton.php from [f4f75cf9cf] to [03f267b710].
︙ | ︙ | |||
122 123 124 125 126 127 128 | } $ut->assignArray($params); $ut->display(); } elseif ($this->file) { | | | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | } $ut->assignArray($params); $ut->display(); } elseif ($this->file) { echo $this->file->fetch(); } else { readfile($this->defaultPath()); } } public function exists() |
︙ | ︙ | |||
171 172 173 174 175 176 177 | return 'text/html'; } elseif ($ext == 'js') { return 'text/javascript'; } if ($this->file) { | | | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | return 'text/html'; } elseif ($ext == 'js') { return 'text/javascript'; } if ($this->file) { return $this->file->mime; } $finfo = \finfo_open(\FILEINFO_MIME_TYPE); return finfo_file($finfo, $this->defaultPath()); } |
︙ | ︙ |
Modified src/www/admin/web/index.php from [9ff309f525] to [fbf5316a30].
︙ | ︙ | |||
13 14 15 16 17 18 19 | if ($current_path) { $cat = Web::get($current_path); if (!$cat) { throw new UserException('Catégorie inconnue'); } } | < < < < < | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | if ($current_path) { $cat = Web::get($current_path); if (!$cat) { throw new UserException('Catégorie inconnue'); } } else { foreach (Web::sync() as $error) { $form->addError($error); } } $order_date = qg('order_date') !== null; |
︙ | ︙ |