diff --git a/temba/contacts/models.py b/temba/contacts/models.py index bb0ea5bec7..71d0922321 100644 --- a/temba/contacts/models.py +++ b/temba/contacts/models.py @@ -735,11 +735,14 @@ def _get_campaign_events(self, campaigns) -> list: if anchor is None: # contact has no value for this event's anchor field continue - when = anchor + event.get_offset() - if event.delivery_hour >= 0 and event.unit in (CampaignEvent.UNIT_DAYS, CampaignEvent.UNIT_WEEKS): - when = when.astimezone(self.org.timezone).replace( - hour=event.delivery_hour, minute=0, second=0, microsecond=0 - ) + try: + when = anchor + event.get_offset() + if event.delivery_hour >= 0 and event.unit in (CampaignEvent.UNIT_DAYS, CampaignEvent.UNIT_WEEKS): + when = when.astimezone(self.org.timezone).replace( + hour=event.delivery_hour, minute=0, second=0, microsecond=0 + ) + except OverflowError: # anchor is so close to the min/max date that the event time isn't representable + continue obj = { "type": "campaign_event", diff --git a/temba/contacts/tests/test_contactcrudl.py b/temba/contacts/tests/test_contactcrudl.py index 9bdb8a41be..fb1c310c4a 100644 --- a/temba/contacts/tests/test_contactcrudl.py +++ b/temba/contacts/tests/test_contactcrudl.py @@ -1350,11 +1350,27 @@ def test_timeline_inactive_and_missing_anchor(self): inactive.is_active = False inactive.save(update_fields=("is_active",)) + # an event on a field whose value is so early that the event time isn't representable -> omitted + due = self.create_field("due", "Due On", value_type=ContactField.TYPE_DATETIME) + contact.fields[str(due.uuid)] = {"text": "0001-01-01", "datetime": "0001-01-01T00:00:00-06:36"} + contact.save(update_fields=("fields",)) + CampaignEvent.create_message_event( + self.org, + self.admin, + campaign, + due, + -3, + unit="D", + translations={"eng": {"text": "Overflow"}}, + base_language="eng", + ) + response = self.requestView(timeline_url, self.admin) future = response.json()["future"] # only the single visible event survives self.assertEqual(1, len(future)) + self.assertEqual([], response.json()["past"]) self.assertEqual(1, response.json()["future_count"]) self.assertEqual(reverse("campaigns.campaign_read", args=[campaign.uuid]), future[0]["url"]) self.assertEqual("Visible", future[0]["message"])