Add PCM Integrations easy pick-and-send on postcard compose: realtor chooses mailing-list contactsor1–2 USPS carrier routes, sees piece count + estimated cost, then places the order through PCM.
This is not official USPS EDDM postage. PCM mails First Class / Standard to a purchased occupant list on the selected carrier routes (ZIP:C###).
Related template ticket: (will comment with westfarn/web_django_template issue after it exists).
UX (Campaign composer → Postcard)
Recipients control becomes:
Mailing list · postcard opt-in (current behavior)
Find USPS carrier routes
Route mode:
Search by ZIP (and optionally listing address + radius miles).
Show routes from PCM POST /list/count/zipcode or /list/count/radius with breakdownType=ZipCRRT.
Checkboxes to pick one or two routes (60540:C002).
Show cost on every row and a selected total (per-piece × households). Cost is blocking UX — do not hide it.
After selection, recount selected routes via POST /list/count/carrier-route so the listCountID matches only those routes (do not order the whole-ZIP count).
Save draft stores targeting + quote. Send places POST /order/postcard/with-list-count.
Mailing-list postcard mode must also show estimated cost (opted-in count × per-piece).
Cost source
PCM design pricing[] (mailClass, price, breakQty) from gallery/design payload.
Fallback env PCM_POSTCARD_PRICE_FIRSTCLASS / PCM_POSTCARD_PRICE_STANDARD if PCM omits pricing.
Default mail class: Standard (cheaper saturation). Sizes 46 / 46S are FirstClass-only.
Default purchased list type: IRL (all residents / occupant). Document GET /list/types.
Data / send
New campaign audience e.g. carrier_route.
JSON on campaign: selected routes, listCountID, recordCount, mail class, estimated cost.
Do not import purchased occupants into Contacts.
Route send = one PCM list-count order (nullable Message.contact or a single batch message), extRefNbr for webhooks.
Report page shows routes + quote instead of a fake per-person list.
Out of scope
Official EDDM indicia, USPS drop-off, clicking routes on a map (table is v1).
Test plan
Postcard compose: mailing list still creates per-contact drafts; cost shown.
ZIP lookup returns mocked ZipCRRT rows with per-route $.
Selecting two routes recounts and updates total $.
Save draft persists listCountID / routes / quote.
Send calls /order/postcard/with-list-count once, not /order/postcard per address.
Email/SMS compose unchanged.
Missing PCM pricing falls back to env rates and still shows a total.
## Goal
Add PCM Integrations **easy pick-and-send** on postcard compose: realtor chooses **mailing-list contacts** *or* **1–2 USPS carrier routes**, sees **piece count + estimated cost**, then places the order through PCM.
This is **not** official USPS EDDM postage. PCM mails First Class / Standard to a purchased occupant list on the selected carrier routes (`ZIP:C###`).
Related template ticket: (will comment with `westfarn/web_django_template` issue after it exists).
## UX (Campaign composer → Postcard)
1. Recipients control becomes:
- **Mailing list · postcard opt-in** (current behavior)
- **Find USPS carrier routes**
2. Route mode:
- Search by ZIP (and optionally listing address + radius miles).
- Show routes from PCM `POST /list/count/zipcode` or `/list/count/radius` with `breakdownType=ZipCRRT`.
- Checkboxes to pick one or two routes (`60540:C002`).
- **Show cost on every row and a selected total** (per-piece × households). Cost is blocking UX — do not hide it.
3. After selection, recount selected routes via `POST /list/count/carrier-route` so the `listCountID` matches only those routes (do not order the whole-ZIP count).
4. Save draft stores targeting + quote. Send places `POST /order/postcard/with-list-count`.
Mailing-list postcard mode must also show estimated cost (`opted-in count × per-piece`).
## Cost source
1. PCM design `pricing[]` (`mailClass`, `price`, `breakQty`) from gallery/design payload.
2. Fallback env `PCM_POSTCARD_PRICE_FIRSTCLASS` / `PCM_POSTCARD_PRICE_STANDARD` if PCM omits pricing.
3. Default mail class: Standard (cheaper saturation). Sizes `46` / `46S` are FirstClass-only.
4. Default purchased list type: `IRL` (all residents / occupant). Document `GET /list/types`.
## Data / send
- New campaign audience e.g. `carrier_route`.
- JSON on campaign: selected routes, `listCountID`, `recordCount`, mail class, estimated cost.
- Do **not** import purchased occupants into Contacts.
- Route send = one PCM list-count order (nullable `Message.contact` or a single batch message), `extRefNbr` for webhooks.
- Report page shows routes + quote instead of a fake per-person list.
## Out of scope
Official EDDM indicia, USPS drop-off, clicking routes on a map (table is v1).
## Test plan
- [ ] Postcard compose: mailing list still creates per-contact drafts; cost shown.
- [ ] ZIP lookup returns mocked ZipCRRT rows with per-route $.
- [ ] Selecting two routes recounts and updates total $.
- [ ] Save draft persists `listCountID` / routes / quote.
- [ ] Send calls `/order/postcard/with-list-count` once, not `/order/postcard` per address.
- [ ] Email/SMS compose unchanged.
- [ ] Missing PCM pricing falls back to env rates and still shows a total.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Goal
Add PCM Integrations easy pick-and-send on postcard compose: realtor chooses mailing-list contacts or 1–2 USPS carrier routes, sees piece count + estimated cost, then places the order through PCM.
This is not official USPS EDDM postage. PCM mails First Class / Standard to a purchased occupant list on the selected carrier routes (
ZIP:C###).Related template ticket: (will comment with
westfarn/web_django_templateissue after it exists).UX (Campaign composer → Postcard)
POST /list/count/zipcodeor/list/count/radiuswithbreakdownType=ZipCRRT.60540:C002).POST /list/count/carrier-routeso thelistCountIDmatches only those routes (do not order the whole-ZIP count).POST /order/postcard/with-list-count.Mailing-list postcard mode must also show estimated cost (
opted-in count × per-piece).Cost source
pricing[](mailClass,price,breakQty) from gallery/design payload.PCM_POSTCARD_PRICE_FIRSTCLASS/PCM_POSTCARD_PRICE_STANDARDif PCM omits pricing.46/46Sare FirstClass-only.IRL(all residents / occupant). DocumentGET /list/types.Data / send
carrier_route.listCountID,recordCount, mail class, estimated cost.Message.contactor a single batch message),extRefNbrfor webhooks.Out of scope
Official EDDM indicia, USPS drop-off, clicking routes on a map (table is v1).
Test plan
listCountID/ routes / quote./order/postcard/with-list-countonce, not/order/postcardper address.Associated template ticket: westfarn/web_django_template#11
PR: #16
Template mirror: westfarn/web_django_template#11 → westfarn/web_django_template#12