Comment: | Implementation start of graphs |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA1: |
9b0d5d20d0f957ee1cfb7d75ad8efbed |
User & Date: | bohwaz on 2020-10-16 01:13:49 |
Other Links: | branch diff | manifest | tags |
2020-10-16
| ||
14:56 | Functional plot graphs for accounting check-in: d7e6950bd8 user: bohwaz tags: dev | |
01:13 | Implementation start of graphs check-in: 9b0d5d20d0 user: bohwaz tags: dev | |
2020-10-15
| ||
14:39 | Implement: quick deposit of cheques check-in: 2b6dc32a67 user: bohwaz tags: dev | |
Added src/include/lib/Garradin/Accounting/Graph.php version [7b8df913cf].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?php namespace Garradin\Accounting; use Garradin\Entities\Accounting\Account; use Garradin\Entities\Accounting\Line; use Garradin\Entities\Accounting\Transaction; use Garradin\Utils; use Garradin\DB; use KD2\DB\EntityManager; use KD2\Graphics\SVG\Plot; use KD2\Graphics\SVG\Plot_Data; class Graph { const PLOT_LINES = [ 'assets' => [ 'Banques' => ['type' => Account::TYPE_BANK], 'Caisses' => ['type' => Account::TYPE_CASH], 'En attente' => ['type' => Account::TYPE_OUTSTANDING], ], 'result' => [ 'Recettes' => ['position' => Account::REVENUE], 'Dépenses' => ['position' => Account::EXPENSE], ], ]; const PLOT_INTERVAL = 604800; // 7 days static public function plot(string $type, array $criterias) { if (!array_key_exists($type, self::PLOT_LINES)) { throw new \InvalidArgumentException('Unknown type'); } $plot = new Plot(400, 300); $lines = self::PLOT_LINES[$type]; $data = []; foreach ($lines as $label => $line_criterias) { $line_criterias = array_merge($criterias, $line_criterias); $graph = new Plot_Data(Reports::getSumsByInterval($line_criterias, self::PLOT_INTERVAL)); $graph->title = $label; $data[] = $graph; } if (count($data)) { /* $labels = []; foreach ($data[0]->get() as $k=>$v) { $labels[] = $k; } $plot->setLabels($labels); */ $i = 0; $colors = ['#c71', '#941', '#fa4', '#fd9', '#ffc', '#cc9']; foreach ($data as $line) { $line->color = $colors[$i++]; $line->width = 2; $plot->add($line); if ($i >= count($colors)) $i = 0; } } return $plot->output(); } } |
Modified src/include/lib/Garradin/Accounting/Reports.php from [11a0817a06] to [d30f5b8640].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php namespace Garradin\Accounting; use Garradin\Entities\Accounting\Account; use Garradin\Entities\Accounting\Line; use Garradin\Entities\Accounting\Transaction; use Garradin\Utils; use Garradin\DB; use KD2\DB\EntityManager; class Reports { static public function getResult(array $criterias): int { $where = self::getWhereClause($criterias); $sql = sprintf('SELECT SUM(l.credit) - SUM(l.debit) FROM %s l INNER JOIN %s t ON t.id = l.id_transaction INNER JOIN %s a ON a.id = l.id_account | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?php namespace Garradin\Accounting; use Garradin\Entities\Accounting\Account; use Garradin\Entities\Accounting\Line; use Garradin\Entities\Accounting\Transaction; use Garradin\Utils; use Garradin\DB; use KD2\DB\EntityManager; class Reports { static public function getWhereClause(array $criterias): string { $where = []; if (!empty($criterias['year'])) { $where[] = sprintf('t.id_year = %d', $criterias['year']); } if (!empty($criterias['position'])) { $db = DB::getInstance(); $where[] = $db->where('position', $criterias['position']); } if (!empty($criterias['type'])) { $db = DB::getInstance(); $criterias['type'] = array_map('intval', (array)$criterias['type']); $where[] = sprintf('a.type IN (%s)', implode(',', $criterias['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']); } if (!count($where)) { throw new \LogicException('Unknown criteria'); } return implode(' AND ', $where); } static public function getSumsByInterval(array $criterias, int $interval) { $where = self::getWhereClause($criterias); $db = DB::getInstance(); $sql = sprintf('SELECT strftime(\'%%s\', MIN(date)) / %d AS start_interval, strftime(\'%%s\', MAX(date)) / %1$d AS end_interval FROM acc_transactions WHERE id_year = %d;', $interval, $criterias['year']); extract((array)$db->first($sql)); $out = array_fill_keys(range($start_interval, $end_interval), 0); $sql = sprintf('SELECT strftime(\'%%s\', t.date) / %d AS interval, SUM(l.credit) - SUM(l.debit) FROM acc_transactions t INNER JOIN acc_transactions_lines l ON l.id_transaction = t.id INNER JOIN acc_accounts a ON a.id = l.id_account WHERE %s GROUP BY interval;', $interval, $where); $data = $db->getAssoc($sql); $sum = 0; foreach ($out as $k => &$v) { if (array_key_exists($k, $data)) { $sum += $v; } $v = $sum; } unset($v); return $out; } static public function getResult(array $criterias): int { $where = self::getWhereClause($criterias); $sql = sprintf('SELECT SUM(l.credit) - SUM(l.debit) FROM %s l INNER JOIN %s t ON t.id = l.id_transaction INNER JOIN %s a ON a.id = l.id_account |
︙ | ︙ | |||
156 157 158 159 160 161 162 | if (null !== $group) { yield $group; } } static public function getClosingSums(int $year_id): array { | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | if (null !== $group) { yield $group; } } static public function getClosingSums(int $year_id): array { // Find sums, link them to accounts $sql = sprintf('SELECT l.id_account, SUM(l.credit) - SUM(l.debit) FROM %s l INNER JOIN %s t ON t.id = l.id_transaction WHERE t.id_year = %d GROUP BY l.id_account;', Line::TABLE, Transaction::TABLE, $year_id); return DB::getInstance()->getAssoc($sql); } /** * Grand livre */ static public function getGeneralLedger(array $criterias): \Generator { $where = self::getWhereClause($criterias); |
︙ | ︙ |
Modified src/templates/acc/charts/import.tpl from [52b086fe03] to [8e3108b717].
|
| | > | 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 | {include file="admin/_head.tpl" title="Importer un nouveau plan comptable" current="acc/charts"} {form_errors} <form method="post" action="{$self_url}" enctype="multipart/form-data"> <fieldset> <legend>Importer un plan comptable</legend> <dl> {input type="text" name="label" label="Libellé" required=1} {input type="select" name="country" label="Pays" required=1 options=$country_list default=$config.pays} {input type="file" name="file" label="Fichier CSV" accept=".csv,text/csv" required=1} <dd class="help"> Règles à suivre pour créer le fichier CSV : <ul> <li>Il est recommandé d'utiliser LibreOffice pour créer le fichier CSV</li> <li>Le fichier doit être en UTF-8</li> <li>Le séparateur doit être le point-virgule ou la virgule</li> <li>Cocher l'option <em>"Mettre en guillemets toutes les cellules du texte"</em></li> <li>Le fichier doit comporter les colonnes suivantes : <em>{$columns}</em></li> <li>Pour obtenir un exemple du format attendu, faire un export d'un exercice existant</li> </ul> </dd> </dl> <p class="submit"> {csrf_field key="acc_charts_import"} <input type="submit" name="import" value="Importer →" /> </p> </fieldset> </form> {include file="admin/_foot.tpl"} |
Added src/templates/acc/index.tpl version [f4381d456e].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | {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_fr:'d/m/Y'} au {$year.end_date|date_fr:'d/m/Y'}</h2> <nav class="tabs"> <ul> <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/trial_balance.php?year={$year.id}">Balance générale</a></li> <li><a href="{$admin_url}acc/reports/statement.php?year={$year.id}">Compte de résultat</a></li> <li><a href="{$admin_url}acc/reports/balance_sheet.php?year={$year.id}">Bilan</a></li> <li><a href="{$admin_url}acc/transactions/search.php?year={$year.id}"><strong>Recherche</strong></a></li> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} <li><a href="{$admin_url}acc/years/import.php?id={$year.id}">Import / export</a></li> {/if} </ul> </nav> <section class="graphs"> {foreach from=$graphs key="url" item="label"} <figure> <img src="{$url|args:$year.id}" alt="" /> <figcaption>{$label}</figcaption> </figure> {/foreach} </section> </section> {foreachelse} <p class="alert"> Il n'y a aucun exercice ouvert en cours.<br /> {linkbutton label="Ouvrir un nouvel exercice" shape="plus" href="acc/years/new.php"} </p> {/foreach} {include file="admin/_foot.tpl"} |
Modified src/templates/acc/transactions/edit.tpl from [4ce3d15d01] to [a9ed311f13].
︙ | ︙ | |||
15 16 17 18 19 20 21 | <fieldset data-types="advanced"> {include file="acc/transactions/_lines_form.tpl" chart_id=$chart.id} </fieldset> <fieldset> <legend>Détails</legend> <dl> | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <fieldset data-types="advanced"> {include file="acc/transactions/_lines_form.tpl" chart_id=$chart.id} </fieldset> <fieldset> <legend>Détails</legend> <dl> {input default=$linked_users type="list" multiple=true name="users" label="Membres associés" target="membres/selector.php"} {input source=$transaction type="text" name="reference" label="Numéro de pièce comptable"} {input source=$transaction type="textarea" name="notes" label="Remarques" rows=4 cols=30} {input source=$transaction type="file" name="file" label="Ajouter un fichier joint"} </dl> </fieldset> |
︙ | ︙ |
Modified src/templates/acc/transactions/new.tpl from [1b39cbd3bf] to [93885bdfd4].
︙ | ︙ | |||
16 17 18 19 20 21 22 | <input type="hidden" name="type" value="{$transaction::TYPE_PAYOFF}" /> <input type="hidden" name="payoff_for" value="{$payoff_for.id}" /> <fieldset> <legend>{if $payoff_for->type == $transaction::TYPE_DEBT}Règlement de dette{else}Règlement de créance{/if}</legend> <dl> <dt>Écriture d'origine</dt> <dd><a class="num" href="{$admin_url}acc/transactions/details.php?id={$payoff_for.id}">#{$payoff_for.id}</a></dd> | | | | 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 | <input type="hidden" name="type" value="{$transaction::TYPE_PAYOFF}" /> <input type="hidden" name="payoff_for" value="{$payoff_for.id}" /> <fieldset> <legend>{if $payoff_for->type == $transaction::TYPE_DEBT}Règlement de dette{else}Règlement de créance{/if}</legend> <dl> <dt>Écriture d'origine</dt> <dd><a class="num" href="{$admin_url}acc/transactions/details.php?id={$payoff_for.id}">#{$payoff_for.id}</a></dd> {input type="list" target="acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:$payoff_targets,$chart_id name="account_payoff" label="Compte de règlement" required=1} </dl> </fieldset> {else} <fieldset> <legend>Type d'écriture</legend> <dl> {input type="radio" name="type" value=$transaction::TYPE_REVENUE default=-1 label="Recette"} {input type="radio" name="type" value=$transaction::TYPE_EXPENSE default=-1 label="Dépense"} {input type="radio" name="type" value=$transaction::TYPE_TRANSFER default=-1 label="Virement" help="Faire un virement entre comptes, déposer des espèces en banque, etc."} {input type="radio" name="type" value=$transaction::TYPE_DEBT default=-1 label="Dette" help="Quand l'association doit de l'argent à un membre ou un fournisseur"} {input type="radio" name="type" value=$transaction::TYPE_CREDIT default=-1 label="Créance" help="Quand un membre ou un fournisseur doit de l'argent à l'association"} {input type="radio" name="type" value=$transaction::TYPE_ADVANCED default=-1 label="Saisie avancée" help="Choisir les comptes du plan comptable, ventiler une écriture sur plusieurs comptes, etc."} </dl> </fieldset> {foreach from=$types item="type"} <fieldset data-types="t{$type.id}"> <legend>{$type.label}</legend> <dl> {foreach from=$type.accounts key="key" item="account"} {input type="list" target="acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:$account.targets,$chart_id name="account_%d_%d"|args:$type.id,$key label=$account.label required=1} {/foreach} </dl> </fieldset> {/foreach} {/if} <fieldset> |
︙ | ︙ | |||
64 65 66 67 68 69 70 | <fieldset data-types="t{$transaction::TYPE_ADVANCED}"> {include file="acc/transactions/_lines_form.tpl" chart_id=$current_year.id_chart} </fieldset> <fieldset> <legend>Détails</legend> <dl> | | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <fieldset data-types="t{$transaction::TYPE_ADVANCED}"> {include file="acc/transactions/_lines_form.tpl" chart_id=$current_year.id_chart} </fieldset> <fieldset> <legend>Détails</legend> <dl> {input type="list" multiple=true name="users" label="Membres associés" target="membres/selector.php"} {input type="text" name="reference" label="Numéro de pièce comptable"} {input type="textarea" name="notes" label="Remarques" rows=4 cols=30} {input type="file" name="file" label="Fichier joint"} </dl> <dl data-types="all-but-advanced"> {if count($analytical_accounts) > 0} |
︙ | ︙ |
Modified src/templates/acc/years/balance.tpl from [5aa8181646] to [221b5a0e74].
︙ | ︙ | |||
39 40 41 42 43 44 45 | <td></td> </tr> </thead> <tbody> {foreach from=$lines key="k" item="line"} <tr> <th> | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <td></td> </tr> </thead> <tbody> {foreach from=$lines key="k" item="line"} <tr> <th> {input type="list" target="acc/accounts/selector.php" name="lines[account][]" default=$line.account_selected} </th> <td>{input type="money" name="lines[debit][]" default=$line.debit size=5}</td> <td>{input type="money" name="lines[credit][]" default=$line.credit size=5}</td> <td>{button label="Enlever la ligne" shape="minus" min="1" name="remove_line"}</td> </tr> {/foreach} </tbody> |
︙ | ︙ |
Modified src/templates/acc/years/new.tpl from [cb76d08b57] to [d4e14f356b].
︙ | ︙ | |||
13 14 15 16 17 18 19 | <dd class="help">{linkbutton shape="settings" label="Gestion des plans comptables" href="acc/charts/"}</dd> {input type="text" name="label" label="Libellé" required=true} {input type="date" label="Début de l'exercice" name="start_date" required=true default=$start_date} {input type="date" label="Fin de l'exercice" name="end_date" required=true default=$end_date} </dl> </fieldset> | < < < | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <dd class="help">{linkbutton shape="settings" label="Gestion des plans comptables" href="acc/charts/"}</dd> {input type="text" name="label" label="Libellé" required=true} {input type="date" label="Début de l'exercice" name="start_date" required=true default=$start_date} {input type="date" label="Fin de l'exercice" name="end_date" required=true default=$end_date} </dl> </fieldset> <p class="submit"> {csrf_field key="acc_years_new"} <input type="submit" name="new" value="Créer ce nouvel exercice →" /> </p> </form> {include file="admin/_foot.tpl"} |
Added src/www/admin/acc/index.php version [c755556632].
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?php namespace Garradin; use Garradin\Accounting\Years; require_once __DIR__ . '/../_inc.php'; $session->requireAccess('compta', Membres::DROIT_ACCES); $years = new Years; $graphs = [ ADMIN_URL . 'acc/reports/graph_plot.php?type=assets&year=%s' => 'Évolution banques et caisses', ADMIN_URL . 'acc/reports/graph_plot.php?type=result&year=%s' => 'Évolution dépenses et recettes', ADMIN_URL . 'acc/reports/graph_plot.php?type=debts&year=%s' => 'Évolution dettes et créances', ADMIN_URL . 'acc/reports/pie.php?type=revenue&year=%s' => 'Répartition recettes', ADMIN_URL . 'acc/reports/pie.php?type=expense&year=%s' => 'Répartition dépenses', ADMIN_URL . 'acc/reports/pie.php?type=assets&year=%s' => 'Répartition actif', ]; $tpl->assign('graphs', $graphs); $tpl->assign('years', $years->listOpen()); $tpl->display('acc/index.tpl'); |
Added src/www/admin/acc/reports/graph_plot.php version [041f1bcddf].
> > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 | <?php namespace Garradin; use Garradin\Accounting\Graph; require_once __DIR__ . '/_inc.php'; header('Content-Type: image/svg+xml'); echo Graph::plot(qg('type'), $criterias); |
Modified src/www/admin/static/admin.css from [60a498a26a] to [0e3671dfc6].
︙ | ︙ | |||
322 323 324 325 326 327 328 | margin: .5em 0 .5em 1.2em; } input[type=radio], input[type=checkbox] { cursor: pointer; } | < | | 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | margin: .5em 0 .5em 1.2em; } input[type=radio], input[type=checkbox] { cursor: pointer; } input:not([type=checkbox]):not([type=radio]):not([type=submit]), textarea, select, .input-list { padding: .4rem .6rem; font-family: inherit; min-width: 20em; max-width: 100%; border: 1px solid #999; font-size: inherit; background: #fff; |
︙ | ︙ | |||
379 380 381 382 383 384 385 | margin-top: .1em; } input[type=button].showPassword:hover { background: none; } | | | | 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | margin-top: .1em; } input[type=button].showPassword:hover { background: none; } select, input[size], input[type=color], button, input[type=button], input[type=submit] { min-width: 0 !important; } form .input-list { padding: 0; display: inline-flex; align-items: center; justify-content: start; |
︙ | ︙ |