TNA: reject notification mail to plan creator (send_reject) #243

Closed
opened 2026-08-13 13:11:39 +00:00 by shridhar.sabat · 1 comment
shridhar.sabat commented 2026-08-13 13:11:39 +00:00 (Migrated from gitea.ai-automation-hub.tech)

Context

Ajay found during dev testing (2026-08-13): rejecting a submitted TNA sends NO mail — the reject endpoint (routers/tna.py reject_tna_workflow) only records the audit event. Mails exist for submit (approval_request) and approve (intimations); rejection is silent outside the app.

Goal

Notify the plan CREATOR when their plan is rejected, with the reason, mirroring submit→approver.

Changes

  1. services/tna/trigger_mail.py: send_reject(rec, note, rejected_by, db=None, rules=None) — recipient = rec.created_by resolved Google-first (org_nodes → admin email_directory → legacy EMAIL_DIRECTORY; raw emails pass through); subject Plan rejected | TNA for Lot <lot>; body = lot/buyer, rejected by, reason, revise-and-resubmit + plan URL; never raises; _log_send event=reject.
  2. routers/tna.py reject endpoint: fire send_reject inside try/except (mail must never break reject, same as submit), response gains rejected_mailed.
  3. Tests: phase2_integration_test.py reject section asserts tna_email_log reject row (to=creator mailbox, dry-run ok).

Verification

  • scratch-DB suite (MAIL_DRY_RUN=true) — phase2 green
  • Live dev: create→submit→reject as pratik → real mail to pratik@sonalapparels.in (allowlisted) From bom@saplind.com; idempotent log rows
## Context Ajay found during dev testing (2026-08-13): rejecting a submitted TNA sends NO mail — the reject endpoint (routers/tna.py `reject_tna_workflow`) only records the audit event. Mails exist for submit (approval_request) and approve (intimations); rejection is silent outside the app. ## Goal Notify the plan CREATOR when their plan is rejected, with the reason, mirroring submit→approver. ## Changes 1. `services/tna/trigger_mail.py`: `send_reject(rec, note, rejected_by, db=None, rules=None)` — recipient = `rec.created_by` resolved Google-first (org_nodes → admin email_directory → legacy EMAIL_DIRECTORY; raw emails pass through); subject `Plan rejected | TNA for Lot <lot>`; body = lot/buyer, rejected by, reason, revise-and-resubmit + plan URL; never raises; `_log_send` event=`reject`. 2. `routers/tna.py` reject endpoint: fire `send_reject` inside try/except (mail must never break reject, same as submit), response gains `rejected_mailed`. 3. Tests: phase2_integration_test.py reject section asserts tna_email_log reject row (to=creator mailbox, dry-run ok). ## Verification - scratch-DB suite (MAIL_DRY_RUN=true) — phase2 green - Live dev: create→submit→reject as pratik → real mail to pratik@sonalapparels.in (allowlisted) From bom@saplind.com; idempotent log rows
shridhar.sabat commented 2026-08-13 15:15:24 +00:00 (Migrated from gitea.ai-automation-hub.tech)

Live verification on dev (2026-08-13, 20:44 IST) — ALL PASSED

Full chain create → submit → reject as pratik (seed admin):

Step Result
create REJECT-204430 → proposed 200 ✓
submit → approval_request to ajay.sable@saplind.com real SMTP mail sent ✓
reject (with note) → rejected 200 ✓
creator resolution (users-table fallback): Pratik Agrawal → pratik@sonalapparels.in ✓
reject mail to pratik@sonalapparels.in (From bom@saplind.com) real SMTP mail sent ✓
tna_email_log event=reject, success=t ✓

Issues found & fixed along the way

  1. reject sent NO mail at all (feature gap) → send_reject shipped (PR #244, promoted to UAT via #246)
  2. external creator not in Google Directory → users-table fallback (PR #248, promoted via #250)
  3. pratik@sonalapparels.in missing from MAIL_ALLOWLIST (compose anchors had 4 addrs, docs list 5) → PR #251, promoted via Promotion dev → uat: pratik@sonalapparels.in in MAIL_ALLOWLIST (refs #243, #251) (#252)
  4. laptop-first build hang (run #426) → timeout + VPS fallback (PR #247)

Deployed: dev + UAT, both containers verified via docker exec (MAIL_DRY_RUN=false, 5-addr allowlist, send_reject present). Phase-2 suite 45/45. Ajay can re-test the reject flow on dev — creator gets the rejection mail now.

## Live verification on dev (2026-08-13, 20:44 IST) — ALL PASSED Full chain create → submit → reject as pratik (seed admin): | Step | Result | |---|---| | create REJECT-204430 → proposed | 200 ✓ | | submit → approval_request to ajay.sable@saplind.com | **real SMTP mail sent** ✓ | | reject (with note) → rejected | 200 ✓ | | creator resolution (users-table fallback): Pratik Agrawal → pratik@sonalapparels.in | ✓ | | reject mail to pratik@sonalapparels.in (From bom@saplind.com) | **real SMTP mail sent** ✓ | | tna_email_log event=reject, success=t | ✓ | ### Issues found & fixed along the way 1. reject sent NO mail at all (feature gap) → send_reject shipped (PR #244, promoted to UAT via #246) 2. external creator not in Google Directory → users-table fallback (PR #248, promoted via #250) 3. pratik@sonalapparels.in missing from MAIL_ALLOWLIST (compose anchors had 4 addrs, docs list 5) → PR #251, promoted via #252 4. laptop-first build hang (run #426) → timeout + VPS fallback (PR #247) Deployed: dev + UAT, both containers verified via docker exec (MAIL_DRY_RUN=false, 5-addr allowlist, send_reject present). Phase-2 suite 45/45. Ajay can re-test the reject flow on dev — creator gets the rejection mail now.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sapl/sapl#243