feat: add shipping client api, views, and pickup scheduling architecture docs
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 39s
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 39s
This commit is contained in:
parent
c3cb4b3637
commit
d2b1a29fa3
3 changed files with 137 additions and 0 deletions
|
|
@ -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 '')
|
'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):
|
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.
|
Schedule / reschedule courier pickup for an order with assigned carrier.
|
||||||
|
|
|
||||||
|
|
@ -1750,6 +1750,12 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||||
'order': OrderSerializer(order).data
|
'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'])
|
@drf_action(detail=True, methods=['get'])
|
||||||
def print_label(self, request, pk=None):
|
def print_label(self, request, pk=None):
|
||||||
|
|
|
||||||
|
|
@ -153,3 +153,73 @@ class Order(models.Model):
|
||||||
- **Tradhox User Experience**:
|
- **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.
|
- 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`.
|
- 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=<AWB>`
|
||||||
|
- **Unified Alias**:
|
||||||
|
`GET /api/v1/carrier/schedule-pickup/?tracking_id=<AWB>`
|
||||||
|
- **POST Route**:
|
||||||
|
`POST /api/v1/carrier/pickup-schedule/` with JSON `{"tracking_id": "<AWB>"}`
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue