From 0fd5cdecb02dad12975744bd5c6c3aae4cbb348b Mon Sep 17 00:00:00 2001 From: vignesh Date: Mon, 12 Oct 2026 19:31:58 +0530 Subject: [PATCH] docs: add Delhivery B2C API specification and pickup scheduling architecture documentation --- documentation/delhivery/b2c_api_spec.md | 45 ++++-- .../pickup_scheduling_architecture.md | 129 ++++++++++++++++++ 2 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 documentation/pickup_scheduling_architecture.md diff --git a/documentation/delhivery/b2c_api_spec.md b/documentation/delhivery/b2c_api_spec.md index 9a1138e..72179ab 100644 --- a/documentation/delhivery/b2c_api_spec.md +++ b/documentation/delhivery/b2c_api_spec.md @@ -280,7 +280,7 @@ Authorization: Token 7328f980a6eca01fedf37a63e39e44893481f856 Schedule a courier pickup at origin seller warehouse. -- **Endpoint**: `POST /fm/request/create/` +- **Endpoint**: `POST /fm/request/new/` - **Headers**: - `Authorization: Token ` - `Content-Type: application/json` @@ -289,21 +289,50 @@ Schedule a courier pickup at origin seller warehouse. ```json { "pickup_time": "14:00:00", - "pickup_date": "2026-10-11", - "pickup_location": "StyleHub Retail Store", - "expected_package_count": 5 + "pickup_date": "2026-10-13", + "pickup_location": "SAM", + "expected_package_count": 1 } ``` -### Response Example (`200 OK`) +### Response Example: Fresh Schedule (`201 Created` / `200 OK`) ```json { - "status": "success", - "pickup_id": 987654, - "message": "Pickup request scheduled successfully for 2026-10-11 at 14:00:00" + "status": true, + "success": true, + "pickup_id": 328094672, + "data": { + "pickup_id": 328094672, + "message": "Pickup request successfully scheduled" + } } ``` +### Response Example: Existing Active Pickup (`Code 669` / `pr_exist = true`) +Delhivery enforces a rule that only **one active pickup request** can exist per warehouse per date/slot. If a request is already confirmed, Delhivery returns: +```json +{ + "status": true, + "success": false, + "pr_exist": true, + "pickup_id": 328094672, + "error": { + "code": 669, + "message": "A Pickup Request 328094672 for this Pickup Location Already Exist for 13 Oct in slot 14:00 - 18:00" + } +} +``` +> **Tradhox Handling**: Tradhox recognizes `pr_exist = true` / code `669` as a **confirmed pickup success**, captures `pickup_id = 328094672`, and links all ready orders at that warehouse to this slot. + +### Response Example: Prepaid Balance Rejection (`400 Bad Request`) +Delhivery One enforces a **minimum wallet balance floor of ₹500.00** before accepting any First Mile pickup dispatch: +```json +{ + "prepaid": "Client wallet balance is 454.32 which is less than 500.0" +} +``` +> **Action Required**: The merchant must recharge their Delhivery One wallet to exceed ₹500.00. + --- ## ❌ 8. Order Edit / Cancellation API diff --git a/documentation/pickup_scheduling_architecture.md b/documentation/pickup_scheduling_architecture.md new file mode 100644 index 0000000..7c7c5b6 --- /dev/null +++ b/documentation/pickup_scheduling_architecture.md @@ -0,0 +1,129 @@ +# Tradhox Multi-Carrier Pickup Scheduling Architecture & Logistics Guide + +This document defines the complete architecture, carrier rules, multi-seller scenarios, multi-location logistics, and data audit structures for scheduling courier pickups across **Delhivery**, **Ekart Logistics**, and **Shadowfax**. + +--- + +## 1. Carrier Capabilities & Pickup Workflow + +| Carrier | Date Selection | Time Slot Selection | Minimum Wallet Balance | Pickup Mechanism | +| :--- | :--- | :--- | :--- | :--- | +| **Delhivery Logistics** | Yes (`YYYY-MM-DD`) | Yes (`10:00:00`, `14:00:00`, `17:00:00`) | **₹500.00 (Strict Floor)** | Live First-Mile API (`POST /fm/request/new/`). Dispatches executive to seller warehouse. | +| **Ekart Logistics** | Yes (`YYYY-MM-DD`) | Daily Hub Sweep (Morning & Evening) | Positive freight balance | Sets preferred `dispatchDate` (`POST /data/shipment/dispatch-date`). Hub sweeps run twice daily. | +| **Shadowfax Logistics** | SLA Scheduled | Automated Cut-Off Sweep | Positive freight balance | Automated route sweeps based on seller warehouse dispatch cutoff times (e.g. 2:00 PM / 4:00 PM). | + +--- + +## 2. Delhivery One Special Rules & Error Handling + +### 2.1. Strict Minimum Wallet Reserve (₹500.00 Floor) +- Delhivery One enforces a **minimum wallet balance floor of ₹500.00** before accepting any First Mile pickup dispatch: + ```json + HTTP 400 Bad Request + { "prepaid": "Client wallet balance is 454.32 which is less than 500.0" } + ``` +- **Rationale**: Delhivery holds a security reserve against potential weight discrepancies, volumetric adjustments, fuel surcharges, and RTO risk. +- **Resolution**: The seller must top up their Delhivery One wallet (via `one.delhivery.com` $\rightarrow$ Finances $\rightarrow$ Wallet) so the balance crosses ₹500.00. + +### 2.2. Single Pickup Request Per Warehouse / Slot (Code 669) +- Delhivery allows only **ONE active Pickup Request per warehouse per date & slot**. +- When a pickup is already booked for warehouse `SAM` on that date/slot and another request is sent, Delhivery returns: + ```json + { + "status": true, + "success": false, + "pr_exist": true, + "pickup_id": 328094672, + "error": { + "code": 669, + "message": "A Pickup Request 328094672 for this Pickup Location Already Exist for 13 Oct in slot 14:00 - 18:00" + } + } + ``` +- **Tradhox Implementation**: + - Code `669` is **not an error**; it confirms that Delhivery already has an active pickup assigned to that location. + - The backend catches `pr_exist = true`, extracts `pickup_id = 328094672`, records it on the order, and presents a green confirmation banner to the seller. + +--- + +## 3. Multi-Seller Scenarios + +### Scenario A: Distinct Store / Workshop Names (Default) +- Even if multiple sellers share the same neighborhood or pincode (`641301`): + - **Seller 1**: Warehouse name = `SAM` + - **Seller 2**: Warehouse name = `KUMAR-CRAFTS` +- Delhivery evaluates pickup requests strictly by **Warehouse Name**, not pincode. +- Seller 1 receives their own Pickup ID (`PUR-1`), and Seller 2 receives their own Pickup ID (`PUR-2`). +- Delhivery dispatches independent field executive visits. + +### Scenario B: Shared Artisan Cluster / Consolidation Hub +- If multiple artisans bring their finished crafts to a shared guild center registered under warehouse `SAM`: + - **Seller A** books a pickup for 13 Oct (2 PM – 5 PM) $\rightarrow$ creates Delhivery Pickup Request `#328094672`. + - **Seller B** at the same hub later reschedules their order for the same date/slot. + - Delhivery returns code `669` (`pr_exist = true`). + - Tradhox automatically attaches Seller B's order to Pickup Request `#328094672`. + - When the Delhivery pickup van arrives at hub `SAM`, the driver scans **all packages waiting at `SAM`** in one consolidated visit. + +--- + +## 4. Multi-Order & Multi-Location Logistics + +### Scenario 1: One Seller Warehouse $\rightarrow$ Multiple Delivery Destinations +- **Origin**: Warehouse `SAM` (Coimbatore / Mettupalayam). +- **Destinations**: Order 1 (Chennai), Order 2 (Bengaluru), Order 3 (Mumbai). +- **Logistics Flow**: + 1. Only **1 Pickup Request** is created for warehouse `SAM`. + 2. Delhivery dispatches **one pickup executive** to `SAM`. + 3. The executive scans all packages into their handheld device and loads them into the local van. + 4. At the central Delhivery sorting hub, Delhivery's sorting automated line splits the packages onto their respective linehaul trucks and airport cargo containers according to destination sort codes (`CHE/GUU`, `BLR/SRA`, `BOM/AND`). + +### Scenario 2: One Seller with Multiple Origin Workshops +- **Workshop 1**: `SAM-Coimbatore` (Coimbatore). +- **Workshop 2**: `SAM-Salem` (Salem). +- **Logistics Flow**: + 1. Each workshop is registered as an independent pickup warehouse in Delhivery. + 2. Orders dispatched from Workshop 1 create a Coimbatore pickup assignment. + 3. Orders dispatched from Workshop 2 create a Salem pickup assignment. + 4. Both pickups execute concurrently without interference. + +--- + +## 5. Database Schema & Audit Logging + +### `seller_central_backend` (`Order` Model) +```python +class Order(models.Model): + ... + eta = models.CharField(max_length=50, default='N/A') + pickup_id = models.CharField(max_length=100, blank=True, null=True, help_text='Carrier Pickup Reference ID') + pickup_payload = models.JSONField(default=dict, blank=True, null=True, help_text='Carrier Pickup Scheduling Response & Audit Details') +``` + +### Stored `pickup_payload` JSON Structure +```json +{ + "latest_status": "success", + "last_scheduled_at": "2026-10-12T13:39:37Z", + "pickup_id": "328094672", + "pickup_date": "2026-10-13", + "pickup_time": "14:00:00", + "carrier": "Delhivery Logistics", + "history": [ + { + "scheduled_at": "2026-10-12T13:39:37Z", + "status": "success", + "pickup_id": "328094672", + "pickup_date": "2026-10-13", + "pickup_time": "14:00:00", + "carrier": "Delhivery Logistics", + "message": "Pickup request #328094672 is already confirmed with Delhivery for 2026-10-13", + "raw_response": { ... } + } + ] +} +``` + +### Microservice Audit Tables (`shippingservice`) +- **Delhivery**: `DelhiveryActivityLog` (records every HTTP status, endpoint, request headers/payload, and response). +- **Ekart**: `EkartActivityLog` (records all token, dispatch-date, and tracking calls). +- **Shadowfax**: `ActivityLog` (records forward/reverse order dispatch and tracking calls).