fix(pay): skip duplicate checkcomponents send on weekly-post retry

This commit is contained in:
Adam Moussa 2026-09-03 18:09:46 -04:00
parent 8d0a9850c6
commit 8bf45f73ee
No known key found for this signature in database
4 changed files with 71 additions and 14 deletions

View file

@ -17,7 +17,8 @@ emails payroll and DMs the pay summary as before. After those go out, it also
sends last week's after-hours dollar lines (by extension, previous Monday
through Sunday) to the paychex-integrations checkcomponents queue. Unassigned
fallback extension 100 and $0 totals are left out. A queue failure does not
block the Monday schedule post.
block the Monday schedule post. A retry or forced re-run of the same week does
not send the lines twice.
## v1.14.0 — July 2, 2026

View file

@ -17,7 +17,8 @@ emails payroll and DMs the pay summary as before. After those go out, it also
sends last week's after-hours dollar lines (by extension, previous Monday
through Sunday) to the paychex-integrations checkcomponents queue. Unassigned
fallback extension 100 and $0 totals are left out. A queue failure does not
block the Monday schedule post.
block the Monday schedule post. A retry or forced re-run of the same week does
not send the lines twice.
## v1.14.0 — July 2, 2026

View file

@ -277,16 +277,18 @@ def build_checkcomponents_payload(pay_record: dict) -> dict:
}
def _send_checkcomponents(pay_record: dict) -> None:
def _send_checkcomponents(pay_record: dict) -> bool:
"""Enqueue sibling dollar lines. Returns True if a message was sent."""
queue_url = os.environ.get("CHECKCOMPONENTS_QUEUE_URL", "").strip()
if not queue_url:
return
return False
payload = build_checkcomponents_payload(pay_record)
if not payload["lines"]:
return
return False
boto3.client("sqs").send_message(
QueueUrl=queue_url, MessageBody=json.dumps(payload)
)
return True
def handler(event, context):
@ -313,8 +315,11 @@ def handler(event, context):
pay_dm_user = os.environ.get("PAY_REPORT_USER")
week_key = prev_monday.strftime("%Y-%m-%d")
if pay_record["breakdown"]:
week_key = prev_monday.strftime("%Y-%m-%d")
existing = schedule.get_pay_record(week_key)
if existing and existing.get("checkcomponents_sent"):
pay_record["checkcomponents_sent"] = True
schedule.save_pay_record(week_key, pay_record)
prev_sunday = prev_monday + timedelta(days=6)
@ -349,14 +354,6 @@ def handler(event, context):
week_key,
)
try:
_send_checkcomponents(pay_record)
except Exception:
logger.exception(
"checkcomponents send failed week=%s",
week_key,
)
# --- Two-week schedule (always starts on Monday of this week) ---
this_monday = now - timedelta(days=now.weekday())
week_start = this_monday.strftime("%Y-%m-%d")
@ -417,6 +414,29 @@ def handler(event, context):
exc_info=True,
)
raise
# Enqueue after the schedule post is persisted so an EventBridge retry
# caused by save_schedule_post cannot send the same week twice. A sent
# flag on the pay record covers a later timeout or a forced re-run.
if pay_record["breakdown"] and not pay_record.get("checkcomponents_sent"):
sent = False
try:
sent = _send_checkcomponents(pay_record)
except Exception:
logger.exception(
"checkcomponents send failed week=%s",
week_key,
)
if sent:
try:
pay_record["checkcomponents_sent"] = True
schedule.save_pay_record(week_key, pay_record)
except Exception:
logger.exception(
"checkcomponents sent flag failed week=%s",
week_key,
)
return {
"posted": True,
"channel": channel_id,

View file

@ -85,6 +85,7 @@ def test_calculates_and_dms_pay(weeklypost_app, schedule, seed, slack, env, sqs)
assert body["windowEnd"] == "2026-06-07"
assert "payPeriodId" not in body
assert body["lines"] == [{"extension": "114", "amount": "50.00"}]
assert schedule.get_pay_record("2026-06-01")["checkcomponents_sent"] is True
@freeze_time(MON_0800)
@ -200,6 +201,40 @@ def test_empty_queue_url_skips_checkcomponents(
assert result["posted"] is True
sqs.send_message.assert_not_called()
assert "checkcomponents_sent" not in (schedule.get_pay_record("2026-06-01") or {})
@freeze_time(MON_0800)
def test_save_failure_does_not_enqueue_checkcomponents(
weeklypost_app, schedule, seed, slack, env, monkeypatch, sqs
):
seed.config(shift_rate="50")
seed.weekly("Monday", "114", "Alice")
monkeypatch.setattr(
weeklypost_app.ShiftSchedule,
"save_schedule_post",
MagicMock(side_effect=Exception("dynamo down")),
)
with pytest.raises(Exception, match="dynamo down"):
weeklypost_app.handler({"force": True}, None)
sqs.send_message.assert_not_called()
assert "checkcomponents_sent" not in (schedule.get_pay_record("2026-06-01") or {})
@freeze_time(MON_0800)
def test_retry_does_not_resend_checkcomponents(
weeklypost_app, schedule, seed, slack, env, sqs
):
seed.config(shift_rate="50")
seed.weekly("Monday", "114", "Alice")
weeklypost_app.handler({"force": True}, None)
weeklypost_app.handler({"force": True}, None)
sqs.send_message.assert_called_once()
assert schedule.get_pay_record("2026-06-01")["checkcomponents_sent"] is True
def test_payload_skips_fallback_and_zero(weeklypost_app):