Count dry-run rename intents separately from real renames 🟥

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Roth 2026-07-29 08:44:20 +00:00
parent 391e7bee37
commit 0135e71590
2 changed files with 141 additions and 48 deletions

View file

@ -9,6 +9,24 @@ field changes are picked up automatically and sent to Abri's scheduling system
---
## At a glance: which fields trigger what
| You change… | Abri's system… |
|---|---|
| **Expected commencement date** — set for the first time | Sends us the tenancy details, so tenant contacts appear on the deal |
| **Confirmed survey date** + **Third-party surveyor identifier** — the moment *both* are filled in | Creates the survey booking (a "job") |
| **Confirmed survey date**, **Confirmed survey time** or **Third-party surveyor identifier** — changed on a deal that already has a booking | Updates the existing appointment |
| **Number of attempts** reaches 3 *and* **Outcome** is one of the four unsuccessful values | Cancels the booking as abandoned |
Three fields are inputs to those triggers rather than triggers themselves:
- **Deal owner** — a HubSpot workflow maps it to the surveyor's Abri resource ID and
stores that in **Third-party surveyor identifier**, so setting the owner is how you set
the surveyor.
- **Client booking reference** — written back **by the system** with Abri's job number
once the booking is created. Never edit it (see [watch-outs](#things-to-watch-out-for)).
- **Deal name** — used as the job description Abri's staff see.
## Which deals does this apply to?
Only deals associated with the **Abri Stock Condition — Privately Funded** project.
@ -16,22 +34,86 @@ Changes to any other deal are ignored by this integration.
## How fast does it happen?
Changes are picked up automatically shortly after you save them in HubSpot — usually
within a few minutes. Nothing fires while you are mid-edit; it's the saved change that
Changes are picked up automatically shortly after you save them in HubSpot — the workflow runs every 20 minutes. Nothing fires while you are mid-edit; it's the saved change that
counts.
---
## What your changes do in Abri's system
## The detail: what each trigger does
| When you… | Abri's system… | How you know it worked |
|---|---|---|
| Set **Expected commencement date** for the first time | Sends us the tenancy details for the property | Tenant contact records appear in HubSpot, linked to the deal — unless Abri reports the property as empty (see below) |
| Set **Confirmed survey date** for the first time | Creates the survey booking (a "job") against the property, assigned to the surveyor on the deal | Abri's job number appears in **Client booking reference** on the deal |
| Change **Confirmed survey date**, **Confirmed survey time** or **Third-party surveyor identifier** on a deal that already has a booking | Updates the existing appointment — new date/time and/or reassigns it to the new surveyor | The deal keeps the same Client booking reference |
| Record a **3rd attempt** (Number of attempts reaches 3) **and** set **Outcome** to an unsuccessful value (see below) | Cancels the booking as abandoned | — (this only fires once per deal) |
### Fetching tenant details
**Unsuccessful outcomes** that count towards abandonment — the wording must match exactly:
Setting **Expected commencement date** for the first time asks Abri for the property's
tenancy details. Tenant contact records then appear in HubSpot, linked to the deal —
unless Abri reports the property as empty (see below). Only the *first* time the date is
set counts; changing it later does not re-fetch.
**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.`. Anything already in that field is
kept, after the prefix. This is expected, and nothing needs re-triggering.
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 surveyor comes from the Deal owner
You don't fill in **Third-party surveyor identifier** yourself. HubSpot workflows do it:
each recognised surveyor has a workflow that takes the **Deal owner**, maps it to their
Abri resource ID, and stores that as the identifier. So the field to get right is **Deal
owner** — set it to the surveyor doing the visit and the identifier follows.
Because the owner is picked from a dropdown it can't be misspelt, so this mostly just
works. The two things that can still go wrong:
- The deal owner is someone who isn't one of the recognised surveyors — no workflow
fires, the identifier stays blank, and no booking is sent.
- Someone edits the identifier field itself by hand — free-typed or misspelt codes are
rejected by Abri's system and the booking won't be made. Leave it to the workflow.
The tech team maintains the workflows and the list of recognised surveyors — if a
surveyor is missing, that's where to raise it.
### Creating the booking
The booking is created the moment the deal has **both** a **Confirmed survey date** and a
**Third-party surveyor identifier**. The order doesn't matter — whichever of the two
lands second is the change that sends the booking. Until both are in place, nothing is
sent.
Since the identifier comes from the Deal owner (above), in practice booking a survey
means making sure the deal owner is the surveyor and setting the confirmed date.
The booking is assigned within Abri's system to the surveyor on the deal on the confirmed date, in the slot
implied by **Confirmed survey time**:
| Confirmed survey time | Slot sent to Abri |
|---|---|
| `morning` | AM |
| `afternoon` | PM |
| A clock time like `09:30` | AM or PM by whether it's before midday |
| Left blank | All day |
**How you know it worked:** Abri's job number appears in **Client booking reference** on
the deal. A deal only ever gets one booking — once it has a job number, later changes
update that booking rather than creating another.
### Changing the booking
Changing **Confirmed survey date**, **Confirmed survey time** or **Third-party surveyor
identifier** on a deal that already has a booking updates the existing appointment —
new date/time and/or reassigns it to the new surveyor. (Reassigning works the same way as
booking: change the **Deal owner** and the workflow updates the identifier.) The deal
keeps the same Client booking reference. The date and surveyor must both still be filled in for the change to
be sent (see [watch-outs](#things-to-watch-out-for) on clearing fields).
### Abandoning the booking
When **Number of attempts** reaches 3 (or more) *and* **Outcome** is set to an
unsuccessful value, the booking is cancelled as abandoned. This fires once per deal.
**Unsuccessful outcomes** that count towards abandonment — the wording must match
exactly:
- `no answer`
- `cancelled / no show`
@ -41,61 +123,56 @@ counts.
Any other outcome (or fewer than 3 attempts) does **not** cancel anything in Abri's
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
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
system only adds it once, so it will not come back on its own.
## The fields, and why they matter
| HubSpot deal field | What it feeds |
|---|---|
| **Expected commencement date** | Setting it the first time triggers the tenant-details fetch |
| **Confirmed survey date** | The appointment date sent to Abri |
| **Confirmed survey time** | The appointment slot: `morning` → AM, `afternoon` → PM, a clock time like `09:30` → AM/PM by whether it's before midday, left blank → all day |
| **Third-party surveyor identifier** | Which surveyor the booking is assigned to in Abri's system. **Required** — a booking or appointment change cannot be sent without it, and it must be one of Abri's valid surveyor codes. Changing it on a deal that already has a booking reassigns that booking to the new surveyor |
| **Number of attempts** + **Outcome** | Together they trigger abandonment (3+ attempts and an unsuccessful outcome) |
| **Client booking reference** | Abri's job number, written back **by the system** after the booking is created |
| **Deal name** | Used as the job description Abri's staff see |
---
## Things that will catch you out
## Things to watch out for
### No booking is made until both date and surveyor are filled in
Entering a survey date on a deal whose **Third-party surveyor identifier** is blank sends
nothing — the system is waiting for the surveyor. It fires as soon as the second of the
two fields is filled in, so a deal can sit half-complete indefinitely without an error.
If a booking you expected hasn't appeared, check both fields are set — and since the
identifier comes from the
[Deal owner](#the-surveyor-comes-from-the-deal-owner), a blank identifier usually means
the owner is missing or isn't a recognised surveyor.
### Clearing the survey date does NOT cancel the booking
If you delete or blank the confirmed survey date, the booking **still exists in Abri's
system** and the surveyor is still expected. There is currently no way to cancel a
booking from HubSpot other than the 3-attempts abandonment route. If a booking needs
cancelling for any other reason, contact the tech team.
If you delete or blank the confirmed survey date (or the surveyor), the booking **still
exists in Abri's system** and the surveyor is still expected — clearing a field sends
nothing. There is currently no way to cancel a booking from HubSpot other than the
3-attempts abandonment route. If a booking needs cancelling for any other reason,
contact the tech team. (Re-entering a date later updates the existing booking; it will
not create a duplicate.)
### Don't edit Client booking reference
It is written automatically with Abri's job number and is how the system finds the
booking when you later change or abandon it. If you overwrite or clear it, appointment
changes and abandonments will stop working for that deal.
It is written automatically with Abri's job number and is how the system tells a new
booking from a change to an existing one:
### The surveyor identifier (Third-Party Surveyor Identifier) must be a valid Abri code
Free-typed or misspelt values will be rejected by Abri's system and the booking won't be
made. The tech team holds the current list of valid surveyor codes from Abri — use a
value from that list. Soon this field will be set automatically from the deal
owner.
- On a deal that is **not yet booked**, typing anything into this field stops the
booking from ever being created — the system reads a value there as "already booked".
- On a **booked** deal, overwriting or clearing it breaks appointment changes and
abandonment for that deal — those changes fail over to the tech team rather than
reaching Abri.
### Outcome wording is exact
Abandonment only recognises the four unsuccessful outcomes listed above, spelled exactly
that way. A variation like "No Answer - left card" won't trigger it.
### Abandonment only cancels an actual booking
Recording 3 unsuccessful attempts on a deal that never got as far as a booking (no
Client booking reference) has nothing to cancel — the attempt lands with the tech team
as an error rather than doing anything in Abri's system. That's harmless, but worth
knowing if you're tidying up deals that never went ahead.
### If the job number never appears
The booking didn't go through — most often because the surveyor identifier was missing
or invalid, or the deal isn't on the Abri project. Failures land with the tech team, not
in HubSpot, so you won't see an error message. If **Client booking reference** is still
empty well after you set the survey date, flag it to the tech team rather than re-editing
fields.
empty well after both the survey date and surveyor are set, flag it to the tech team
rather than re-editing fields.

View file

@ -331,3 +331,19 @@ def test_dry_run_logs_would_rename_without_calling_api(
sp.rename_file.assert_not_called()
assert any("Would rename" in r.message for r in caplog.records)
def test_dry_run_counts_intents_separately_from_renames() -> None:
# Arrange
sp = MagicMock()
sp.get_folders_in_path.return_value = {
"value": [_make_file("Survey.pdf", "id-1"), _make_file("Report.docx", "id-2")]
}
# Act
summary = _make_orchestrator(sp, dry_run=True)._process_folder(
"some/path", "100", "1 High St", "AB1 2CD"
)
# Assert
assert summary == RenameSummary(renamed=0, would_rename=2)