Closed (fixed)
Project:
Gift Aid management
Version:
1.x-dev
Component:
User interface
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Reporter:
Created:
26 Nov 2025 at 19:44 UTC
Updated:
24 Feb 2026 at 14:44 UTC
Jump to comment: Most recent
Comments
Comment #2
jonathanshawLet's find the simplest, quickest solution to this. I'm open to something as simple as adding warning text to the description on any of the critical fields if an admin is editing a locked declaration. And maybe some use of css in support of that.
Comment #3
adamps commentedAn important case of this issue is "retraction" - i.e. creating a cancellation with
start_datefar in the past whencanRetroactivelyCancel()is false. I've closed #3557360: Support retraction as a duplicate of this.As a preliminary step, we should check what limits should apply to staff. In particular, I suspect that at the moment they can do retraction, so we need to block that.
In terms of a UI, I feel attracted to something like this, which resembles what I've seen quite often in shops, and is similar to what you wrote on the retraction issue.
unlockedquery parameter, which fetches the form with fields open (but would lose existing edits?).The is an existing mechanism for delete that is in effect also an admin override. We can investigate integrating that into this new framework. E.g. we could disable the delete button when the form is locked.
When an override is submitted that potentially alters claims that have already been submitted to HMRC then it would be useful to set a flag on the declaration revision.
Comment #4
adamps commentedProposed new restrictions for staff (i.e. not yet coded - not necessarily new concepts):
start_datecan be at most XX days in the past unlesscanRetroactivelyCancel(). I guess that probablySTABILITY_WINDOW(7 days) is too short for XX - if staff are on holiday for a week then they would be too late. 30 days feels good to matchRETROSPECTIVE_WINDOW. Then the admin should avoid claiming any donation until 30 days have elapsed in case there is a cancellation sitting on a staff desk. Already we knew they should wait 30 days in case of confirmations so it fits in.Comment #5
jonathanshawYes, editing and deletion of declarations and cancellations all needs to obey the same locked logic.
And yes to the various messages and disabling fields not removing them.
For implementation, I quite like the approach I suggested in the other issue:
(1) Add some warning text and a confirmation checkbox, but hide it using #states unless the cancellation start date is in the past.
(2) In the form validation require the checkbox to be checked if the start date is in the past.
I think some version of this also works for unlocking. A checkbox can use #states to enable/disable other fields. No custom js or ajax needed.
You make excellent points.
(i) Yes, if canRetroactivelyCancel() then no start date limit.
(ii) I don't think we should use RETROSPECTIVE_WINDOW, that is linked to a particular compliance requirement.
(iii) The need here feels much closer to STABILITY_WINDOW.
(iv) I agree with your point about 7 days being too little. I don't see a problem with changing STABILITY_WINDOW to 14 or 30 days.
The real solution here long-term might be that the process of exporting declarations to make a claim could record the date of export on declarations, which would enable us to know for sure whether a declaration might have been used to make a claim. But I think just extending the stability window here is good enough in practice.
Comment #6
adamps commentedYes this is what I meant in my option 1)
Apologies, my reference to js was confusing. I was referring to the fact that
#statesis implemented in terms of js, so without any contact with the web server - in contrast to the other options which use Ajax or a new page request and so do contact the server.I would be very happy to use solution 1). However when I come to write it, I see a possibility that it doesn't work out easily as #states (hence not contacting the server) is fundamentally less flexible than AJAX (which does contact the server). So I'd like to keep AJAX on the table as option B in case it turns out to be both quicker and better UI.
Comment #7
adamps commentedThe
#statesmethod mostly works well. AFAICS it can't remove the CSS class that highlights the locked fields. This means that after the admin selects to override, the locked fields are still visually highlighted (currently I added the word "locked" in red as a prefix to the label), but they do become enabled. I feel it's OK: the label tells them which fields to be careful about editing. However they might initially be confused that enabling the override checkbox didn't remove the labels.I found a complication with disabling fields instead of removing them. It would mean that we override the #access = FALSE and replace it with a client-side restriction that can be circumvented with web developer tools. True, we would hope we can trust staff, however I suppose that the Drupal.org security team might class this as a security vulnerability. How do you feel about leaving that part out for now?
I suggested that we could set a flag on the declaration revision to indicate that it potentially alters claims that have already been submitted to HMRC. Are you interested in that??
For retroactive cancellation, I feel that the date should be specifically the exact start date of the declaration. It's not really a wildcard "any date you like". Earlier could be wrong: maybe there was a rock solid declaration for the previous 10 years that ended with a standard forward cancelled and we mustn't disturb it. And later could be wrong as it could fail to cover the declaration and lead to an incorrect claim. So I feel instead we could have a checkbox for retroactive cancel that hides the date input. This is handy, because it gives me something that I can disable by default, then enable with the override.
Comment #8
adamps commentedComment #10
jonathanshawI wonder if there's a .form-disabled class on a #disabled element wrapper we could use.
We can use form validation to guard against that? i.e. disallow a submission if it makes an unauthorised change to a critical field .
I don't think so. There are real questions about how we help reporting on these kind of cases, but I'm not sure the flag you suggest is the right solution.
Excellent point. Clearly, for a donor doing a retroactive cancel in response to a confirmation email, you're right.
But for an admin making a retroactive cancel, coming from a donor, they do in fact wish to cancel based on a whole period of time not a specific declaration. That's part of why cancellations are their own entities, one cancellation can cancel many declarations.
Here's a possible solution:
(1) Create a seperate confirmation cancellation form and route, with a route parameter that takes a declaration id. This should then default to cancelling from the declaration start date. Ideally it could be changed to later, so that the donor could technically choose to allow gift aid in the early years but not later years, but that's not an important use case to support immediately.
(2) But on the main cancellation form, coming from the donor, there is no single declaration the cancellation relates to so no start date to pin to. The default date is today.
One interesting point is that the scenario you describe indicates that the business of sending validation emails long after the initial declaration to try to revalidate declarations later found invalid, is very tricky, as we don't want to inadvertently invalidate perfectly good but overlapping declarations for the same donor. We might one day need to add a declaration reference field to cancellations to track that and limit the damage of this particular kind of retroactive cancellation. Out of scope for now though.
I'm not sure I see the problem. Except that if there was a declaration end date, then it would be meaningless for the cancellation start date to be after it.
Comment #11
adamps commentedWe have similar ideas, and the latest commit covers some of what you suggest already. You are right, there are really two different processes here:
denied.That's a reasonable idea. As an alternative we could try and simplify. We could stay like the current code having one route/form with two options.
I tend to feel your suggestion is better, if we don't mind a little extra development time/complexity.
Comment #12
adamps commentedI can see two options:
Either way I feel that a denial contains no dates - it simply changes the state of a declaration.
Comment #13
adamps commentedI created #3572561: Invalidation as a separate process from Cancellation.
The intended scope of this issue is admin overrides, i.e. breaking the restrictions of what is allowed in either of the scenarios. I have mostly completed that now. Are you OK for me to commit once I fixed the tests, then we continue in the other issue?
Comment #14
jonathanshawI agree with seperating the issues and committing the admin overrides protection. I tried it review it, but there's stuff about retroactive cancellation that I'm not sure about mixed in that seems somewhat more connected with #3572561: Invalidation as a separate process from Cancellation.
I'm happy for you to commit whatever you like here, but if you do commit the Donor retroactive cancel bits, I might open another issue to discuss it further or discuss it in [#3572561.
Comment #15
jonathanshawI agree with seperating the issues and committing the admin overrides protection. I tried it review it, but there's stuff about retroactive cancellation that I'm not sure about mixed in that seems somewhat more connected with #3572561: Invalidation as a separate process from Cancellation.
I'm happy for you to commit whatever you like here, but if you do commit the Donor retroactive cancel bits, I might open another issue to discuss it further or discuss it in [#3572561.
Comment #16
adamps commentedThanks. I agree this already contains some efforts towards #3572561: Invalidation as a separate process from Cancellation that might not be correct. I propose to commit it anyway and then correct it on the other issue. (Otherwise I feel I will take time making more changes here likely without really saving any time on the other issue.)
Let's discuss it in #3572561: Invalidation as a separate process from Cancellation as you suggest.
Comment #18
adamps commented