diff --git a/api/shipping_client.py b/api/shipping_client.py index 6df78a5..49a8c5d 100644 --- a/api/shipping_client.py +++ b/api/shipping_client.py @@ -1004,6 +1004,67 @@ class ShippingServiceClient: 'time_slot_label': 'Standard Daily Hub Sweep (Morning & Evening)' if is_ekart else ('Automated Cutoff Sweep' if not is_delhivery else '') } + def get_pickup_schedule(self, awb_number): + """ + Check courier pickup schedule (date, time, slot, pickup_id, location, carrier, status) + for a given tracking / AWB number via shipping microservice. + """ + trk = str(awb_number or '').strip() + if not trk or trk in ('Pending', 'N/A'): + return {'status': 'error', 'message': 'Valid tracking ID / AWB number is required'} + + target_urls = [ + f"{self.base_url}/api/v1/carrier/pickup-schedule/?tracking_id={trk}", + f"http://127.0.0.1:8086/api/v1/carrier/pickup-schedule/?tracking_id={trk}", + f"http://localhost:8086/api/v1/carrier/pickup-schedule/?tracking_id={trk}", + f"https://beta-shipping.tradhox.com/api/v1/carrier/pickup-schedule/?tracking_id={trk}" + ] + for u in target_urls: + try: + res = requests.get(u, headers=self._get_headers(), timeout=5, verify=False) + if res.status_code == 200: + return res.json() + except Exception: + continue + + # Direct database / local order fallback + try: + from .models import Order + ord_obj = Order.objects.filter(tracking=trk).first() + if not ord_obj and trk.startswith('TRDX-ORD-'): + ord_obj = Order.objects.filter(order_id=trk).first() + if ord_obj: + p_date = None + p_time = None + if ord_obj.eta and 'Pickup:' in str(ord_obj.eta): + import re + m_d = re.search(r'Pickup:\s*([\d-]+)', str(ord_obj.eta)) + m_t = re.search(r'\(([\d:]+)\)', str(ord_obj.eta)) + if m_d: p_date = m_d.group(1) + if m_t: p_time = m_t.group(1) + + p_slot = ord_obj.eta.replace('Pickup:', '').strip() if ord_obj.eta else 'Afternoon Slot' + return { + 'status': 'success', + 'tracking_id': trk, + 'order_id': ord_obj.order_id, + 'carrier': ord_obj.carrier, + 'carrier_display': ord_obj.carrier, + 'pickup_scheduled': bool(ord_obj.pickup_id), + 'pickup_id': ord_obj.pickup_id, + 'pickup_date': p_date or time.strftime('%Y-%m-%d'), + 'pickup_time': p_time or '14:00:00', + 'pickup_slot': p_slot, + 'status_display': f"Pickup Scheduled with {ord_obj.carrier} (ID: #{ord_obj.pickup_id})" if ord_obj.pickup_id else f"Ready for Pickup with {ord_obj.carrier}" + } + except Exception as e: + logger.warning(f"Order fallback in get_pickup_schedule: {e}") + + return { + 'status': 'error', + 'message': f"No pickup schedule found for tracking {trk}" + } + def schedule_pickup(self, order, pickup_date=None, pickup_time=None, package_count=1, notes=None, supplier_profile=None): """ Schedule / reschedule courier pickup for an order with assigned carrier. diff --git a/api/views.py b/api/views.py index 128a016..600bc6d 100644 --- a/api/views.py +++ b/api/views.py @@ -1750,6 +1750,12 @@ class OrderViewSet(viewsets.ModelViewSet): 'order': OrderSerializer(order).data }) + @drf_action(detail=True, methods=['get']) + def pickup_schedule(self, request, pk=None): + order = self.get_object() + awb = request.query_params.get('awb') or request.query_params.get('tracking_id') or order.tracking + res = shipping_client.get_pickup_schedule(awb) + return Response(res) @drf_action(detail=True, methods=['get']) def print_label(self, request, pk=None): diff --git a/documentation/pickup_scheduling_architecture.md b/documentation/pickup_scheduling_architecture.md index bb66236..8cde392 100644 --- a/documentation/pickup_scheduling_architecture.md +++ b/documentation/pickup_scheduling_architecture.md @@ -153,3 +153,73 @@ class Order(models.Model): - **Tradhox User Experience**: - In Tradhox Supplier Central, once an order is manifested and scheduled, Tradhox surfaces the active **Pickup Request ID (`PR #328095871`)** and **`PICKUP SCHEDULED`** badge. - Merchants do not need to manually click "Add to Pickup" inside the Delhivery One portal; the courier executive will collect all ready packages for warehouse `SAM`. + +--- + +## 7. Check Pickup Schedule by Tracking ID (AWB) Specification + +### 7.1. REST Endpoints +Clients can query the pickup schedule using either endpoint: + +- **Primary Route**: + `GET /api/v1/carrier/pickup-schedule/?tracking_id=` +- **Unified Alias**: + `GET /api/v1/carrier/schedule-pickup/?tracking_id=` +- **POST Route**: + `POST /api/v1/carrier/pickup-schedule/` with JSON `{"tracking_id": ""}` + +#### Supported Query Parameters +- `tracking_id` / `awb` / `tracking` / `waybill`: The carrier waybill or client order ID (e.g. `69873010000232`). + +### 7.2. Response Schema +```json +{ + "status": "success", + "tracking_id": "69873010000232", + "order_id": "32-18", + "carrier": "DELHIVERY", + "carrier_display": "Delhivery Logistics", + "pickup_scheduled": true, + "pickup_id": "328095871", + "pickup_date": "2026-10-14", + "pickup_time": "10:00:00", + "pickup_slot": "Morning Slot (10:00 AM – 1:00 PM)", + "pickup_location": "SAM", + "shipment_status": "Manifested", + "pickup_status": "CONFIRMED", + "status_display": "Pickup Confirmed with Delhivery Logistics (PR #328095871) for 2026-10-14 (Morning Slot (10:00 AM – 1:00 PM))", + "destination": { + "city": "Kanchipuram", + "pincode": 600059 + }, + "consignee": { + "name": "Vignesh Krishnasamy", + "city": "Kanchipuram", + "state": "Tamil Nadu", + "pincode": 600059 + }, + "scans": [ + { + "scan": "Manifested", + "instructions": "Manifest uploaded", + "location": "Mettupalayam_IshwaryNgr_D (Tamil Nadu)", + "timestamp": "2026-10-12T19:04:35.275" + } + ], + "details": { + "expected_package_count": 1, + "manifest_date": "2026-10-12T19:04:35.245", + "picked_up_date": null + } +} +``` + +### 7.3. Resolution Hierarchy +1. **`shipping_pickupschedule` Table**: Checks if a local record exists. +2. **Live Carrier Tracking API**: + - For **Delhivery**, calls `/api/v1/packages/json/?waybill={trk}`, extracting origin warehouse, consignee address, and shipment status. + - Cross-checks with active warehouse pickup requests. +3. **Cross-Database Order Lookup**: + - Queries `api_order` in `customerprofile_beta` to correlate `pickup_id` and ETA slot. +4. **Auto-Caching**: Updates the `PickupSchedule` model so subsequent lookups are instant. +