diff --git a/config/sync/user.role.admin.yml b/config/sync/user.role.admin.yml index 8815621..4a4ee65 100644 --- a/config/sync/user.role.admin.yml +++ b/config/sync/user.role.admin.yml @@ -16,3 +16,4 @@ permissions: - 'create ligne_comptable content' - 'delete any ligne_comptable content' - 'edit any ligne_comptable content' + - 'view ligne_comptable revisions' diff --git a/config/sync/user.role.editeur.yml b/config/sync/user.role.editeur.yml index e1e6815..8c9cd99 100644 --- a/config/sync/user.role.editeur.yml +++ b/config/sync/user.role.editeur.yml @@ -16,3 +16,4 @@ permissions: - 'create ligne_comptable content' - 'delete any ligne_comptable content' - 'edit any ligne_comptable content' + - 'view ligne_comptable revisions' diff --git a/config/sync/user.role.user.yml b/config/sync/user.role.user.yml index 5036c41..87d0f3d 100644 --- a/config/sync/user.role.user.yml +++ b/config/sync/user.role.user.yml @@ -2,7 +2,10 @@ uuid: 24f4e46d-c714-486d-b000-f092eb727470 langcode: en status: true dependencies: + config: + - node.type.ligne_comptable module: + - node - system id: user label: 'Utilisateur (lecture seule)' @@ -10,3 +13,4 @@ weight: 4 is_admin: false permissions: - 'access content' + - 'view ligne_comptable revisions' diff --git a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.links.menu.yml b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.links.menu.yml index 3b673cd..58a5346 100644 --- a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.links.menu.yml +++ b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.links.menu.yml @@ -13,3 +13,11 @@ figli_compta_ledger.dashboard: menu_name: admin parent: system.admin weight: -10 + +figli_compta_ledger.history: + title: 'Historique du grand livre' + description: 'Toutes les révisions de toutes les lignes comptables' + route_name: figli_compta_ledger.history + menu_name: admin + parent: system.admin + weight: -9 diff --git a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.module b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.module index 3833e4d..00945f8 100644 --- a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.module +++ b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.module @@ -2,8 +2,11 @@ /** * @file - * Enforces the one invariant that explained most of the errors found in the - * historical spreadsheets: sum(répartition.montant) must equal montant_ht. + * Enforces two invariants for ligne_comptable: sum(répartition.montant) + * must equal montant_ht (explained most of the errors found in the + * historical spreadsheets), and every save must create a revision with no + * way to opt out, for an audit trail of who changed the shared ledger and + * when. */ use Drupal\node\NodeInterface; @@ -33,6 +36,21 @@ function figli_compta_ledger_form_alter(&$form, FormStateInterface $form_state, } $form['#validate'][] = 'figli_compta_ledger_validate_repartition'; + // Every save must create a revision, unconditionally -- there's no + // legitimate reason to skip it for an accounting record, so don't let + // anyone opt out. #access = FALSE (rather than just leaving the + // #default_value alone) makes Form API discard whatever a tampered + // request might submit for this field and fall back to #default_value. + // figli_compta_ledger_node_presave() enforces the same thing for any + // save that doesn't go through this form at all. + if (isset($form['revision'])) { + $form['revision']['#default_value'] = TRUE; + $form['revision']['#access'] = FALSE; + } + if (isset($form['revision_log'])) { + $form['revision_log']['#access'] = FALSE; + } + $request = \Drupal::request(); $wrapper_formats = ['drupal_ajax', 'drupal_modal', 'drupal_dialog']; $is_ajax_modal = in_array($request->query->get('_wrapper_format'), $wrapper_formats, TRUE) @@ -110,6 +128,20 @@ function figli_compta_ledger_node_presave(NodeInterface $node) { if ($node->bundle() !== 'ligne_comptable') { return; } + + // Force a revision on every save, with no log message and the actual + // acting user as its author -- this is the audit trail for a shared + // ledger, not the répartition-sum check below, so it's never exempted + // (not even during a skip_validation import: migrated data still needs + // an honest revision history). + $node->setNewRevision(TRUE); + $node->setRevisionLogMessage(''); + $node->setRevisionUserId(\Drupal::currentUser()->id()); + // setNewRevision() alone doesn't refresh this -- it stays whatever it + // was on the entity as loaded (the previous revision's timestamp), + // silently mislabeling every edit with its predecessor's save time. + $node->setRevisionCreationTime(\Drupal::time()->getRequestTime()); + if (!$node->hasField('field_montant_ht') || !$node->hasField('field_repartition')) { return; } @@ -142,7 +174,7 @@ function figli_compta_ledger_node_presave(NodeInterface $node) { function figli_compta_ledger_theme($existing, $type, $theme, $path) { return [ 'figli_compta_home' => [ - 'variables' => [], + 'variables' => ['can_view_history' => FALSE], 'template' => 'figli-compta-home', ], 'figli_compta_dashboard' => [ diff --git a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.routing.yml b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.routing.yml index 0d2148b..9ddd8fc 100644 --- a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.routing.yml +++ b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.routing.yml @@ -13,3 +13,11 @@ figli_compta_ledger.dashboard: _title: 'Tableau de bord - SAS Figures Libres' requirements: _permission: 'access content' + +figli_compta_ledger.history: + path: '/lignes/historique' + defaults: + _controller: '\Drupal\figli_compta_ledger\Controller\HistoryController::history' + _title: 'Historique du grand livre - SAS Figures Libres' + requirements: + _permission: 'view ligne_comptable revisions' diff --git a/web/modules/custom/figli_compta_ledger/src/Controller/DashboardController.php b/web/modules/custom/figli_compta_ledger/src/Controller/DashboardController.php index e335921..be4c611 100644 --- a/web/modules/custom/figli_compta_ledger/src/Controller/DashboardController.php +++ b/web/modules/custom/figli_compta_ledger/src/Controller/DashboardController.php @@ -17,6 +17,7 @@ class DashboardController extends ControllerBase { public function home() { return [ '#theme' => 'figli_compta_home', + '#can_view_history' => $this->currentUser()->hasPermission('view ligne_comptable revisions'), '#attached' => [ 'library' => ['figli_compta_ledger/home'], ], diff --git a/web/modules/custom/figli_compta_ledger/src/Controller/HistoryController.php b/web/modules/custom/figli_compta_ledger/src/Controller/HistoryController.php new file mode 100644 index 0000000..470fa91 --- /dev/null +++ b/web/modules/custom/figli_compta_ledger/src/Controller/HistoryController.php @@ -0,0 +1,84 @@ +entityTypeManager()->getStorage('node'); + $date_formatter = \Drupal::service('date.formatter'); + + $revision_ids = $this->entityTypeManager()->getStorage('node')->getQuery() + ->accessCheck(TRUE) + ->allRevisions() + ->condition('type', 'ligne_comptable') + ->sort('revision_timestamp', 'DESC') + ->pager(50) + ->execute(); + + $rows = []; + foreach ($revision_ids as $vid => $nid) { + $revision = $node_storage->loadRevision($vid); + if (!$revision) { + continue; + } + $author = $revision->getRevisionUser(); + $rows[] = [ + $date_formatter->format($revision->getRevisionCreationTime(), 'short'), + $author ? $author->getDisplayName() : $this->t('Utilisateur supprimé'), + $revision->hasField('field_date_ligne') ? $revision->get('field_date_ligne')->value : '', + $revision->label(), + $revision->hasField('field_montant_ht') && !$revision->get('field_montant_ht')->isEmpty() + ? $revision->get('field_montant_ht')->value . ' €' + : '', + [ + 'data' => [ + '#type' => 'link', + '#title' => $this->t('Voir cette version'), + '#url' => Url::fromRoute('entity.node.revision', [ + 'node' => $nid, + 'node_revision' => $vid, + ]), + ], + ], + ]; + } + + return [ + 'table' => [ + '#type' => 'table', + '#header' => [ + $this->t('Modifié le'), + $this->t('Par'), + $this->t('Date de la ligne'), + $this->t('Ligne'), + $this->t('Montant HT'), + $this->t('Version'), + ], + '#rows' => $rows, + '#empty' => $this->t('Aucune révision.'), + '#attributes' => ['class' => ['figli-historique-table']], + ], + 'pager' => [ + '#type' => 'pager', + ], + '#cache' => [ + 'contexts' => ['user.permissions'], + 'tags' => ['node_list:ligne_comptable'], + ], + ]; + } + +} diff --git a/web/modules/custom/figli_compta_ledger/templates/figli-compta-home.html.twig b/web/modules/custom/figli_compta_ledger/templates/figli-compta-home.html.twig index f47560c..df10fd3 100644 --- a/web/modules/custom/figli_compta_ledger/templates/figli-compta-home.html.twig +++ b/web/modules/custom/figli_compta_ledger/templates/figli-compta-home.html.twig @@ -9,6 +9,11 @@
+ Ajouter une ligne + {% endverbatim %} + {% if can_view_history %} + Historique + {% endif %} + {% verbatim %}