Skip to content
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

Conversation

Sepehr-Sobhani
Copy link
Contributor

@Sepehr-Sobhani Sepehr-Sobhani commented Mar 19, 2025

@Sepehr-Sobhani Sepehr-Sobhani force-pushed the chore/2685-operation-representative-address-can-not-be-deleted branch from a6961a8 to 6953d1c Compare March 19, 2025 23:54
@Sepehr-Sobhani Sepehr-Sobhani force-pushed the chore/2685-operation-representative-address-can-not-be-deleted branch 2 times, most recently from 2d4ca2a to d82c4f6 Compare March 27, 2025 23:28
Copy link
Contributor

@BCerki BCerki left a 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:
    image.
  • If I try to assign a contact without an address as a op rep (with rjsf required turned off), I get this:
    image (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
Copy link
Contributor

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

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Copy link
Contributor Author

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'
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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

Comment on lines +97 to +106
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)
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test reverted 😃
#3052 (comment)

@@ -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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

start function name with _?

@Sepehr-Sobhani Sepehr-Sobhani force-pushed the chore/2685-operation-representative-address-can-not-be-deleted branch from 9005ea6 to 5550a4f Compare March 28, 2025 18:31
@Sepehr-Sobhani
Copy link
Contributor Author

That contact is an Operation Representative, so we must prevent the user from clearing address fields.

@Sepehr-Sobhani
Copy link
Contributor Author

  • I was able to create an new contact with role operation rep without address info:
    image.

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.

@Sepehr-Sobhani
Copy link
Contributor Author

  • If I try to assign a contact without an address as a op rep (with rjsf required turned off), I get this:
    image (a user would have a really hard time getting here, probably not worth addressing

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.
I'm going to make a small tweak to show the first error when a validation error occurs. I don't expect this to happen often, but we can improve it later if we need to display multiple errors at once.

@Sepehr-Sobhani Sepehr-Sobhani force-pushed the chore/2685-operation-representative-address-can-not-be-deleted branch from 56a608a to 84ca896 Compare March 28, 2025 21:51
@BCerki
Copy link
Contributor

BCerki commented Mar 28, 2025

That contact is an Operation Representative, so we must prevent the user from clearing address fields.

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.

@Sepehr-Sobhani
Copy link
Contributor Author

That contact is an Operation Representative, so we must prevent the user from clearing address fields.

Yes! I mean that usually 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.

😮

Copy link
Contributor

@BCerki BCerki left a 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

@Sepehr-Sobhani
Copy link
Contributor Author

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.

@Sepehr-Sobhani Sepehr-Sobhani force-pushed the chore/2685-operation-representative-address-can-not-be-deleted branch from c262049 to 61c135a Compare March 28, 2025 23:01
@Sepehr-Sobhani
Copy link
Contributor Author

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

Fixed!

@Sepehr-Sobhani Sepehr-Sobhani force-pushed the chore/2685-operation-representative-address-can-not-be-deleted branch from 4d8df10 to c8afcd7 Compare March 28, 2025 23:59
@Sepehr-Sobhani Sepehr-Sobhani merged commit 2e04ec7 into develop Mar 29, 2025
22 checks passed
@Sepehr-Sobhani Sepehr-Sobhani deleted the chore/2685-operation-representative-address-can-not-be-deleted branch March 29, 2025 00:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants