How Frasers DORs Work
A DOR (Denial of Receipt) is the form a customer signs to confirm they never received their goods. The courier (DPD) needs it before they'll pay a lost/disputed claim.
The key thing to understand: we don't email the customer a document to print and sign. We email them a link to an online form. They tick which items were missing and draw a signature in their browser. Nothing comes back by email — it comes back as data in our system.
Two programs do the work, and they only talk to each other through the database:
FrasersGroup.ClaimManager— a background console app. Sends the link, and later builds the signed PDF.- Claimit Portal — the website that hosts the signing form at
/dor/....
A small table, FrasersGroup_DOR, tracks each DOR with a Status number that ticks up as it moves through the journey. Follow the number, follow the flow.
The Journey at a Glance
Step 1 — We Send Out the Link (Status 1 → 2)
Handled by the console method CheckForEmailingDORs() — FrasersGroup.ClaimManager/Automation.cs:4414.
- Find every
FrasersGroup_DORrow atStatus == 1(just created) for the Frasers client. —Automation.cs:4424 - Look up the customer's Freshdesk ticket from the order data. —
Automation.cs:4439 - Send an HTML email as a reply on that Freshdesk ticket, so it reaches the customer in their existing support thread, from the Frasers support address. —
Automation.cs:4482 - The email body is just a link —
Click here to access the form→{PortalBaseUrl}/dor/{token}. —Automation.cs:4457 - Flip the row to
Status = 2(Sent), move the claim to "DOR Sent", and set the Freshdesk ticket to "Waiting on DOR". —Automation.cs:4520
The link's
tokenis a signed code (HMAC-SHA256 of the DOR id + a secret, then Base62-encoded) — built inSDK/Objects/Frasers Group/FGEmailLink.cs. It can't be guessed or forged. The link itself is the password.
Step 2 — The Customer Signs It Online (Status 2 → 3)
This happens on the Portal website, not the console.
The customer clicks the link and lands on the form at /dor/{token} (DORPage). They tick which products were missing, draw a signature on a signature pad, and submit.
Their browser posts to /dor/{dorId}/submit → DORController.Submit — frontend/Claimit.Portal/DOR/DORController.cs:27. The endpoint is public ([AllowAnonymous]) — the signed token is the only key needed. It then:
- Re-validates the token and confirms the id matches. —
DORController.cs:35 - Saves the signature image (a PNG from the canvas) to blob storage at
dor/{id}/signature.png. —DORController.cs:68 - Records which items and quantities they selected. —
DORController.cs:77 - Sets
CompletedAt, the signature path, andStatus = 3(Signed). —DORController.cs:76
At this point we have the signature, but there is still no DOR PDF and no file attached to the claim yet.
Step 3 — We Build the Real DOR (Status 3 → 4)
Back on the console, method CheckForReturnedSignedDORs() — FrasersGroup.ClaimManager/Automation.cs:4554.
- Find every row at
Status == 3. - Build the DOR PDF: customer name, tracking number, declared value, the pasted signature image, and the date. —
Automation.cs:4636 - Attach the PDF to the Freshdesk ticket as a private note. —
Automation.cs:4720 - Write the file against the claim — a
FileDatarow (FileDataType = DOR,OwnerId = claimId) plus the PDF in blob storage. This is the record everything downstream reads. —Automation.cs:4735 - Set
Status = 4(PDF created) and move the claim to DORReceived. —Automation.cs:4880
The DPD Temporal workflow then sees the claim is signed (Status >= 4) and submits it to MyDPD with the DOR attached (it slots into the Proof-of-Cost upload).
The Status Numbers
| Status | Meaning | Set by |
|---|---|---|
| 1 | Created — waiting to be emailed | Claim creation |
| 2 | Sent — link emailed, waiting on customer | CheckForEmailingDORs (Automation.cs:4520) |
| 3 | Signed — customer completed the online form | DORController.Submit (DORController.cs:78) |
| 4 | PDF created — DOR built + saved against the claim | CheckForReturnedSignedDORs (Automation.cs:4880) |
| 5 | Expired — customer never signed in time | CheckForNonReturnedDORs |
Why This Matters If DORs Aren't Going Out
Both the send and the PDF build live in the one console app, FrasersGroup.ClaimManager. Its Program.cs runs the methods top-to-bottom once and then exits — there is no built-in loop or scheduler. So:
- If the console isn't being run on a schedule → no links go out (rows stuck at Status 1), and signed DORs never become PDFs (rows stuck at Status 3 — the customer signed, but nothing ever attaches to the claim).
- The signing form lives in a separate app (Portal). If its base URL or the secret key is misconfigured, the link breaks or the token fails to validate.
Fastest health check: count FrasersGroup_DOR rows stuck at Status 1 (never emailed) or Status 3 (signed but no PDF). Either pile sitting there means the console isn't doing its job.
Code Path Reference
| What | Where |
|---|---|
| Send the link (Status 1 → 2) | FrasersGroup.ClaimManager/Automation.cs:4414 (CheckForEmailingDORs) |
| Signed link / token generation | SDK/Objects/Frasers Group/FGEmailLink.cs |
| Signing form submit (Status 2 → 3) | frontend/Claimit.Portal/DOR/DORController.cs:27 (Submit) |
| Signature stored to blob | DORController.cs:68 (dor/{id}/signature.png) |
| Build PDF + save file (Status 3 → 4) | FrasersGroup.ClaimManager/Automation.cs:4554 (CheckForReturnedSignedDORs) |
| The file written against the claim | Automation.cs:4735 (FileData, FileDataType = DOR) |
| State table model | SDK/Objects/Frasers Group/FrasersGroupDOR.cs |
| Console run order | FrasersGroup.ClaimManager/Program.cs |