diff --git a/shipping/__pycache__/models.cpython-314.pyc b/shipping/__pycache__/models.cpython-314.pyc index e191f60..c307252 100644 Binary files a/shipping/__pycache__/models.cpython-314.pyc and b/shipping/__pycache__/models.cpython-314.pyc differ diff --git a/shipping/__pycache__/views.cpython-314.pyc b/shipping/__pycache__/views.cpython-314.pyc index 6265873..f399066 100644 Binary files a/shipping/__pycache__/views.cpython-314.pyc and b/shipping/__pycache__/views.cpython-314.pyc differ diff --git a/shipping/migrations/0004_shipmenttracking_and_more.py b/shipping/migrations/0004_shipmenttracking_and_more.py new file mode 100644 index 0000000..b138f80 --- /dev/null +++ b/shipping/migrations/0004_shipmenttracking_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 6.1 on 2026-10-14 16:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shipping', '0003_pickupschedule'), + ] + + operations = [ + migrations.CreateModel( + name='ShipmentTracking', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('tracking_id', models.CharField(db_index=True, max_length=100, unique=True)), + ('client_order_id', models.CharField(blank=True, db_index=True, default='', max_length=100)), + ('carrier', models.CharField(db_index=True, default='DELHIVERY', max_length=50)), + ('current_status', models.CharField(default='in_transit', max_length=100)), + ('expected_delivery_date', models.CharField(blank=True, default='', max_length=100, null=True)), + ('picked_up_date', models.CharField(blank=True, default='', max_length=100, null=True)), + ('origin', models.CharField(blank=True, default='', max_length=150)), + ('destination', models.CharField(blank=True, default='', max_length=150)), + ('raw_payload', models.JSONField(blank=True, default=dict)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'ordering': ['-updated_at'], + }, + ), + migrations.AddField( + model_name='pickupschedule', + name='expected_delivery_date', + field=models.CharField(blank=True, default='', max_length=100, null=True), + ), + ] diff --git a/shipping/migrations/__pycache__/0004_shipmenttracking_and_more.cpython-314.pyc b/shipping/migrations/__pycache__/0004_shipmenttracking_and_more.cpython-314.pyc new file mode 100644 index 0000000..0b40952 Binary files /dev/null and b/shipping/migrations/__pycache__/0004_shipmenttracking_and_more.cpython-314.pyc differ diff --git a/shipping/models.py b/shipping/models.py index 36e3ba6..ef5682a 100644 --- a/shipping/models.py +++ b/shipping/models.py @@ -71,6 +71,7 @@ class PickupSchedule(models.Model): pickup_location = models.CharField(max_length=150, blank=True, default='') status = models.CharField(max_length=50, choices=STATUS_CHOICES, default='SCHEDULED') expected_package_count = models.IntegerField(default=1) + expected_delivery_date = models.CharField(max_length=100, blank=True, null=True, default='') notes = models.TextField(blank=True, default='') raw_response = models.JSONField(default=dict, blank=True) created_at = models.DateTimeField(auto_now_add=True) @@ -82,3 +83,23 @@ class PickupSchedule(models.Model): def __str__(self): return f"[{self.carrier}] Pickup {self.pickup_id or 'Pending'} - AWB: {self.tracking_id} on {self.pickup_date} ({self.pickup_time})" + +class ShipmentTracking(models.Model): + tracking_id = models.CharField(max_length=100, unique=True, db_index=True) + client_order_id = models.CharField(max_length=100, blank=True, default='', db_index=True) + carrier = models.CharField(max_length=50, default='DELHIVERY', db_index=True) + current_status = models.CharField(max_length=100, default='in_transit') + expected_delivery_date = models.CharField(max_length=100, blank=True, null=True, default='') + picked_up_date = models.CharField(max_length=100, blank=True, null=True, default='') + origin = models.CharField(max_length=150, blank=True, default='') + destination = models.CharField(max_length=150, blank=True, default='') + raw_payload = models.JSONField(default=dict, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ['-updated_at'] + + def __str__(self): + return f"Shipment {self.tracking_id} [{self.carrier}] - ETA: {self.expected_delivery_date or 'N/A'}" + diff --git a/shipping/views.py b/shipping/views.py index 3c90cb7..64dbc08 100644 --- a/shipping/views.py +++ b/shipping/views.py @@ -8,7 +8,7 @@ from django.http import JsonResponse, HttpResponse from django.views.decorators.csrf import csrf_exempt from django.db.models import Q -from .models import ActivityLog, RiderAssignment +from .models import ActivityLog, RiderAssignment, PickupSchedule, ShipmentTracking from .sfx_client import execute_sfx_call, get_base_url, get_default_token from ekart.client import EkartAPIClient @@ -557,6 +557,20 @@ def webhook_listener_api(request): } ) saved_rider_id = rider_obj.id + + # Save ETA to DB if present in webhook payload + eta_wh = b_data.get('expected_delivery_date') or b_data.get('eta') or (b_data.get('order_details') or {}).get('expected_delivery_date') + if awb and eta_wh: + ShipmentTracking.objects.update_or_create( + tracking_id=str(awb).strip(), + defaults={ + 'client_order_id': str(client_ord).strip(), + 'current_status': str(st).strip(), + 'expected_delivery_date': str(eta_wh).strip(), + 'raw_payload': b_data + } + ) + PickupSchedule.objects.filter(tracking_id=str(awb).strip()).update(expected_delivery_date=str(eta_wh).strip()) except Exception as parse_err: pass @@ -729,6 +743,27 @@ def api_forward_router(request, action_name): 'timestamp': scan_detail.get('ScanDateTime') or scan_detail.get('StatusDateTime') }) norm_curr = 'delivered' if 'delivered' in curr_st.lower() else ('out_for_delivery' if 'out for delivery' in curr_st.lower() else ('in_transit' if 'transit' in curr_st.lower() or any(h['status'] == 'picked_up' for h in formatted_history) else curr_st.lower().replace(' ', '_'))) + eta_val = ship.get('ExpectedDeliveryDate') + + # Persist ETA & tracking internally to DB + try: + ShipmentTracking.objects.update_or_create( + tracking_id=awb_str, + defaults={ + 'carrier': 'DELHIVERY', + 'current_status': norm_curr, + 'expected_delivery_date': str(eta_val or ''), + 'picked_up_date': str(ship.get('PickedupDate') or ship.get('PickUpDate') or ''), + 'origin': str(ship.get('Origin') or ''), + 'destination': str(ship.get('Destination') or ''), + 'raw_payload': ship + } + ) + if eta_val: + PickupSchedule.objects.filter(tracking_id=awb_str).update(expected_delivery_date=str(eta_val)) + except Exception: + pass + return JsonResponse({ 'status': 'success', 'carrier': 'Delhivery Logistics',