Comment: | Merge with trunk |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA1: |
ab8bff586d57139d9a6fff63673cd00a |
User & Date: | bohwaz on 2020-12-10 18:21:10 |
Other Links: | branch diff | manifest | tags |
2020-12-10
| ||
20:01 | Migrate more from Fichiers to File and Files check-in: 332a8b6494 user: bohwaz tags: dev | |
18:21 | Merge with trunk check-in: ab8bff586d user: bohwaz tags: dev | |
18:17 | New release check-in: 031185e587 user: bohwaz tags: trunk, stable, 1.0.0-rc12 | |
2020-12-07
| ||
01:17 | First steps of file management implementation check-in: 1d45c38659 user: bohwaz tags: dev | |
Added doc/index.md version [0009160b56].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 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 | # Garradin, le gestionnaire d'association libre et simple <nav id="gnav"> * [Guides d'installation](/wiki/?name=Installation) * [Documentation](/wiki/?name=Documentation) * <a href="https://garradin.eu/" target="_blank">Essayer gratuitement</a> * [Entraide](/wiki/?name=Entraide) <ul id="download"> </ul> </nav> <p id="give"><a href="http://kd2.org/asso/soutien/" target="_blank">Soutenir Garradin en effectuant un don :-)</a></p> <script type="text/javascript"> document.head.innerHTML += `<style type="text/css"> #give { text-align: center; padding: 1em; } #give a { display: inline-block; padding: .5em; padding-left: 70px; border-radius: .5em; font-size: 1.5em; background: #ffc url("https://kd2.org/asso/soutien/coins.png") no-repeat .5em .5em; border: 2px solid #990; } #gnav ul { display: flex; padding: 0; margin: 1em; margin-bottom: 1em; font-size: 1.2em; list-style: none; justify-content: center; align-items: center; } #gnav li { margin: 0; padding: 0; font-size: 1.2em; margin: .5em; text-align: center; } #gnav li a { height: 100%; padding: .5rem; background: #ddf; color: black; display: flex; align-items: center; justify-content: center; border-radius: .5em; border: 2px solid #99f; text-decoration: none; } #gnav li strong, #gnav li em { height: 100%; padding: .5rem; display: block; } #gnav li a:hover { text-decoration: underline; opacity: 0.7; } #download li { font-size: 1em; } #download li a { border-color: #060; background: #dfd; } `; function isNewerVersion (oldVer, newVer) { const oldParts = oldVer.split('.') const newParts = newVer.split('.') for (var i = 0; i < newParts.length; i++) { const a = ~~newParts[i] // parse int const b = ~~oldParts[i] // parse int if (a > b) return true if (a < b) return false } return false } fetch('/garradin/juvlist').then((r) => { r.json().then((list) => { let last; let selected; list.forEach((file) => { var v = file.name.match(/^garradin-(.*)\.tar\.bz2/); if (!v) { return; } if (!last || isNewerVersion(last, v[1])) { last = v[1]; selected = file; } }); let days = ((+new Date)/1000 - selected.mtime) / 3600 / 24; if (days < 31) { time = Math.ceil(days) + ' jours'; } else if (days >= 31) { time = Math.round(days / 30.5) + ' mois'; } document.querySelector('#download').innerHTML += `<li><strong>Dernière version : ${last}</strong></li> <li><em>il y a ${time}</em></li> <li><a href="$ROOT/wiki/?name=Changelog">Nouveautés</a></li> <li><a href="$ROOT/uv/${selected.name}">Télécharger</a></li>`; }); }); </script> ## C'est quoi ? <a href="$ROOT/raw/7bb068963b9f6301b27b81fe925caae9e86a229b?m=image/png" target="_blank" style="float: right; margin: 1em;"><img src="/garradin/raw/7bb068963b9f6301b27b81fe925caae9e86a229b?m=image/png" alt="Liste des membres" width="400" /></a> Garradin est un logiciel de gestion d'association (loi 1901 / ASBL / etc.). Son but est de permettre : * la gestion des __adhérent⋅e⋅s__ : ajout, modification, suppression, possibilité de choisir les informations présentes sur les fiches adhérent, envoi de mails collectifs aux adhérent⋅e⋅s * la tenue de la __comptabilité__ : avoir une gestion comptable complète à même de satisfaire un expert-comptable tout en restant à la portée de celles et ceux qui ne savent pas ce qu'est la comptabilité à double entrée, permettre la production des rapports et bilans annuels et de suivre au jour le jour le budget de l'association * la gestion des __cotisations__ et __activités__ : suivi des cotisations à jour, inscriptions et paiement des activités, rappels automatiques par e-mail, etc. * le travail __collaboratif__ et __collectif__ : gestion fine des droits d'accès aux fonctions, échange de mails entre membres… * la __simplification administrative__ : prise de notes en réunion, archivage et partage de fichiers (afin d'éliminer le besoin d'archiver les documents papier), etc. * la publication d'un __site web__ pour l'association, simple mais suffisamment flexible pour pouvoir adapter le fonctionnement à la plupart des besoins * l'__autonomisation des adhérents__ : possibilité de mettre à jour leurs informations par eux-même, ou de s'inscrire seul depuis un ordinateur ou un smartphone * la possibilité d'adapter aux besoins spécifiques de chaque association via des __extensions__. Tous ces objectifs ne sont pas encore réalisés, voir : * [la liste des fonctionnalités disponibles](/wiki/?name=Fonctionnalités) pour ce qui est actuellement disponible ; * [la feuille de route](/wiki/?name=Roadmap) pour la liste des fonctionnalités qu'il reste à implémenter. Garradin est un logiciel libre disponible sous licence [AGPL v3](https://www.gnu.org/licenses/why-affero-gpl.fr.html). Garradin signifie *argent* en *Wagiman*, un dialecte aborigène du nord de l'Australie. ## Documentation et entraide * D'abord lire la [documentation](/wiki/?name=Documentation) et notamment la [foire aux questions](/wiki/?name=FAQ) * La [liste de discussion d'entraide entre utilisateurs](https://admin.kd2.org/lists/aide@garradin.eu) est le meilleur moyen de vous faire aider :) * [Chat d'entraide en direct](https://kiwiirc.com/nextclient/#irc://irc.freenode.net/#garradin?nick=garradin%7C?), ou via IRC : salon `#garradin` sur `irc.freenode.net` ## Participer Tout coup de main est le bienvenu, pas besoin d'avoir des connaissances techniques ! Nous avons un [guide de contribution](/wiki/?name=Contribuer) pour vous aider à voir comment vous pouvez participer à Garradin :) ### Développement Garradin est un logiciel libre, développé en PHP, utilisant la base de données SQLite, et avec une interface utilisant HTML, CSS et un peu de Javascript. Nous acceptons les contributions (plugins, patch, code, tickets, etc.) avec plaisir, consultez la [documentation développeur⋅euse](/wiki/?name=Documentation développeur) pour découvrir comment vous pouvez contribuer. |
Modified src/VERSION from [4225b16d8a] to [4c3ec47899].
|
| | | 1 | 1.0.0-rc12 |
Added src/include/data/1.0.0-rc10_migration.sql version [ab6262425c].
> > > > | 1 2 3 4 | UPDATE acc_accounts SET type = 8, position = 4 WHERE id_chart = (SELECT id FROM acc_charts WHERE code IS NOT NULL) AND (code LIKE '86_%'); UPDATE acc_accounts SET type = 8, position = 5 WHERE id_chart = (SELECT id FROM acc_charts WHERE code IS NOT NULL) AND (code LIKE '87_%'); UPDATE acc_accounts SET position = 3 WHERE code IN ('5112', '5115', '530') AND code IS NOT NULL; |
Modified src/include/data/1.0.0_schema.sql from [2cb870482b] to [4710567bc0].
︙ | ︙ | |||
228 229 230 231 232 233 234 235 236 237 238 239 240 241 | start_date TEXT NOT NULL CHECK (date(start_date) IS NOT NULL AND date(start_date) = start_date), end_date TEXT NOT NULL CHECK (date(end_date) IS NOT NULL AND date(end_date) = end_date), closed INTEGER NOT NULL DEFAULT 0, id_chart INTEGER NOT NULL REFERENCES acc_charts (id) ); CREATE INDEX IF NOT EXISTS acc_years_closed ON acc_years (closed); CREATE TABLE IF NOT EXISTS acc_transactions -- Opérations comptables ( id INTEGER PRIMARY KEY NOT NULL, | > > > > | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | start_date TEXT NOT NULL CHECK (date(start_date) IS NOT NULL AND date(start_date) = start_date), end_date TEXT NOT NULL CHECK (date(end_date) IS NOT NULL AND date(end_date) = end_date), closed INTEGER NOT NULL DEFAULT 0, id_chart INTEGER NOT NULL REFERENCES acc_charts (id) ); CREATE TRIGGER IF NOT EXISTS acc_years_delete BEFORE DELETE ON acc_years BEGIN UPDATE services_fees SET id_account = NULL, id_year = NULL WHERE id_year = OLD.id; END; CREATE INDEX IF NOT EXISTS acc_years_closed ON acc_years (closed); CREATE TABLE IF NOT EXISTS acc_transactions -- Opérations comptables ( id INTEGER PRIMARY KEY NOT NULL, |
︙ | ︙ |
Modified src/include/data/charts/fr_1999.csv from [38d9f67e8d] to [4db158f82a].
︙ | ︙ | |||
173 174 175 176 177 178 179 | 49,DEPRECIATION DES COMPTES DE TIERS,,Actif, 491,Dépréciation des comptes clients,,Actif, 496,Dépréciation des comptes débiteurs divers,,Actif, 5,Classe 5 — Comptes financiers,,Actif, 50,VALEURS MOBILIÈRES DE PLACEMENT,,Actif, 51,"BANQUES, ÉTABLISSEMENTS FINANCIERS ET ASSIMILÉS",,Actif, 511,Valeurs à l'encaissement,,Actif, | | | | | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | 49,DEPRECIATION DES COMPTES DE TIERS,,Actif, 491,Dépréciation des comptes clients,,Actif, 496,Dépréciation des comptes débiteurs divers,,Actif, 5,Classe 5 — Comptes financiers,,Actif, 50,VALEURS MOBILIÈRES DE PLACEMENT,,Actif, 51,"BANQUES, ÉTABLISSEMENTS FINANCIERS ET ASSIMILÉS",,Actif, 511,Valeurs à l'encaissement,,Actif, 5112,Chèques à encaisser,,Actif ou passif,Attente d'encaissement 5115,Paiements par carte à encaisser,,Actif ou passif, 512,Banques,,Actif ou passif, 53,CAISSE,,Actif, 530,Caisse,,Actif ou passif,Caisse 54,RÉGIES D'AVANCES ET ACCRÉDITIFS,,Actif, 58,VIREMENTS INTERNES,,Actif, 59,PROVISIONS POUR DÉPRÉCIATION DES COMPTES FINANCIERS,,Actif, 6,Classe 6 — Comptes de charges,,Charge, 60,ACHATS,,Charge, 601,Achats stockés - Matières premières et fournitures,,Charge, 602,Achats stockés - Autres approvisionnements,,Charge, |
︙ | ︙ | |||
279 280 281 282 283 284 285 | 78,REPRISES SUR AMORTISSEMENTS ET PROVISIONS,,Produit, 79,TRANSFERT DE CHARGES,,Produit, 791,Transferts de charges d'exploitation,,Produit, 796,Transferts de charges financières,,Produit, 797,Transferts de charges exceptionnels,,Produit, 8,Classe 8 — Comptes spéciaux,,, 86,RÉPARTITION PAR NATURE DE CHARGES,,Charge, | | | | | | | | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | 78,REPRISES SUR AMORTISSEMENTS ET PROVISIONS,,Produit, 79,TRANSFERT DE CHARGES,,Produit, 791,Transferts de charges d'exploitation,,Produit, 796,Transferts de charges financières,,Produit, 797,Transferts de charges exceptionnels,,Produit, 8,Classe 8 — Comptes spéciaux,,, 86,RÉPARTITION PAR NATURE DE CHARGES,,Charge, 861,Mise à dispositions gratuites de biens,,Charge,Bénévolat 862,Prestations,,Charge,Bénévolat 864,Personnel bénévole,,Charge,Bénévolat 87,RÉPARTITION PAR NATURE DE RESSOURCES,,Produit, 870,Bénévolat,,Produit,Bénévolat 871,Prestations en nature,,Produit,Bénévolat 875,Dons en nature,,Produit,Bénévolat 89,BILAN,,, 890,Bilan d'ouverture,,,Ouverture 891,Bilan de clôture,,,Clôture 9,Classe 9 — Comptes analytiques,,, 99,Projets,,,Analytique |
Modified src/include/data/charts/fr_2018.csv from [7724166e91] to [387ccd52c1].
︙ | ︙ | |||
225 226 227 228 229 230 231 | 496,Prov. pour dépreci. cptes débiteurs divers,,Passif, 5,Classe 5 — Comptes financiers,,Actif, 50,VALEURS MOBILIERES DE PLACEMENT,,Actif, 503,Actions,,Actif, 506,Obligations,,Actif, 508,Autres VMP et créances assimilées,,Actif, 51,"BANQUES, ETABLISSEMENTS FINANCIERS",,Actif, | | | | | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | 496,Prov. pour dépreci. cptes débiteurs divers,,Passif, 5,Classe 5 — Comptes financiers,,Actif, 50,VALEURS MOBILIERES DE PLACEMENT,,Actif, 503,Actions,,Actif, 506,Obligations,,Actif, 508,Autres VMP et créances assimilées,,Actif, 51,"BANQUES, ETABLISSEMENTS FINANCIERS",,Actif, 5112,Chèques à encaisser,,Actif ou passif,Attente d'encaissement 5115,Paiements par carte à encaisser,,Actif ou passif, 512,Banques,,Actif ou passif, 5186,Intérêts courus à payer,,Passif, 5187,Intérêts courus à recevoir,,Actif, 53,Caisses,,Actif ou passif, 530,Caisse,,Actif ou passif,Caisse 58,VIREMENTS INTERNES,,Actif ou passif, 580,Virements internes,,Actif ou passif, 59,DEPRECIATIONS DES COMPTES FINANCIERS,,Actif ou passif, 5903,Actions,,Actif ou passif, 5906,Obligations,,Actif ou passif, 5908,Autres VMP et créances assimilées,,Actif ou passif, 6,Classe 6 — Comptes de charges,,Charge, |
︙ | ︙ | |||
526 527 528 529 530 531 532 | 7896,Utilisations des fds dédiés / générosité,,Produit, 79,TRANSFERT DE CHARGES,,Produit, 791,Transferts de charges d'exploitation,,Produit, 796,Transferts de charges financières,,Produit, 797,Transferts de charges exceptionnelles,,Produit, 8,Classe 8 — Comptes spéciaux,,, 86,EMPLOIS CONTRIBUTIONS VOLONTAIRES EN NATURE,,, | | | | | | | | | | | | | 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 | 7896,Utilisations des fds dédiés / générosité,,Produit, 79,TRANSFERT DE CHARGES,,Produit, 791,Transferts de charges d'exploitation,,Produit, 796,Transferts de charges financières,,Produit, 797,Transferts de charges exceptionnelles,,Produit, 8,Classe 8 — Comptes spéciaux,,, 86,EMPLOIS CONTRIBUTIONS VOLONTAIRES EN NATURE,,, 860,Secours en nature,,Charge,Bénévolat 8601,Alimentaires,,Charge,Bénévolat 8602,Vestimentaires,,Charge,Bénévolat 861,Mise à dispositions gratuites de biens,,Charge,Bénévolat 8611,Locaux,,Charge,Bénévolat 8612,Matériels,,Charge,Bénévolat 862,Prestations,,Charge,Bénévolat 864,Personnel bénévole,,Charge,Bénévolat 87,CONTRIBUTIONS VOLONTAIRES EN NATURE,,, 870,Dons en nature,,Produit,Bénévolat 871,Prestations en nature,,Produit,Bénévolat 875,Bénévolat,,Produit,Bénévolat 89,COMPTES DE BILAN,,, 890,Bilan d'ouverture,,,Ouverture 891,Bilan de clôture,,,Clôture 9,Classe 9 — Comptes analytiques,,, 90,COMPTES RÉFLÉCHIS,,, 906,Charges réfléchies,,, 907,Produits réfléchis,,, 99,Projets,,,Analytique |
Modified src/include/lib/Garradin/Accounting/Accounts.php from [104aa964ca] to [36e8a3ddb7].
︙ | ︙ | |||
41 42 43 44 45 46 47 | /** * Return common accounting accounts from current chart * (will not return analytical and volunteering accounts) */ public function listCommonTypes(): array { | | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /** * Return common accounting accounts from current chart * (will not return analytical and volunteering accounts) */ public function listCommonTypes(): array { return $this->em->all('SELECT * FROM @TABLE WHERE id_chart = ? AND type != 0 AND type NOT IN (?) ORDER BY code COLLATE NOCASE;', $this->chart_id, Account::TYPE_ANALYTICAL); } /** * Return all accounts from current chart */ public function listAll(): array { |
︙ | ︙ |
Modified src/include/lib/Garradin/Accounting/Graph.php from [a1b33675d1] to [01efa75756].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php namespace Garradin\Accounting; use Garradin\Entities\Accounting\Account; use Garradin\Entities\Accounting\Line; use Garradin\Entities\Accounting\Transaction; use Garradin\Utils; use Garradin\Config; use Garradin\DB; use const Garradin\ADMIN_COLOR1; use const Garradin\ADMIN_COLOR2; use const Garradin\ADMIN_URL; use KD2\DB\EntityManager; use KD2\Graphics\SVG\Plot; use KD2\Graphics\SVG\Plot_Data; | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php namespace Garradin\Accounting; use Garradin\Entities\Accounting\Account; use Garradin\Entities\Accounting\Line; use Garradin\Entities\Accounting\Transaction; use Garradin\Utils; use Garradin\Config; use Garradin\DB; use Garradin\Static_Cache; use const Garradin\ADMIN_COLOR1; use const Garradin\ADMIN_COLOR2; use const Garradin\ADMIN_URL; use KD2\DB\EntityManager; use KD2\Graphics\SVG\Plot; use KD2\Graphics\SVG\Plot_Data; |
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 | const MONTHLY_INTERVAL = 2635200; // 1 month static public function plot(string $type, array $criterias, int $interval = self::WEEKLY_INTERVAL, int $width = 700) { if (!array_key_exists($type, self::PLOT_TYPES)) { throw new \InvalidArgumentException('Unknown type'); } $plot = new Plot($width, 300); $lines = self::PLOT_TYPES[$type]; $data = []; foreach ($lines as $label => $line_criterias) { | > > > > > > | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | const MONTHLY_INTERVAL = 2635200; // 1 month static public function plot(string $type, array $criterias, int $interval = self::WEEKLY_INTERVAL, int $width = 700) { if (!array_key_exists($type, self::PLOT_TYPES)) { throw new \InvalidArgumentException('Unknown type'); } $cache_id = sha1(json_encode(func_get_args())); if (!Static_Cache::expired($cache_id)) { return Static_Cache::get($cache_id); } $plot = new Plot($width, 300); $lines = self::PLOT_TYPES[$type]; $data = []; foreach ($lines as $label => $line_criterias) { |
︙ | ︙ | |||
109 110 111 112 113 114 115 | $plot->add($line); if ($i >= count($colors)) $i = 0; } } | | > > > > > > > > > > | 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 | $plot->add($line); if ($i >= count($colors)) $i = 0; } } $out = $plot->output(); Static_Cache::store($cache_id, $out); return $out; } static public function pie(string $type, array $criterias) { if (!array_key_exists($type, self::PIE_TYPES)) { throw new \InvalidArgumentException('Unknown type'); } $cache_id = sha1(json_encode(func_get_args())); if (!Static_Cache::expired($cache_id)) { return Static_Cache::get($cache_id); } $pie = new Pie(700, 300); $pie_criterias = self::PIE_TYPES[$type]; $data = Reports::getClosingSumsWithAccounts(array_merge($criterias, $pie_criterias), 'ABS(sum) DESC'); $others = 0; |
︙ | ︙ | |||
157 158 159 160 161 162 163 | if ($others != 0) { $pie->add(new Pie_Data(abs($others) / 100, 'Autres', '#ccc')); } $pie->togglePercentage(true); | | > > > > | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | if ($others != 0) { $pie->add(new Pie_Data(abs($others) / 100, 'Autres', '#ccc')); } $pie->togglePercentage(true); $out = $pie->output(); Static_Cache::store($cache_id, $out); return $out; } static protected function getColors() { $config = Config::getInstance(); $c1 = $config->get('couleur1') ?: ADMIN_COLOR1; $c2 = $config->get('couleur2') ?: ADMIN_COLOR2; |
︙ | ︙ |
Modified src/include/lib/Garradin/Accounting/Reports.php from [bd1f8f1a69] to [657f1c3461].
︙ | ︙ | |||
26 27 28 29 30 31 32 | if (!empty($criterias['exclude_position'])) { $db = DB::getInstance(); $where[] = $db->where('position', 'NOT IN', $criterias['exclude_position']); } if (!empty($criterias['type'])) { | < > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | if (!empty($criterias['exclude_position'])) { $db = DB::getInstance(); $where[] = $db->where('position', 'NOT IN', $criterias['exclude_position']); } if (!empty($criterias['type'])) { $criterias['type'] = array_map('intval', (array)$criterias['type']); $where[] = sprintf('a.type IN (%s)', implode(',', $criterias['type'])); } if (!empty($criterias['exclude_type'])) { $criterias['exclude_type'] = array_map('intval', (array)$criterias['exclude_type']); $where[] = sprintf('a.type NOT IN (%s)', implode(',', $criterias['exclude_type'])); } if (!empty($criterias['user'])) { $where[] = sprintf('t.id IN (SELECT id_transaction FROM acc_transactions_users WHERE id_user = %d)', $criterias['user']); } if (!empty($criterias['creator'])) { $where[] = sprintf('t.id_creator = %d', $criterias['creator']); |
︙ | ︙ | |||
60 61 62 63 64 65 66 | /** * Return account sums per year or per account * @param bool $order_year If true will return accounts grouped by year, if false it will return years grouped by account */ static public function getAnalyticalSums(bool $by_year = false): \Generator { | > | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | /** * Return account sums per year or per account * @param bool $order_year If true will return accounts grouped by year, if false it will return years grouped by account */ static public function getAnalyticalSums(bool $by_year = false): \Generator { $sql = 'SELECT a.label AS account_label, a.description AS account_description, a.id AS id_account, y.id AS id_year, y.label AS year_label, y.start_date, y.end_date, SUM(l.credit - l.debit) AS sum, SUM(l.credit) AS credit, SUM(l.debit) AS debit FROM acc_transactions_lines l INNER JOIN acc_transactions t ON t.id = l.id_transaction INNER JOIN acc_accounts a ON a.id = l.id_analytical INNER JOIN acc_years y ON y.id = t.id_year GROUP BY %s ORDER BY %s;'; |
︙ | ︙ | |||
103 104 105 106 107 108 109 110 111 112 113 114 115 116 | $current = null; } if (null === $current) { $current = (object) [ 'id' => $by_year ? $row->id_year : $row->id_account, 'label' => $by_year ? $row->year_label : $row->account_label, 'credit' => 0, 'debit' => 0, 'sum' => 0, 'items' => [] ]; } | > | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | $current = null; } if (null === $current) { $current = (object) [ 'id' => $by_year ? $row->id_year : $row->id_account, 'label' => $by_year ? $row->year_label : $row->account_label, 'description' => !$by_year ? $row->account_description : null, 'credit' => 0, 'debit' => 0, 'sum' => 0, 'items' => [] ]; } |
︙ | ︙ | |||
440 441 442 443 444 445 446 | if (null === $transaction) { return; } yield $transaction; } | | > > > > > > > > > > > > > > > > > > > > > > | 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | if (null === $transaction) { return; } yield $transaction; } static public function getStatement(array $criterias): array { $revenue = Reports::getClosingSumsWithAccounts($criterias + ['position' => Account::REVENUE]); $expense = Reports::getClosingSumsWithAccounts($criterias + ['position' => Account::EXPENSE], null, true); $get_sum = function (array $in): int { $sum = 0; foreach ($in as $row) { $sum += $row->sum; } return abs($sum); }; $revenue_sum = $get_sum($revenue); $expense_sum = $get_sum($expense); $result = $revenue_sum - $expense_sum; return compact('revenue', 'expense', 'revenue_sum', 'expense_sum', 'result'); } } |
Modified src/include/lib/Garradin/Accounting/Transactions.php from [6561462819] to [4a62ce7a06].
︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 | $status[] = $v; } } $row->status = implode(', ', $status); $row->date = \DateTime::createFromFormat('Y-m-d', $row->date); $row->date = $row->date->format('d/m/Y'); } $row->credit = Utils::money_format($row->credit, ',', ''); $row->debit = Utils::money_format($row->debit, ',', ''); | > < | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | $status[] = $v; } } $row->status = implode(', ', $status); $row->date = \DateTime::createFromFormat('Y-m-d', $row->date); $row->date = $row->date->format('d/m/Y'); $previous_id = $row->id; } $row->credit = Utils::money_format($row->credit, ',', ''); $row->debit = Utils::money_format($row->debit, ',', ''); yield $row; } } static public function importCSV(Year $year, array $file, int $user_id) { if ($year->closed) { |
︙ | ︙ | |||
187 188 189 190 191 192 193 | throw new UserException('cette ligne n\'est reliée à aucune écriture'); } if ($row->id) { $transaction = self::get((int)$row->id); if (!$transaction) { | | > > > > | | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | throw new UserException('cette ligne n\'est reliée à aucune écriture'); } if ($row->id) { $transaction = self::get((int)$row->id); if (!$transaction) { throw new UserException(sprintf('l\'écriture #%d est introuvable', $row->id)); } if (!$transaction->id_year != $year->id()) { throw new UserException(sprintf('l\'écriture #%d appartient à un autre exercice', $row->id)); } if ($transaction->validated) { throw new UserException(sprintf('l\'écriture #%d est validée et ne peut être modifiée', $row->id)); } } else { $transaction = new Transaction; $transaction->id_creator = $user_id; $transaction->id_year = $year->id(); } |
︙ | ︙ | |||
385 386 387 388 389 390 391 | LEFT JOIN acc_accounts b ON b.id = l.id_analytical'; $conditions = sprintf('t.type = %s AND t.id_year = %d', $type, $year_id); $sum = 0; $list = new DynamicList($columns, $tables, $conditions); $list->orderBy('date', true); | | | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | LEFT JOIN acc_accounts b ON b.id = l.id_analytical'; $conditions = sprintf('t.type = %s AND t.id_year = %d', $type, $year_id); $sum = 0; $list = new DynamicList($columns, $tables, $conditions); $list->orderBy('date', true); $list->setCount('COUNT(DISTINCT t.id)'); $list->groupBy('t.id'); $list->setModifier(function (&$row) use (&$sum, $reverse) { $row->date = \DateTime::createFromFormat('!Y-m-d', $row->date); }); $list->setExportCallback(function (&$row) { $row->change = Utils::money_format($row->change, '.', '', false); }); return $list; } } |
Modified src/include/lib/Garradin/Accounting/Years.php from [31fc8b31a2] to [adce096083].
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | return EntityManager::findOneById(Year::class, $year_id); } static public function getCurrentOpenYear() { return EntityManager::findOne(Year::class, 'SELECT * FROM @TABLE WHERE closed = 0 ORDER BY start_date LIMIT 1;'); } static public function listOpen() { $db = EntityManager::getInstance(Year::class)->DB(); return $db->get('SELECT *, (SELECT COUNT(*) FROM acc_transactions WHERE id_year = acc_years.id) AS nb_transactions FROM acc_years WHERE closed = 0 ORDER BY end_date;'); } | > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | return EntityManager::findOneById(Year::class, $year_id); } static public function getCurrentOpenYear() { return EntityManager::findOne(Year::class, 'SELECT * FROM @TABLE WHERE closed = 0 ORDER BY start_date LIMIT 1;'); } static public function getCurrentOpenYearId() { return EntityManager::getInstance(Year::class)->col('SELECT id FROM @TABLE WHERE closed = 0 ORDER BY start_date LIMIT 1;'); } static public function listOpen() { $db = EntityManager::getInstance(Year::class)->DB(); return $db->get('SELECT *, (SELECT COUNT(*) FROM acc_transactions WHERE id_year = acc_years.id) AS nb_transactions FROM acc_years WHERE closed = 0 ORDER BY end_date;'); } |
︙ | ︙ | |||
41 42 43 44 45 46 47 | { return DB::getInstance()->count(Year::TABLE, 'closed = 1'); } static public function list(bool $reverse = false) { $desc = $reverse ? 'DESC' : ''; | | > | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | { return DB::getInstance()->count(Year::TABLE, 'closed = 1'); } static public function list(bool $reverse = false) { $desc = $reverse ? 'DESC' : ''; return DB::getInstance()->get(sprintf('SELECT *, (SELECT COUNT(*) FROM acc_transactions WHERE id_year = acc_years.id) AS nb_transactions FROM acc_years ORDER BY end_date %s;', $desc)); } static public function getNewYearDates(): array { $last_year = EntityManager::findOne(Year::class, 'SELECT * FROM @TABLE ORDER BY end_date DESC LIMIT 1;'); if ($last_year) { |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Services/Fee.php from [6f825919cc] to [242617c34e].
︙ | ︙ | |||
156 157 158 159 160 161 162 | ]; $tables = 'services_users su INNER JOIN membres m ON m.id = su.id_user INNER JOIN services_fees sf ON sf.id = su.id_fee LEFT JOIN acc_transactions_users tu ON tu.id_service_user = su.id LEFT JOIN acc_transactions_lines l ON l.id_transaction = tu.id_transaction'; | | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | ]; $tables = 'services_users su INNER JOIN membres m ON m.id = su.id_user INNER JOIN services_fees sf ON sf.id = su.id_fee LEFT JOIN acc_transactions_users tu ON tu.id_service_user = su.id LEFT JOIN acc_transactions_lines l ON l.id_transaction = tu.id_transaction'; $conditions = sprintf('su.id_fee = %d AND su.paid = 1 AND (su.expiry_date >= date() OR su.expiry_date IS NULL)', $this->id()); $list = new DynamicList($columns, $tables, $conditions); $list->groupBy('su.id_user'); $list->orderBy('date', true); $list->setCount('COUNT(DISTINCT su.id_user)'); return $list; } |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Services/Service.php from [5fa29d7db5] to [b46b1da9ff].
︙ | ︙ | |||
103 104 105 106 107 108 109 | ], ]; $tables = 'services_users su INNER JOIN membres m ON m.id = su.id_user INNER JOIN services s ON s.id = su.id_service INNER JOIN services_fees sf ON sf.id = su.id_fee'; | | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | ], ]; $tables = 'services_users su INNER JOIN membres m ON m.id = su.id_user INNER JOIN services s ON s.id = su.id_service INNER JOIN services_fees sf ON sf.id = su.id_fee'; $conditions = sprintf('su.id_service = %d AND su.paid = 1 AND (su.expiry_date >= date() OR su.expiry_date IS NULL)', $this->id()); $list = new DynamicList($columns, $tables, $conditions); $list->groupBy('su.id_user'); $list->orderBy('date', true); $list->setCount('COUNT(DISTINCT su.id_user)'); return $list; } |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Services/Service_User.php from [d694479106] to [c3706f63b1].
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace Garradin\Entities\Services; use Garradin\DB; use Garradin\Entity; use Garradin\ValidationException; use Garradin\Services\Fees; use Garradin\Services\Services; use Garradin\Entities\Accounting\Transaction; class Service_User extends Entity { | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php namespace Garradin\Entities\Services; use Garradin\DB; use Garradin\Entity; use Garradin\Membres; use Garradin\ValidationException; use Garradin\Services\Fees; use Garradin\Services\Services; use Garradin\Entities\Accounting\Transaction; class Service_User extends Entity { |
︙ | ︙ | |||
84 85 86 87 88 89 90 | if (null === $this->_fee) { $this->_fee = Fees::get($this->id_fee); } return $this->_fee; } | | > > > > > > > > | | 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 | if (null === $this->_fee) { $this->_fee = Fees::get($this->id_fee); } return $this->_fee; } public function addPayment(int $user_id, ?array $source = null): Transaction { if (null === $source) { $source = $_POST; } $transaction = new Transaction; $transaction->id_creator = $user_id; $transaction->id_year = $this->fee()->id_year; $source['type'] = Transaction::TYPE_REVENUE; $key = sprintf('account_%d_', $source['type']); $source[$key . '0'] = [$this->fee()->id_account => '']; $source[$key . '1'] = isset($source['account']) ? $source['account'] : null; $label = $this->service()->label; if ($this->fee()->label != $label) { $label .= ' - ' . $this->fee()->label; } $label .= sprintf(' (%s)', (new Membres)->getNom($this->id_user)); $source['label'] = $label; $source['date'] = $this->date->format('d/m/Y'); $transaction->importFromNewForm($source); $transaction->save(); $transaction->linkToUser($this->id_user, $this->id()); return $transaction; |
︙ | ︙ | |||
128 129 130 131 132 133 134 | if ($su->id_fee && $su->fee()->id_account && $su->id_user) { $su->expected_amount = $su->fee()->getAmountForUser($su->id_user); } $su->save(); | | > > | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | if ($su->id_fee && $su->fee()->id_account && $su->id_user) { $su->expected_amount = $su->fee()->getAmountForUser($su->id_user); } $su->save(); if ($su->id_fee && $su->fee()->id_account && !empty($source['amount']) && !empty($source['create_payment'])) { $su->addPayment($user_id, $source); } $db->commit(); return $su; } } |
Modified src/include/lib/Garradin/Files/Files.php from [85cdda535c] to [9ec11674dc].
︙ | ︙ | |||
12 13 14 15 16 17 18 | static public function callStorage(string $function, ...$args) { $storage = FILE_STORAGE_BACKEND ?? 'SQLite'; $class_name = get_class(__NAMESPACE__ . '\\Backend\\' . $storage); return call_user_func_array([$class_name, $function], $args); } | | | > | > > > > > > > > > > > > > | 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 | static public function callStorage(string $function, ...$args) { $storage = FILE_STORAGE_BACKEND ?? 'SQLite'; $class_name = get_class(__NAMESPACE__ . '\\Backend\\' . $storage); return call_user_func_array([$class_name, $function], $args); } static public function migrateStorage(string $from, string $to): void { $res = EM::getInstance(File::class)->iterate('SELECT * FROM @TABLE;'); $from = get_class(__NAMESPACE__ . '\\Backend\\' . $from); $to = get_class(__NAMESPACE__ . '\\Backend\\' . $to); foreach ($res as $file) { $from_path = call_user_func([$from, 'path'], $file); call_user_func([$to, 'store'], $file, $from_path); } } static public function generatePathsIndex(): void { $all = DB::getInstance()->getAssoc('SELECT path, path FROM files GROUP BY path;'); $paths = []; foreach ($all as $path) { $path = explode('/', $path); foreach ($path as $part) { } } } } |
Modified src/include/lib/Garradin/Recherche.php from [5f2af851fc] to [1d10925eeb].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php namespace Garradin; use Garradin\Entities\Accounting\Transaction; class Recherche { const TYPE_JSON = 'json'; const TYPE_SQL = 'sql'; const TARGETS = [ 'membres', 'compta', ]; protected function _checkFields($data) | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php namespace Garradin; use Garradin\Entities\Accounting\Transaction; class Recherche { const TYPE_JSON = 'json'; const TYPE_SQL = 'sql'; const TYPE_SQL_UNPROTECTED = 'sql_unprotected'; const TARGETS = [ 'membres', 'compta', ]; protected function _checkFields($data) |
︙ | ︙ | |||
29 30 31 32 33 34 35 | } if (array_key_exists('id_membre', $data) && null !== $data['id_membre'] && !$db->test('membres', 'id = ?', $data['id_membre'])) { throw new \InvalidArgumentException('Numéro d\'utilisateur inconnu.'); } | > > | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | } if (array_key_exists('id_membre', $data) && null !== $data['id_membre'] && !$db->test('membres', 'id = ?', $data['id_membre'])) { throw new \InvalidArgumentException('Numéro d\'utilisateur inconnu.'); } static $types = [self::TYPE_SQL, self::TYPE_JSON, self::TYPE_SQL_UNPROTECTED]; if (array_key_exists('type', $data) && !in_array($data['type'], $types)) { throw new \InvalidArgumentException('Type de recherche inconnu.'); } if (array_key_exists('cible', $data) && !in_array($data['cible'], self::TARGETS, true)) { throw new \InvalidArgumentException('Cible de recherche invalide.'); |
︙ | ︙ | |||
163 164 165 166 167 168 169 170 171 | return []; } $out = []; $columns = $this->getColumns($target); foreach (reset($result) as $key => $v) { foreach ($columns as $ckey => $config) { if ($ckey == $key) { | > > > > > > | > > | > > > > > > > | 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 | return []; } $out = []; $columns = $this->getColumns($target); foreach (reset($result) as $key => $v) { if (substr($key, 0, 1) == '_') { continue; } $label = null; foreach ($columns as $ckey => $config) { if ($ckey == $key) { $label = $config->label; break; } elseif (isset($config->alias) && $config->alias == $key) { $key = $config->alias; $label = $config->label; break; } } if (!$label) { $label = $key; } $out[$key] = $label; } return $out; } /** * Renvoie la liste des colonnes d'une cible |
︙ | ︙ | |||
241 242 243 244 245 246 247 | } elseif ($target === 'compta') { $columns['t.id'] = (object) [ 'textMatch'=> false, 'label' => 'Numéro écriture', 'type' => 'integer', 'null' => false, | | | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | } elseif ($target === 'compta') { $columns['t.id'] = (object) [ 'textMatch'=> false, 'label' => 'Numéro écriture', 'type' => 'integer', 'null' => false, 'alias' => 'transaction_id', ]; $columns['t.date'] = (object) [ 'textMatch'=> false, 'label' => 'Date', 'type' => 'date', 'null' => false, |
︙ | ︙ | |||
498 499 500 501 502 503 504 | { throw new UserException('Aucune clause trouvée dans la recherche.'); } // Ajout du champ identité si pas présent if ($target == 'membres') { | | | 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | { throw new UserException('Aucune clause trouvée dans la recherche.'); } // Ajout du champ identité si pas présent if ($target == 'membres') { $query_columns = array_merge([$config->get('champ_identite')], $query_columns); } // Ajout de champs compta si pas présents elseif ($target == 'compta') { $query_columns = array_merge(['t.id', 't.date', 't.label', 'l.debit', 'l.credit', 'a.code'], $query_columns); } |
︙ | ︙ | |||
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | INNER JOIN acc_transactions_lines AS l ON l.id_transaction = t.id INNER JOIN acc_accounts AS a ON l.id_account = a.id LEFT JOIN acc_accounts AS a2 ON l.id_analytical = a2.id WHERE %s GROUP BY t.id ORDER BY %s %s LIMIT %d;', $query_columns, $query_groups, $order, $desc, (int) $limit); $sql_query = preg_replace('/"(a|a2|l|t)\./', '"$1"."', $sql_query); } else { $sql_query = sprintf('SELECT id, %s FROM %s WHERE %s ORDER BY %s %s LIMIT %d;', $query_columns, $target, $query_groups, $order, $desc, (int) $limit); } return $sql_query; } /** * Lancer une recherche SQL */ | > > > > | | | > > > > > > > | | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 | INNER JOIN acc_transactions_lines AS l ON l.id_transaction = t.id INNER JOIN acc_accounts AS a ON l.id_account = a.id LEFT JOIN acc_accounts AS a2 ON l.id_analytical = a2.id WHERE %s GROUP BY t.id ORDER BY %s %s LIMIT %d;', $query_columns, $query_groups, $order, $desc, (int) $limit); $sql_query = preg_replace('/"(a|a2|l|t)\./', '"$1"."', $sql_query); } else if ('membres' === $target) { $sql_query = sprintf('SELECT id AS _user_id, %s FROM %s WHERE %s ORDER BY %s %s LIMIT %d;', $query_columns, $target, $query_groups, $order, $desc, (int) $limit); } else { $sql_query = sprintf('SELECT id, %s FROM %s WHERE %s ORDER BY %s %s LIMIT %d;', $query_columns, $target, $query_groups, $order, $desc, (int) $limit); } return $sql_query; } /** * Lancer une recherche SQL */ public function searchSQL(string $target, $query, array $force_select = null, bool $no_limit = false, bool $unprotected = false) { if (!in_array($target, self::TARGETS, true)) { throw new \InvalidArgumentException('Cible inconnue : ' . $target); } if (null !== $force_select) { $query = preg_replace('/^\s*SELECT.*FROM\s+/Ui', 'SELECT ' . implode(', ', $force_select) . ' FROM ', $query); } if (!$no_limit && !preg_match('/LIMIT\s+\d+/i', $query)) { $query = preg_replace('/;?\s*$/', '', $query); $query .= ' LIMIT 100'; } try { $db = DB::getInstance(); static $allowed = [ 'compta' => ['acc_transactions' => null, 'acc_transactions_lines' => null, 'acc_accounts' => null, 'acc_charts' => null, 'acc_years' => null, 'acc_transactions_users' => null], 'membres' => ['membres' => null, 'membres_categories' => null], ]; if ($unprotected) { $allowed_tables = null; } else { $allowed_tables = $allowed[$target]; } $db->protectSelect($allowed_tables, $query); return $db->get($query); } catch (\Exception $e) { $message = 'Erreur dans la requête : ' . $e->getMessage(); if (null !== $force_select) { |
︙ | ︙ |
Modified src/include/lib/Garradin/Services/Fees.php from [40efe50c3b] to [7018d3147a].
︙ | ︙ | |||
54 55 56 57 58 59 60 | return $result; } public function listWithStats() { $db = DB::getInstance(); return $db->get('SELECT f.*, | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | return $result; } public function listWithStats() { $db = DB::getInstance(); return $db->get('SELECT f.*, (SELECT COUNT(DISTINCT id_user) FROM services_users WHERE id_fee = f.id AND (expiry_date >= date() OR expiry_date IS NULL) AND paid = 1) AS nb_users_ok, (SELECT COUNT(DISTINCT id_user) FROM services_users WHERE id_fee = f.id AND expiry_date < date()) AS nb_users_expired, (SELECT COUNT(DISTINCT id_user) FROM services_users WHERE id_fee = f.id AND paid = 0) AS nb_users_unpaid FROM services_fees f WHERE id_service = ? ORDER BY amount, transliterate_to_ascii(label) COLLATE NOCASE;', $this->service_id); } } |
Modified src/include/lib/Garradin/Services/Services.php from [8034954296] to [c0c763cc9c].
︙ | ︙ | |||
20 21 22 23 24 25 26 | } static public function listGroupedWithFees(?int $user_id = null) { $services = DB::getInstance()->getGrouped('SELECT id, label, duration, start_date, end_date, description, CASE WHEN end_date IS NOT NULL THEN end_date WHEN duration IS NOT NULL THEN date(\'now\', \'+\'||duration||\' days\') ELSE NULL END AS expiry_date | | | | 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 | } static public function listGroupedWithFees(?int $user_id = null) { $services = DB::getInstance()->getGrouped('SELECT id, label, duration, start_date, end_date, description, CASE WHEN end_date IS NOT NULL THEN end_date WHEN duration IS NOT NULL THEN date(\'now\', \'+\'||duration||\' days\') ELSE NULL END AS expiry_date FROM services ORDER BY label COLLATE NOCASE;'); $fees = Fees::listAllByService($user_id); $out = []; foreach ($services as $service) { $out[$service->id] = $service; $out[$service->id]->fees = []; } foreach ($fees as $fee) { $out[$fee->id_service]->fees[] = $fee; } return $out; } static public function listWithStats() { $db = DB::getInstance(); return $db->get('SELECT s.*, (SELECT COUNT(DISTINCT id_user) FROM services_users WHERE id_service = s.id AND (expiry_date IS NULL OR expiry_date >= date()) AND paid = 1) AS nb_users_ok, (SELECT COUNT(DISTINCT id_user) FROM services_users WHERE id_service = s.id AND expiry_date < date()) AS nb_users_expired, (SELECT COUNT(DISTINCT id_user) FROM services_users WHERE id_service = s.id AND paid = 0) AS nb_users_unpaid FROM services s ORDER BY transliterate_to_ascii(s.label) COLLATE NOCASE;'); } } |
Modified src/include/lib/Garradin/Upgrade.php from [846a817db9] to [b4ea8189bd].
︙ | ︙ | |||
85 86 87 88 89 90 91 92 93 94 95 96 97 98 | if (version_compare($v, '1.0.0-beta1', '>=') && version_compare($v, '1.0.0-rc3', '<')) { $db->beginSchemaUpdate(); $db->import(ROOT . '/include/data/1.0.0-rc3_migration.sql'); $db->commitSchemaUpdate(); } // Vérification de la cohérence des clés étrangères $db->foreignKeyCheck(); Utils::clearCaches(); $config->setVersion(garradin_version()); | > > > > > > > > > > > > > > > | 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 | if (version_compare($v, '1.0.0-beta1', '>=') && version_compare($v, '1.0.0-rc3', '<')) { $db->beginSchemaUpdate(); $db->import(ROOT . '/include/data/1.0.0-rc3_migration.sql'); $db->commitSchemaUpdate(); } if (version_compare($v, '1.0.0-beta1', '>=') && version_compare($v, '1.0.0-rc10', '<')) { $db->beginSchemaUpdate(); $db->import(ROOT . '/include/data/1.0.0-rc10_migration.sql'); $db->commitSchemaUpdate(); } if (version_compare($v, '1.0.0-beta1', '>=') && version_compare($v, '1.0.0-rc11', '<')) { // Missing trigger $db->beginSchemaUpdate(); $db->import(ROOT . '/include/data/1.0.0_schema.sql'); $db->commitSchemaUpdate(); } // Vérification de la cohérence des clés étrangères $db->foreignKeyCheck(); Utils::clearCaches(); $config->setVersion(garradin_version()); |
︙ | ︙ |
Modified src/templates/acc/accounts/index.tpl from [d00593bab9] to [6db865d25e].
1 2 3 4 5 6 7 8 | {include file="admin/_head.tpl" title="Comptes favoris" current="acc/accounts"} {include file="acc/_year_select.tpl"} <nav class="tabs"> <ul> <li class="current"><a href="{$admin_url}acc/accounts/">Comptes favoris</a></li> <li><a href="{$admin_url}acc/reports/trial_balance.php?year={$current_year.id}">Balance générale (tous les comptes)</a></li> | > > > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | {include file="admin/_head.tpl" title="Comptes favoris" current="acc/accounts"} {include file="acc/_year_select.tpl"} <nav class="tabs"> <aside> {linkbutton shape="search" href="!acc/search.php?year=%d"|args:$current_year.id label="Recherche"} </aside> <ul> <li class="current"><a href="{$admin_url}acc/accounts/">Comptes favoris</a></li> <li><a href="{$admin_url}acc/reports/trial_balance.php?year={$current_year.id}">Balance générale (tous les comptes)</a></li> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} <li><a href="{$admin_url}acc/charts/accounts/all.php?id={$chart_id}">Plan comptable</a></li> {/if} </ul> </nav> {include file="acc/_simple_help.tpl" link="../reports/trial_balance.php?year=%d"|args:$current_year.id type=null} |
︙ | ︙ |
Modified src/templates/acc/accounts/journal.tpl from [eea4b60d81] to [98708499a9].
︙ | ︙ | |||
48 49 50 51 52 53 54 55 56 57 58 59 60 61 | <nav class="tabs"> <aside> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} {linkbutton href="%s&export=csv"|args:$self_url label="Export CSV" shape="export"} {linkbutton href="%s&export=ods"|args:$self_url label="Export tableur" shape="export"} {/if} {if $year.id == CURRENT_YEAR_ID} {linkbutton href="!acc/transactions/new.php?account=%d"|args:$account.id label="Saisir une écriture dans ce compte" shape="plus"} {/if} </aside> <ul> <li{if $simple} class="current"{/if}><a href="?id={$account.id}&simple=1&year={$year.id}">Vue simplifiée</a></li> <li{if !$simple} class="current"{/if}><a href="?id={$account.id}&simple=0&year={$year.id}">Vue comptable</a></li> | > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <nav class="tabs"> <aside> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} {linkbutton href="%s&export=csv"|args:$self_url label="Export CSV" shape="export"} {linkbutton href="%s&export=ods"|args:$self_url label="Export tableur" shape="export"} {/if} {linkbutton shape="search" href="!acc/search.php?year=%d&account=%s"|args:$year.id,$account.code label="Recherche"} {if $year.id == CURRENT_YEAR_ID} {linkbutton href="!acc/transactions/new.php?account=%d"|args:$account.id label="Saisir une écriture dans ce compte" shape="plus"} {/if} </aside> <ul> <li{if $simple} class="current"{/if}><a href="?id={$account.id}&simple=1&year={$year.id}">Vue simplifiée</a></li> <li{if !$simple} class="current"{/if}><a href="?id={$account.id}&simple=0&year={$year.id}">Vue comptable</a></li> |
︙ | ︙ |
Modified src/templates/acc/accounts/simple.tpl from [d13f106002] to [55f085e703].
1 2 | {include file="admin/_head.tpl" title="Suivi : %s"|args:$types[$type] current="acc/simple"} | < | < < < < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | {include file="admin/_head.tpl" title="Suivi : %s"|args:$types[$type] current="acc/simple"} {include file="acc/_year_select.tpl"} <nav class="tabs"> <aside> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} {linkbutton href="?type=%d&export=csv"|args:$type label="Export CSV" shape="export"} {linkbutton href="?type=%d&export=ods"|args:$type label="Export tableur" shape="export"} {/if} {linkbutton shape="search" href="!acc/search.php?year=%d&type=%d"|args:$year.id,$type label="Recherche"} </aside> <ul> {foreach from=$types key="key" item="label"} <li{if $type == $key} class="current"{/if}><a href="?type={$key}">{$label}</a></li> {/foreach} </ul> </nav> |
︙ | ︙ |
Modified src/templates/acc/index.tpl from [275f4e0a55] to [b029a4865a].
1 2 3 4 5 6 7 8 9 | {include file="admin/_head.tpl" title="Comptabilité" current="acc"} {foreach from=$years item="year"} <section class="year-infos"> <h2 class="ruler">{$year.label} — Du {$year.start_date|date_short} au {$year.end_date|date_short}</h2> <nav class="tabs"> <aside> | < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | {include file="admin/_head.tpl" title="Comptabilité" current="acc"} {foreach from=$years item="year"} <section class="year-infos"> <h2 class="ruler">{$year.label} — Du {$year.start_date|date_short} au {$year.end_date|date_short}</h2> <nav class="tabs"> <aside> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} {linkbutton shape="upload" href="!acc/years/import.php?year=%d"|args:$year.id label="Import & export"} {/if} {linkbutton shape="search" href="!acc/search.php?year=%d"|args:$year.id label="Recherche"} </aside> <ul> <li><a href="{$admin_url}acc/reports/graphs.php?year={$year.id}">Graphiques</a></li> <li><a href="{$admin_url}acc/reports/trial_balance.php?year={$year.id}">Balance générale</a></li> <li><a href="{$admin_url}acc/reports/journal.php?year={$year.id}">Journal général</a></li> <li><a href="{$admin_url}acc/reports/ledger.php?year={$year.id}">Grand livre</a></li> <li><a href="{$admin_url}acc/reports/statement.php?year={$year.id}">Compte de résultat</a></li> |
︙ | ︙ |
Modified src/templates/acc/reports/_header.tpl from [c9b9d26764] to [8e4c7a5fd3].
︙ | ︙ | |||
10 11 12 13 14 15 16 | <li{if $current == "journal"} class="current"{/if}><a href="{$admin_url}acc/reports/journal.php?{$criterias_query}">Journal général</a></li> <li{if $current == "ledger"} class="current"{/if}><a href="{$admin_url}acc/reports/ledger.php?{$criterias_query}">Grand livre</a></li> <li{if $current == "statement"} class="current"{/if}><a href="{$admin_url}acc/reports/statement.php?{$criterias_query}">Compte de résultat</a></li> <li{if $current == "balance_sheet"} class="current"{/if}><a href="{$admin_url}acc/reports/balance_sheet.php?{$criterias_query}">Bilan</a></li> </ul> </nav> | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <li{if $current == "journal"} class="current"{/if}><a href="{$admin_url}acc/reports/journal.php?{$criterias_query}">Journal général</a></li> <li{if $current == "ledger"} class="current"{/if}><a href="{$admin_url}acc/reports/ledger.php?{$criterias_query}">Grand livre</a></li> <li{if $current == "statement"} class="current"{/if}><a href="{$admin_url}acc/reports/statement.php?{$criterias_query}">Compte de résultat</a></li> <li{if $current == "balance_sheet"} class="current"{/if}><a href="{$admin_url}acc/reports/balance_sheet.php?{$criterias_query}">Bilan</a></li> </ul> </nav> <h2>{$config.nom_asso} — {$title}</h2> {if isset($analytical)} <h3>Projet : {$analytical.label}</h3> {/if} {if isset($year)} <p>Exercice comptable {if $year.closed}clôturé{else}en cours{/if} du {$year.start_date|date_short} au {$year.end_date|date_short}, généré le {$close_date|date_short}</p> {/if} <p class="noprint print-btn"> <button onclick="window.print(); return false;" class="icn-btn" data-icon="⎙">Imprimer</button> </p> </div> |
Modified src/templates/acc/reports/_journal.tpl from [37e07b0e8b] to [4ecba1da68].
1 2 3 | <table class="list multi"> <thead> <tr> | > | | > | 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 | <table class="list multi"> <thead> <tr> <td class="num">N°</td> <td>Pièce comptable</td> <td>Date</td> <th>Libellé</th> <td>Comptes</td> <td class="money">Débit</td> <td class="money">Crédit</td> <td>Libellé ligne</td> <td>Réf. ligne</td> </tr> </thead> {foreach from=$journal item="transaction"} <tbody> <tr> <td rowspan="{$transaction.lines|count}" class="num"><a href="{$admin_url}acc/transactions/details.php?id={$transaction.id}">#{$transaction.id}</a></td> <td rowspan="{$transaction.lines|count}">{$transaction.reference}</td> <td rowspan="{$transaction.lines|count}">{$transaction.date|date_short}</td> <th rowspan="{$transaction.lines|count}">{$transaction.label}</th> {foreach from=$transaction.lines item="line"} <td>{$line.account_code} - {$line.account_label}</td> <td class="money">{$line.debit|raw|html_money}</td> <td class="money">{$line.credit|raw|html_money}</td> <td>{$line.label}</td> |
︙ | ︙ |
Added src/templates/acc/reports/_statement.tpl version [2408ac0c3b].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <table class="statement"> <colgroup> <col width="50%" /> <col width="50%" /> </colgroup> <tbody> <tr> <td> {include file="acc/reports/_statement_table.tpl" accounts=$statement.expense caption=$caption1} </td> <td> {include file="acc/reports/_statement_table.tpl" accounts=$statement.revenue caption=$caption2} </td> </tr> </tbody> <tfoot> <tr> <td> <table> <tfoot> <tr> <th>Total</th> <td class="money">{$statement.expense_sum|raw|html_money:false}</td> </tr> </tfoot> </table> </td> <td> <table> <tfoot> <tr> <th>Total</th> <td class="money">{$statement.revenue_sum|raw|html_money:false}</td> </tr> </tfoot> </table> </td> </tr> {if $statement.result} <tr> <td> {if ($statement.result < 0)} <table> <tfoot> <tr> <th>Résultat (perte)</th> <td class="money">{$statement.result|raw|html_money:false}</td> </tr> </tfoot> </table> {/if} </td> <td> {if ($statement.result >= 0)} <table> <tfoot> <tr> <th>Résultat (excédent)</th> <td class="money">{$statement.result|raw|html_money:false}</td> </tr> </tfoot> </table> {/if} </td> </tr> {/if} </tfoot> </table> |
Modified src/templates/acc/reports/balance_sheet.tpl from [0249d23d75] to [a7a5928f43].
1 2 | {include file="admin/_head.tpl" title="Bilan" current="acc/years"} | | > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | {include file="admin/_head.tpl" title="Bilan" current="acc/years"} {include file="acc/reports/_header.tpl" current="balance_sheet" title="Bilan"} {if $asset_sum != $liability_sum} <p class="alert block"> <strong>Le bilan n'est pas équilibré !</strong><br /> Vérifiez que vous n'avez pas oublié de reporter des soldes depuis le précédent exercice. </p> {/if} <table class="statement"> <colgroup> <col width="50%" /> <col width="50%" /> </colgroup> <tbody> |
︙ | ︙ |
Modified src/templates/acc/reports/graphs.tpl from [526c29e562] to [f56b53d0dd].
1 2 | {include file="admin/_head.tpl" title="Graphiques" current="acc"} | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | {include file="admin/_head.tpl" title="Graphiques" current="acc"} {include file="acc/reports/_header.tpl" current="graphs" title="Graphiques"} <section class="year-infos"> <section class="graphs"> {foreach from=$graphs key="url" item="label"} <figure> <img src="{$url|args:$criterias_query}" alt="" /> <figcaption>{$label}</figcaption> </figure> {/foreach} </section> </section> {include file="admin/_foot.tpl"} |
Modified src/templates/acc/reports/journal.tpl from [5b585d401b] to [fd83a86c0b].
1 2 | {include file="admin/_head.tpl" title="Journal général" current="acc/years"} | | | 1 2 3 4 5 6 7 8 9 | {include file="admin/_head.tpl" title="Journal général" current="acc/years"} {include file="acc/reports/_header.tpl" current="journal" title="Journal général"} {include file="acc/reports/_journal.tpl"} <p class="help">Toutes les écritures sont libellées en {$config.monnaie}.</p> {include file="admin/_foot.tpl"} |
Modified src/templates/acc/reports/ledger.tpl from [58b1dc5251] to [a06ebc88ec].
1 2 | {include file="admin/_head.tpl" title="Grand livre" current="acc/years"} | | < < < < < < < < < | > | > | | | 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 | {include file="admin/_head.tpl" title="Grand livre" current="acc/years"} {include file="acc/reports/_header.tpl" current="ledger" title="Grand livre"} <div class="year-header noprint"> <button type="button" data-icon="↓" class="icn-btn" id="open_details">Déplier tous les comptes</button> <button type="button" data-icon="↑" class="icn-btn" id="close_details">Replier tous les comptes</button> </div> {foreach from=$ledger item="account"} <details open="open"> <summary><h2 class="ruler"><a href="{$admin_url}acc/accounts/journal.php?id={$account.id}&year={$account.id_year}">{$account.code} — {$account.label}</a></h2></summary> <table class="list"> <thead> <tr> <td></td> <td>N° pièce</td> <td>Réf. ligne</td> <td>Date</td> <th>Intitulé</th> <td class="money">Débit</td> <td class="money">Crédit</td> <td class="money">Solde</td> </tr> </thead> <tbody> {foreach from=$account.lines item="line"} <tr> <td class="num"><a href="{$admin_url}acc/transactions/details.php?id={$line.id}">#{$line.id}</a></td> <td>{$line.reference}</td> <td>{$line.line_reference}</td> <td>{$line.date|date_short}</td> <th>{$line.label}{if $line.line_label} <em>({$line.line_label})</em>{/if}</th> <td class="money">{$line.debit|raw|html_money}</td> <td class="money">{$line.credit|raw|html_money}</td> <td class="money">{$line.running_sum|raw|html_money:false}</td> </tr> {/foreach} </tbody> <tfoot> <tr> <td colspan="4"></td> <th>Solde final</th> <td class="money">{$account.debit|raw|html_money}</td> <td class="money">{$account.credit|raw|html_money}</td> <td class="money">{$account.sum|raw|html_money:false}</td> </tr> </tfoot> </table> |
︙ | ︙ |
Modified src/templates/acc/reports/projects.tpl from [a54424c23b] to [097c5f9f39].
︙ | ︙ | |||
11 12 13 14 15 16 17 | <li><a href="{$admin_url}acc/years/">Exercices</a></li> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} <li><a href="{$admin_url}acc/years/new.php">Nouvel exercice</a></li> {/if} <li class="current"><a href="{$admin_url}acc/reports/projects.php">Projets <em>(compta analytique)</em></a></li> </ul> | < < < > > > > > > > > | | > | 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 | <li><a href="{$admin_url}acc/years/">Exercices</a></li> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} <li><a href="{$admin_url}acc/years/new.php">Nouvel exercice</a></li> {/if} <li class="current"><a href="{$admin_url}acc/reports/projects.php">Projets <em>(compta analytique)</em></a></li> </ul> <ul class="sub"> <li{if !$by_year} class="current"{/if}><a href="{$self_url_no_qs}">Par projet</a></li> <li{if $by_year} class="current"{/if}><a href="{$self_url_no_qs}?by_year=1">Par exercice</a></li> </ul> </nav> <div class="year-header"> <h2>{$config.nom_asso} — Projets</h2> <p class="noprint print-btn"> <button onclick="window.print(); return false;" class="icn-btn" data-icon="⎙">Imprimer</button> </p> </div> {if !empty($list)} <table class="list projects"> <thead> <tr> <td>Année</td> <td></td> <td class="money">Débits</td> <td class="money">Crédits</td> <td class="money">Solde</td> </tr> </thead> {foreach from=$list item="parent"} <tbody> <tr class="title"> <th colspan="5"> <h2 class="ruler">{$parent.label}</h2> {if $parent.description}<p class="help">{$parent.description|escape|nl2br}</p>{/if} </th> </tr> {foreach from=$parent.items item="item"} <tr> <th>{$item.label}</th> <td> <span class="noprint"> |
︙ | ︙ |
Modified src/templates/acc/reports/statement.tpl from [8f97001d6f] to [176cb70b6d].
1 2 | {include file="admin/_head.tpl" title="Compte de résultat" current="acc/years"} | | < < < < < < < < | | < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < | < < < > | < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | {include file="admin/_head.tpl" title="Compte de résultat" current="acc/years"} {include file="acc/reports/_header.tpl" current="statement" title="Compte de résultat"} {include file="acc/reports/_statement.tpl" statement=$general caption1="Charges" caption2="Produits"} {if !empty($volunteering.expense_sum) || !empty($volunteering.revenue_sum)} <h2 class="ruler">Contributions en nature</h2> {include file="acc/reports/_statement.tpl" statement=$volunteering header=false caption1="Emplois des contributions volontaires en nature" caption2="Contributions volontaires en nature"} {/if} <p class="help">Toutes les écritures sont libellées en {$config.monnaie}.</p> {include file="admin/_foot.tpl"} |
Modified src/templates/acc/reports/trial_balance.tpl from [f08d97e86e] to [98a27d9282].
1 2 | {include file="admin/_head.tpl" title="Balance générale" current="acc/years"} | | | 1 2 3 4 5 6 7 8 9 10 | {include file="admin/_head.tpl" title="Balance générale" current="acc/years"} {include file="acc/reports/_header.tpl" current="trial_balance" title="Balance générale"} <table class="list"> <thead> <tr> <td>Numéro</td> <th>Compte</th> <td class="money">Total des débits</td> |
︙ | ︙ |
Modified src/templates/acc/search.tpl from [b1c54ffaff] to [1184e1ecfe].
︙ | ︙ | |||
26 27 28 29 30 31 32 | </tr> </thead> <tbody> {foreach from=$result item="row"} <tr> {*if $session->canAccess('membres', Membres::DROIT_ADMIN)}<td class="check"><input type="checkbox" name="selected[]" value="{$row.id}" /></td>{/if*} {foreach from=$row key="key" item="value"} | | > | > | 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 | </tr> </thead> <tbody> {foreach from=$result item="row"} <tr> {*if $session->canAccess('membres', Membres::DROIT_ADMIN)}<td class="check"><input type="checkbox" name="selected[]" value="{$row.id}" /></td>{/if*} {foreach from=$row key="key" item="value"} {if $key == 'transaction_id'} <td class="num"> <a href="{$admin_url}acc/transactions/details.php?id={$value}">{$value}</a> </td> {else} <td> {if $key == 'credit' || $key == 'debit'} {$value|raw|html_money:false} {elseif null == $value} <em>(nul)</em> {else} {$value} {/if} </td> {/if} {/foreach} <td class="actions"> {if $row.transaction_id} {linkbutton shape="search" label="Détails" href="!acc/transactions/details.php?id=%d"|args:$row.transaction_id} {/if} </td> </tr> {/foreach} </tbody> {*if $session->canAccess('membres', Membres::DROIT_ADMIN)} {include file="admin/membres/_list_actions.tpl" colspan=count($result_header)+1} {/if*} |
︙ | ︙ |
Modified src/templates/acc/transactions/edit.tpl from [e42938496f] to [4887135527].
︙ | ︙ | |||
50 51 52 53 54 55 56 | {/if} </fieldset> {/foreach} <fieldset> <legend>Détails facultatifs</legend> <dl data-types="t{$transaction::TYPE_REVENUE} t{$transaction::TYPE_EXPENSE} t{$transaction::TYPE_TRANSFER}"> | | | | 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 | {/if} </fieldset> {/foreach} <fieldset> <legend>Détails facultatifs</legend> <dl data-types="t{$transaction::TYPE_REVENUE} t{$transaction::TYPE_EXPENSE} t{$transaction::TYPE_TRANSFER}"> {input type="text" name="payment_reference" label="Référence de paiement" help="Numéro de chèque, numéro de transaction CB, etc." default=$first_line.reference} </dl> <dl> {input type="list" multiple=true name="users" label="Membres associés" target="membres/selector.php" default=$linked_users} {input type="textarea" name="notes" label="Remarques" rows=4 cols=30 source=$transaction} {input type="file" name="file" label="Ajouter un fichier joint"} </dl> <dl data-types="all-but-advanced"> {if count($analytical_accounts) > 1} {input type="select" name="id_analytical" label="Projet (compte analytique)" options=$analytical_accounts default=$first_line.id_analytical} {/if} </dl> </fieldset> <p class="submit"> {csrf_field key="acc_edit_%d"|args:$transaction.id} {button type="submit" name="save" label="Enregistrer" shape="right" class="main"} |
︙ | ︙ |
Modified src/templates/acc/years/index.tpl from [1a1526a67f] to [f4fb5ef736].
1 2 3 4 5 6 7 8 9 10 | {include file="admin/_head.tpl" title="Exercices" current="acc/years"} <nav class="tabs"> <ul> <li class="current"><a href="{$self_url}">Exercices</a></li> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} <li><a href="{$admin_url}acc/years/new.php">Nouvel exercice</a></li> {/if} <li><a href="{$admin_url}acc/reports/projects.php">Projets <em>(compta analytique)</em></a></li> </ul> | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | {include file="admin/_head.tpl" title="Exercices" current="acc/years"} <nav class="tabs"> <aside> {linkbutton shape="search" href="!acc/search.php" label="Recherche"} </aside> <ul> <li class="current"><a href="{$self_url}">Exercices</a></li> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} <li><a href="{$admin_url}acc/years/new.php">Nouvel exercice</a></li> {/if} <li><a href="{$admin_url}acc/reports/projects.php">Projets <em>(compta analytique)</em></a></li> </ul> |
︙ | ︙ | |||
29 30 31 32 33 34 35 | <figure> <img src="{$admin_url}acc/reports/graph_plot_all.php?type=result" alt="" /> </figure> </section> </section> {/if} | | > > | < < < | | | > > | | | | | | | > > > > | < | | | | | | | | | < | > > > | | 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 78 79 80 81 82 | <figure> <img src="{$admin_url}acc/reports/graph_plot_all.php?type=result" alt="" /> </figure> </section> </section> {/if} <table class="list"> {foreach from=$list item="year"} <tbody> <tr> <th><h3>{$year.label}</h3></th> <td>{$year.nb_transactions} écritures | <a href="../charts/accounts/?id={$year.id_chart}">Plan comptable</a></td> </tr> <tr> <td>{$year.start_date|date_short} au {$year.end_date|date_short}</td> <td> <a href="{$admin_url}acc/reports/graphs.php?year={$year.id}">Graphiques</a> | <a href="{$admin_url}acc/reports/trial_balance.php?year={$year.id}">Balance générale</a> | <a href="{$admin_url}acc/reports/journal.php?year={$year.id}">Journal général</a> | <a href="{$admin_url}acc/reports/ledger.php?year={$year.id}">Grand livre</a> | <a href="{$admin_url}acc/reports/statement.php?year={$year.id}">Compte de résultat</a> | <a href="{$admin_url}acc/reports/balance_sheet.php?year={$year.id}">Bilan</a> </td> </tr> <tr> <td><em>{if $year.closed}Clôturé{else}En cours{/if}</em></td> <td> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} {linkbutton label="Export CSV" shape="export" href="import.php?id=%d&export=csv"|args:$year.id} {linkbutton label="Export tableur" shape="export" href="import.php?id=%d&export=ods"|args:$year.id} {if !$year.closed} {linkbutton label="Import" shape="upload" href="import.php?id=%d"|args:$year.id} {linkbutton label="Balance d'ouverture" shape="reset" href="balance.php?id=%d"|args:$year.id} {linkbutton label="Modifier" shape="edit" href="edit.php?id=%d"|args:$year.id} {linkbutton label="Clôturer" shape="lock" href="close.php?id=%d"|args:$year.id} {linkbutton label="Supprimer" shape="delete" href="delete.php?id=%d"|args:$year.id} {/if} {/if} </td> </tr> </tbody> {/foreach} </table> {else} <p class="block alert"> Il n'y a pas d'exercice en cours. </p> {/if} {include file="admin/_foot.tpl"} |
Modified src/templates/admin/login.tpl from [2b0fb0f94e] to [5f99a8a020].
︙ | ︙ | |||
57 58 59 60 61 62 63 | <a href="{$admin_url}password.php">Pas de mot de passe ou mot de passe perdu ?</a> </p> </form> {literal} <script type="text/javascript"> | < | < > > | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | <a href="{$admin_url}password.php">Pas de mot de passe ou mot de passe perdu ?</a> </p> </form> {literal} <script type="text/javascript"> if (window.navigator.userAgent.match(/MSIE|Trident\/|Edge\//)) { document.getElementById('old_browser').style.display = 'block'; } g.enhancePasswordField($('#f_passe')); </script> {/literal} {include file="admin/_foot.tpl"} |
Modified src/templates/admin/membres/recherche.tpl from [9258528c92] to [5a5c80b31f].
︙ | ︙ | |||
10 11 12 13 14 15 16 | {/if} <p class="help">{$result|count} membres trouvés pour cette recherche.</p> <table class="list search"> <thead> <tr> {if $session->canAccess('membres', Membres::DROIT_ADMIN)}<td class="check"><input type="checkbox" value="Tout cocher / décocher" id="f_all" /><label for="f_all"></label></td>{/if} | | | | | | | | | | | | > > > | | | > | | 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 | {/if} <p class="help">{$result|count} membres trouvés pour cette recherche.</p> <table class="list search"> <thead> <tr> {if $session->canAccess('membres', Membres::DROIT_ADMIN)}<td class="check"><input type="checkbox" value="Tout cocher / décocher" id="f_all" /><label for="f_all"></label></td>{/if} {foreach from=$result_header item="label"} <td>{$label}</td> {/foreach} <td></td> </tr> </thead> <tbody> {foreach from=$result item="row"} <tr> {if $session->canAccess('membres', Membres::DROIT_ADMIN)}<td class="check">{if $row._user_id}{input type="checkbox" name="selected[]" value=$row._user_id}{/if}</td>{/if} {foreach from=$row key="key" item="value"} <?php $link = false; ?> {if isset($result_header[$key])} <td> {if !$link && $row._user_id} <a href="{$admin_url}membres/fiche.php?id={$row._user_id}"> {/if} {$value|raw|display_champ_membre:$key} {if !$link} <?php $link = true; ?> </a> {/if} </td> {elseif substr($key, 0, 1) != '_'} <td>{$value}</td> {/if} {/foreach} <td class="actions"> {if $row._user_id} {linkbutton shape="user" label="Fiche membre" href="!membres/fiche.php?id=%d"|args:$row._user_id} {if $session->canAccess('membres', Membres::DROIT_ECRITURE)} {linkbutton shape="edit" label="Modifier" href="!membres/modifier.php?id=%d"|args:$row._user_id} {/if} {/if} </td> </tr> {/foreach} </tbody> {if $session->canAccess('membres', Membres::DROIT_ADMIN) && $row._user_id} {include file="admin/membres/_list_actions.tpl" colspan=count($result_header)+1} {/if} </table> {if $session->canAccess('membres', Membres::DROIT_ECRITURE)} </form> {/if} |
︙ | ︙ |
Modified src/templates/common/search/advanced.tpl from [793be9f367] to [e6d27e3303].
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <?php assert(isset($columns)); assert(isset($action_url)); assert(isset($query)); assert(isset($is_admin)); $sql_disabled = !$is_admin || (!$session->canAccess('config', Membres::DROIT_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('config', Membres::DROIT_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}" /> {if $search.id} {button name="save" value=1 type="submit" label="Enregistrer : %s"|args:$search.intitule|truncate:40:"…":true shape="upload"} {else} {button name="save" value=1 type="submit" label="Enregistrer cette recherche" shape="upload"} {/if} </p> {elseif !$sql_disabled} <legend>Rechercher</legend> <div class="queryBuilder" id="queryBuilder"></div> <p class="actions"> <label>Trier par <select name="order"> {foreach from=$columns key="column" item="properties"} <option value="{$column}"{if $query.order == $column} selected="selected"{/if}>{$properties.label}</option> |
︙ | ︙ | |||
48 49 50 51 52 53 54 55 56 57 58 59 60 61 | {else} {button name="save" value=1 type="submit" label="Enregistrer cette recherche" shape="upload"} {/if} {if $is_admin} {button name="to_sql" value=1 type="submit" label="Recherche SQL" shape="edit"} {/if} </p> {/if} </fieldset> </form> <script type="text/javascript"> var columns = {$columns|escape:'json'}; | > > > | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | {else} {button name="save" value=1 type="submit" label="Enregistrer cette recherche" shape="upload"} {/if} {if $is_admin} {button name="to_sql" value=1 type="submit" label="Recherche SQL" shape="edit"} {/if} </p> {else} <legend>Recherche enregistrée</legend> <h3>{$search.intitule}</h3> {/if} </fieldset> </form> <script type="text/javascript"> var columns = {$columns|escape:'json'}; |
︙ | ︙ |
Modified src/templates/common/search/saved_searches.tpl from [22a975d3e5] to [4b50bfbcd9].
︙ | ︙ | |||
14 15 16 17 18 19 20 | {if $mode == 'edit'} {form_errors} <form method="post" action="{$self_url}"> <fieldset> <legend>Modifier une recherche enregistrée</legend> <dl> | < | > | | > | > > > > > > | 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 | {if $mode == 'edit'} {form_errors} <form method="post" action="{$self_url}"> <fieldset> <legend>Modifier une recherche enregistrée</legend> <dl> {input type="text" name="intitule" label="Intitulé" required=1 source=$recherche} <dt>Statut</dt> <?php $checked = (int)(bool)$recherche->id_membre; ?> {input type="radio" name="prive" value="1" default=$checked label="Recherche privée" help="Visible seulement par moi-même"} {input type="radio" name="prive" value="0" default=$checked label="Recherche publique" help="Visible et exécutable par tous les membres ayant accès à la gestion %s"|args:$target} <dt>Type</dt> <dd> {if $recherche.type == Recherche::TYPE_JSON} Avancée {elseif $recherche.type == Recherche::TYPE_SQL_UNPROTECTED} SQL non protégée {else} SQL {/if}</dd> <dt>Cible</dt> <dd>{$recherche.cible}</dd> </dl> </fieldset> <p class="submit"> {csrf_field key="edit_recherche_%s"|args:$recherche.id} |
︙ | ︙ |
Modified src/templates/services/save.tpl from [69201d6af6] to [76ea220052].
︙ | ︙ | |||
84 85 86 87 88 89 90 | {input type="date" name="expiry_date" default=$today label="Date d'expiration de l'inscription"} {input type="checkbox" name="paid" value="1" default="1" label="Marquer cette inscription comme payée"} <dd class="help">Décocher cette case pour pouvoir suivre les règlements de personnes qui payent en plusieurs fois. Il sera possible de cocher cette case lorsque le solde aura été réglé.</dd> </dl> </fieldset> <fieldset class="accounting"> | | > | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | {input type="date" name="expiry_date" default=$today label="Date d'expiration de l'inscription"} {input type="checkbox" name="paid" value="1" default="1" label="Marquer cette inscription comme payée"} <dd class="help">Décocher cette case pour pouvoir suivre les règlements de personnes qui payent en plusieurs fois. Il sera possible de cocher cette case lorsque le solde aura été réglé.</dd> </dl> </fieldset> <fieldset class="accounting"> <legend>{input type="checkbox" name="create_payment" value=1 default=1 label="Enregistrer en comptabilité"}</legend> <dl> {input type="money" name="amount" label="Montant réglé par le membre" fake_required=1 help="En cas de règlement en plusieurs fois il sera possible d'ajouter des règlements via la page de suivi des activités de ce membre."} {input type="list" target="acc/charts/accounts/selector.php?targets=%s"|args:$account_targets name="account" label="Compte de règlement" required=1} {input type="text" name="reference" label="Numéro de pièce comptable" help="Numéro de facture, de note de frais, etc."} {input type="text" name="payment_reference" label="Référence de paiement" help="Numéro de chèque, numéro de transaction CB, etc."} </dl> {/if} </fieldset> <p class="submit"> {csrf_field key=$csrf_key} |
︙ | ︙ | |||
149 150 151 152 153 154 155 156 157 158 159 | }); var selected = document.querySelector('input[name="id_service"]:checked, input[name="id_service"]'); selected.checked = true; g.toggle('.accounting', false); selectService(selected); </script> {/literal} {include file="admin/_foot.tpl"} | > > > > | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | }); var selected = document.querySelector('input[name="id_service"]:checked, input[name="id_service"]'); selected.checked = true; g.toggle('.accounting', false); selectService(selected); $('#f_create_payment_1').onchange = (e) => { g.toggle('.accounting dl', $('#f_create_payment_1').checked); }; </script> {/literal} {include file="admin/_foot.tpl"} |
Modified src/www/admin/acc/reports/graph_pie.php from [ca1e3741d3] to [f97272ab9f].
1 2 3 4 5 6 7 8 9 | <?php namespace Garradin; use Garradin\Accounting\Graph; require_once __DIR__ . '/_inc.php'; header('Content-Type: image/svg+xml'); | > > > > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php namespace Garradin; use Garradin\Accounting\Graph; require_once __DIR__ . '/_inc.php'; header('Content-Type: image/svg+xml'); $expiry = time() + 1800; $hash = sha1('pie_' . json_encode($criterias)); if (!Utils::HTTPCache($hash, $expiry)) { echo Graph::pie(qg('type'), $criterias); } |
Modified src/www/admin/acc/reports/graph_plot.php from [041f1bcddf] to [77ffd7aec0].
1 2 3 4 5 6 7 8 9 | <?php namespace Garradin; use Garradin\Accounting\Graph; require_once __DIR__ . '/_inc.php'; header('Content-Type: image/svg+xml'); | > > > > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php namespace Garradin; use Garradin\Accounting\Graph; require_once __DIR__ . '/_inc.php'; header('Content-Type: image/svg+xml'); $expiry = time() + 1800; $hash = sha1('plot_' . json_encode($criterias)); if (!Utils::HTTPCache($hash, $expiry)) { echo Graph::plot(qg('type'), $criterias); } |
Modified src/www/admin/acc/reports/graph_plot_all.php from [d2282ca501] to [80e43ce701].
1 2 3 4 5 6 7 8 9 10 11 | <?php namespace Garradin; use Garradin\Accounting\Graph; require_once __DIR__ . '/../_inc.php'; qv(['type' => 'string|required']); header('Content-Type: image/svg+xml'); | > > > > | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php namespace Garradin; use Garradin\Accounting\Graph; require_once __DIR__ . '/../_inc.php'; qv(['type' => 'string|required']); header('Content-Type: image/svg+xml'); $expiry = time() + 1800; $hash = sha1('plot_all'); if (!Utils::HTTPCache($hash, $expiry)) { echo Graph::plot(qg('type'), [], Graph::MONTHLY_INTERVAL, 600); } |
Modified src/www/admin/acc/reports/statement.php from [a66219b6ce] to [3be4e9af46].
1 2 3 4 5 6 7 8 9 | <?php namespace Garradin; use Garradin\Accounting\Reports; use Garradin\Entities\Accounting\Account; require_once __DIR__ . '/_inc.php'; | < < | < < | < < | < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace Garradin; use Garradin\Accounting\Reports; use Garradin\Entities\Accounting\Account; require_once __DIR__ . '/_inc.php'; $tpl->assign('general', Reports::getStatement($criterias + ['exclude_type' => Account::TYPE_VOLUNTEERING])); $tpl->assign('volunteering', Reports::getStatement($criterias + ['type' => Account::TYPE_VOLUNTEERING])); $tpl->display('acc/reports/statement.tpl'); |
Modified src/www/admin/acc/transactions/edit.php from [4ea18fe075] to [88e340bd1d].
︙ | ︙ | |||
77 78 79 80 81 82 83 84 85 86 87 88 89 90 | else { $lines = $transaction->getLinesWithAccounts(); foreach ($lines as $k => &$line) { $line->account = [$line->id_account => sprintf('%s — %s', $line->account_code, $line->account_name)]; } } if ($transaction->type != Transaction::TYPE_ADVANCED) { $types_accounts = $transaction->getTypesAccounts(); } $amount = $transaction->getLinesCreditSum(); | > > | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | else { $lines = $transaction->getLinesWithAccounts(); foreach ($lines as $k => &$line) { $line->account = [$line->id_account => sprintf('%s — %s', $line->account_code, $line->account_name)]; } } $first_line = $transaction->getFirstLine(); if ($transaction->type != Transaction::TYPE_ADVANCED) { $types_accounts = $transaction->getTypesAccounts(); } $amount = $transaction->getLinesCreditSum(); $tpl->assign(compact('transaction', 'lines', 'types_accounts', 'amount', 'first_line')); $tpl->assign('types_details', Transaction::getTypesDetails()); $tpl->assign('chart_id', $chart->id()); $tpl->assign('analytical_accounts', ['' => '-- Aucun'] + $accounts->listAnalytical()); $tpl->assign('linked_users', $transaction->listLinkedUsersAssoc()); $tpl->display('acc/transactions/edit.tpl'); |
Modified src/www/admin/acc/transactions/new.php from [7173ab8670] to [bb4740dd64].
︙ | ︙ | |||
80 81 82 83 84 85 86 | Utils::redirect(Utils::getSelfURL(false) . '?ok=' . $transaction->id()); } catch (UserException $e) { $form->addError($e->getMessage()); } } | > > | > > > > > > > > | 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 | Utils::redirect(Utils::getSelfURL(false) . '?ok=' . $transaction->id()); } catch (UserException $e) { $form->addError($e->getMessage()); } } $date = new \DateTime; if ($session->get('acc_last_date')) { $date = \DateTime::createFromFormat('!d/m/Y', $session->get('acc_last_date')); } if (!$date || ($date < $current_year->start_date || $date > $current_year->end_date)) { $date = $current_year->start_date; } $tpl->assign('date', $date->format('d/m/Y')); $tpl->assign(compact('transaction', 'payoff_for', 'amount', 'lines')); $tpl->assign('payoff_targets', implode(':', [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING])); $tpl->assign('ok', (int) qg('ok')); $tpl->assign('types_details', Transaction::getTypesDetails()); $tpl->assign('chart_id', $chart->id()); $tpl->assign('analytical_accounts', ['' => '-- Aucun'] + $accounts->listAnalytical()); $tpl->display('acc/transactions/new.tpl'); |
Modified src/www/admin/acc/years/balance.php from [1c5fd61cf1] to [19f637ea79].
︙ | ︙ | |||
16 17 18 19 20 21 22 | throw new UserException('Exercice inconnu.'); } if ($year->closed) { throw new UserException('Impossible de modifier un exercice clôturé.'); } | < < < < | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | throw new UserException('Exercice inconnu.'); } if ($year->closed) { throw new UserException('Impossible de modifier un exercice clôturé.'); } if (f('save') && $form->check('acc_years_balance_' . $year->id())) { try { $transaction = new Transaction; $transaction->importFromBalanceForm($year); $transaction->save(); |
︙ | ︙ | |||
41 42 43 44 45 46 47 | $previous_year = null; $chart_change = false; $lines = [[]]; $lines_accounts = [[]]; $years = Years::listClosed(); | > | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | $previous_year = null; $chart_change = false; $lines = [[]]; $lines_accounts = [[]]; $years = Years::listClosed(); // Empty balance if (!count($years) || f('from_year') === '') { $previous_year = 0; } elseif (null !== f('from_year')) { $previous_year = (int)f('from_year'); $previous_year = Years::get($previous_year); if (!$previous_year) { |
︙ | ︙ |
Modified src/www/admin/acc/years/index.php from [7a17ddf96c] to [9f9b364525].
1 2 3 4 5 6 7 8 9 | <?php namespace Garradin; use Garradin\Accounting\Years; require_once __DIR__ . '/../../_inc.php'; $session->requireAccess('compta', Membres::DROIT_ACCES); | < < | | 1 2 3 4 5 6 7 8 9 10 11 12 | <?php namespace Garradin; use Garradin\Accounting\Years; require_once __DIR__ . '/../../_inc.php'; $session->requireAccess('compta', Membres::DROIT_ACCES); $tpl->assign('list', Years::list(true)); $tpl->display('acc/years/index.tpl'); |
Modified src/www/admin/common/delete_file.php from [f9d6581182] to [dbcb7d7cc0].
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace Garradin; use Garradin\Services\Services; if (!defined('Garradin\ROOT')) { die('Access denied.'); } if (!isset($csrf_key, $redirect)) { throw new \InvalidArgumentException('Missing params'); } | > | > > > > | 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 | <?php namespace Garradin; use Garradin\Services\Services; if (!defined('Garradin\ROOT')) { die('Access denied.'); } if (!isset($csrf_key, $redirect)) { throw new \InvalidArgumentException('Missing params'); } try { $file = new Fichiers(qg('id')); } catch (\InvalidArgumentException $e) { throw new UserException($e->getMessage()); } if (!$file->checkAccess($session)) { throw new UserException('Vous n\'avez pas accès à ce fichier.'); } $form->runIf('delete', function () use ($file) { $file->remove(); }, $csrf_key, $redirect); $tpl->assign(compact('file', 'csrf_key')); $tpl->display('common/delete_file.tpl'); |
Modified src/www/admin/common/search.php from [19bc74c76e] to [f997b1b1c1].
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | $text_query = trim(qg('qt')); $result = null; $sql_query = null; $search = null; $id = f('id') ?: qg('id'); // Recherche simple if ($text_query !== '' && $target === 'membres' && empty($query->query)) { $query = $recherche->buildSimpleMemberQuery($text_query); } // Recherche existante elseif ($id && empty($query->query)) { $search = $recherche->get($id); if (!$search) { throw new UserException('Recherche inconnue ou invalide'); } | > > > | > > > > > > > > > > | | > | > > > > > > > | 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 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 | $text_query = trim(qg('qt')); $result = null; $sql_query = null; $search = null; $id = f('id') ?: qg('id'); $is_unprotected = false; // Recherche simple if ($text_query !== '' && $target === 'membres' && empty($query->query)) { $query = $recherche->buildSimpleMemberQuery($text_query); } // Recherche existante elseif ($id && empty($query->query)) { $search = $recherche->get($id); if (!$search) { throw new UserException('Recherche inconnue ou invalide'); } if ($search->type != Recherche::TYPE_JSON) { if ($search->type == Recherche::TYPE_SQL_UNPROTECTED) { $is_unprotected = true; } $sql_query = $search->contenu; } else { $query = $search->query; $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, Membres::DROIT_ADMIN); $sql_query = f('sql_query'); if ($session->canAccess('config', Membres::DROIT_ADMIN)) { $is_unprotected = (bool) f('unprotected'); } else { $is_unprotected = false; } } // Execute search if ($query->query || $sql_query) { try { if ($sql_query) { $sql = $sql_query; } else { $sql = $recherche->buildQuery($target, $query->query, $query->order, $query->desc, $query->limit); } $result = $recherche->searchSQL($target, $sql, null, false, $is_unprotected); } catch (UserException $e) { $form->addError($e->getMessage()); } if (f('to_sql')) { $sql_query = $sql; } } if (null !== $result) { if (count($result) == 1 && $text_query !== '' && $target === 'membres') { Utils::redirect(ADMIN_URL . 'membres/fiche.php?id=' . (int)$result[0]->_user_id); } if (f('save') && !$form->hasErrors()) { if (!$sql_query) { $type = Recherche::TYPE_JSON; } elseif ($is_unprotected) { $type = Recherche::TYPE_SQL_UNPROTECTED; } else { $type = Recherche::TYPE_SQL; } if ($id) { $recherche->edit($id, [ 'type' => $type, 'contenu' => $sql_query ?: $query, ]); } |
︙ | ︙ | |||
116 117 118 119 120 121 122 | ], ], ]]; $result = null; } elseif ($target === 'compta') { | | > > > > > | > > > > > > > > > > > > > > > > > | | 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 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | ], ], ]]; $result = null; } elseif ($target === 'compta') { // Default $query->query = [[ 'operator' => 'AND', 'conditions' => [ [ 'column' => 't.id_year', 'operator' => '= ?', 'values' => [(int)qg('year') ?: Years::getCurrentOpenYearId()], ], [ 'column' => 't.label', 'operator' => 'LIKE %?%', 'values' => '', ], [ 'column' => 't.reference', 'operator' => 'LIKE %?%', 'values' => '', ], ], ]]; if (null !== qg('type')) { $query->query[0]['conditions'][] = [ 'column' => 't.type', 'operator' => '= ?', 'values' => [(int)qg('type')], ]; } if (null !== qg('account')) { $query->query[0]['conditions'][] = [ 'column' => 'a.code', 'operator' => '= ?', 'values' => [qg('account')], ]; } $query->desc = true; $result = null; } $columns = $recherche->getColumns($target); $is_admin = $session->canAccess($target, Membres::DROIT_ADMIN); $schema = $recherche->schema($target); $tpl->assign(compact('query', 'sql_query', 'result', 'columns', 'is_admin', 'schema', 'search', 'target', 'is_unprotected')); if ($target == 'compta') { $tpl->display('acc/search.tpl'); } else { $tpl->display('admin/membres/recherche.tpl'); } |
Modified src/www/admin/membres/modifier.php from [6fe3b3ffab] to [a765c3047e].
︙ | ︙ | |||
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | 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', $cats->listSimple()); $tpl->assign('current_cat', f('id_categorie') ?: $membre->id_categorie); $tpl->assign('can_change_id', $session->canAccess('membres', Membres::DROIT_ADMIN)); $tpl->assign('membre', $membre); $tpl->display('admin/membres/modifier.tpl'); | > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | catch (UserException $e) { $form->addError($e->getMessage()); } } } $config = Config::getInstance(); $tpl->assign('id_field_name', $config->get('champ_identifiant')); $tpl->assign('passphrase', Utils::suggestPassword()); $tpl->assign('champs', $champs->getAll()); $tpl->assign('membres_cats', $cats->listSimple()); $tpl->assign('current_cat', f('id_categorie') ?: $membre->id_categorie); $tpl->assign('can_change_id', $session->canAccess('membres', Membres::DROIT_ADMIN)); $tpl->assign('membre', $membre); $tpl->display('admin/membres/modifier.tpl'); |
Modified src/www/admin/static/handheld.css from [02f3d81c1c] to [82c8dcdda9].
︙ | ︙ | |||
153 154 155 156 157 158 159 | width: auto; } dl.describe { margin: 0 .5em; } | < < < < < < < < < < < < < < | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | width: auto; } dl.describe { margin: 0 .5em; } fieldset { border-left: none; border-right: none; } .shortFormRight, .shortFormLeft { float: none; |
︙ | ︙ | |||
193 194 195 196 197 198 199 | display: inline-block; border-left: 2px dashed #999; width: auto !important; } colgroup { /* Hack pour désactiver les largeurs de colonnes */ | < < < < | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | display: inline-block; border-left: 2px dashed #999; width: auto !important; } colgroup { /* Hack pour désactiver les largeurs de colonnes */ display: none; } .radio-btn input { position: absolute; right: 1em; } |
︙ | ︙ | |||
262 263 264 265 266 267 268 269 270 | dl.list { text-align: center; } table.statement td, table.statement tr { display: block; } dl.describe { | > > > > > > > > > | > > > > > > > | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | dl.list { text-align: center; } table.statement td, table.statement tr { display: block; } aside.describe { float: none; margin: 1em auto; } dl.describe { display: block; margin: 1em; text-align: left; } dl.describe > dt { text-align: unset; } dl.describe > dd { font-size: 1.2em; margin-bottom: .8em; } .datepicker-parent { position: static; } dialog.datepicker { margin: auto; } } |
Modified src/www/admin/static/scripts/datepicker2.js from [180ac8cc87] to [6f612d2aa2].
︙ | ︙ | |||
14 15 16 17 18 19 20 | this.button = button; this.input = input; this.date = null; Object.assign(this, { format: 0, // 0 = Y-m-d, 1 = d/m/Y lang: 'fr', | | > | > | 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 | this.button = button; this.input = input; this.date = null; Object.assign(this, { format: 0, // 0 = Y-m-d, 1 = d/m/Y lang: 'fr', class: 'datepicker', onchange: null }, config); var c = document.createElement('dialog'); c.className = this.class; this.container = button.parentNode.insertBefore(c, button.nextSibling); button.onclick = () => { this.container.hasAttribute('open') ? this.close() : this.open() }; } open() { var d = this.input ? this.input.value : ''; if (d == '') { d = new CalendarDate; } else if (this.format == 1) { d = d.split('/'); d = new CalendarDate(d[2], d[1] - 1, d[0]); } |
︙ | ︙ | |||
173 174 175 176 177 178 179 | this.date.setDate(parseInt(e.target.textContent, 10)); } var y = this.date.getFullYear(), m = ('0' + (this.date.getMonth() + 1)).substr(-2), d = ('0' + this.date.getDate()).substr(-2); | | > > > > > > > > > | 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 | this.date.setDate(parseInt(e.target.textContent, 10)); } var y = this.date.getFullYear(), m = ('0' + (this.date.getMonth() + 1)).substr(-2), d = ('0' + this.date.getDate()).substr(-2); let v = this.format == 1 ? d + '/' + m + '/' + y : y + '-' + m + '-' + d; if (this.input) { this.input.value = v; } this.close(); if (this.onchange) { this.onchange(v, this); } } focus() { this.container.querySelectorAll('tbody td').forEach((cell) => { var v = parseInt(cell.innerHTML, 10); |
︙ | ︙ |
Modified src/www/admin/static/scripts/global.js from [054e0148e7] to [a4e04fc8bc].
︙ | ︙ | |||
102 103 104 105 106 107 108 | } g.dialog = document.createElement('dialog'); g.dialog.id = 'dialog'; g.dialog.open = true; var btn = document.createElement('button'); | | > > > > | | > > > > | 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 140 141 142 143 144 145 146 | } g.dialog = document.createElement('dialog'); g.dialog.id = 'dialog'; g.dialog.open = true; var btn = document.createElement('button'); btn.className = 'icn-btn closeBtn'; btn.setAttribute('data-icon', '✘'); btn.type = 'button'; btn.innerHTML = 'Fermer'; btn.onclick = g.closeDialog; g.dialog.appendChild(btn); if (typeof content == 'string') { var container = document.createElement('div'); container.innerHTML = content; content = container; } else if (content instanceof DocumentFragment) { var container = document.createElement('div'); container.appendChild(content.cloneNode(true)); content = container; } g.dialog.appendChild(content); content.style.opacity = g.dialog.style.opacity = 0; g.dialog.onclick = (e) => { if (e.target == g.dialog) g.closeDialog(); }; window.onkeyup = (e) => { if (e.key == 'Escape') g.closeDialog(); }; document.body.appendChild(g.dialog); // Restore CSS defaults window.setTimeout(() => { g.dialog.style.opacity = ''; }, 50); window.setTimeout(() => { content.style.opacity = ''; }, 100); return content; } g.openFrameDialog = function (url) { var iframe = document.createElement('iframe'); iframe.src = url; iframe.name = 'dialog'; iframe.frameborder = '0'; |
︙ | ︙ |
Modified src/www/admin/static/scripts/wiki-encryption.js from [2ac75e40c2] to [2f0eae2745].
︙ | ︙ | |||
60 61 62 63 64 65 66 | // nl2br content = content.replace(/\r/g, '').replace(/\n/g, '<br />'); return content; } | | | | | | | > | > > > > > > | | 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 140 141 142 143 144 | // nl2br content = content.replace(/\r/g, '').replace(/\n/g, '<br />'); return content; } window.wikiDecrypt = function () { loadAESlib(); encryptPassword = window.prompt('Mot de passe ?'); if (!encryptPassword) { encryptPassword = null; if (document.getElementById('f_contenu')) { if (window.confirm("Aucun mot de passe entré.\nDésactiver le chiffrement et effacer le contenu ?")) { document.getElementById('f_contenu').value = ''; document.getElementById('f_chiffrement').checked = false; checkEncryption(document.getElementById('f_chiffrement')); } else { wikiDecrypt(); } } return; } iteration = 0; decrypt(); }; var decrypt = function () { if (typeof GibberishAES == 'undefined') { if (iteration >= 10) { iteration = 0; encryptPassword = null; window.alert("Impossible de charger la bibliothèque AES, empêchant le déchiffrement de la page.\nAttendez quelques instants avant de recommencer ou rechargez la page."); return; } iteration++; window.setTimeout(decrypt, 500); return; } var content = document.getElementById('f_contenu'); var edit = true; if (!content) { content = document.getElementById('wikiEncryptedContent'); edit = false; } var wikiContent = content.value || content.innerText; wikiContent = wikiContent.replace(/\s+/g, ''); try { wikiContent = GibberishAES.dec(wikiContent, encryptPassword); } catch (e) { encryptPassword = null; window.alert('Impossible de déchiffrer. Mauvais mot de passe ?'); if (edit) { // Redemander le mot de passe wikiDecrypt(); } return false; } if (!edit) { content.style.display = 'block'; |
︙ | ︙ |
Deleted src/www/admin/static/scripts/wiki_editor.css version [30c3c98262].
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/www/admin/static/scripts/wiki_editor.js version [2d8c9cc5c3].
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Modified src/www/admin/static/styles/03-forms.css from [c5b73006cf] to [cb1bad2db4].
︙ | ︙ | |||
137 138 139 140 141 142 143 144 145 146 147 148 149 150 | input:hover + label::before { color: rgb(var(--gSecondColor)); } input:checked + label::before { text-shadow: 1px 1px 5px #ff9; } input:focus, button:focus, select:focus, textarea:focus, input[type=radio]:focus + label::before, input[type=checkbox]:focus + label::before { box-shadow: 0 0 5px .2rem rgb(var(--gSecondColor)); outline: 0; } /* buttons */ | > > > > > | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | input:hover + label::before { color: rgb(var(--gSecondColor)); } input:checked + label::before { text-shadow: 1px 1px 5px #ff9; } #queryBuilder input[type=checkbox] { position: unset; opacity: unset; } input:focus, button:focus, select:focus, textarea:focus, input[type=radio]:focus + label::before, input[type=checkbox]:focus + label::before { box-shadow: 0 0 5px .2rem rgb(var(--gSecondColor)); outline: 0; } /* buttons */ |
︙ | ︙ | |||
231 232 233 234 235 236 237 238 239 240 241 242 243 244 | } input[disabled]:hover, input[readonly]:hover { background-color: unset; color: unset; border-color: unset; } select, input[size], input[type=color], button, input[type=button], input[type=submit], input[type=number] { min-width: 0; } /* Radio button lists (eg. new transaction) */ form .radio-btn { | > > > > > > > > > | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | } input[disabled]:hover, input[readonly]:hover { background-color: unset; color: unset; border-color: unset; } input[disabled] + label { color: #666; } input[disabled] + label::before { color: #999; cursor: not-allowed; } select, input[size], input[type=color], button, input[type=button], input[type=submit], input[type=number] { min-width: 0; } /* Radio button lists (eg. new transaction) */ form .radio-btn { |
︙ | ︙ |
Modified src/www/admin/static/styles/04-dialogs.css from [9ca4a3ffe3] to [9ade8c8089].
︙ | ︙ | |||
40 41 42 43 44 45 46 | #dialog > iframe { width: 90%; height: 90%; border: none; box-shadow: 0px 0px 5px #000; } | | | > > > > > > | 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 | #dialog > iframe { width: 90%; height: 90%; border: none; box-shadow: 0px 0px 5px #000; } #dialog > button.closeBtn { background: unset; border: unset; box-shadow: unset; color: #fff; font-size: 1.3em; display: block; width: 90%; } #dialog > button.closeBtn:hover { color: #999 !important; } .loader { width: 100%; min-height: 32px; display: block; position: relative; } |
︙ | ︙ |
Modified src/www/admin/static/styles/10-accounting.css from [2779caf5e1] to [c404c68c91].
︙ | ︙ | |||
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 108 | 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; } |
Added tools/categories_as_projects.sh version [02cc9257ed].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/bin/bash # Ce script permet de convertir les anciennes catégories en projets # et d'affecter ces projets aux écritures. # # - Création de nouveaux comptes de projets # - Affectation des lignes des écritures à ces nouvelles écritures # # Le premier argument doit être l'ancienne base de données (version 0.9.8) # Le second argument doit être la nouvelle base de données (1.0) # # Évidemment ça ne marche que si la BDD 1.0 est une mise à jour de la BDD de la 0.9.8 ! # Sinon ça sera tout mélangé ! if [ ! -f "$1" ] || [ ! -f "$2" ]; then echo "Usage: $0 OLD_DATABASE NEW_DATABASE" exit 1 fi sqlite3 "$1" <<EOF CREATE TEMP TABLE projects_categories (id, code, label, description); INSERT INTO projects_categories SELECT id, NULL, intitule, description FROM compta_categories; UPDATE projects_categories SET code = printf('99%03d', rowid); --SELECT code, label FROM projects_categories; --SELECT id, (SELECT code FROM projects_categories WHERE id = id_categorie) FROM compta_journal WHERE id_categorie IS NOT NULL; CREATE TEMP TABLE projects_transactions (id, code, account_id); INSERT INTO projects_transactions SELECT id, (SELECT code FROM projects_categories WHERE id = id_categorie), NULL FROM compta_journal WHERE id_categorie IS NOT NULL; ATTACH '${2}' AS new; BEGIN; INSERT INTO new.acc_accounts (id_chart, code, label, description, position, type, user) SELECT (SELECT id FROM acc_charts WHERE code = 'PCGA1999'), code, label, description, 0, 7, -- type 1 FROM projects_categories; UPDATE projects_transactions AS t SET account_id = (SELECT id FROM new.acc_accounts a WHERE a.code = t.code); UPDATE new.acc_transactions_lines AS l SET id_analytical = (SELECT account_id FROM projects_transactions t WHERE t.id = l.id_transaction); COMMIT; EOF |