Добавлен поллер накладной

This commit is contained in:
Раис Юсупалиев
2026-05-23 20:24:23 +03:00
parent c494d50566
commit 5526f90cb3
23 changed files with 1481 additions and 61 deletions
@@ -19,13 +19,17 @@ from app.adapters.delivery_providers.cdek.mapper import (
map_cdek_response_for_tariff_code,
)
from app.adapters.delivery_providers.cdek.order_mapper import (
CDEKOrderInfo,
CDEKOrderMappingError,
CDEKOrderRegistrationResult,
CDEKWaybillInfo,
centimeters_string_to_int,
kilograms_string_to_grams,
map_cdek_existing_order_response,
map_cdek_order_info_response,
map_cdek_order_request,
map_cdek_order_response,
map_cdek_waybill_info_response,
resolve_cdek_city_code,
)
from app.cities import cities_map
@@ -61,6 +65,7 @@ class CDEKClient:
normalized_base_url = base_url.rstrip("/")
self._tariff_url = f"{normalized_base_url}/calculator/tarifflist"
self._orders_url = f"{normalized_base_url}/orders"
self._print_orders_url = f"{normalized_base_url}/print/orders"
self._timeout_seconds = timeout_seconds
self._retry_attempts = retry_attempts
self._retry_backoff_seconds = retry_backoff_seconds
@@ -222,6 +227,85 @@ class CDEKClient:
raise CDEKClientError("CDEK order registration failed unexpectedly.")
async def get_order(self, cdek_order_uuid: str) -> CDEKOrderInfo:
url = f"{self._orders_url}/{cdek_order_uuid}"
raw_payload = await self._get_json(
url,
failure_message="CDEK get order failed",
)
try:
return map_cdek_order_info_response(raw_payload)
except CDEKOrderMappingError as exc:
raise CDEKClientError(
"CDEK get order response payload is invalid."
) from exc
async def get_waybill(self, cdek_waybill_uuid: str) -> CDEKWaybillInfo:
url = f"{self._print_orders_url}/{cdek_waybill_uuid}"
raw_payload = await self._get_json(
url,
failure_message="CDEK get waybill failed",
)
try:
return map_cdek_waybill_info_response(raw_payload)
except CDEKOrderMappingError as exc:
raise CDEKClientError(
"CDEK get waybill response payload is invalid."
) from exc
async def _get_json(self, url: str, *, failure_message: str) -> dict[str, Any]:
for attempt in range(self._retry_attempts + 1):
try:
token = await self._auth_client.get_access_token()
response = await self._http_client.get(
url,
headers={"Authorization": f"Bearer {token}"},
timeout=self._timeout_seconds,
)
except (httpx.TimeoutException, httpx.TransportError) as exc:
if attempt < self._retry_attempts:
await self._sleep(self._retry_delay(attempt))
continue
raise CDEKClientError(
f"{failure_message} after retry attempts."
) from exc
if self._should_retry(response.status_code):
if attempt < self._retry_attempts:
await self._sleep(self._retry_delay(attempt))
continue
raise CDEKClientError(
f"{failure_message} with retriable status "
f"{response.status_code}."
)
if 400 <= response.status_code < 500:
log.warning(
"cdek_get_request_rejected",
url=url,
status_code=response.status_code,
response_body=_response_text_or_none(response),
)
raise CDEKRequestError(
f"{failure_message} with status {response.status_code}."
)
try:
response.raise_for_status()
raw_payload = response.json()
except (httpx.HTTPError, TypeError, ValueError) as exc:
raise CDEKClientError(
f"{failure_message}: invalid response payload."
) from exc
if not isinstance(raw_payload, dict):
raise CDEKClientError(
f"{failure_message}: payload must be a JSON object."
)
return raw_payload
raise CDEKClientError(f"{failure_message} unexpectedly.")
def _retry_delay(self, attempt: int) -> float:
return self._retry_backoff_seconds * (attempt + 1)
@@ -361,6 +445,12 @@ class CDEKProvider(DeliveryProvider):
) -> CDEKOrderRegistrationResult:
return await self._client.register_order(request)
async def get_order(self, cdek_order_uuid: str) -> CDEKOrderInfo:
return await self._client.get_order(cdek_order_uuid)
async def get_waybill(self, cdek_waybill_uuid: str) -> CDEKWaybillInfo:
return await self._client.get_waybill(cdek_waybill_uuid)
def _response_json_or_none(response: httpx.Response) -> object | None:
try: