healthcare: fix 4 bugs in segment-assignment + free-check email

Found during a bug-review pass of the one-email-per-provider work:

1. assign_all overwrite bug: an email on MULTIPLE rows (shared practice inbox /
   multiple NPIs -- 2,592 such emails, 299 with mixed status) was assigned by
   the LAST row, so a less-urgent row could clobber an urgent one (overdue ->
   free check). Now keeps the most-urgent (lowest-priority) assignment.

2. warm_segment double-import + wrong-row render: all of an email's rows passed
   the candidate filter, so it could be imported twice (over-counting the slice)
   and attribs_for could render a sibling row's blank due-date in the overdue
   email. Now requires row_matches(seg) for the specific row AND dedupes by
   email (one row per email).

3. free-check email rendered broken text ('last updated on  -- about  years
   ago', 'Last updated  . ~ yrs ago') for any provider whose NPPES date isn't
   cached yet (the free check goes to everyone, and the fill is gradual). Wrapped
   the example sentence + official-record card in listmonk {{ if
   .nppes_last_updated }}...{{ else }}...{{ end }}; added a date-free else
   branch. altbody keeps the conditionals (listmonk evaluates body+altbody), and
   the test/preview renderer gained a minimal {{ if/else/end }} evaluator so
   previews match real sends. Verified both branches render with zero unfilled
   tokens.

4. cross-cron double-send: pw-hc-campaign (warmup file) and pw-hc-nppes (63k
   file) share state but tracked imports per-segment; 312 emails overlap both
   files, so a provider could get an urgent email from one cron AND the free
   check from the other. Added load_all_imported() global guard (union of all
   segment state) so each provider gets exactly one healthcare email overall.

All verified: assignment regression test (10 cases) + new dup-email/guard checks
pass; all 6 templates render clean.
This commit is contained in:
justin 2026-06-20 16:14:44 -05:00
parent 0320dc17ba
commit 1acae2f20c
3 changed files with 92 additions and 8 deletions

View file

@ -14,7 +14,7 @@
<tr><td class="pw-pad" style="padding:28px;font-family:Inter,system-ui,sans-serif;color:#1f2937;">
<p style="font-size:15px;margin:0 0 18px;line-height:1.5;">Hi {{ .Subscriber.Name }},</p>
<h2 style="font-size:19px;margin:0 0 14px;color:#0f172a;line-height:1.3;">We pulled the public records for NPI {{ .Subscriber.Attribs.npi }} &mdash; here&rsquo;s a free check</h2>
<p style="font-size:14px;line-height:1.7;margin:0 0 18px;">As a quick example, the public NPPES NPI Registry shows the record for <strong>{{ .Subscriber.Attribs.practice }}</strong> was <strong>last updated on {{ .Subscriber.Attribs.nppes_last_updated }}</strong> &mdash; about <strong>{{ .Subscriber.Attribs.nppes_years_stale }} years ago</strong>. That&rsquo;s usually fine, but it&rsquo;s only one of several things payers and CMS check. Our free tool runs your NPI against the public government sources in one place &mdash; <strong>no signup, no cost</strong> &mdash; and tells you exactly where you stand.</p>
<p style="font-size:14px;line-height:1.7;margin:0 0 18px;">{{ if .Subscriber.Attribs.nppes_last_updated }}As a quick example, the public NPPES NPI Registry shows the record for <strong>{{ .Subscriber.Attribs.practice }}</strong> was <strong>last updated on {{ .Subscriber.Attribs.nppes_last_updated }}</strong> &mdash; about <strong>{{ .Subscriber.Attribs.nppes_years_stale }} years ago</strong>. That&rsquo;s usually fine, but it&rsquo;s only one of several things payers and CMS check. {{ else }}Your NPI touches several public government records &mdash; NPPES, the Medicare revalidation list, and the federal exclusion lists &mdash; and any one of them being off can hold up your payments. {{ end }}Our free tool runs your NPI against those public sources in one place &mdash; <strong>no signup, no cost</strong> &mdash; and tells you exactly where you stand.</p>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:22px 0;"><tr><td style="background:#f0fdfa;border:1px solid #99f6e4;border-radius:10px;padding:18px;">
<p style="margin:0 0 10px;font-size:14px;color:#0f766e;font-weight:700;">Your free check covers:</p>
@ -31,7 +31,9 @@
<div style="font-size:13px;color:#065f46;line-height:1.7;">Payers, clearinghouses, and CMS pull from NPPES. A stale address, taxonomy, or contact can cause <strong>claim denials, mail you never receive, and failed credentialing</strong>. CMS requires you to correct your NPPES record within 30 days of any change.</div>
</td></tr></table>
<!-- Official-record card: NPPES is fully public, so this mirrors the registry. -->
<!-- Official-record card: NPPES is fully public, so this mirrors the registry.
Only shown when we have the real Last Updated date for this NPI. -->
{{ if .Subscriber.Attribs.nppes_last_updated }}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:22px 0;">
<tr><td style="border:1px solid #cbd5e1;border-radius:10px;overflow:hidden;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
@ -49,6 +51,7 @@
</table>
</td></tr>
</table>
{{ end }}
<!-- Free-first reassurance: the check is free; a fix is optional + flat-fee. -->
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:18px 0;"><tr><td style="background:#f8fafc;border:1px solid #e5e7eb;border-radius:10px;padding:14px 18px;">