Comment: | Merge with trunk |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | blocks |
Files: | files | file ages | folders |
SHA3-256: |
56711fffde762852aeeb116693076a99 |
User & Date: | bohwaz on 2022-05-02 11:56:09 |
Other Links: | branch diff | manifest | tags |
2022-05-03
| ||
21:53 | Hide blocks for now check-in: d83accac28 user: bohwaz tags: blocks | |
2022-05-02
| ||
11:56 | Merge with trunk check-in: 56711fffde user: bohwaz tags: blocks | |
11:44 | Fix opening balance automatic balancing check-in: 1ca75d8773 user: bohwaz tags: trunk, stable | |
2022-04-26
| ||
02:20 | Move menu bar to bottom of screen on mobile, improve HTML for menu for navigation with text browser and accessibility check-in: 6152ca9264 user: bohwaz tags: blocks | |
Added src/include/lib/Garradin/Accounting/AssistedReconciliation.php version [53fcb114af].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | <?php namespace Garradin\Accounting; use Garradin\CSV_Custom; use Garradin\UserException; use Garradin\Utils; use Garradin\Membres\Session; use Garradin\Entities\Accounting\Transaction; /** * Provides assisted reconciliation */ class AssistedReconciliation { const COLUMNS = [ 'label' => 'Libellé', 'date' => 'Date', //'notes' => 'Remarques', //'reference' => 'Numéro pièce comptable', //'p_reference' => 'Référence paiement', 'amount' => 'Montant', 'debit' => 'Débit', 'credit' => 'Crédit', 'balance' => 'Solde', ]; protected $csv; public function __construct() { $this->csv = new CSV_Custom(Session::getInstance(), 'acc_reconcile_csv'); $this->csv->setColumns(self::COLUMNS); $this->csv->setMandatoryColumns(['label', 'date']); $this->csv->setModifier(function (\stdClass $line): \stdClass { $date = \DateTime::createFromFormat('!d/m/Y', $line->date); if (!$date) { throw new UserException(sprintf('Date invalide : %s (format attendu : JJ/MM/AAAA)', $line->date)); } $line->date = $date; static $has_amount = null; if (null === $has_amount) { $has_amount = in_array('amount', $this->csv->getTranslationTable()); } if (!$has_amount) { $line->amount = $line->credit ?: '-' . ltrim($line->debit, '- \t\r\n'); } $line->amount = (substr($line->amount, 0, 1) == '-' ? -1 : 1) * Utils::moneyToInteger($line->amount); if (!empty($line->balance)) { $line->balance = (substr($line->balance, 0, 1) == '-' ? -1 : 1) * Utils::moneyToInteger($line->balance); } $line->new_params = http_build_query([ 'a' => abs($line->amount)/100, 'l' => $line->label, 'd' => $date->format('Y-m-d'), 't' => $line->amount < 0 ? Transaction::TYPE_EXPENSE : Transaction::TYPE_REVENUE, ]); return $line; }); } public function csv(): CSV_Custom { return $this->csv; } public function setSettings(array $translation_table, int $skip): void { $this->csv->setTranslationTable($translation_table); if (!((in_array('credit', $translation_table) && in_array('debit', $translation_table)) || in_array('amount', $translation_table))) { throw new UserException('Il est nécessaire de sélectionner une colonne "montant" ou deux colonnes "débit" et "crédit"'); } $this->csv->skip($skip); } public function getStartAndEndDates(): ?array { $start = $end = null; if (!$this->csv->ready()) { return compact('start', 'end'); } foreach ($this->csv->iterate() as $line) { if (null === $start || $line->date < $start) { $start = $line->date; } if (null === $end || $line->date > $end) { $end = $line->date; } } return compact('start', 'end'); } public function mergeJournal(\Generator $journal) { $lines = []; $csv = iterator_to_array($this->csv->iterate()); $journal = iterator_to_array($journal); $i = 0; $sum = 0; foreach ($journal as $j) { $id = $j->date->format('Ymd') . '.' . $i++; $row = (object) ['csv' => null, 'journal' => $j]; if (isset($j->debit)) { foreach ($csv as &$line) { if (!isset($line->date)) { continue; } // Match date, amount and label if ($j->date->format('Ymd') == $line->date->format('Ymd') && ($j->credit * -1 == $line->amount || $j->debit == $line->amount) && strtolower($j->label) == strtolower($line->label)) { $row->csv = $line; $line = null; break; } } } $lines[$id] = $row; } unset($line, $row, $j); // Second round to match only amount and label foreach ($lines as $row) { if ($row->csv || !isset($row->journal->debit)) { continue; } $j = $row->journal; foreach ($csv as &$line) { if (!isset($line->date)) { continue; } if ($j->date->format('Ymd') == $line->date->format('Ymd') && ($j->credit * -1 == $line->amount || $j->debit == $line->amount)) { $row->csv = $line; $line = null; break; } } } unset($j); // Then add CSV lines on the right foreach ($csv as $line) { if (null == $line) { continue; } $id = $line->date->format('Ymd') . '.' . ($i++); $lines[$id] = (object) ['csv' => $line, 'journal' => null]; } ksort($lines); $prev = null; foreach ($lines as &$line) { $line->add = false; if (isset($line->csv)) { $sum += $line->csv->amount; $line->csv->running_sum = $sum; if ($prev && ($prev->date->format('Ymd') != $line->csv->date->format('Ymd') || $prev->label != $line->csv->label)) { $prev = null; } } if (isset($line->csv) && isset($line->journal)) { $prev = null; } if (isset($line->csv) && !isset($line->journal) && !$prev) { $line->add = true; $prev = $line->csv; } } return $lines; } } |
Modified src/include/lib/Garradin/Accounting/Reports.php from [73e1a05e76] to [4754478645].
︙ | ︙ | |||
102 103 104 105 106 107 108 | INNER JOIN acc_years y ON y.id = t.id_year GROUP BY %s ORDER BY %s;'; $order = $order_code ? 'a.code COLLATE U_NOCASE' : 'a.label COLLATE U_NOCASE'; if ($by_year) { | | | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | INNER JOIN acc_years y ON y.id = t.id_year GROUP BY %s ORDER BY %s;'; $order = $order_code ? 'a.code COLLATE U_NOCASE' : 'a.label COLLATE U_NOCASE'; if ($by_year) { $group = 'y.id, a.code'; $order = 'y.start_date DESC, ' . $order; } else { $group = 'a.code, y.id'; $order = $order . ', y.id'; } $sql = sprintf($sql, Account::EXPENSE, Account::REVENUE, $group, $order); $current = null; |
︙ | ︙ | |||
133 134 135 136 137 138 139 | $out->{$s} = $current->{$s}; } return $out; }; foreach (DB::getInstance()->iterate($sql) as $row) { | | | > | 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 | $out->{$s} = $current->{$s}; } return $out; }; foreach (DB::getInstance()->iterate($sql) as $row) { $id = $by_year ? $row->id_year : $row->account_code; if (null !== $current && $current->selector !== $id) { $current->items[] = $total($current, $by_year); yield $current; $current = null; } if (null === $current) { $current = (object) [ 'selector' => $id, 'id' => $by_year ? $row->id_year : $row->id_account, 'label' => $by_year ? $row->year_label : ($order_code ? $row->account_code . ' - ' : '') . $row->account_label, 'description' => !$by_year ? $row->account_description : null, 'items' => [] ]; foreach ($sums as $s) { |
︙ | ︙ | |||
256 257 258 259 260 261 262 | $balances = DB::getInstance()->getAssoc($sql); return ($balances[Account::REVENUE] ?? 0) - ($balances[Account::EXPENSE] ?? 0); } static public function getBalancesSQL(array $parts = []) { | | > > > > | | | | | | | | | | | | | | | < < < > > | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | $balances = DB::getInstance()->getAssoc($sql); return ($balances[Account::REVENUE] ?? 0) - ($balances[Account::EXPENSE] ?? 0); } static public function getBalancesSQL(array $parts = []) { return sprintf('SELECT %s id_year, id, label, code, type, debit, credit, position, balance, is_debt FROM ( SELECT %s t.id_year, a.id, a.label, a.code, a.type, SUM(l.credit) AS credit, SUM(l.debit) AS debit, CASE -- 3 = dynamic asset or liability depending on balance WHEN position = 3 AND SUM(l.debit - l.credit) > 0 THEN 1 -- 1 = Asset (actif) comptes fournisseurs, tiers créditeurs WHEN position = 3 THEN 2 -- 2 = Liability (passif), comptes clients, tiers débiteurs ELSE position END AS position, CASE WHEN position IN (1, 4) -- 1 = asset, 4 = expense OR (position = 3 AND SUM(l.debit - l.credit) > 0) THEN SUM(l.debit - l.credit) ELSE SUM(l.credit - l.debit) END AS balance, CASE WHEN SUM(l.debit - l.credit) > 0 THEN 1 ELSE 0 END AS is_debt 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_account %s %s GROUP BY %s ) %s %s ORDER BY %s', isset($parts['select']) ? $parts['select'] . ',' : '', isset($parts['inner_select']) ? $parts['inner_select'] . ',' : '', $parts['inner_join'] ?? '', isset($parts['inner_where']) ? 'WHERE ' . $parts['inner_where'] : '', $parts['inner_group'] ?? 'a.id, t.id_year', isset($parts['where']) ? 'WHERE ' . $parts['where'] : '', isset($parts['group']) ? 'GROUP BY ' . $parts['group'] : '', $order ?? 'code' ); } /** * Returns accounts balances according to $criterias |
︙ | ︙ | |||
319 320 321 322 323 324 325 | if (empty($criterias['analytical']) && empty($criterias['user']) && empty($criterias['creator']) && empty($criterias['subscription'])) { $table = 'acc_accounts_balances'; } // Specific queries that can't rely on acc_accounts_balances if (!$table) { | > > > > > > > > > > > > > | > | | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | if (empty($criterias['analytical']) && empty($criterias['user']) && empty($criterias['creator']) && empty($criterias['subscription'])) { $table = 'acc_accounts_balances'; } // Specific queries that can't rely on acc_accounts_balances if (!$table) { $where = null; // The position if (!empty($criterias['position'])) { $criterias['position'] = (array)$criterias['position']; if (in_array(Account::LIABILITY, $criterias['position']) || in_array(Account::ASSET, $criterias['position'])) { $where = self::getWhereClause(['position' => $criterias['position']]); $criterias['position'][] = Account::ASSET_OR_LIABILITY; } } $inner_where = self::getWhereClause($criterias, 't', 'l', 'a'); $remove_zero = $remove_zero ? ', ' . $remove_zero : ''; $inner_group = empty($criterias['year']) ? 'a.id' : null; $sql = self::getBalancesSQL(['group' => 'code ' . $having] + compact('order', 'inner_where', 'where', 'inner_group')); } else { $where = self::getWhereClause($criterias); $query = 'SELECT *, SUM(credit) AS credit, SUM(debit) AS debit, SUM(balance) AS balance FROM %s WHERE %s GROUP BY %s %s |
︙ | ︙ |
Modified src/include/lib/Garradin/Accounting/Transactions.php from [da53e119ac] to [d402e5412a].
︙ | ︙ | |||
35 36 37 38 39 40 41 | 'date' => 'Date', 'notes' => 'Remarques', 'reference' => 'Numéro pièce comptable', // Lines 'line_id' => 'Numéro ligne', 'account' => 'Compte', | < > | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 'date' => 'Date', 'notes' => 'Remarques', 'reference' => 'Numéro pièce comptable', // Lines 'line_id' => 'Numéro ligne', 'account' => 'Compte', 'debit' => 'Débit', 'credit' => 'Crédit', 'line_reference' => 'Référence ligne', 'line_label' => 'Libellé ligne', 'reconciled' => 'Rapprochement', 'analytical' => 'Compte analytique', 'linked_users' => 'Membres associés', ]; |
︙ | ︙ | |||
130 131 132 133 134 135 136 137 138 139 140 141 142 143 | } $ids[] = (int)$row->id; $line = new Line; $line->importForm([ 'reference' => $row->line_reference, 'id_account' => $row->id_account, ]); $line->credit = $row->debit; $transaction->addLine($line); } | > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | } $ids[] = (int)$row->id; $line = new Line; $line->importForm([ 'reference' => $row->line_reference, 'label' => $row->line_label, 'id_account' => $row->id_account, ]); $line->credit = $row->debit; $transaction->addLine($line); } |
︙ | ︙ | |||
366 367 368 369 370 371 372 373 374 375 376 377 378 379 | throw new UserException(sprintf('le type "%s" est inconnu', $row->type)); } $transaction->type = $types[$row->type]; $fields = array_intersect_key((array)$row, array_flip(['label', 'date', 'notes', 'reference'])); $transaction->importForm($fields); } $data = []; if (!empty($row->analytical)) { $id_analytical = $accounts->getIdFromCode($row->analytical); | > > > > > > > > > > > > > > | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 | throw new UserException(sprintf('le type "%s" est inconnu', $row->type)); } $transaction->type = $types[$row->type]; $fields = array_intersect_key((array)$row, array_flip(['label', 'date', 'notes', 'reference'])); $transaction->importForm($fields); // Set status if (!empty($row->status)) { $status_list = array_map('trim', explode(',', $row->status)); $status = 0; foreach (Transaction::STATUS_NAMES as $k => $v) { if (in_array($v, $status_list)) { $status |= $k; } } $transaction->status = $status; } } $data = []; if (!empty($row->analytical)) { $id_analytical = $accounts->getIdFromCode($row->analytical); |
︙ | ︙ |
Modified src/include/lib/Garradin/CSV_Custom.php from [1994e729ee] to [329155a591].
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace Garradin; use KD2\UserSession; class CSV_Custom { protected $session; protected $key; protected $csv; protected $translation; protected $columns; | | > | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php namespace Garradin; use KD2\UserSession; class CSV_Custom { protected $session; protected $key; protected $csv; protected $translation; protected $columns; protected array $mandatory_columns = []; protected int $skip = 1; protected $modifier = null; protected array $_default; public function __construct(UserSession $session, string $key) { $this->session = $session; $this->key = $key; $this->csv = $this->session->get($this->key); $this->translation = $this->session->get($this->key . '_translation') ?: []; |
︙ | ︙ | |||
50 51 52 53 54 55 56 | throw new \LogicException('No file has been loaded'); } if (!$this->columns || !$this->translation) { throw new \LogicException('Missing columns or translation table'); } | > | > | | > | > | > > | > > | > > | | | | | | | | | | > > > | > > > > > > | | > > > > > | 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 | throw new \LogicException('No file has been loaded'); } if (!$this->columns || !$this->translation) { throw new \LogicException('Missing columns or translation table'); } for ($i = 0; $i < count($this->csv); $i++) { if ($i <= $this->skip) { continue; } yield $i => $this->getLine($i); } } public function getLine(int $i): ?\stdClass { if (!isset($this->csv[$i])) { return null; } if (!isset($this->_default)) { $this->_default = array_map(function ($a) { return null; }, array_flip($this->translation)); } $row = $this->_default; foreach ($this->csv[$i] as $col => $value) { if (!isset($this->translation[$col])) { continue; } $row[$this->translation[$col]] = trim($value); } $row = (object) $row; if (null !== $this->modifier) { try { $row = call_user_func($this->modifier, $row); } catch (UserException $e) { throw new UserException(sprintf('Ligne %d : %s', $i, $e->getMessage())); } } return $row; } public function getFirstLine(): array { if (!$this->loaded()) { throw new \LogicException('No file has been loaded'); } return current($this->csv); } public function setModifier(callable $callback): void { $this->modifier = $callback; } public function getSelectedTable(?array $source = null): array { if (null === $source && isset($_POST['translation_table'])) { $source = $_POST['translation_table']; } |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Accounting/Account.php from [88520a7e00] to [283323d3dd].
︙ | ︙ | |||
328 329 330 331 332 333 334 | } if (!$only_non_reconciled) { yield (object) ['sum' => $sum, 'reconciled_sum' => $reconciled_sum, 'date' => $end_date]; } } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | } if (!$only_non_reconciled) { yield (object) ['sum' => $sum, 'reconciled_sum' => $reconciled_sum, 'date' => $end_date]; } } public function getDepositJournal(int $year_id, array $checked = []): \Generator { $res = DB::getInstance()->iterate('SELECT l.debit, l.credit, t.id, t.date, t.reference, l.reference AS line_reference, t.label, l.label AS line_label, l.reconciled, l.id AS id_line, l.id_account FROM acc_transactions_lines l INNER JOIN acc_transactions t ON t.id = l.id_transaction WHERE t.id_year = ? AND l.id_account = ? AND l.credit = 0 AND NOT (t.status & ?) ORDER BY t.date, t.id;', |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Accounting/Transaction.php from [da7db0f220] to [f2a97d2dca].
︙ | ︙ | |||
295 296 297 298 299 300 301 | return [ $type->accounts[0]->position == 'credit' ? $credit : $debit, $type->accounts[1]->position == 'credit' ? $credit : $debit, ]; } | | | 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | return [ $type->accounts[0]->position == 'credit' ? $credit : $debit, $type->accounts[1]->position == 'credit' ? $credit : $debit, ]; } /**duplic * Creates a new Transaction entity (not saved) from an existing one, * trying to adapt to a different chart if possible * @param int $id * @param Year $year Target year * @return Transaction */ public function duplicate(Year $year): Transaction |
︙ | ︙ | |||
344 345 346 347 348 349 350 351 352 353 354 355 356 357 | $new->addLine($line); } // Only set date if valid if ($this->date >= $year->start_date && $this->date <= $year->end_date) { $new->date = clone $this->date; } return $new; } public function payment_reference(): ?string { $line = current($this->getLines()); | > > | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | $new->addLine($line); } // Only set date if valid if ($this->date >= $year->start_date && $this->date <= $year->end_date) { $new->date = clone $this->date; } $new->status = 0; return $new; } public function payment_reference(): ?string { $line = current($this->getLines()); |
︙ | ︙ | |||
695 696 697 698 699 700 701 | } if ($debit != $credit) { // Add final balance line $line = new Line; if ($debit > $credit) { | | | | 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 | } if ($debit != $credit) { // Add final balance line $line = new Line; if ($debit > $credit) { $line->credit = $debit - $credit; } else { $line->debit = $credit - $debit; } $open_account = EntityManager::findOne(Account::class, 'SELECT * FROM @TABLE WHERE id_chart = ? AND type = ? LIMIT 1;', $year->id_chart, Account::TYPE_OPENING); if (!$open_account) { throw new ValidationException('Aucun compte favori de bilan d\'ouverture n\'existe dans le plan comptable'); } |
︙ | ︙ |
Modified src/include/lib/Garradin/Membres.php from [38f3fd4508] to [bedba4d423].
︙ | ︙ | |||
120 121 122 123 124 125 126 | } } $data[$key] = $binary; } elseif (!is_numeric($data[$key]) || $data[$key] < 0 || $data[$key] > PHP_INT_MAX) { | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | } } $data[$key] = $binary; } elseif (!is_numeric($data[$key]) || $data[$key] < 0 || $data[$key] > PHP_INT_MAX) { throw new UserException(sprintf('Le champs "%s" ne contient pas une valeur binaire.', $key)); } } // Un champ texte vide c'est un champ NULL if (is_string($data[$key]) && trim($data[$key]) === '') { $data[$key] = null; |
︙ | ︙ |
Modified src/templates/acc/accounts/reconcile_assist.tpl from [d7fc6d0db8] to [283d27b0f7].
︙ | ︙ | |||
13 14 15 16 17 18 19 | <form method="post" action="{$self_url}" enctype="multipart/form-data"> {if !$csv->loaded()} <fieldset> <legend>Relevé de compte</legend> <p class="help block"> Le rapprochement assisté permet de s'aider d'un relevé de compte au format CSV pour trouver les écritures manquantes ou erronées.<br /> | | > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <form method="post" action="{$self_url}" enctype="multipart/form-data"> {if !$csv->loaded()} <fieldset> <legend>Relevé de compte</legend> <p class="help block"> Le rapprochement assisté permet de s'aider d'un relevé de compte au format CSV pour trouver les écritures manquantes ou erronées.<br /> <a href="https://garradin.eu/rapprochement_assiste" target="_blank">Aide détaillée</a> </p> <dl> {include file="common/_csv_help.tpl"} <dd class="help"> Le fichier doit également disposer soit d'une colonne <strong>Montant</strong>, soit de deux colonnes <strong>Débit</strong> et <strong>Crédit</strong>. </dd> {input type="file" name="file" label="Fichier CSV" accept=".csv,text/csv" required=1} </dl> <p class="submit"> {csrf_field key=$csrf_key} {button type="submit" name="upload" label="Envoyer le fichier" class="main" shape="upload"} </p> </fieldset> |
︙ | ︙ | |||
114 115 116 117 118 119 120 | {$line.journal.debit|raw|money} {/if} </td> <td class="money">{if $line.journal.running_sum > 0}-{/if}{$line.journal.running_sum|abs|raw|money:false}</td> <th style="text-align: right">{$line.journal.label}</th> {else} <td colspan="5"></td> | | < | < | | 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 | {$line.journal.debit|raw|money} {/if} </td> <td class="money">{if $line.journal.running_sum > 0}-{/if}{$line.journal.running_sum|abs|raw|money:false}</td> <th style="text-align: right">{$line.journal.label}</th> {else} <td colspan="5"></td> <td class="actions"> {if $line.add} {linkbutton label="Saisir cette écriture" target="_dialog" href="!acc/transactions/new.php?%s"|args:$line.csv.new_params shape="plus"} {/if} </td> {/if} <td class="separator"> {if $line->journal && $line->csv} == {else} <b class="icn">⚠</b> {/if} </td> {if isset($line->csv)} <th class="separator">{$line.csv.label}</th> <td class="money"> {$line.csv.amount|raw|money} </td> <td class="money">{if $line.csv.balance}{$line.csv.balance|raw|money}{else}{$line.csv.running_sum|raw|money}{/if}</td> <td>{$line.csv.date|date_short}</td> {else} <td colspan="4" class="separator"></td> {/if} </tr> {/if} {/foreach} |
︙ | ︙ |
Modified src/templates/acc/transactions/details.tpl from [3757af2106] to [424b045f3a].
︙ | ︙ | |||
57 58 59 60 61 62 63 | <dd><a class="num" href="?id={$transaction.id_related}">#{$transaction.id_related}</a> {if $transaction.type == $transaction::TYPE_DEBT || $transaction.type == $transaction::TYPE_CREDIT}(en règlement de){/if} </dd> {/if} {if count($related_transactions)} <dt>Écritures liées</dt> {foreach from=$related_transactions item="related"} | | < < | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | <dd><a class="num" href="?id={$transaction.id_related}">#{$transaction.id_related}</a> {if $transaction.type == $transaction::TYPE_DEBT || $transaction.type == $transaction::TYPE_CREDIT}(en règlement de){/if} </dd> {/if} {if count($related_transactions)} <dt>Écritures liées</dt> {foreach from=$related_transactions item="related"} <dd><a href="?id={$related.id}" class="num">#{$related.id}</a> — {$related.label} — {$related.date|date_short}</dd> {/foreach} {/if} <dt>Date</dt> <dd>{$transaction.date|date:'l j F Y (d/m/Y)'}</dd> <dt>Numéro pièce comptable</dt> <dd>{if $transaction.reference}{$transaction.reference}{else}-{/if}</dd> |
︙ | ︙ | |||
110 111 112 113 114 115 116 | <table class="list"> <thead> <tr> <td class="num">N° compte</td> <th>Compte</th> <td class="money">Débit</td> <td class="money">Crédit</td> | | | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | <table class="list"> <thead> <tr> <td class="num">N° compte</td> <th>Compte</th> <td class="money">Débit</td> <td class="money">Crédit</td> <td>Libellé ligne</td> <td>Référence ligne</td> <td>Projet</td> </tr> </thead> <tbody> {foreach from=$transaction->getLinesWithAccounts(false) item="line"} <tr> <td class="num"><a href="{$admin_url}acc/accounts/journal.php?id={$line.id_account}&year={$transaction.id_year}">{$line.account_code}</a></td> |
︙ | ︙ |
Modified src/templates/acc/transactions/new.tpl from [0187576c59] to [5d2788881b].
︙ | ︙ | |||
66 67 68 69 70 71 72 73 74 75 76 77 78 79 | <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=$transaction->payment_reference()} </dl> <dl> {input type="list" multiple=true name="users" label="Membres associés" target="!membres/selector.php"} {input type="textarea" name="notes" label="Remarques" rows=4 cols=30} </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=$id_analytical} {/if} </dl> </fieldset> | > > > | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | <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=$transaction->payment_reference()} </dl> <dl> {input type="list" multiple=true name="users" label="Membres associés" target="!membres/selector.php"} {input type="textarea" name="notes" label="Remarques" rows=4 cols=30} </dl> <dl data-types="t{$transaction::TYPE_ADVANCED}"> {input type="number" name="id_related" label="Lier à l'écriture numéro" source=$transaction help="Indiquer ici un numéro d'écriture pour faire le lien par exemple avec une dette"} </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=$id_analytical} {/if} </dl> </fieldset> |
︙ | ︙ |
Modified src/templates/admin/config/backup/restore.tpl from [0ec7bed822] to [03715cceb0].
︙ | ︙ | |||
76 77 78 79 80 81 82 | <td>Date</td> <td>Version</td> <td></td> </tr> </thead> {foreach from=$list item="backup"} <tr> | | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | <td>Date</td> <td>Version</td> <td></td> </tr> </thead> {foreach from=$list item="backup"} <tr> <td class="check">{if $backup.can_restore}{input type="radio" name="selected" value=$backup.filename}{/if}</td> <th><label for="f_selected_{$backup.filename}">{$backup.name}</label></th> <td>{$backup.size|size_in_bytes}</td> <td>{$backup.date|date_short:true}</td> <td>{$backup.version}{if !$backup.can_restore} — <span class="alert">Version trop ancienne pour pouvoir être restaurée</span>{/if}</td> <td class="actions"> {linkbutton href="?download=%s"|args:$backup.filename label="Télécharger" shape="download"} </td> |
︙ | ︙ |
Modified src/www/admin/acc/accounts/reconcile_assist.php from [b1a074fbc8] to [b465b25a48].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <?php namespace Garradin; use Garradin\Accounting\Accounts; use Garradin\Accounting\Transactions; require_once __DIR__ . '/../_inc.php'; $session->requireAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN); if (!CURRENT_YEAR_ID) { Utils::redirect(ADMIN_URL . 'acc/years/?msg=OPEN'); } $account = Accounts::get((int)qg('id')); if (!$account) { throw new UserException("Le compte demandé n'existe pas."); } $csrf_key = 'acc_reconcile_assist_' . $account->id(); | > < < < < < < < < < | | | < | < < < | < < < < < < | | < < | | < < < < < | < < | > | 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 | <?php namespace Garradin; use Garradin\Accounting\Accounts; use Garradin\Accounting\Transactions; use Garradin\Accounting\AssistedReconciliation; require_once __DIR__ . '/../_inc.php'; $session->requireAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN); if (!CURRENT_YEAR_ID) { Utils::redirect(ADMIN_URL . 'acc/years/?msg=OPEN'); } $account = Accounts::get((int)qg('id')); if (!$account) { throw new UserException("Le compte demandé n'existe pas."); } $csrf_key = 'acc_reconcile_assist_' . $account->id(); $ar = new AssistedReconciliation; $csv = $ar->csv(); $form->runIf('cancel', function () use ($csv) { $csv->clear(); }, $csrf_key, Utils::getSelfURI()); $form->runIf(f('upload') && isset($_FILES['file']['name']), function () use ($csv) { $csv->load($_FILES['file']); }, $csrf_key, Utils::getSelfURI()); $form->runIf('assign', function () use ($ar) { $ar->setSettings(f('translation_table'), (int)f('skip_first_line')); }, $csrf_key, Utils::getSelfURI()); extract($ar->getStartAndEndDates()); $journal = null; if ($start && $end) { if ($start < $current_year->start_date || $start > $current_year->end_date) { $start = clone $current_year->start_date; } if ($end < $current_year->start_date || $end > $current_year->end_date) { $end = clone $current_year->end_date; } $journal = $account->getReconcileJournal(CURRENT_YEAR_ID, $start, $end); } // Enregistrement des cases cochées $form->runIf('save', function () use ($journal, $csv) { Transactions::saveReconciled($journal, f('reconcile')); $csv->clear(); }, $csrf_key, Utils::getSelfURI()); $lines = null; if ($journal && $csv->ready()) { try { $lines = $ar->mergeJournal($journal); } catch (UserException $e) { $form->addError($e->getMessage()); } } $tpl->assign(compact( 'account', 'start', 'end', 'lines', 'ar', 'csv', 'csrf_key' )); $tpl->display('acc/accounts/reconcile_assist.tpl'); |
Modified src/www/admin/acc/transactions/new.php from [be9d05a5cb] to [4b7865c3e6].
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\Entities\Accounting\Account; use Garradin\Entities\Accounting\Transaction; use Garradin\Entities\Files\File; use Garradin\Accounting\Transactions; use Garradin\Accounting\Years; require_once __DIR__ . '/../_inc.php'; $session->requireAccess($session::SECTION_ACCOUNTING, $session::ACCESS_WRITE); if (!CURRENT_YEAR_ID) { Utils::redirect(ADMIN_URL . 'acc/years/?msg=OPEN'); } $chart = $current_year->chart(); $accounts = $chart->accounts(); $transaction = new Transaction; $lines = [[], []]; $amount = 0; $types_accounts = null; $id_analytical = null; // Duplicate transaction if (qg('copy')) { $old = Transactions::get((int)qg('copy')); if (!$old) { throw new UserException('Cette écriture n\'existe pas (ou plus).'); | > > > > > > > > > > > > > > > > > > | 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 | <?php namespace Garradin; use Garradin\Entities\Accounting\Account; use Garradin\Entities\Accounting\Transaction; use Garradin\Entities\Files\File; use Garradin\Accounting\AssistedReconciliation; use Garradin\Accounting\Transactions; use Garradin\Accounting\Years; require_once __DIR__ . '/../_inc.php'; $session->requireAccess($session::SECTION_ACCOUNTING, $session::ACCESS_WRITE); if (!CURRENT_YEAR_ID) { Utils::redirect(ADMIN_URL . 'acc/years/?msg=OPEN'); } $chart = $current_year->chart(); $accounts = $chart->accounts(); $transaction = new Transaction; $lines = [[], []]; $amount = 0; $types_accounts = null; $id_analytical = null; // Quick-fill transaction from query parameters if (qg('a')) { $amount = Utils::moneyToInteger(qg('a')); } if (qg('l')) { $transaction->label = qg('l'); } if (qg('d')) { $transaction->date = new \DateTime(qg('d')); } if (qg('t')) { $transaction->type = (int) qg('t'); } // Duplicate transaction if (qg('copy')) { $old = Transactions::get((int)qg('copy')); if (!$old) { throw new UserException('Cette écriture n\'existe pas (ou plus).'); |
︙ | ︙ |