"Void. " not "Void ||"

This commit is contained in:
Daniel Roth 2026-07-27 13:40:47 +00:00
parent c053c41b0a
commit ed43ed48ca
4 changed files with 10 additions and 16 deletions

View file

@ -473,7 +473,7 @@ The auth credential required by the New EPC API; stored in the `EPC_AUTH_TOKEN`
_Avoid_: API key, auth token, secret
**Void Property**:
A place Abri reports as having no live tenancy, signalled by `getSCSTenantData` returning a `RelayError` whose message is exactly `No_tenancy_found`. A normal outcome of the tenant-data sync, not a failure: there are no signatories to create as deal contacts, so the flow completes instead of dead-lettering the message, prefixing the deal's **Extra booking information** (`notes_for_surveyor`) with the `Void ||` marker. HubSpot has no append, so that property is read live and rewritten whole — never rewritten from the scraped snapshot, which lags by a scrape cycle and would discard a coordinator's unscraped edit. The marker doubles as the idempotency guard: a note already carrying it is left untouched, so redelivery never stacks markers. Distinct from a tenancy carrying no tenants (a tenancy exists; nobody is recorded against it), and from the same words arriving as a dumped 4GL query (`No Tenancy Found for Query FOR EACH ...`), which stays a rejection — Abri does not send that form to mean "known empty".
A place Abri reports as having no live tenancy, signalled by `getSCSTenantData` returning a `RelayError` whose message is exactly `No_tenancy_found`. A normal outcome of the tenant-data sync, not a failure: there are no signatories to create as deal contacts, so the flow completes instead of dead-lettering the message, prefixing the deal's **Extra booking information** (`notes_for_surveyor`) with the `Void. ` marker. HubSpot has no append, so that property is read live and rewritten whole — never rewritten from the scraped snapshot, which lags by a scrape cycle and would discard a coordinator's unscraped edit. The marker doubles as the idempotency guard: a note already carrying it is left untouched, so redelivery never stacks markers. Distinct from a tenancy carrying no tenants (a tenancy exists; nobody is recorded against it), and from the same words arriving as a dumped 4GL query (`No Tenancy Found for Query FOR EACH ...`), which stays a rejection — Abri does not send that form to mean "known empty".
_Avoid_: empty property, no tenancy (names the message, not the state), untenanted
## Team

View file

@ -43,12 +43,12 @@ system.
**Empty properties.** If Abri has no live tenancy for the property, no tenant contacts
appear on the deal — there is nobody to add — and **Extra booking information** is
prefixed with `Void ||` so the surveyor sees it first. Anything already in that field is
prefixed with `Void. ` so the surveyor sees it first. Anything already in that field is
kept, after the prefix. This is expected, not a fault, and nothing needs re-triggering.
Note that Abri sends the same signal for a property reference it does not recognise, so
if you were expecting tenants, check the property is right before assuming it's empty.
Delete the `Void ||` prefix if you establish the property is not empty after all — the
Delete the `Void. ` prefix if you establish the property is not empty after all — the
system only adds it once, so it will not come back on its own.
## The fields, and why they matter

View file

@ -32,7 +32,7 @@ NOTES_FOR_SURVEYOR_DEAL_PROPERTY = "notes_for_surveyor"
# Prepended to that property when Abri reports the place as having no live
# tenancy, so a surveyor sees it first. Doubles as the idempotency marker: a
# note already carrying it is left alone, so redelivery never stacks markers.
VOID_MARKER = "Void ||"
VOID_MARKER = "Void. "
def _vulnerability_description(tenant: Tenant) -> Optional[str]:
@ -338,9 +338,7 @@ class AbriOrchestrator:
AmendJobRequest(
job_no=job_no,
appointment_date=change.confirmed_survey_date,
appointment_time=slot_for_confirmed_time(
change.confirmed_survey_time
),
appointment_time=slot_for_confirmed_time(change.confirmed_survey_time),
resource=change.third_party_surveyor_identifier,
)
)
@ -373,9 +371,7 @@ class AbriOrchestrator:
client_ref=client_ref_for_deal(booking.deal_id),
place_ref=booking.place_ref,
appointment_date=booking.confirmed_survey_date,
appointment_time=slot_for_confirmed_time(
booking.confirmed_survey_time
),
appointment_time=slot_for_confirmed_time(booking.confirmed_survey_time),
short_description=descriptions.short_description,
long_description=descriptions.long_description,
resource=booking.third_party_surveyor_identifier,

View file

@ -75,9 +75,7 @@ class FakeDealDatabase:
@pytest.fixture()
def orchestrator(
mock_session: MagicMock, sdk_client: MagicMock
) -> AbriOrchestrator:
def orchestrator(mock_session: MagicMock, sdk_client: MagicMock) -> AbriOrchestrator:
with patch(
"infrastructure.abri.abri_client.requests.Session",
return_value=mock_session,
@ -587,7 +585,7 @@ def test_a_void_property_is_flagged_at_the_front_of_the_deals_booking_informatio
sdk_client.crm.deals.basic_api.update.assert_called_once_with(
DEAL_ID,
simple_public_object_input=DealPropertyInput(
properties={"notes_for_surveyor": "Void || Key safe code 1234"}
properties={"notes_for_surveyor": "Void. Key safe code 1234"}
),
)
@ -612,7 +610,7 @@ def test_a_void_property_with_no_existing_booking_information_is_flagged_alone(
sdk_client.crm.deals.basic_api.update.assert_called_once_with(
DEAL_ID,
simple_public_object_input=DealPropertyInput(
properties={"notes_for_surveyor": "Void ||"}
properties={"notes_for_surveyor": "Void. "}
),
)
@ -627,7 +625,7 @@ def test_a_deal_already_flagged_void_is_left_alone_so_redelivery_never_stacks_fl
"abri_relay_getscstenantdata_notenancyfound_response.xml"
)
sdk_client.crm.deals.basic_api.get_by_id.return_value.properties = {
"notes_for_surveyor": "Void || Key safe code 1234"
"notes_for_surveyor": "Void. Key safe code 1234"
}
# Act