Postcard compose: mailing list or PCM carrier-route pick-and-send with cost #15

Closed
opened 2026-09-21 11:25:00 -07:00 by westfarn · 2 comments
Owner

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.
## 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.
Author
Owner

Associated template ticket: westfarn/web_django_template#11

Associated template ticket: https://git.aimloperations.com/westfarn/web_django_template/issues/11
Author
Owner
PR: https://git.aimloperations.com/ai_ml_operations/monica_site/pulls/16 Template mirror: https://git.aimloperations.com/westfarn/web_django_template/issues/11 → https://git.aimloperations.com/westfarn/web_django_template/pulls/12
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ai_ml_operations/monica_site#15