# WMS API Reference — Grouped by Role

> **Base URL:** `https://api.astrov3.app`
>
> **Hub:** `wss://api.astrov3.app/hubs/wms`&#x20;
>
> **Version:** v1

***

### 1. Architecture Overview

{% code lineNumbers="true" %}

```mermaid
flowchart TD
    subgraph Mobile["Flutter Mobile (Delivery Staff)"]
        FM[Delivery App<br/>GetX + Dio + SignalR]
    end

    subgraph Web["Web Platform"]
        WB[Admin Dashboard<br/>Customer Portal]
    end

    subgraph Backend[".NET 10 Backend"]
        API[Minimal API<br/>Controllers]
        CQRS[CQRS + MediatR<br/>FluentValidation]
        DOM[Domain Events<br/>Shipment, COD, Alert]
        NOTI[Notification<br/>FCM + SignalR + InApp]
        BG[BackgroundService<br/>Auto-retry delivery]
        PRICING[PricingEngine<br/>ShippingFeeCalculator]
        BARCODE[BarcodeService<br/>ZXing + Cloudinary]
    end

    subgraph Storage["Storage & Data"]
        MSSQL[(MSSQL<br/>EF Core)]
        CLOUDINARY[(Cloudinary<br/>Media)]
        FCM[Firebase<br/>Push Notifications]
    end

    FM --> API
    WB --> API
    API --> CQRS
    CQRS --> DOM
    DOM --> NOTI
    DOM --> BG
    API --> PRICING
    API --> BARCODE
    BARCODE --> CLOUDINARY
    API --> MSSQL
    NOTHI[NOTI] --> FCM
```

{% endcode %}

***

### 2. Roles & Permissions Summary

| Role          | Description                | Auth Method | Inherits             |
| ------------- | -------------------------- | ----------- | -------------------- |
| **ANONYMOUS** | Public access              | None        | —                    |
| **USER**      | Customer (sender/receiver) | JWT Bearer  | —                    |
| **WAREHOUSE** | Warehouse staff            | JWT Bearer  | USER                 |
| **DELIVERY**  | Delivery driver            | JWT Bearer  | USER                 |
| **MANAGER**   | Operations manager         | JWT Bearer  | WAREHOUSE + DELIVERY |
| **ADMIN**     | System administrator       | JWT Bearer  | All                  |

```mermaid
flowchart TD
    A[ANONYMOUS] --> B[USER]
    B --> C1[DELIVERY]
    B --> C2[WAREHOUSE]
    C2 --> D[MANAGER]
    D --> E[ADMIN]
```

***

### 3. Module Groups

{% code overflow="wrap" expandable="true" %}

```mermaid
flowchart TD
    subgraph Identity["Identity Module"]
        I1[AuthController<br/>UsersController<br/>UserNotificationController]
    end

    subgraph Shipments["Shipments Module"]
        I2[ShipmentsController<br/>DriversController<br/>AlertsController]
    end

    subgraph Tracking["Tracking Module"]
        I3[ScansController<br/>BarcodesController]
    end

    subgraph Commerce["Commerce Module"]
        I4[ProductsController<br/>ReceiverAddressesController<br/>CodController]
    end

    subgraph Logistics["Logistics Module"]
        I5[PostOfficesController<br/>RegionsController<br/>LocationController]
    end

    subgraph Config["Config Module"]
        I6[ConfigurationController<br/>WmsConfigsController]
    end

    subgraph Media["Media Module"]
        I7[MediaController]
    end

    subgraph Pricing["Pricing Module"]
        I8[PricingController]
    end

    subgraph Reporting["Reporting Module"]
        I9[ReportingController<br/>TestPushNotificationController]
    end
```

{% endcode %}

***

### 4. ANONYMOUS / PUBLIC

No authentication required.

#### Auth

| Method | Path                                  | Description                 |
| ------ | ------------------------------------- | --------------------------- |
| POST   | `/api/v1/auth/login-by-email`         | Login with email + password |
| POST   | `/api/v1/auth/login-by-phone`         | Login with phone + password |
| POST   | `/api/v1/auth/login-by-customer-code` | Login with customer code    |
| POST   | `/api/v1/auth/register`               | Register new account        |
| POST   | `/api/v1/auth/refresh-token`          | Refresh JWT token           |

#### Shipments

| Method | Path                                | Description                          |
| ------ | ----------------------------------- | ------------------------------------ |
| POST   | `/api/v1/shipments`                 | Create new shipment                  |
| POST   | `/api/v1/shipments/checkout-review` | Preview shipping fee before creating |
| GET    | `/api/v1/shipments/track/{code}`    | Public tracking by tracking code     |

#### Pricing

| Method | Path                              | Description                             |
| ------ | --------------------------------- | --------------------------------------- |
| POST   | `/api/v1/pricing/estimate`        | Calculate shipping fee (full breakdown) |
| POST   | `/api/v1/pricing/simple-estimate` | Calculate shipping fee (simple)         |

#### Tracking

| Method | Path                            | Description                              |
| ------ | ------------------------------- | ---------------------------------------- |
| GET    | `/api/v1/scans/validate/{code}` | Validate barcode/QR code without logging |

#### Config

| Method | Path                    | Description                       |
| ------ | ----------------------- | --------------------------------- |
| GET    | `/api/v1/config/public` | Get public configs (SLA, pricing) |

#### Locations

| Method | Path                             | Description                         |
| ------ | -------------------------------- | ----------------------------------- |
| GET    | `/api/v1/locations/ip`           | Get current location by IP          |
| GET    | `/api/v1/locations/search`       | Search locations                    |
| GET    | `/api/v1/locations/autocomplete` | Autocomplete location search        |
| GET    | `/api/v1/locations/reverse`      | Reverse geocoding                   |
| GET    | `/api/v1/locations/route`        | Calculate route between two points  |
| GET    | `/api/v1/locations/route-matrix` | Distance matrix for multiple points |
| GET    | `/api/v1/locations/isoline`      | Isochrone / reachable area          |
| GET    | `/api/v1/locations/places`       | Search POI / places                 |
| GET    | `/api/v1/locations/staticmap`    | Generate static map image           |

#### Post Offices

| Method | Path                          | Description              |
| ------ | ----------------------------- | ------------------------ |
| GET    | `/api/v1/post-offices`        | List all post offices    |
| GET    | `/api/v1/post-offices/{id}`   | Get post office by ID    |
| GET    | `/api/v1/post-offices/nearby` | Find nearby post offices |

#### Regions

| Method | Path                     | Description                  |
| ------ | ------------------------ | ---------------------------- |
| GET    | `/api/v1/regions/level1` | Get provinces/municipalities |
| GET    | `/api/v1/regions/level2` | Get districts by province    |

***

### 5. USER (Customer — sender/receiver)

JWT Bearer required. All USER endpoints are also available to WAREHOUSE, DELIVERY, MANAGER, ADMIN.

#### Auth

| Method | Path                               | Description              |
| ------ | ---------------------------------- | ------------------------ |
| GET    | `/api/v1/auth/profile`             | Get current user profile |
| GET    | `/api/v1/auth/sessions`            | List active sessions     |
| POST   | `/api/v1/auth/revoke-session`      | Revoke one session       |
| POST   | `/api/v1/auth/revoke-all-sessions` | Revoke all sessions      |
| POST   | `/api/v1/auth/logout`              | Logout current session   |
| POST   | `/api/v1/auth/logout-all`          | Logout all sessions      |

#### Shipments

| Method | Path                              | Description                    |
| ------ | --------------------------------- | ------------------------------ |
| POST   | `/api/v1/shipments`               | Create new shipment            |
| GET    | `/api/v1/shipments`               | List own shipments (paginated) |
| GET    | `/api/v1/shipments/{id}`          | Get shipment detail            |
| GET    | `/api/v1/shipments/{id}/tracking` | Get tracking history           |

#### Cash Flow (User)

| Method | Path                                   | Description                                 |
| ------ | -------------------------------------- | ------------------------------------------- |
| GET    | `/api/v1/users/me/cash-flow`           | Get personal cash flow summary              |
| GET    | `/api/v1/users/me/cash-flow/shipments` | Get shipment list contributing to cash flow |

**Query parameters (`cash-flow` and `cash-flow/shipments`):**

| Param      | Type      | Description                              |
| ---------- | --------- | ---------------------------------------- |
| `dateFrom` | DateTime? | Filter shipments created from this date  |
| `dateTo`   | DateTime? | Filter shipments created up to this date |

**`cash-flow` response — chart-friendly fields:**

| Field                                                 | Type    | Description                                                                                                       |
| ----------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
| `role`                                                | string  | SENDER or RECEIVER                                                                                                |
| `totalShippingFees`                                   | decimal | Total shipping fees                                                                                               |
| `totalCodAmount`                                      | decimal | Total COD amount                                                                                                  |
| `totalCodCollectedOnBehalf`                           | decimal | COD collected on behalf of sender                                                                                 |
| `totalRefundsReceived`                                | decimal | Refunds received                                                                                                  |
| `monthlyBreakdown[]`                                  | array   | **Monthly trend for line/bar chart** — `{year, month, collected, remitted, pending, shippingFees, shipmentCount}` |
| `shipmentCount`                                       | int     | Total shipments in period                                                                                         |
| `deliveredCount` / `returnedCount` / `cancelledCount` | int     | Breakdown by status                                                                                               |

**Cash flow for sender:**

* Total shipping fees paid
* Total COD collected on behalf
* Total refunds received (returned shipments)

**Cash flow for receiver:**

* Total COD payable
* Total shipping fees paid (if paymentBy = RECEIVER)
* Total refunds received

#### Notifications

| Method | Path                                 | Description                    |
| ------ | ------------------------------------ | ------------------------------ |
| GET    | `/api/v1/notifications`              | List notifications (paginated) |
| GET    | `/api/v1/notifications/{id}`         | Get notification detail        |
| GET    | `/api/v1/notifications/counts`       | Get unread counts              |
| POST   | `/api/v1/notifications/{id}/read`    | Mark as read                   |
| POST   | `/api/v1/notifications/read-all`     | Mark all as read               |
| POST   | `/api/v1/notifications/{id}/archive` | Archive notification           |
| DELETE | `/api/v1/notifications/{id}`         | Delete notification            |

#### Users

| Method | Path                                 | Description                |
| ------ | ------------------------------------ | -------------------------- |
| GET    | `/api/v1/users/me`                   | Get own profile            |
| PUT    | `/api/v1/users/{id}`                 | Update profile (self only) |
| PUT    | `/api/v1/users/{id}/add-fcm-token`   | Register FCM push token    |
| PUT    | `/api/v1/users/{id}/change-password` | Change password            |
| PUT    | `/api/v1/users/{id}/location`        | Update GPS location        |

#### Products

| Method | Path                    | Description        |
| ------ | ----------------------- | ------------------ |
| GET    | `/api/v1/products`      | List products      |
| GET    | `/api/v1/products/{id}` | Get product detail |
| POST   | `/api/v1/products`      | Create product     |
| PUT    | `/api/v1/products/{id}` | Update product     |
| DELETE | `/api/v1/products/{id}` | Delete product     |

#### Receiver Addresses

| Method | Path                              | Description                   |
| ------ | --------------------------------- | ----------------------------- |
| GET    | `/api/v1/receiver-addresses`      | List saved receiver addresses |
| GET    | `/api/v1/receiver-addresses/{id}` | Get address detail            |
| POST   | `/api/v1/receiver-addresses`      | Create new receiver address   |
| PUT    | `/api/v1/receiver-addresses/{id}` | Update receiver address       |
| DELETE | `/api/v1/receiver-addresses/{id}` | Delete receiver address       |

#### Product Types

| Method | Path                         | Description             |
| ------ | ---------------------------- | ----------------------- |
| GET    | `/api/v1/product-types`      | List product types      |
| GET    | `/api/v1/product-types/{id}` | Get product type detail |
| POST   | `/api/v1/product-types`      | Create product type     |
| PUT    | `/api/v1/product-types/{id}` | Update product type     |
| DELETE | `/api/v1/product-types/{id}` | Delete product type     |

***

### 6. WAREHOUSE

All USER permissions, plus:

#### Shipments (warehouse operations)

| Method | Path                              | Description                         |
| ------ | --------------------------------- | ----------------------------------- |
| GET    | `/api/v1/shipments`               | List all shipments (no user filter) |
| POST   | `/api/v1/shipments/{id}/status`   | Update status of any shipment       |
| POST   | `/api/v1/shipments/{id}/assign`   | Assign driver to shipment           |
| POST   | `/api/v1/shipments/{id}/unassign` | Unassign driver from shipment       |
| POST   | `/api/v1/shipments/{id}/cancel`   | Cancel shipment                     |

#### Post Offices

| Method | Path                        | Description            |
| ------ | --------------------------- | ---------------------- |
| POST   | `/api/v1/post-offices`      | Create new post office |
| PUT    | `/api/v1/post-offices/{id}` | Update post office     |
| DELETE | `/api/v1/post-offices/{id}` | Delete post office     |

***

### 7. DELIVERY (Driver)

All USER permissions, plus:

#### My Shipments

| Method | Path                             | Description                   |
| ------ | -------------------------------- | ----------------------------- |
| GET    | `/api/v1/shipments/my-shipments` | List shipments assigned to me |

#### Shipment Operations

| Method | Path                                       | Description                                                 |
| ------ | ------------------------------------------ | ----------------------------------------------------------- |
| POST   | `/api/v1/shipments/{id}/status`            | Update status (assigned shipments only, subset of statuses) |
| GET    | `/api/v1/shipments/{id}/driver-location`   | Get current driver location                                 |
| GET    | `/api/v1/shipments/{id}/delivery-attempts` | Get delivery attempt history                                |
| GET    | `/api/v1/shipments/{id}/route`             | Get shipment route through post offices                     |

#### Scanning

| Method | Path                                  | Description                     |
| ------ | ------------------------------------- | ------------------------------- |
| POST   | `/api/v1/scans`                       | Log barcode/QR scan             |
| GET    | `/api/v1/scans/shipment/{shipmentId}` | Get scan history for a shipment |

#### Barcodes

| Method | Path                                    | Description                  |
| ------ | --------------------------------------- | ---------------------------- |
| GET    | `/api/v1/barcodes/{trackingCode}`       | Get barcode/QR metadata      |
| GET    | `/api/v1/barcodes/{trackingCode}/png`   | Get barcode PNG image        |
| GET    | `/api/v1/barcodes/{trackingCode}/qr`    | Get QR code PNG image        |
| GET    | `/api/v1/barcodes/{trackingCode}/label` | Get printable shipping label |

#### Media

| Method | Path                                             | Description                   |
| ------ | ------------------------------------------------ | ----------------------------- |
| POST   | `/api/v1/media/uploads`                          | Upload photo (POD, signature) |
| GET    | `/api/v1/media/entities/{entityType}/{entityId}` | Get media attached to entity  |
| POST   | `/api/v1/media/attach`                           | Attach media to entity        |
| POST   | `/api/v1/media/{mediaId}/set-primary`            | Set as primary photo          |
| DELETE | `/api/v1/media/{mediaId}`                        | Delete media                  |
| DELETE | `/api/v1/media/{mediaId}/detach`                 | Detach without deleting       |

#### Alerts

| Method | Path                      | Description               |
| ------ | ------------------------- | ------------------------- |
| GET    | `/api/v1/alerts/assigned` | Get alerts assigned to me |

#### Statistics

| Method | Path                                | Description                       |
| ------ | ----------------------------------- | --------------------------------- |
| GET    | `/api/v1/statistics/delivery-staff` | Get my delivery performance stats |

#### Cash Flow (Driver)

| Method | Path                                           | Description                               |
| ------ | ---------------------------------------------- | ----------------------------------------- |
| GET    | `/api/v1/delivery-staff/cod-summary`           | Get COD cash flow summary                 |
| GET    | `/api/v1/delivery-staff/cod-summary/shipments` | Get shipments contributing to COD summary |
| PUT    | `/api/v1/delivery-staff/cod-summary/remit`     | Confirm COD has been remitted to company  |

**Query parameters (`cod-summary` and `cod-summary/shipments`):**

| Param      | Type      | Description                                    |
| ---------- | --------- | ---------------------------------------------- |
| `dateFrom` | DateTime? | Filter by delivery date (>=)                   |
| `dateTo`   | DateTime? | Filter by delivery date (<=)                   |
| `status`   | string?   | Filter by shipment status (default: DELIVERED) |

**Query parameters (`cod-summary/shipments`):**

| Param          | Type    | Description                 |
| -------------- | ------- | --------------------------- |
| `isRemitted`   | bool?   | Filter by remittance status |
| `trackingCode` | string? | Search by tracking code     |

**`remit` request body:**

| Field         | Type         | Description                                                 |
| ------------- | ------------ | ----------------------------------------------------------- |
| `shipmentIds` | List\<Guid>? | Optional: remit specific shipments only; omit = all pending |
| `dateFrom`    | DateTime?    | Optional: remit all pending within date range               |
| `dateTo`      | DateTime?    | Optional: remit all pending within date range               |

**Driver COD summary fields:**

* `totalCodCollected` — SUM(CodAmount) of DELIVERED shipments assigned to driver
* `totalCodRemitted` — SUM(CodAmount) where driver confirmed remittance
* `totalCodPending` — totalCodCollected - totalCodRemitted
* `shipmentCount` — total shipments in collection
* `dailyBreakdown[]` — **daily COD trend for line chart** (array of `{date, collected, remitted, pending, shipmentCount}`)

**Driver COD shipments — `GET /cod-summary/shipments`:**

| Field             | Type      | Description               |
| ----------------- | --------- | ------------------------- |
| `shipmentId`      | Guid      | Shipment ID               |
| `trackingCode`    | string    | Tracking code             |
| `receiverName`    | string    | Receiver name             |
| `codAmount`       | decimal   | COD amount                |
| `collectedAmount` | decimal   | Amount actually collected |
| `isCollected`     | bool      | COD collected at delivery |
| `collectedAt`     | DateTime? | When COD was collected    |
| `isRemitted`      | bool      | COD remitted to company   |
| `remittedAt`      | DateTime? | When COD was remitted     |
| `deliveredAt`     | DateTime? | Delivery timestamp        |

***

### 8. MANAGER

All WAREHOUSE + DELIVERY permissions, plus:

#### Drivers Management

| Method | Path                      | Description                                                            |
| ------ | ------------------------- | ---------------------------------------------------------------------- |
| GET    | `/api/v1/drivers`         | List all drivers (filter by zone, vehicleType, postOfficeId, isActive) |
| GET    | `/api/v1/drivers/{id}`    | Get driver details                                                     |
| GET    | `/api/v1/drivers/nearest` | Find nearest available driver                                          |

#### Dispatch

| Method | Path                              | Description                                     |
| ------ | --------------------------------- | ----------------------------------------------- |
| POST   | `/api/v1/shipments/{id}/dispatch` | Auto-assign driver based on zone + vehicle type |
| POST   | `/api/v1/shipments/{id}/reassign` | Manually reassign to different driver           |

#### COD & Reporting

| Method | Path                     | Description                       |
| ------ | ------------------------ | --------------------------------- |
| GET    | `/api/v1/cod/statistics` | Get company-wide COD statistics   |
| GET    | `/api/v1/cod/by-driver`  | Get COD summary grouped by driver |
| GET    | `/api/v1/cod/by-date`    | Get COD summary grouped by date   |

**Query parameters (`by-driver`):**

| Param      | Type      | Description                       |
| ---------- | --------- | --------------------------------- |
| `dateFrom` | DateTime? | Filter deliveries from this date  |
| `dateTo`   | DateTime? | Filter deliveries up to this date |

**Query parameters (`by-date`):**

| Param      | Type      | Description                      |
| ---------- | --------- | -------------------------------- |
| `dateFrom` | DateTime? | From date (default: 30 days ago) |
| `dateTo`   | DateTime? | To date (default: now)           |

**`by-driver` response — chart-friendly fields:**

| Field                            | Type     | Description                                                                                                        |
| -------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `items[].codCollected`           | decimal  | Total COD collected                                                                                                |
| `items[].codRemitted`            | decimal  | Total COD remitted                                                                                                 |
| `items[].codPending`             | decimal  | COD pending (not yet remitted)                                                                                     |
| `items[].collectionRate`         | decimal  | Remitted% (for bar chart sort/filter)                                                                              |
| `items[].averageDailyCollection` | decimal? | Avg daily collection (for comparison)                                                                              |
| `summary.collectionRate`         | decimal  | Overall collection rate                                                                                            |
| `summary.totalActiveDrivers`     | int      | Active drivers in period                                                                                           |
| `summary.totalShipments`         | int      | Total shipments in period                                                                                          |
| `dailyTrend[]`                   | array    | **Daily trend for line chart** — `{date, totalCollected, totalRemitted, totalPending, driverCount, shipmentCount}` |

**`by-date` response — chart-friendly fields:**

| Field                      | Type     | Description                      |
| -------------------------- | -------- | -------------------------------- |
| `items[].date`             | Date     | Date (X-axis for line/bar chart) |
| `items[].codCollected`     | decimal  | Collected on this date           |
| `items[].codRemitted`      | decimal  | Remitted on this date            |
| `items[].codPending`       | decimal  | Pending as of this date          |
| `items[].shipmentCount`    | int      | Shipments that day               |
| `items[].averagePerDriver` | decimal? | Avg per driver (for stacked bar) |

#### Testing

| Method | Path                                       | Description            |
| ------ | ------------------------------------------ | ---------------------- |
| POST   | `/api/v1/test/push-notifications/shipment` | Test push notification |

***

### 9. ADMIN

All permissions, plus:

#### User Management

| Method | Path                 | Description     |
| ------ | -------------------- | --------------- |
| GET    | `/api/v1/users`      | List all users  |
| POST   | `/api/v1/users`      | Create new user |
| DELETE | `/api/v1/users/{id}` | Delete user     |

#### System Configuration (full CRUD)

| Method | Path                                 | Description                              |
| ------ | ------------------------------------ | ---------------------------------------- |
| GET    | `/api/v1/config`                     | List all configs (paginated, filterable) |
| GET    | `/api/v1/config/{key}`               | Get single config                        |
| GET    | `/api/v1/config/category/{category}` | List configs by category                 |
| POST   | `/api/v1/config`                     | Create new config                        |
| PUT    | `/api/v1/config/{key}`               | Update config value                      |
| DELETE | `/api/v1/config/{key}`               | Delete config                            |
| POST   | `/api/v1/config/bulk`                | Bulk upsert configs                      |
| GET    | `/api/v1/config/export`              | Export all configs as JSON               |
| POST   | `/api/v1/config/import`              | Import configs from JSON                 |

#### WmsConfigs (legacy)

| Method | Path                                  | Description                                 |
| ------ | ------------------------------------- | ------------------------------------------- |
| GET    | `/api/v1/configs`                     | List all WmsConfigs                         |
| GET    | `/api/v1/configs/category/{category}` | List by category                            |
| GET    | `/api/v1/configs/keys/{key}`          | Get by key                                  |
| PUT    | `/api/v1/configs/{key}`               | Update value (ADMIN/SUPER\_ADMIN)           |
| POST   | `/api/v1/configs/seed`                | Reseed default configs (ADMIN/SUPER\_ADMIN) |

***

### 10. Shipment Lifecycle API Flow

```mermaid
flowchart LR
    subgraph Create["Tạo đơn"]
        A1["ANONYMOUS<br/>POST /shipments"] --> A2["USER<br/>POST /shipments"]
        A2 --> A3["WAREHOUSE<br/>POST /shipments"]
    end

    subgraph Assign["Gán tài xế"]
        B1["MANAGER<br/>POST /shipments/{id}/assign"] --> B2["MANAGER<br/>POST /shipments/{id}/dispatch<br/>auto-assign"]
        B2 --> B3["MANAGER<br/>POST /shipments/{id}/reassign<br/>manual"]
    end

    subgraph Status["Cập nhật trạng thái"]
        C1["WAREHOUSE<br/>POST /shipments/{id}/status<br/>any shipment"] --> C2["DELIVERY<br/>POST /shipments/{id}/status<br/>assigned only"]
    end

    subgraph Cancel["Hủy đơn"]
        D1["WAREHOUSE<br/>POST /shipments/{id}/cancel"] --> D2["MANAGER<br/>POST /shipments/{id}/cancel"]
    end
```

#### Status Transition Matrix

```
CREATED
  └─→ COLLECTED         (DELIVERY: assigned driver)
  └─→ CANCELLED         (WAREHOUSE, MANAGER)

COLLECTED
  └─→ TRANSPORTING      (DELIVERY: assigned driver, vehicleType required)

TRANSPORTING
  └─→ ARRIVED           (DELIVERY: postOfficeId required)

ARRIVED
  └─→ RECEIVED          (DELIVERY: assigned driver)
  └─→ CANCELLED         (WAREHOUSE, MANAGER)

RECEIVED
  └─→ IN_TRANSIT        (DELIVERY: assigned driver)
  └─→ CANCELLED         (WAREHOUSE, MANAGER)

IN_TRANSIT
  └─→ OUT_FOR_DELIVERY  (DELIVERY: assigned driver)

OUT_FOR_DELIVERY
  ├─→ DELIVERED         (DELIVERY: assigned driver, POD required)
  └─→ FAILED_DELIVERY   (DELIVERY: assigned driver, failReason required)

FAILED_DELIVERY
  ├─→ OUT_FOR_DELIVERY  (retry: auto or manual)
  └─→ RETURN_INITIATED  (3 failed attempts + 24h)

RETURN_INITIATED
  └─→ RETURNING         (WAREHOUSE, MANAGER)

RETURNING
  └─→ RETURN_COMPLETED  (WAREHOUSE, MANAGER)

RETURN_COMPLETED
  └─→ RETURNED          (WAREHOUSE, MANAGER)

DELIVERED         [terminal]
CANCELLED         [terminal]
RETURNED          [terminal]
```

***

### 11. Cash Flow Data Model

#### Driver COD Summary (DELIVERY role)

```
GET /api/v1/delivery-staff/cod-summary

{
  "totalCodCollected": 850000,
  "totalCodRemitted":  800000,
  "totalCodPending":   50000,
  "shipmentCount": 42,
  "lastUpdatedAt": "2026-05-23T..."
}
```

```
GET /api/v1/delivery-staff/cod-summary/shipments

{
  "items": [
    {
      "shipmentId": "guid",
      "trackingCode": "AST-2026-001234",
      "receiverName": "Nguyen Van A",
      "codAmount": 150000,
      "collectedAt": "2026-05-22T14:00:00Z",
      "isRemitted": true,
      "remittedAt": "2026-05-22T18:00:00Z"
    }
  ],
  "totalCodCollected": 850000,
  "totalCodRemitted": 800000,
  "totalCodPending": 50000
}
```

```
PUT /api/v1/delivery-staff/cod-summary/remit

Request:
{
  "shipmentIds": ["guid1", "guid2"]   // optional: remit specific shipments; omit = remit all pending
}

Response: updated cod-summary
```

#### User Cash Flow (USER role)

```
GET /api/v1/users/me/cash-flow

As SENDER:
{
  "role": "SENDER",
  "totalShippingFeesPaid": 1500000,
  "totalCodCollectedOnBehalf": 8500000,
  "totalRefundsReceived": 0,
  "shipmentCount": 25,
  "period": { "from": "...", "to": "..." }
}

As RECEIVER:
{
  "role": "RECEIVER",
  "totalCodPayable": 8500000,
  "totalShippingFeesPaid": 300000,
  "totalRefundsReceived": 150000,
  "shipmentCount": 12,
  "period": { "from": "...", "to": "..." }
}
```

#### Company Cash Flow (MANAGER / ADMIN role)

```
GET /api/v1/cod/by-driver

{
  "items": [
    {
      "driverId": "guid",
      "driverName": "Nguyen Van Tai",
      "codCollected": 850000,
      "codRemitted": 800000,
      "codPending": 50000,
      "shipmentCount": 42
    }
  ],
  "summary": {
    "totalCompanyCodCollected": 50000000,
    "totalCompanyCodRemitted": 45000000,
    "totalCompanyCodPending": 5000000
  }
}
```

```
GET /api/v1/cod/by-date?dateFrom=...&dateTo=...

{
  "items": [
    {
      "date": "2026-05-23",
      "codCollected": 5000000,
      "codRemitted": 4500000,
      "codPending": 500000,
      "shipmentCount": 125
    }
  ]
}
```

***

### 12. SignalR Real-time Events

#### Connection

```
Endpoint: wss://api.astrov3.app/hubs/wms
Header:   Authorization: Bearer {jwt}
Query:    ?access_token={jwt} (alternative)
```

Client receives events on method: `dispatch(eventName, payload)`

#### Event Reference

| Event                    | Trigger API                   | Groups notified                          | Payload                 |
| ------------------------ | ----------------------------- | ---------------------------------------- | ----------------------- |
| `ShipmentCreated`        | POST /shipments               | `wms`                                    | shipment summary        |
| `ShipmentAssigned`       | POST /shipments/{id}/assign   | `user:{driverId}`, `role:DELIVERY`       | driver info + shipment  |
| `ShipmentUnassigned`     | POST /shipments/{id}/unassign | `user:{prevDriverId}`                    | shipment info           |
| `ShipmentStatusUpdated`  | POST /shipments/{id}/status   | `wms`, `shipment:{id}`                   | new status + timestamp  |
| `ShipmentDelivered`      | DELIVERED status              | `wms`, `shipment:{id}`, sender, receiver | delivery proof          |
| `ShipmentDeliveryFailed` | FAILED\_DELIVERY status       | `wms`, `role:MANAGER`                    | fail reason + attempt # |
| `RouteDeparted`          | TRANSPORTING status           | `wms`, `shipment:{id}`                   | origin + destination    |
| `RouteArrived`           | ARRIVED status                | `wms`, `shipment:{id}`, receiver         | post office info        |
| `ReturnInitiated`        | RETURN\_INITIATED status      | `wms`, sender, receiver                  | return reason           |
| `DriverLocationUpdated`  | PUT /users/{id}/location      | `shipment:{id}` (if active)              | lat, lng                |
| `ShipmentScanned`        | POST /scans                   | `wms`                                    | scan purpose + location |

{% code overflow="wrap" expandable="true" %}

```mermaid
flowchart TD

    subgraph Triggers["API Actions (triggers)"]
        T1["POST /shipments\ncreate"] --> E1["ShipmentCreated"]
        T2["POST /shipments/{id}/assign\nassign"] --> E2["ShipmentAssigned"]
        T3["POST /shipments/{id}/status\nany status change"] --> E3["ShipmentStatusUpdated"]
        T4["POST /shipments/{id}/status\nDELIVERED"] --> E4["ShipmentDelivered"]
        T5["POST /shipments/{id}/status\nFAILED_DELIVERY"] --> E5["ShipmentDeliveryFailed"]
        T6["POST /shipments/{id}/status\nTRANSPORTING"] --> E6["RouteDeparted"]
        T7["POST /shipments/{id}/status\nARRIVED"] --> E7["RouteArrived"]
        T8["PUT /users/{id}/location\ndriver GPS"] --> E8["DriverLocationUpdated"]
        T9["POST /scans\nscan barcode"] --> E9["ShipmentScanned"]
    end

    subgraph Groups["SignalR Groups"]
        E1 --> G1["wms\n(all connected)"]

        E2 --> G2["user:{driverId}"]
        E2 --> G2b["role:DELIVERY"]

        E3 --> G3["wms + shipment:{id}"]

        E4 --> G4["wms + shipment:{id}"]
        E4 --> G4b["sender + receiver"]

        E5 --> G5["wms"]
        E5 --> G5b["role:MANAGER"]

        E6 --> G6["wms + shipment:{id}"]

        E7 --> G7["wms + shipment:{id}"]
        E7 --> G7b["user:{receiverId}"]

        E8 --> G8["shipment:{id}"]

        E9 --> G9["wms"]
    end

    subgraph Consumers["Consumers"]
        G1 --> C1["Admin Dashboard\nReal-time overview"]

        G2 --> C2["Driver App\nNew assignment alert"]

        G3 --> C3["Warehouse + Admin\nStatus board"]

        G4 --> C4["Customer App\nDelivered notification"]

        G5 --> C5["Manager App\nDelivery failure alert"]

        G6 --> C6["Tracking Timeline\nDeparture update"]

        G7 --> C7["Customer App\nArrived notification"]

        G8 --> C8["Tracking Page\nDriver map"]

        G9 --> C9["Warehouse Board\nScan confirmation"]
    end
```

{% endcode %}

***

### Appendix: Controllers Summary

| #  | Controller                     | Route prefix                    | Auth              |
| -- | ------------------------------ | ------------------------------- | ----------------- |
| 1  | AuthControllerV1               | /api/v1/auth                    | Mixed             |
| 2  | ShipmentsController            | /api/v1/shipments               | Mixed             |
| 3  | PricingController              | /api/v1/pricing                 | Anonymous         |
| 4  | ConfigurationController        | /api/v1/config                  | ADMIN / Anonymous |
| 5  | BarcodesController             | /api/v1/barcodes                | JWT               |
| 6  | ScansController                | /api/v1/scans                   | JWT / Anonymous   |
| 7  | DriversController              | /api/v1/drivers                 | ADMIN, MANAGER    |
| 8  | WmsConfigsController           | /api/v1/configs                 | JWT / ADMIN       |
| 9  | LocationController             | /api/v1/locations               | Anonymous         |
| 10 | CodController                  | /api/v1/cod                     | MANAGER, ADMIN    |
| 11 | ProductsController             | /api/v1/products                | JWT               |
| 12 | PostOfficesController          | /api/v1/post-offices            | Mixed             |
| 13 | UserNotificationController     | /api/v1/notifications           | JWT               |
| 14 | UsersController                | /api/v1/users                   | JWT / ADMIN       |
| 15 | ReceiverAddressesController    | /api/v1/receiver-addresses      | JWT               |
| 16 | RegionsController              | /api/v1/regions                 | Anonymous         |
| 17 | ReportingController            | /api/v1/statistics              | DELIVERY          |
| 18 | TestPushNotificationController | /api/v1/test/push-notifications | ADMIN, MANAGER    |
| 19 | ProductTypesController         | /api/v1/product-types           | JWT               |
| 20 | MediaController                | /api/v1/media                   | JWT               |
| 21 | AlertsController               | /api/v1/alerts                  | DELIVERY          |

**Total: 21 controllers, \~94 endpoints**


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.astrov3.app/wms-api-reference-grouped-by-role.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
