feat: add carrier checker template and multi-carrier routing URL endpoints
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 38s
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 38s
This commit is contained in:
parent
e01501b00f
commit
92791be802
15 changed files with 1040 additions and 357 deletions
Binary file not shown.
|
|
@ -264,8 +264,26 @@ class DelhiveryAPIClient:
|
|||
"cgm": str(weight),
|
||||
"pt": payment_type
|
||||
}
|
||||
resp = self._execute_request("Get Rate Estimate", "GET", "/api/kcs/v1/summary/get", params=params)
|
||||
return resp.json()
|
||||
try:
|
||||
resp = self._execute_request("Get Rate Estimate", "GET", "/api/kcs/v1/summary/get", params=params)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
if isinstance(data, list) and len(data) > 0 and data[0].get('total_amount'):
|
||||
data[0]['total_amount'] = float(data[0]['total_amount'])
|
||||
return data[0]
|
||||
elif isinstance(data, dict) and data.get('total_amount'):
|
||||
return data
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
weight_slabs = max(1, int(weight) // 500)
|
||||
# Delhivery standard B2C surface rate slab
|
||||
base_rate = 35.0 if str(origin_pin)[:3] == str(dest_pin)[:3] else 46.0
|
||||
return {
|
||||
"total_amount": round(base_rate + ((weight_slabs - 1) * 11.0), 2),
|
||||
"currency": "INR",
|
||||
"estimated": True
|
||||
}
|
||||
|
||||
# --- 5. Labels & Packing Slips ---
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -8,8 +8,8 @@ from .models import EkartActivityLog, EkartCredential, EkartShipment
|
|||
|
||||
DEFAULT_EKART_BASE_URL = getattr(settings, 'EKART_BASE_URL', 'https://app.elite.ekartlogistics.in')
|
||||
DEFAULT_EKART_CLIENT_ID = getattr(settings, 'EKART_CLIENT_ID', 'EKART_6aba5a11229f5801fef04696')
|
||||
DEFAULT_EKART_USERNAME = getattr(settings, 'EKART_USERNAME', 'sample_username')
|
||||
DEFAULT_EKART_PASSWORD = getattr(settings, 'EKART_PASSWORD', 'sample_password')
|
||||
DEFAULT_EKART_USERNAME = getattr(settings, 'EKART_USERNAME', 'tradiraw@gmail.com')
|
||||
DEFAULT_EKART_PASSWORD = getattr(settings, 'EKART_PASSWORD', 'Database@2026')
|
||||
|
||||
|
||||
class EkartAPIClient:
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -251,19 +251,32 @@ def check_shadowfax_availability(pickup_pincode, drop_pincode, service='express'
|
|||
try:
|
||||
res = execute_sfx_call('FORWARD', 'Carrier Allocation Serviceability Check', 'GET', url, headers, env=env)
|
||||
if res.get('status_code') == 200:
|
||||
parsed = json.loads(res.get('response', '{}')) if isinstance(res.get('response'), str) else res.get('response', {})
|
||||
data = parsed.get('data', {})
|
||||
p_list = data.get('pincodes', []) if isinstance(data, dict) else []
|
||||
resp_payload = res.get('response', '')
|
||||
if isinstance(resp_payload, str):
|
||||
try:
|
||||
parsed = json.loads(resp_payload)
|
||||
except Exception:
|
||||
parsed = {}
|
||||
else:
|
||||
parsed = resp_payload
|
||||
|
||||
pickup_info = next((p for p in p_list if str(p.get('pincode')) == str(pickup_pincode)), None)
|
||||
drop_info = next((p for p in p_list if str(p.get('pincode')) == str(drop_pincode)), None)
|
||||
if isinstance(parsed, list):
|
||||
p_list = parsed
|
||||
elif isinstance(parsed, dict):
|
||||
data = parsed.get('data', {})
|
||||
p_list = data.get('pincodes', []) if isinstance(data, dict) else parsed.get('pincodes', [])
|
||||
else:
|
||||
p_list = []
|
||||
|
||||
if str(pickup_pincode) == str(drop_pincode) and p_list:
|
||||
pickup_info = next((p for p in p_list if str(p.get('code') or p.get('pincode')) == str(pickup_pincode)), None)
|
||||
drop_info = next((p for p in p_list if str(p.get('code') or p.get('pincode')) == str(drop_pincode)), None)
|
||||
|
||||
if str(pickup_pincode) == str(drop_pincode) and p_list and not (pickup_info and drop_info):
|
||||
pickup_info = drop_info = p_list[0]
|
||||
|
||||
fm_ok = pickup_info.get('serviceable', True) if pickup_info else True
|
||||
lm_ok = drop_info.get('serviceable', True) if drop_info else True
|
||||
cod_ok = drop_info.get('cod_available', True) if drop_info else True
|
||||
fm_ok = bool(pickup_info) if p_list else True
|
||||
lm_ok = bool(drop_info) if p_list else True
|
||||
cod_ok = bool(drop_info.get('cod_available', True)) if drop_info else True
|
||||
|
||||
fm_city = (pickup_info.get('city') if pickup_info else None) or p_city
|
||||
fm_state = (pickup_info.get('state') if pickup_info else None) or p_state
|
||||
|
|
@ -272,6 +285,20 @@ def check_shadowfax_availability(pickup_pincode, drop_pincode, service='express'
|
|||
|
||||
overall_serviceable = bool(fm_ok and lm_ok)
|
||||
|
||||
# Available Shadowfax delivery types
|
||||
sfx_services = []
|
||||
if drop_info and isinstance(drop_info.get('services'), list):
|
||||
sfx_services = drop_info.get('services')
|
||||
elif pickup_info and isinstance(pickup_info.get('services'), list):
|
||||
sfx_services = pickup_info.get('services')
|
||||
else:
|
||||
sfx_services = ['Regular', 'Surface']
|
||||
|
||||
delivery_types = ['Prepaid', 'COD (Cash on Delivery)']
|
||||
for s in sfx_services:
|
||||
delivery_types.append(f"{s} Cargo/Delivery")
|
||||
delivery_types.append('Reverse Pickup')
|
||||
|
||||
first_mile = {
|
||||
"serviceable": bool(fm_ok),
|
||||
"pincode": str(pickup_pincode),
|
||||
|
|
@ -295,9 +322,13 @@ def check_shadowfax_availability(pickup_pincode, drop_pincode, service='express'
|
|||
return {
|
||||
"carrier": "SHADOWFAX",
|
||||
"serviceable": overall_serviceable,
|
||||
"first_mile_available": bool(fm_ok),
|
||||
"last_mile_available": bool(lm_ok),
|
||||
"cod_available": bool(cod_ok),
|
||||
"estimated_cost": estimated_rate if overall_serviceable else None,
|
||||
"estimated_charge": estimated_rate if overall_serviceable else None,
|
||||
"currency": "INR",
|
||||
"delivery_types": delivery_types,
|
||||
"first_mile": first_mile,
|
||||
"last_mile": last_mile,
|
||||
"details": drop_info or parsed
|
||||
|
|
@ -415,8 +446,18 @@ def check_ekart_availability(pickup_pincode, drop_pincode, weight=500, payment_t
|
|||
if isinstance(res, dict):
|
||||
serviceable = res.get('serviceable', res.get('status', True))
|
||||
cod_available = res.get('cod_available', cod_available)
|
||||
elif isinstance(res, list) and len(res) > 0:
|
||||
serviceable = True
|
||||
first_item = res[0]
|
||||
charges = first_item.get('forwardDeliveredCharges', {})
|
||||
total_est = charges.get('totalForwardDeliveredEstimate')
|
||||
if total_est:
|
||||
try:
|
||||
live_cost = float(total_est)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
live_cost = estimated_rate
|
||||
live_cost = live_cost if 'live_cost' in locals() else estimated_rate
|
||||
try:
|
||||
rate_res = client.get_rate_estimate(
|
||||
pickup_pincode=pickup_pincode,
|
||||
|
|
@ -454,12 +495,21 @@ def check_ekart_availability(pickup_pincode, drop_pincode, weight=500, payment_t
|
|||
"message": f"Last-mile delivery serviceable in {d_city}, {d_state}" if serviceable else f"Destination pincode {drop_pincode} not serviceable"
|
||||
}
|
||||
|
||||
delivery_types = ['Prepaid', 'Forward Drop (Doorstep Delivery)', 'Forward Pickup (Hub Sweep)']
|
||||
if cod_available:
|
||||
delivery_types.append('COD (Cash on Delivery)')
|
||||
delivery_types.extend(['Reverse Pickup (RVP)', 'Reverse Drop', 'Surface / Air Delivery'])
|
||||
|
||||
return {
|
||||
"carrier": "EKART",
|
||||
"serviceable": bool(serviceable),
|
||||
"first_mile_available": bool(serviceable),
|
||||
"last_mile_available": bool(serviceable),
|
||||
"cod_available": bool(cod_available),
|
||||
"estimated_cost": live_cost if serviceable else None,
|
||||
"estimated_charge": live_cost if serviceable else None,
|
||||
"currency": "INR",
|
||||
"delivery_types": delivery_types,
|
||||
"first_mile": first_mile,
|
||||
"last_mile": last_mile,
|
||||
"details": res
|
||||
|
|
@ -589,15 +639,34 @@ def check_delhivery_availability(pickup_pincode, drop_pincode, weight=500, payme
|
|||
except Exception as e:
|
||||
api_details['rate_api_warning'] = str(e)
|
||||
|
||||
delivery_types = []
|
||||
if drop_info and drop_info.get('prepaid'):
|
||||
delivery_types.append('Prepaid')
|
||||
else:
|
||||
delivery_types.append('Prepaid')
|
||||
if drop_info and drop_info.get('cod'):
|
||||
delivery_types.append('COD (Cash on Delivery)')
|
||||
if drop_info and drop_info.get('cash'):
|
||||
delivery_types.append('Cash Support')
|
||||
if drop_info and drop_info.get('reverse_pickup'):
|
||||
delivery_types.append('Reverse Pickup (RVP)')
|
||||
if drop_info and drop_info.get('repl'):
|
||||
delivery_types.append('Replacement (REPL)')
|
||||
delivery_types.extend(['Surface Cargo', 'Express Delivery'])
|
||||
|
||||
return {
|
||||
"carrier": "DELHIVERY",
|
||||
"serviceable": True,
|
||||
"first_mile_available": bool(p_ok),
|
||||
"last_mile_available": bool(d_ok),
|
||||
"cod_available": cod_avail,
|
||||
"estimated_cost": live_cost,
|
||||
"estimated_charge": live_cost,
|
||||
"currency": "INR",
|
||||
"city": d_city,
|
||||
"state": d_state,
|
||||
"state_code": d_st_code,
|
||||
"delivery_types": delivery_types,
|
||||
"first_mile": first_mile,
|
||||
"last_mile": last_mile,
|
||||
"details": api_details
|
||||
|
|
@ -731,6 +800,277 @@ def select_best_carrier(availability_dict, selection_strategy='CHEAPEST', prefer
|
|||
}
|
||||
|
||||
|
||||
def check_single_pincode_all_carriers(pincode, weight=500, use_mock=False):
|
||||
"""
|
||||
Evaluates logistics capabilities and estimated charges for a single pincode across
|
||||
all three carriers: Shadowfax, Delhivery, and Ekart.
|
||||
|
||||
Returns structured data for each carrier containing:
|
||||
- city, state, pincode
|
||||
- first_mile_available (bool) & description (hub/sweep status)
|
||||
- last_mile_available (bool) & description (doorstep delivery status)
|
||||
- delivery_types (list of supported services: Prepaid, COD, Reverse Pickup, Surface, Express, etc.)
|
||||
- estimated_charge (float freight cost in INR)
|
||||
"""
|
||||
pin_str = str(pincode or "").strip()
|
||||
city, state = resolve_city_state_by_pincode(pin_str)
|
||||
weight_slabs = max(1, int(weight) // 500)
|
||||
|
||||
# 1. Ekart Logistics Check
|
||||
ekart_data = {}
|
||||
if use_mock:
|
||||
ekart_data = {
|
||||
"carrier": "EKART",
|
||||
"pincode": pin_str,
|
||||
"city": city,
|
||||
"state": state,
|
||||
"serviceable": True,
|
||||
"first_mile_available": True,
|
||||
"first_mile_details": f"First-mile pickup serviceable in {city}, {state} (Hub Sweep Active)",
|
||||
"last_mile_available": True,
|
||||
"last_mile_details": f"Last-mile delivery serviceable in {city}, {state} (Doorstep Active)",
|
||||
"cod_available": True,
|
||||
"delivery_types": ["Prepaid", "COD (Max ₹49,999)", "Forward Drop (Doorstep)", "Forward Pickup (Hub Sweep)", "Reverse Pickup (RVP)", "Reverse Drop", "Surface / Air Delivery"],
|
||||
"estimated_charge": round(38.0 + (weight_slabs * 12.0), 2),
|
||||
"currency": "INR"
|
||||
}
|
||||
else:
|
||||
try:
|
||||
client = EkartAPIClient()
|
||||
v2_res = client.check_serviceability_v2(pin_str)
|
||||
e_serv = bool(v2_res.get('status'))
|
||||
e_det = v2_res.get('details', {}) if e_serv else {}
|
||||
e_city = e_det.get('city') or city
|
||||
e_state = e_det.get('state') or state
|
||||
e_fm = bool(e_det.get('forward_pickup', e_serv))
|
||||
e_lm = bool(e_det.get('forward_drop', e_serv))
|
||||
e_cod = bool(e_det.get('cod', e_serv))
|
||||
e_max_cod = e_det.get('max_cod_amount', 49999)
|
||||
|
||||
e_types = ['Prepaid']
|
||||
if e_cod:
|
||||
e_types.append(f"COD (Max Rs. {e_max_cod:,})" if isinstance(e_max_cod, (int, float)) else "COD (Cash on Delivery)")
|
||||
if e_det.get('forward_drop', e_serv):
|
||||
e_types.append('Forward Drop (Doorstep Delivery)')
|
||||
if e_det.get('forward_pickup', e_serv):
|
||||
e_types.append('Forward Pickup (Hub Sweep)')
|
||||
if e_det.get('reverse_pickup', e_serv):
|
||||
e_types.append('Reverse Pickup (RVP)')
|
||||
if e_det.get('reverse_drop', e_serv):
|
||||
e_types.append('Reverse Drop')
|
||||
e_types.append('Surface / Air Delivery')
|
||||
|
||||
# Ekart Rate calculation
|
||||
e_cost = round(33.04 + ((weight_slabs - 1) * 12.0), 2)
|
||||
try:
|
||||
v3_res = client.check_serviceability_v3(pin_str, pin_str, 10, 10, 10, weight, 'Prepaid', 1000)
|
||||
if isinstance(v3_res, list) and len(v3_res) > 0:
|
||||
chg = v3_res[0].get('forwardDeliveredCharges', {}).get('totalForwardDeliveredEstimate')
|
||||
if chg:
|
||||
e_cost = float(chg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
ekart_data = {
|
||||
"carrier": "EKART",
|
||||
"pincode": pin_str,
|
||||
"city": e_city,
|
||||
"state": e_state,
|
||||
"serviceable": e_serv,
|
||||
"first_mile_available": e_fm,
|
||||
"first_mile_details": f"First-mile pickup serviceable in {e_city}, {e_state} (Hub Sweep Active)" if e_fm else "Pickup Hub Sweep Blocked",
|
||||
"last_mile_available": e_lm,
|
||||
"last_mile_details": f"Last-mile delivery serviceable in {e_city}, {e_state} (Doorstep Active)" if e_lm else "Doorstep Delivery Blocked",
|
||||
"cod_available": e_cod,
|
||||
"delivery_types": e_types if e_serv else [],
|
||||
"estimated_charge": e_cost if e_serv else None,
|
||||
"currency": "INR"
|
||||
}
|
||||
except Exception as e:
|
||||
ekart_data = {
|
||||
"carrier": "EKART",
|
||||
"pincode": pin_str,
|
||||
"city": city,
|
||||
"state": state,
|
||||
"serviceable": False,
|
||||
"first_mile_available": False,
|
||||
"first_mile_details": str(e),
|
||||
"last_mile_available": False,
|
||||
"last_mile_details": str(e),
|
||||
"cod_available": False,
|
||||
"delivery_types": [],
|
||||
"estimated_charge": None,
|
||||
"currency": "INR",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
# 2. Delhivery Logistics Check
|
||||
d_info = get_delhivery_pincode_info(pin_str)
|
||||
d_serv = bool(d_info and d_info.get('serviceable'))
|
||||
d_city = (d_info.get('city') if d_info else None) or city
|
||||
d_state = (d_info.get('state') if d_info else None) or state
|
||||
d_fm = bool(d_serv and (d_info.get('reverse_pickup', True) if d_info else True))
|
||||
d_lm = bool(d_serv)
|
||||
d_cod = bool(d_info.get('cod', True) if d_info else False)
|
||||
d_types = []
|
||||
if d_info and d_info.get('prepaid'):
|
||||
d_types.append('Prepaid')
|
||||
if d_cod:
|
||||
d_types.append('COD (Cash on Delivery)')
|
||||
if d_info and d_info.get('cash'):
|
||||
d_types.append('Cash Support')
|
||||
if d_info and d_info.get('reverse_pickup'):
|
||||
d_types.append('Reverse Pickup (RVP)')
|
||||
if d_info and d_info.get('repl'):
|
||||
d_types.append('Replacement (REPL)')
|
||||
if d_serv:
|
||||
d_types.extend(['Surface Cargo', 'Express Delivery'])
|
||||
|
||||
d_cost = round(35.0 + ((weight_slabs - 1) * 11.0), 2) if d_serv else None
|
||||
|
||||
delhivery_data = {
|
||||
"carrier": "DELHIVERY",
|
||||
"pincode": pin_str,
|
||||
"city": d_city,
|
||||
"state": d_state,
|
||||
"serviceable": d_serv,
|
||||
"first_mile_available": d_fm,
|
||||
"first_mile_details": f"First-mile pickup serviceable in {d_city}, {d_state} (B2C Express Hub Active)" if d_fm else "Pickup Hub Blocked",
|
||||
"last_mile_available": d_lm,
|
||||
"last_mile_details": f"Last-mile delivery serviceable in {d_city}, {d_state} (Doorstep Active)" if d_lm else "Destination Pincode Unserviceable",
|
||||
"cod_available": d_cod,
|
||||
"delivery_types": d_types,
|
||||
"estimated_charge": d_cost,
|
||||
"currency": "INR"
|
||||
}
|
||||
|
||||
# 3. Shadowfax Logistics Check
|
||||
s_cost = round(45.0 + ((weight_slabs - 1) * 15.0), 2)
|
||||
sfx_data = {}
|
||||
if use_mock:
|
||||
sfx_data = {
|
||||
"carrier": "SHADOWFAX",
|
||||
"pincode": pin_str,
|
||||
"city": city,
|
||||
"state": state,
|
||||
"serviceable": True,
|
||||
"first_mile_available": True,
|
||||
"first_mile_details": f"First-mile pickup serviceable in {city}, {state} (Store Sweep Active)",
|
||||
"last_mile_available": True,
|
||||
"last_mile_details": f"Last-mile delivery serviceable in {city}, {state} (Doorstep Active)",
|
||||
"cod_available": True,
|
||||
"delivery_types": ["Prepaid", "COD (Cash on Delivery)", "Regular Cargo/Delivery", "Surface Cargo/Delivery", "Reverse Pickup"],
|
||||
"estimated_charge": s_cost,
|
||||
"currency": "INR"
|
||||
}
|
||||
else:
|
||||
try:
|
||||
base_url = get_base_url('production')
|
||||
token = get_default_token('production')
|
||||
headers = {"Authorization": f"Token {token}", "Content-Type": "application/json"}
|
||||
url = f"{base_url}/v1/clients/serviceability/?service=customer_delivery&page=1&count=10&pincodes={pin_str}"
|
||||
res = execute_sfx_call('FORWARD', 'Single Pincode Serviceability Check', 'GET', url, headers, env='production')
|
||||
if res.get('status_code') == 200:
|
||||
resp_payload = res.get('response', '')
|
||||
parsed = json.loads(resp_payload) if isinstance(resp_payload, str) else resp_payload
|
||||
p_list = parsed if isinstance(parsed, list) else (parsed.get('data', {}).get('pincodes', []) if isinstance(parsed, dict) else [])
|
||||
match = next((p for p in p_list if str(p.get('code') or p.get('pincode')) == pin_str), None)
|
||||
|
||||
s_serv = bool(match) if p_list else True
|
||||
s_services = match.get('services', ['Regular', 'Surface']) if match else ['Regular', 'Surface']
|
||||
s_types = ['Prepaid', 'COD (Cash on Delivery)']
|
||||
for s in s_services:
|
||||
s_types.append(f"{s} Cargo/Delivery")
|
||||
s_types.append('Reverse Pickup')
|
||||
|
||||
sfx_data = {
|
||||
"carrier": "SHADOWFAX",
|
||||
"pincode": pin_str,
|
||||
"city": city,
|
||||
"state": state,
|
||||
"serviceable": s_serv,
|
||||
"first_mile_available": s_serv,
|
||||
"first_mile_details": f"First-mile pickup serviceable in {city}, {state} (Store Sweep Active)" if s_serv else "Pickup Sweep Blocked",
|
||||
"last_mile_available": s_serv,
|
||||
"last_mile_details": f"Last-mile delivery serviceable in {city}, {state} (Doorstep Active)" if s_serv else "Doorstep Delivery Blocked",
|
||||
"cod_available": True,
|
||||
"delivery_types": s_types if s_serv else [],
|
||||
"estimated_charge": s_cost if s_serv else None,
|
||||
"currency": "INR"
|
||||
}
|
||||
else:
|
||||
sfx_data = {
|
||||
"carrier": "SHADOWFAX",
|
||||
"pincode": pin_str,
|
||||
"city": city,
|
||||
"state": state,
|
||||
"serviceable": False,
|
||||
"first_mile_available": False,
|
||||
"first_mile_details": f"SFX API returned status {res.get('status_code')}",
|
||||
"last_mile_available": False,
|
||||
"last_mile_details": f"SFX API returned status {res.get('status_code')}",
|
||||
"cod_available": False,
|
||||
"delivery_types": [],
|
||||
"estimated_charge": None,
|
||||
"currency": "INR"
|
||||
}
|
||||
except Exception as e:
|
||||
sfx_data = {
|
||||
"carrier": "SHADOWFAX",
|
||||
"pincode": pin_str,
|
||||
"city": city,
|
||||
"state": state,
|
||||
"serviceable": False,
|
||||
"first_mile_available": False,
|
||||
"first_mile_details": str(e),
|
||||
"last_mile_available": False,
|
||||
"last_mile_details": str(e),
|
||||
"cod_available": False,
|
||||
"delivery_types": [],
|
||||
"estimated_charge": None,
|
||||
"currency": "INR",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
carriers_map = {
|
||||
"SHADOWFAX": sfx_data,
|
||||
"DELHIVERY": delhivery_data,
|
||||
"EKART": ekart_data
|
||||
}
|
||||
|
||||
# Best carrier recommendation
|
||||
serviceable_carriers = [
|
||||
c for c in carriers_map.values()
|
||||
if c.get('serviceable') and c.get('estimated_charge') is not None
|
||||
]
|
||||
if serviceable_carriers:
|
||||
serviceable_carriers.sort(key=lambda x: (x['estimated_charge'], CARRIER_PRIORITY.get(x['carrier'].upper(), 99)))
|
||||
best = serviceable_carriers[0]
|
||||
best_carrier = {
|
||||
"selected": True,
|
||||
"carrier": best['carrier'],
|
||||
"charge": best['estimated_charge'],
|
||||
"reason": f"Optimal carrier for pincode {pin_str} with lowest estimated charge of Rs. {best['estimated_charge']}"
|
||||
}
|
||||
else:
|
||||
best_carrier = {
|
||||
"selected": False,
|
||||
"carrier": None,
|
||||
"charge": None,
|
||||
"reason": f"Pincode {pin_str} is not serviceable by any registered carrier."
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"pincode": pin_str,
|
||||
"city": city,
|
||||
"state": state,
|
||||
"weight": weight,
|
||||
"carriers": carriers_map,
|
||||
"best_carrier": best_carrier
|
||||
}
|
||||
|
||||
|
||||
|
||||
def dispatch_auto_allocated_order(order_payload, selection_strategy='CHEAPEST', preferred_carrier='SHADOWFAX', allow_fallback=True, env='production'):
|
||||
"""
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -276,6 +276,32 @@ class ShippingAPITestCase(TestCase):
|
|||
self.assertTrue(delhivery['first_mile']['serviceable'])
|
||||
self.assertTrue(delhivery['last_mile']['serviceable'])
|
||||
|
||||
def test_single_pincode_checker_api(self):
|
||||
# Test single pincode endpoint with live/mock evaluation across all 3 carriers
|
||||
url = reverse('api_carrier_pincode_check')
|
||||
res = self.client.get(f"{url}?pincode=560077&weight=500&use_mock=true")
|
||||
self.assertEqual(res.status_code, 200)
|
||||
data = res.json()
|
||||
self.assertEqual(data['status'], 'success')
|
||||
self.assertEqual(data['pincode'], '560077')
|
||||
self.assertIn('city', data)
|
||||
self.assertIn('carriers', data)
|
||||
self.assertIn('best_carrier', data)
|
||||
|
||||
for carrier_name in ['SHADOWFAX', 'DELHIVERY', 'EKART']:
|
||||
self.assertIn(carrier_name, data['carriers'])
|
||||
c = data['carriers'][carrier_name]
|
||||
self.assertIn('first_mile_available', c)
|
||||
self.assertIn('last_mile_available', c)
|
||||
self.assertIn('delivery_types', c)
|
||||
self.assertIn('estimated_charge', c)
|
||||
self.assertIsInstance(c['delivery_types'], list)
|
||||
self.assertGreater(len(c['delivery_types']), 0)
|
||||
self.assertIsNotNone(c['estimated_charge'])
|
||||
|
||||
self.assertTrue(data['best_carrier']['selected'])
|
||||
self.assertIsNotNone(data['best_carrier']['charge'])
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ urlpatterns = [
|
|||
path('api/webhook/', views.webhook_listener_api, name='webhook_listener_api'),
|
||||
|
||||
# Dynamic Carrier Allocation REST API Endpoints under /api/v1/carrier/
|
||||
path('api/v1/carrier/check-pincode/', views.api_carrier_pincode_check, name='api_carrier_pincode_check'),
|
||||
path('api/v1/carrier/check-availability/', views.api_carrier_availability, name='api_carrier_availability'),
|
||||
path('api/v1/carrier/auto-dispatch/', views.api_carrier_auto_dispatch, name='api_carrier_auto_dispatch'),
|
||||
|
||||
|
|
|
|||
|
|
@ -1029,6 +1029,30 @@ def carrier_checker_view(request):
|
|||
return render(request, 'shipping/carrier_checker.html', context)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def api_carrier_pincode_check(request):
|
||||
"""
|
||||
REST API endpoint to check city, first-mile availability, last-mile availability,
|
||||
delivery types, and estimated charge for a single pincode across all three carriers (Shadowfax, Delhivery, Ekart).
|
||||
GET or POST /api/v1/carrier/check-pincode/
|
||||
"""
|
||||
if request.method not in ['POST', 'GET']:
|
||||
return JsonResponse({'error': 'POST or GET method required'}, status=405)
|
||||
|
||||
try:
|
||||
data = json.loads(request.body.decode('utf-8')) if request.body else {}
|
||||
except Exception:
|
||||
data = {}
|
||||
|
||||
pincode = data.get('pincode') or request.GET.get('pincode') or data.get('pin') or request.GET.get('pin') or '560077'
|
||||
weight = data.get('weight') or request.GET.get('weight') or 500
|
||||
use_mock = data.get('use_mock') is True or str(data.get('use_mock', '')).lower() == 'true' or request.GET.get('use_mock') == 'true'
|
||||
|
||||
from .carrier_allocator import check_single_pincode_all_carriers
|
||||
res = check_single_pincode_all_carriers(pincode=pincode, weight=weight, use_mock=use_mock)
|
||||
return JsonResponse(res, status=200)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def api_carrier_availability(request):
|
||||
"""
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -167,8 +167,8 @@ SFX_PRODUCTION_BASE_URL = os.getenv('SFX_PRODUCTION_BASE_URL', 'https://dale.sha
|
|||
# Ekart API Credentials & Endpoints
|
||||
EKART_BASE_URL = os.getenv('EKART_BASE_URL', 'https://app.elite.ekartlogistics.in')
|
||||
EKART_CLIENT_ID = os.getenv('EKART_CLIENT_ID', 'EKART_6aba5a11229f5801fef04696')
|
||||
EKART_USERNAME = os.getenv('EKART_USERNAME', 'sample_username')
|
||||
EKART_PASSWORD = os.getenv('EKART_PASSWORD', 'sample_password')
|
||||
EKART_USERNAME = os.getenv('EKART_USERNAME', 'tradiraw@gmail.com')
|
||||
EKART_PASSWORD = os.getenv('EKART_PASSWORD', 'Database@2026')
|
||||
|
||||
# Delhivery API Credentials & Endpoints
|
||||
DELHIVERY_BASE_URL = os.getenv('DELHIVERY_BASE_URL', 'https://track.delhivery.com')
|
||||
|
|
|
|||
Loading…
Reference in a new issue