Template
Add PCM carrier-route postcard pick-and-send (#11)
## Summary Closes #11. - Mirror Monica: compose can use the postcard mailing list or 1–2 USPS carrier routes, with estimated cost shown. - Place selected routes through PCM list-count orders instead of importing occupant addresses.
This commit is contained in:
1 parent
f3e34f5099
commit
3a1ef797b8
17 files changed
+1192
-27
No files matched your search
@@ -305,6 +305,208 @@ class PostcardUtmPanelTests(TestCase):
|
||||
self.assertContains(response, "data-utm-qr")
|
||||
self.assertContains(response, reverse("campaign_short_link"))
|
||||
|
||||
|
||||
class CarrierRoutePostcardTests(TestCase):
|
||||
def setUp(self):
|
||||
User = get_user_model()
|
||||
self.user = User.objects.create_user(
|
||||
username="routes", password="test-pass-123"
|
||||
)
|
||||
self.client = Client()
|
||||
self.client.login(username="routes", password="test-pass-123")
|
||||
self.contact = Contact.objects.create(
|
||||
email="mail@example.com",
|
||||
postal_address=Contact.make_postal_address(line1="9 Main"),
|
||||
)
|
||||
|
||||
def test_pick_unit_price_uses_break_qty_and_fallback(self):
|
||||
from directmail.providers.postcard import pcm as pcm_mod
|
||||
|
||||
price, source = pcm_mod.pick_unit_price(
|
||||
[
|
||||
{"mailClass": "Standard", "price": 0.8, "breakQty": 1},
|
||||
{"mailClass": "Standard", "price": 0.5, "breakQty": 200},
|
||||
],
|
||||
mail_class="Standard",
|
||||
quantity=250,
|
||||
)
|
||||
self.assertEqual(price, 0.5)
|
||||
self.assertEqual(source, "pcm")
|
||||
with self.settings(PCM_POSTCARD_PRICE_STANDARD="0.42"):
|
||||
price, source = pcm_mod.pick_unit_price(
|
||||
[], mail_class="Standard", quantity=10
|
||||
)
|
||||
self.assertEqual(price, 0.42)
|
||||
self.assertEqual(source, "fallback")
|
||||
|
||||
def test_quote_search_returns_route_costs(self):
|
||||
payload = {
|
||||
"listCountID": 11,
|
||||
"recordCount": 300,
|
||||
"breakdown": [
|
||||
{
|
||||
"breakdownType": "ZipCRRT",
|
||||
"data": [
|
||||
{"code": "60540:C001", "text": "NAPERVILLE", "total": 120},
|
||||
{"code": "60540:C002", "text": "NAPERVILLE", "total": 180},
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
with self.settings(PCM_POSTCARD_PRICE_STANDARD="0.50"):
|
||||
with patch(
|
||||
"directmail.providers.postcard.pcm.design_pricing", return_value=[]
|
||||
):
|
||||
with patch(
|
||||
"directmail.providers.postcard.pcm.list_count_zipcode",
|
||||
return_value=payload,
|
||||
):
|
||||
response = self.client.post(
|
||||
reverse("directmail:postcard_quote"),
|
||||
data='{"action":"search","zip":"60540","size":"68"}',
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json()
|
||||
self.assertEqual(len(data["routes"]), 2)
|
||||
self.assertEqual(data["routes"][0]["cost"], 60.0)
|
||||
self.assertEqual(data["mail_class"], "Standard")
|
||||
|
||||
def test_quote_select_and_save_draft(self):
|
||||
import json
|
||||
|
||||
select_payload = {
|
||||
"listCountID": 99,
|
||||
"recordCount": 300,
|
||||
"breakdown": [
|
||||
{
|
||||
"breakdownType": "ZipCRRT",
|
||||
"data": [
|
||||
{"code": "60540:C001", "text": "A", "total": 120},
|
||||
{"code": "60540:C002", "text": "B", "total": 180},
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
with self.settings(PCM_POSTCARD_PRICE_STANDARD="0.50"):
|
||||
with patch(
|
||||
"directmail.providers.postcard.pcm.design_pricing", return_value=[]
|
||||
):
|
||||
with patch(
|
||||
"directmail.providers.postcard.pcm.list_count_carrier_route",
|
||||
return_value=select_payload,
|
||||
):
|
||||
quoted = self.client.post(
|
||||
reverse("directmail:postcard_quote"),
|
||||
data=(
|
||||
'{"action":"select","size":"68",'
|
||||
'"routes":["60540:C001","60540:C002"]}'
|
||||
),
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(quoted.status_code, 200)
|
||||
quote = quoted.json()
|
||||
self.assertEqual(quote["list_count_id"], 99)
|
||||
self.assertEqual(quote["total"], 150.0)
|
||||
|
||||
targeting = {
|
||||
"selected_codes": quote["selected_codes"],
|
||||
"list_count_id": quote["list_count_id"],
|
||||
"record_count": quote["record_count"],
|
||||
"mail_class": quote["mail_class"],
|
||||
"estimated": quote["estimated"],
|
||||
"route_rows": quote["routes"],
|
||||
}
|
||||
with patch(
|
||||
"directmail.views._fetch_pcm_designs",
|
||||
return_value=(
|
||||
[{"design_id": "42", "name": "Spring card", "size": "68"}],
|
||||
"",
|
||||
),
|
||||
):
|
||||
response = self.client.post(
|
||||
reverse("directmail:campaign_list"),
|
||||
{
|
||||
"name": "Farm drop",
|
||||
"audience": Campaign.Audience.CARRIER_ROUTE,
|
||||
"template_id": "d:42",
|
||||
"route_targeting": json.dumps(targeting),
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
campaign = Campaign.objects.get(name="Farm drop")
|
||||
self.assertEqual(campaign.audience, Campaign.Audience.CARRIER_ROUTE)
|
||||
self.assertEqual(campaign.route_targeting["list_count_id"], 99)
|
||||
self.assertEqual(campaign.messages.count(), 1)
|
||||
self.assertIsNone(campaign.messages.get().contact_id)
|
||||
|
||||
def test_send_uses_list_count_endpoint(self):
|
||||
from directmail.providers.postcard import pcm as pcm_mod
|
||||
from directmail.services import enqueue_campaign_send
|
||||
|
||||
template = MessageTemplate.objects.create(
|
||||
name="Route design",
|
||||
channel=Channel.POSTCARD,
|
||||
body="Postcard",
|
||||
postcard_front={"design_id": 42, "size": "68", "provider": "pcm"},
|
||||
)
|
||||
campaign = create_campaign_draft(
|
||||
name="Routes",
|
||||
audience=Campaign.Audience.CARRIER_ROUTE,
|
||||
body="note",
|
||||
created_by=self.user,
|
||||
template=template,
|
||||
route_targeting={
|
||||
"selected_codes": ["60540:C001"],
|
||||
"list_count_id": 77,
|
||||
"record_count": 50,
|
||||
"mail_class": "Standard",
|
||||
},
|
||||
)
|
||||
pcm_mod.clear_token_cache()
|
||||
with self.settings(
|
||||
PCM_API_KEY="key",
|
||||
PCM_API_SECRET="secret",
|
||||
PCM_RETURN_ADDRESS=(
|
||||
'{"firstName":"Mo","lastName":"D","address":"1 Main",'
|
||||
'"city":"Naperville","state":"IL","zipCode":"60540"}'
|
||||
),
|
||||
):
|
||||
with patch("directmail.providers.postcard.pcm.login", return_value="tok"):
|
||||
with patch(
|
||||
"directmail.providers.postcard.pcm.pcm_request",
|
||||
return_value={"orderID": 555},
|
||||
) as req:
|
||||
enqueue_campaign_send(campaign)
|
||||
path = req.call_args.args[1]
|
||||
self.assertEqual(path, "/order/postcard/with-list-count")
|
||||
body = req.call_args.kwargs["json_body"]
|
||||
self.assertEqual(body["listCountID"], 77)
|
||||
self.assertEqual(body["recordCount"], 50)
|
||||
campaign.refresh_from_db()
|
||||
self.assertEqual(campaign.messages.get().provider_message_id, "555")
|
||||
|
||||
def test_mailing_list_postcard_still_creates_contacts(self):
|
||||
with patch(
|
||||
"directmail.views._fetch_pcm_designs",
|
||||
return_value=(
|
||||
[{"design_id": "42", "name": "Spring card", "size": "46"}],
|
||||
"",
|
||||
),
|
||||
):
|
||||
response = self.client.post(
|
||||
reverse("directmail:campaign_list"),
|
||||
{
|
||||
"name": "List mailer",
|
||||
"audience": Campaign.Audience.POSTCARD_OPT_IN,
|
||||
"template_id": "d:42",
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
campaign = Campaign.objects.get(name="List mailer")
|
||||
self.assertEqual(campaign.messages.count(), 1)
|
||||
self.assertEqual(campaign.messages.get().contact_id, self.contact.pk)
|
||||
|
||||
def test_postcard_does_not_inject_into_body(self):
|
||||
campaign = create_campaign_draft(
|
||||
name="March mailer",
|
||||
|
||||
Reference in new issue
Block a user