From fe2f3ba680c88a1561271d6e03c9a3c6a5877273 Mon Sep 17 00:00:00 2001 From: bosd <5e2fd43-d292-4c90-9d1f-74ff3436329a@anonaddy.me> Date: Thu, 14 May 2026 21:28:58 +0200 Subject: [PATCH] [ADD] base_tier_validation: allow_bulk_approve on tier.definition Reviewers often need to validate many reviews at once (e.g. month-end batch approval of expense lines). Add an opt-in Boolean on tier.definition; when set, the tier.review list view exposes a Validate header button that approves every selected eligible review. Eligibility (AND semantics): the review is pending and assigned to the current user, it is the reviewer-of-the-moment (can_review), the definition opts in via allow_bulk_approve, and the definition does not require a comment (which cannot be collected in a bulk action). Reviews that fail any of these are skipped silently; each underlying record's own _validate_tier is invoked so notifications and approve-sequence promotion keep working. The counter is updated once for the whole selection rather than once per record. ``_update_counter`` pushes a bus notification to which the systray answers with a full recount of its badge, so notifying per record turns one bulk approval into (documents x reviewers x open tabs) recounts -- precisely the pattern this feature is meant to make routine. Batched, a selection costs one notification per reviewer however large it is. --- base_tier_validation/README.rst | 3 + .../models/tier_definition.py | 6 ++ base_tier_validation/models/tier_review.py | 51 +++++++++++++ base_tier_validation/readme/CONFIGURE.md | 3 + .../static/description/index.html | 3 + .../tests/test_tier_validation.py | 72 +++++++++++++++++++ .../views/tier_definition_view.xml | 1 + .../views/tier_review_view.xml | 7 ++ 8 files changed, 146 insertions(+) diff --git a/base_tier_validation/README.rst b/base_tier_validation/README.rst index 64ed4c72..ca50c24d 100644 --- a/base_tier_validation/README.rst +++ b/base_tier_validation/README.rst @@ -80,6 +80,9 @@ To configure this module, you need to: Reject. - If check *Approve by sequence*, reviewers is forced to review by specified sequence. +- If check *Allow Bulk Approve*, reviewers can validate several reviews + at once from the Tier Review list. Reviews on definitions without this + flag are skipped from a bulk selection. To configure Tier Validation Exceptions, you need to: diff --git a/base_tier_validation/models/tier_definition.py b/base_tier_validation/models/tier_definition.py index b36a82f6..886a3d44 100644 --- a/base_tier_validation/models/tier_definition.py +++ b/base_tier_validation/models/tier_definition.py @@ -105,6 +105,12 @@ def _get_tier_validation_model_names(self): default=False, help="Approval order by the specified sequence number", ) + allow_bulk_approve = fields.Boolean( + default=False, + help="When set, reviewers can approve several reviews at once from the " + "tier review list view. Reviews whose definition does not have this " + "flag are skipped from a bulk selection.", + ) approve_sequence_bypass = fields.Boolean( help="Bypassed (auto validated), if previous tier was validated " "by same reviewer", diff --git a/base_tier_validation/models/tier_review.py b/base_tier_validation/models/tier_review.py index bed4caaf..fdfcbcd6 100644 --- a/base_tier_validation/models/tier_review.py +++ b/base_tier_validation/models/tier_review.py @@ -67,6 +67,7 @@ class TierReview(models.Model): approve_sequence_bypass = fields.Boolean( related="definition_id.approve_sequence_bypass" ) + allow_bulk_approve = fields.Boolean(related="definition_id.allow_bulk_approve") last_reminder_date = fields.Datetime(readonly=True) @api.depends("status") @@ -212,3 +213,53 @@ def _schedule_review_reminder_activity(self, record): note=self._notify_review_reminder_body(), user_id=self.reviewer_ids.id, ) + + def _filter_bulk_validatable(self): + """Return the subset of self that the current user may bulk-approve. + + A review is eligible when its definition opts in via + ``allow_bulk_approve``, the review is pending and assigned to the + current user, the user is the reviewer-of-the-moment (``can_review``), + and the definition does not require a per-review comment (which + cannot be collected in a bulk action). + """ + return self.filtered( + lambda r: r.allow_bulk_approve + and r.status == "pending" + and r.can_review + and self.env.user in r.reviewer_ids + and not r.has_comment + ) + + def action_bulk_validate(self): + """Approve every eligible review in ``self`` in a single call. + + Reviews are grouped by their underlying record so that each record's + own ``_validate_tier`` runs once -- preserving notifications, the + approve-sequence promotion logic, and the review counter update. + Ineligible reviews are skipped silently; the action is meant to be + wired to the list view header. + """ + eligible = self._filter_bulk_validatable() + if not eligible: + return False + by_key = {} + for review in eligible: + by_key.setdefault((review.model, review.res_id), self.browse()) + by_key[(review.model, review.res_id)] |= review + validated = {} + for (model, res_id), reviews in by_key.items(): + record = self.env[model].browse(res_id) + record._validate_tier(reviews) + validated[model] = validated.get(model, record.browse()) | record + # Update the counter once for the whole batch, not once per record. + # ``_update_counter`` re-promotes the remaining reviews and pushes a + # bus notification, and the systray answers every notification with a + # recount of its badge. Per record, approving a selection of N + # documents fans out to N notifications per reviewer -- multiplied by + # each reviewer's open tabs -- which is exactly the pattern bulk + # approve is meant to make routine. Batched, the selection costs one + # notification per reviewer however large it is. + for records in validated.values(): + records._update_counter({"review_deleted": True}) + return True diff --git a/base_tier_validation/readme/CONFIGURE.md b/base_tier_validation/readme/CONFIGURE.md index 4bd817e6..944fc755 100644 --- a/base_tier_validation/readme/CONFIGURE.md +++ b/base_tier_validation/readme/CONFIGURE.md @@ -15,6 +15,9 @@ To configure this module, you need to: Reject. - If check *Approve by sequence*, reviewers is forced to review by specified sequence. +- If check *Allow Bulk Approve*, reviewers can validate several reviews at + once from the Tier Review list. Reviews on definitions without this flag + are skipped from a bulk selection. To configure Tier Validation Exceptions, you need to: diff --git a/base_tier_validation/static/description/index.html b/base_tier_validation/static/description/index.html index 3a8f165f..7a89a1be 100644 --- a/base_tier_validation/static/description/index.html +++ b/base_tier_validation/static/description/index.html @@ -446,6 +446,9 @@
To configure Tier Validation Exceptions, you need to: