karrio-server-events 2026.1.3__py3-none-any.whl → 2026.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,9 @@ class EventTypes(lib.StrEnum):
12
12
  shipment_delivery_failed = "shipment_delivery_failed"
13
13
  tracker_created = "tracker_created"
14
14
  tracker_updated = "tracker_updated"
15
+ pickup_scheduled = "pickup_scheduled"
16
+ pickup_cancelled = "pickup_cancelled"
17
+ pickup_closed = "pickup_closed"
15
18
  order_created = "order_created"
16
19
  order_updated = "order_updated"
17
20
  order_fulfilled = "order_fulfilled"
@@ -13,6 +13,7 @@ def register_signals():
13
13
  signals.post_save.connect(shipment_updated, sender=models.Shipment)
14
14
  signals.post_delete.connect(shipment_cancelled, sender=models.Shipment)
15
15
  signals.post_save.connect(tracker_updated, sender=models.Tracking)
16
+ signals.post_save.connect(pickup_updated, sender=models.Pickup)
16
17
 
17
18
  logger.info("Karrio events signals registered")
18
19
 
@@ -134,3 +135,47 @@ def tracker_updated(
134
135
  return
135
136
 
136
137
  tasks.notify_webhooks(event, data, event_at, context, schema=settings.schema)
138
+
139
+
140
+ @utils.disable_for_loaddata
141
+ @utils.error_wrapper
142
+ def pickup_updated(
143
+ sender, instance, created, raw, using, update_fields, *args, **kwargs
144
+ ):
145
+ """Pickup related events:
146
+ - pickup scheduled (created)
147
+ - pickup cancelled (status changed to cancelled)
148
+ - pickup closed (status changed to closed)
149
+ """
150
+ changes = update_fields or []
151
+ status_updated = "status" in changes
152
+
153
+ if created:
154
+ event = EventTypes.pickup_scheduled.value
155
+ elif (
156
+ status_updated
157
+ and instance.status == serializers.PickupStatus.cancelled.value
158
+ ):
159
+ event = EventTypes.pickup_cancelled.value
160
+ elif (
161
+ status_updated
162
+ and instance.status == serializers.PickupStatus.closed.value
163
+ ):
164
+ event = EventTypes.pickup_closed.value
165
+ else:
166
+ return
167
+
168
+ data = serializers.Pickup(instance).data
169
+ event_at = instance.updated_at
170
+ context = dict(
171
+ user_id=utils.failsafe(lambda: instance.created_by.id),
172
+ test_mode=instance.test_mode,
173
+ org_id=utils.failsafe(
174
+ lambda: instance.org.first().id if hasattr(instance, "org") else None
175
+ ),
176
+ )
177
+
178
+ if settings.MULTI_ORGANIZATIONS and context["org_id"] is None:
179
+ return
180
+
181
+ tasks.notify_webhooks(event, data, event_at, context, schema=settings.schema)
@@ -49,8 +49,24 @@ def periodic_data_archiving(*args, **kwargs):
49
49
  _run()
50
50
 
51
51
 
52
+ @db_periodic_task(crontab(hour=0, minute=30))
53
+ @with_task_telemetry("daily_pickup_close")
54
+ def daily_pickup_close():
55
+ from karrio.server.events.task_definitions.base import pickup
56
+
57
+ @utils.run_on_all_tenants
58
+ def _run(**kwargs):
59
+ utils.failsafe(
60
+ lambda: pickup.close_past_pickups(),
61
+ "An error occurred during pickup auto-close: $error",
62
+ )
63
+
64
+ _run()
65
+
66
+
52
67
  TASK_DEFINITIONS = [
53
68
  background_trackers_update,
54
69
  periodic_data_archiving,
70
+ daily_pickup_close,
55
71
  notify_webhooks,
56
72
  ]
@@ -0,0 +1,66 @@
1
+ import datetime
2
+ from django.utils import timezone
3
+
4
+ from karrio.server.core.logging import logger
5
+ import karrio.server.manager.models as models
6
+
7
+
8
+ def close_past_pickups():
9
+ """Auto-close one_time pickups whose pickup_date has passed.
10
+
11
+ Only closes pickups that are:
12
+ - Still in "scheduled" status
13
+ - Have pickup_type == "one_time" (stored in meta)
14
+ - Have a pickup_date strictly before today
15
+
16
+ Recurring and daily pickups without an end_date are excluded to avoid
17
+ prematurely closing open-ended recurring schedules.
18
+ """
19
+ today = timezone.now().date()
20
+
21
+ # Get all scheduled pickups with pickup_date in the past
22
+ past_pickups = models.Pickup.objects.filter(
23
+ status="scheduled",
24
+ pickup_date__lt=today,
25
+ )
26
+
27
+ # Separate one_time pickups (always close) from recurring/daily pickups
28
+ one_time_ids = []
29
+ recurring_expired_ids = []
30
+
31
+ for pickup in past_pickups.values("id", "meta"):
32
+ meta = pickup.get("meta") or {}
33
+ pickup_type = meta.get("pickup_type", "one_time")
34
+
35
+ if pickup_type == "one_time":
36
+ one_time_ids.append(pickup["id"])
37
+ elif pickup_type in ("daily", "recurring"):
38
+ # Only close recurring/daily pickups that have an explicit end_date
39
+ # that has passed. Leave open-ended recurring pickups alone.
40
+ recurrence = meta.get("recurrence") or {}
41
+ end_date_str = recurrence.get("end_date")
42
+
43
+ if end_date_str:
44
+ try:
45
+ end_date = datetime.date.fromisoformat(end_date_str)
46
+ if end_date < today:
47
+ recurring_expired_ids.append(pickup["id"])
48
+ except (ValueError, TypeError):
49
+ pass
50
+
51
+ ids_to_close = one_time_ids + recurring_expired_ids
52
+
53
+ if not ids_to_close:
54
+ logger.info("No past pickups to auto-close")
55
+ return
56
+
57
+ count = models.Pickup.objects.filter(id__in=ids_to_close).update(
58
+ status="closed",
59
+ )
60
+
61
+ logger.info(
62
+ "Auto-closed past pickups",
63
+ count=count,
64
+ one_time=len(one_time_ids),
65
+ recurring_expired=len(recurring_expired_ids),
66
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karrio_server_events
3
- Version: 2026.1.3
3
+ Version: 2026.1.5
4
4
  Summary: Multi-carrier shipping API Events module
5
5
  Author-email: karrio <hello@karrio.io>
6
6
  License-Expression: LGPL-3.0
@@ -4,7 +4,7 @@ karrio/server/events/apps.py,sha256=DIV8JdPB_3hD4DwhcQTevhph3PaceMkFSEGM1lOcdVY,
4
4
  karrio/server/events/filters.py,sha256=vMeY42IsoFl482-yX1BI17sw-9x9euOnzTl-860W3sg,2230
5
5
  karrio/server/events/models.py,sha256=WQQKpDnbUW97beFmCBJDTckSoE-h-HtxeRfjoNWepQY,2582
6
6
  karrio/server/events/router.py,sha256=IBUR7rfBkdEHQzWxYOPcVSM8NBp3fte9G6Q5BVTUNNw,95
7
- karrio/server/events/signals.py,sha256=3VlwVPWEyALcjn1MEbuh3u9Xojq8hSgmwYFz7lulW3M,4544
7
+ karrio/server/events/signals.py,sha256=PJ-E9-u7PwXEZ6CdmySPCJeUIPlnly1AVFyzF7pGZZo,5917
8
8
  karrio/server/events/tasks.py,sha256=kqs-W2VnDHOQNvieN8Ltehk578W5FTXTobj7t_mi9Xk,787
9
9
  karrio/server/events/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
10
10
  karrio/server/events/urls.py,sha256=oLTTzDWPhEFfK93EvzHTDRxqkw8Fr3iXneDXwFUkvcU,217
@@ -20,12 +20,13 @@ karrio/server/events/migrations/0009_alter_webhook_enabled_events.py,sha256=0bhn
20
20
  karrio/server/events/migrations/0010_event_event_created_at_idx.py,sha256=9vLPYVIId5Ni4W_EnmaCfL-F29YzoQ070963D3q6KgA,498
21
21
  karrio/server/events/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  karrio/server/events/serializers/__init__.py,sha256=S5KJONM1VFbB_Wyl__F2YbdSV7R_5RPNFos0ERkfDko,52
23
- karrio/server/events/serializers/base.py,sha256=Civqqod7X2u8wUfQpx7NejFpQfqQD8UWjztSOX4iWzg,2158
23
+ karrio/server/events/serializers/base.py,sha256=7D9qkzluhNVvJJYRQlyF_sjrzpTl_V-Eh9vIzhRz59g,2278
24
24
  karrio/server/events/serializers/event.py,sha256=dKYqldfSEjKMY7uB1rBFfEfSZ_dfEp8EPxbkBBSWzcc,289
25
25
  karrio/server/events/serializers/webhook.py,sha256=Ri5JS1SXpSsPUvQH989T_7Amu1p46y5TP54MEEhK8j8,889
26
26
  karrio/server/events/task_definitions/__init__.py,sha256=bpT73UG7mZL_JjEqMwbYx6q69jA8J5Jcoul1LcDokhA,81
27
- karrio/server/events/task_definitions/base/__init__.py,sha256=2c3k6qtrdTZ6gxV3JJIpVo7CV83xzVn0vLF8nBpud28,1622
27
+ karrio/server/events/task_definitions/base/__init__.py,sha256=ZrFjiU_3JdfdIchWDVi-8WC1hCwv4Jvkwb5JfTyKs7M,2045
28
28
  karrio/server/events/task_definitions/base/archiving.py,sha256=HVUfgzTSsfa-o2u6N1VuyPopnVLdFRwvv-3ix5LjKHc,7270
29
+ karrio/server/events/task_definitions/base/pickup.py,sha256=6QUcRnfEXGplUw2EW0yFJEtD-iCFPIb6B2_X9eiEAiQ,2164
29
30
  karrio/server/events/task_definitions/base/tracking.py,sha256=z5ja6lOjNMBtj5nu2TlKJxgnCelL58vxR5kpyDoUays,7179
30
31
  karrio/server/events/task_definitions/base/webhook.py,sha256=QNRq3xhLWQEbrs3zSy6Sa0AXUPhWE7YenKpPzZoFwL0,3616
31
32
  karrio/server/events/tests/__init__.py,sha256=5szv9hDvu0R0jtm3XWktY19rCaE8bZ2LdjRm4oJ0pYA,220
@@ -39,7 +40,7 @@ karrio/server/graph/schemas/events/__init__.py,sha256=Yg1rWew5DgSb_OnLy8n7qqwPLf
39
40
  karrio/server/graph/schemas/events/inputs.py,sha256=Mgy9qCUrbL-sMI5dNXsnbW8K5_PnGKM13brzZrc5OWk,1526
40
41
  karrio/server/graph/schemas/events/mutations.py,sha256=QDng4EXOBCBDVPuO4hxxRteQ_UfUxWUbjtHOk24X4LQ,1752
41
42
  karrio/server/graph/schemas/events/types.py,sha256=TPlrDwQV4LZXu-1t4gFl_eYayU39THmgYT3uv7VXnnw,2759
42
- karrio_server_events-2026.1.3.dist-info/METADATA,sha256=E-ltZ-s1lGP1ZZfzKHx-k0NOuwuMcqCQ7jUfArr3Iys,701
43
- karrio_server_events-2026.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
44
- karrio_server_events-2026.1.3.dist-info/top_level.txt,sha256=D1D7x8R3cTfjF_15mfiO7wCQ5QMtuM4x8GaPr7z5i78,12
45
- karrio_server_events-2026.1.3.dist-info/RECORD,,
43
+ karrio_server_events-2026.1.5.dist-info/METADATA,sha256=0WXhlnbfFUUiQzme5efWxpbdOv0ZbkDmpNyDWAkbr6I,701
44
+ karrio_server_events-2026.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
45
+ karrio_server_events-2026.1.5.dist-info/top_level.txt,sha256=D1D7x8R3cTfjF_15mfiO7wCQ5QMtuM4x8GaPr7z5i78,12
46
+ karrio_server_events-2026.1.5.dist-info/RECORD,,