-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Chore: 2685 operation representative address can not be deleted #3052
Chore: 2685 operation representative address can not be deleted #3052
Conversation
a6961a8
to
6953d1c
Compare
2d4ca2a
to
d82c4f6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work on this, looks very thorough! I had a couple questions about triggers because I haven't used them before.
For manual testing, I turned off the required fields in the FE and tried to submit missing addresses bits and got the following:
- I don't think your PR introduced this, but when editing an existing contact there's a problem with the error clearing: https://www.loom.com/share/aed330b2cd9342b0b242438d0cb0e4fb
- I was able to create an new contact with role operation rep without address info:
.
- If I try to assign a contact without an address as a op rep (with rjsf required turned off), I get this:
(a user would have a really hard time getting here, probably not worth addressing
if exists ( | ||
select 1 | ||
from erc.contact | ||
where address_id = new.id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is new
something that comes from pgtrigger? The docs talk about OLD and NEW but I didn't see an example like this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, new is from PostgreSQL’s trigger system, used by pgtrigger. In a BEFORE UPDATE
trigger, new is the proposed row data.
https://www.postgresql.org/docs/current/plpgsql-trigger.html#PLPGSQL-DML-TRIGGER-NEW
when=pgtrigger.Before, # Trigger before the update | ||
operation=pgtrigger.Update, # Only for UPDATE operations | ||
condition=pgtrigger.Q( | ||
old__address__isnull=False, # Old address was not null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we'd want to prevent a null new address even if the old address was null. Unless this is to accommodate some incomplete imported data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After implementing this, I noticed that removing the old__address__isnull=False
condition causes an issue. If a contact already exists in the database and the user selects it in the OP REP step of registration, but the contact has no address, the user is prevented from filling out the address fields. This happens because we update the contact fields first, and by the time we process the address, an error occurs.
new__address__isnull=True, # New address is null | ||
), | ||
func=""" | ||
if new.business_role_id = 'Operation Representative' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment may be irrelevant once I understand how new
works, but if the old role was operation rep and we didn't supply a new role because nothing changed, will this still block setting an address to null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if new.business_role_id is 'Operation Representative' (either unchanged or explicitly set), the func will raise the exception.
with patch('registration.models.BusinessRole.objects.get', return_value=senior_officer_role) as mock_get: | ||
# Assert that we have only one contact(to make sure we are updating the contact and not creating a new one) | ||
assert Contact.objects.count() == 1 | ||
# Assert that we are not creating a new address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest adding to the comment where the two addresses come from. I always forget which bakers add addresses
def test_no_trigger_when_address_already_null(self): | ||
"""Test that updating a contact with NULL address doesn’t trigger if address stays NULL.""" | ||
# Update another field | ||
self.null_address_contact.email = "[email protected]" | ||
self.null_address_contact.save() | ||
|
||
# Verify the update succeeded and address is still NULL | ||
self.null_address_contact.refresh_from_db() | ||
self.assertEqual(self.null_address_contact.email, "[email protected]") | ||
self.assertIsNone(self.null_address_contact.address) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to above question, do we need to allow this case? If someone somehow got away with giving us a null address, we'd probably want to them fix it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed that part of the trigger so this test is no longer valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test reverted 😃
#3052 (comment)
bc_obps/service/contact_service.py
Outdated
@@ -136,3 +142,12 @@ def raise_exception_if_contact_missing_address_information(cls, contact_id: int) | |||
raise Exception( | |||
f'The contact {contact.first_name} {contact.last_name} is missing address information. Please return to Contacts and fill in their address information before assigning them as an Operation Representative here.' | |||
) | |||
|
|||
@classmethod | |||
def validate_operation_representative_address(cls, contact_id: int, address_data: DictStrAny) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
start function name with _?
9005ea6
to
5550a4f
Compare
That contact is an Operation Representative, so we must prevent the user from clearing address fields. |
You can create an OP REP without an address, but you cannot delete the address or remove address fields. This is because the contact instance is created first, and the address is assigned afterward (FK relationship). Preventing users from creating the contact isn't an option, as many other contact types don't require an address. |
This is correct because schema validation prevents the user from doing that. However, we currently don't have a way to display backend schema validation errors since we make fields required in RJSF. |
56a608a
to
84ca896
Compare
Yes! I mean that usually when a user triggers an error, fixes it, and then resubmits, the error disappears on successful submission. On the contact page, the error doesn't disappear until you refresh. |
😮 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to address the error message refresh in this PR, I can look again, otherwise it can be a separate bug ticket and this is good to go
I'll try to fix it if it's a quick fix; otherwise, I'll create a bug ticket. |
c262049
to
61c135a
Compare
Fixed! |
…resentatives Signed-off-by: SeSo <[email protected]>
…esentatives Signed-off-by: SeSo <[email protected]>
Signed-off-by: SeSo <[email protected]>
Signed-off-by: SeSo <[email protected]>
…rors Signed-off-by: SeSo <[email protected]>
Signed-off-by: SeSo <[email protected]>
Signed-off-by: SeSo <[email protected]>
4d8df10
to
c8afcd7
Compare
ISSUE 2685