first
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
"""Objednávky přepravy / svozu — tvorba (batch), stav, vyhledání, zrušení.
|
||||
|
||||
Stejně jako zásilky jsou objednávky asynchronní: POST order/batch vrátí batchId
|
||||
(Location hlavička), stav zpracování se polluje přes GET order/batch/{batchId}.
|
||||
|
||||
Typy objednávek (pole orderType v těle):
|
||||
- CollectionOrder — svoz z registrované adresy zákazníka (bez recipient)
|
||||
- TransportOrder — přeprava z libovolné adresy (sender i recipient povinné)
|
||||
"""
|
||||
import asyncio
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Query
|
||||
|
||||
from ..config import (
|
||||
BATCH_POLL_INTERVAL_SECONDS,
|
||||
BATCH_WAIT_TIMEOUT_SECONDS,
|
||||
TRANSLITERATE_DEFAULT,
|
||||
)
|
||||
from ..cpl_client import (
|
||||
batch_id_from_location,
|
||||
cpl_request,
|
||||
ensure_success,
|
||||
relay_json,
|
||||
)
|
||||
from ..credentials import Credentials, get_credentials
|
||||
from ..logging_config import get_logger
|
||||
from ..transliterate import transliterate_json
|
||||
|
||||
log = get_logger("pplcpl.orders")
|
||||
|
||||
router = APIRouter(prefix="/orders", tags=["orders"])
|
||||
|
||||
_EXAMPLE_ORDER_BODY = {
|
||||
"orders": [
|
||||
{
|
||||
"orderType": "TransportOrder",
|
||||
"referenceId": "ORD-0001",
|
||||
"productType": "BUSS",
|
||||
"shipmentCount": 1,
|
||||
"sendDate": "2026-07-17",
|
||||
"customerReference": "Zakazka 123",
|
||||
"email": "odesilatel@example.com",
|
||||
"sender": {
|
||||
"name": "Firma s.r.o.",
|
||||
"street": "Prazska 123/4",
|
||||
"city": "Praha",
|
||||
"zipCode": "10000",
|
||||
"country": "CZ",
|
||||
"phone": "+420601123456",
|
||||
},
|
||||
"recipient": {
|
||||
"name": "Jan Novak",
|
||||
"street": "Brnenska 10",
|
||||
"city": "Brno",
|
||||
"zipCode": "60200",
|
||||
"country": "CZ",
|
||||
"phone": "+420602123456",
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
_PENDING_STATES = ("Accepted", "InProcess")
|
||||
|
||||
|
||||
def _maybe_transliterate(body: dict, transliterate: bool | None) -> dict:
|
||||
apply = TRANSLITERATE_DEFAULT if transliterate is None else transliterate
|
||||
return transliterate_json(body) if apply else body
|
||||
|
||||
|
||||
@router.post(
|
||||
"/batch",
|
||||
status_code=201,
|
||||
summary="Vytvoření objednávky přepravy / svozu (asynchronní)",
|
||||
)
|
||||
async def create_order_batch(
|
||||
body: dict = Body(..., examples=[_EXAMPLE_ORDER_BODY]),
|
||||
transliterate: bool | None = Query(default=None),
|
||||
creds: Credentials = Depends(get_credentials),
|
||||
):
|
||||
"""`POST order/batch` — vrací batchId z Location hlavičky (max 100 objednávek)."""
|
||||
resp = await cpl_request(
|
||||
creds, "POST", "/order/batch", json_body=_maybe_transliterate(body, transliterate)
|
||||
)
|
||||
ensure_success(resp)
|
||||
return {
|
||||
"batchId": batch_id_from_location(resp),
|
||||
"location": resp.headers.get("location"),
|
||||
"correlationId": resp.headers.get("x-correlation-id"),
|
||||
}
|
||||
|
||||
|
||||
@router.get("/batch/{batch_id}", summary="Stav zpracování objednávky (batch)")
|
||||
async def get_order_batch_status(
|
||||
batch_id: str,
|
||||
creds: Credentials = Depends(get_credentials),
|
||||
):
|
||||
"""`GET order/batch/{batchId}` — importState: Accepted/InProcess/Complete/Error."""
|
||||
resp = await cpl_request(creds, "GET", f"/order/batch/{batch_id}")
|
||||
return relay_json(resp)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/create-and-wait",
|
||||
summary="Vytvoření objednávky a počkání na zpracování (synchronní obálka)",
|
||||
)
|
||||
async def create_order_and_wait(
|
||||
body: dict = Body(..., examples=[_EXAMPLE_ORDER_BODY]),
|
||||
timeout_seconds: float = Query(default=BATCH_WAIT_TIMEOUT_SECONDS, ge=1, le=120),
|
||||
transliterate: bool | None = Query(default=None),
|
||||
creds: Credentials = Depends(get_credentials),
|
||||
):
|
||||
"""Convenience: POST order/batch + polling stavu v jednom requestu.
|
||||
`completed=false` znamená timeout — zpracování v PPL běží dál."""
|
||||
create_resp = await cpl_request(
|
||||
creds, "POST", "/order/batch", json_body=_maybe_transliterate(body, transliterate)
|
||||
)
|
||||
ensure_success(create_resp)
|
||||
batch_id = batch_id_from_location(create_resp)
|
||||
|
||||
deadline = time.monotonic() + timeout_seconds
|
||||
status_data: dict = {}
|
||||
completed = False
|
||||
while True:
|
||||
status_resp = await cpl_request(creds, "GET", f"/order/batch/{batch_id}")
|
||||
ensure_success(status_resp)
|
||||
status_data = status_resp.json() or {}
|
||||
items = status_data.get("items") or []
|
||||
pending = [i for i in items if i.get("importState") in _PENDING_STATES]
|
||||
if items and not pending:
|
||||
completed = True
|
||||
break
|
||||
if time.monotonic() >= deadline:
|
||||
log.warning(
|
||||
"Order batch %s nebyl zpracován do %ss, vracím completed=false.",
|
||||
batch_id,
|
||||
timeout_seconds,
|
||||
)
|
||||
break
|
||||
await asyncio.sleep(BATCH_POLL_INTERVAL_SECONDS)
|
||||
|
||||
return {"batchId": batch_id, "completed": completed, **status_data}
|
||||
|
||||
|
||||
@router.get("", summary="Vyhledání objednávek přepravy")
|
||||
async def find_orders(
|
||||
shipment_numbers: list[str] | None = Query(default=None, alias="shipmentNumbers"),
|
||||
customer_references: list[str] | None = Query(
|
||||
default=None, alias="customerReferences"
|
||||
),
|
||||
order_references: list[str] | None = Query(
|
||||
default=None,
|
||||
alias="orderReferences",
|
||||
description="referenceId hodnoty z POST order/batch.",
|
||||
),
|
||||
order_numbers: list[str] | None = Query(default=None, alias="orderNumbers"),
|
||||
order_ids: list[int] | None = Query(default=None, alias="orderIds"),
|
||||
date_from: str | None = Query(default=None, alias="dateFrom"),
|
||||
date_to: str | None = Query(default=None, alias="dateTo"),
|
||||
send_date: str | None = Query(default=None, alias="sendDate"),
|
||||
product_type: str | None = Query(default=None, alias="productType"),
|
||||
order_states: str | None = Query(
|
||||
default=None,
|
||||
alias="orderStates",
|
||||
description="None, Created, PickedUp, NotPickedUp, Canceled.",
|
||||
),
|
||||
order_type: str | None = Query(
|
||||
default=None, alias="orderType", description="CollectionOrder / TransportOrder."
|
||||
),
|
||||
limit: int = Query(default=100, ge=1, le=1000),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
creds: Credentials = Depends(get_credentials),
|
||||
):
|
||||
"""`GET order` — informace o objednávkách vč. stavu a přidělených čísel zásilek."""
|
||||
params: dict[str, Any] = {"Limit": limit, "Offset": offset}
|
||||
if shipment_numbers:
|
||||
params["ShipmentNumbers"] = shipment_numbers
|
||||
if customer_references:
|
||||
params["CustomerReferences"] = customer_references
|
||||
if order_references:
|
||||
params["OrderReferences"] = order_references
|
||||
if order_numbers:
|
||||
params["OrderNumbers"] = order_numbers
|
||||
if order_ids:
|
||||
params["OrderIds"] = order_ids
|
||||
if date_from:
|
||||
params["DateFrom"] = date_from
|
||||
if date_to:
|
||||
params["DateTo"] = date_to
|
||||
if send_date:
|
||||
params["SendDate"] = send_date
|
||||
if product_type:
|
||||
params["ProductType"] = product_type
|
||||
if order_states:
|
||||
params["OrderStates"] = order_states
|
||||
if order_type:
|
||||
params["OrderType"] = order_type
|
||||
resp = await cpl_request(creds, "GET", "/order", params=params)
|
||||
return relay_json(resp)
|
||||
|
||||
|
||||
@router.post("/cancel", summary="Zrušení objednávky svozu / přepravy")
|
||||
async def cancel_order(
|
||||
customer_reference: str | None = Query(
|
||||
default=None, alias="customerReference", description="Reference odesílatele."
|
||||
),
|
||||
order_reference: str | None = Query(
|
||||
default=None, alias="orderReference", description="Reference objednávky."
|
||||
),
|
||||
body: dict = Body(default={}, examples=[{"note": "Zrušeno zákazníkem"}]),
|
||||
creds: Credentials = Depends(get_credentials),
|
||||
):
|
||||
"""`POST order/cancel` — identifikace přes customerReference nebo orderReference."""
|
||||
params: dict[str, Any] = {}
|
||||
if customer_reference:
|
||||
params["customerReference"] = customer_reference
|
||||
if order_reference:
|
||||
params["orderReference"] = order_reference
|
||||
resp = await cpl_request(
|
||||
creds, "POST", "/order/cancel", params=params or None, json_body=body or {}
|
||||
)
|
||||
return relay_json(resp)
|
||||
Reference in New Issue
Block a user