Add refusals tab to the order dashboard
Orders whose Nova Poshta status is a refusal (102/103/105/108) now show only under «Отказы», whether or not they have a receipt. GET /orders takes tab=no_receipt|has_receipt|refused instead of has_receipt. NP statuses are also polled for orders that already have a receipt, until NP reports a final status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
from decimal import ROUND_HALF_UP, Decimal
|
||||
@@ -20,6 +21,14 @@ _CRM_STATUS = "APPROVED"
|
||||
# NP отклоняет запросы с более чем 100 накладными за раз (см. np_client.py).
|
||||
_NP_BATCH_SIZE = 100
|
||||
|
||||
# Коды NP "відмова одержувача" / "припинено зберігання" — клиент не забрал
|
||||
# посылку, заказ уходит во вкладку «Отказы».
|
||||
NP_REFUSAL_STATUS_CODES = ("102", "103", "105", "108")
|
||||
|
||||
# Коды NP "відправлення отримано" и отказы — после них статус ТТН больше не
|
||||
# опрашивается.
|
||||
_NP_FINAL_STATUS_CODES = ("9", "10", "11", *NP_REFUSAL_STATUS_CODES)
|
||||
|
||||
# Поля, которые кассир может править в карточке заказа (кроме goods/total).
|
||||
_EDITABLE_FIELDS = (
|
||||
"recipient_name",
|
||||
@@ -30,6 +39,12 @@ _EDITABLE_FIELDS = (
|
||||
)
|
||||
|
||||
|
||||
class OrderTab(enum.StrEnum):
|
||||
NO_RECEIPT = "no_receipt"
|
||||
HAS_RECEIPT = "has_receipt"
|
||||
REFUSED = "refused"
|
||||
|
||||
|
||||
class OrderEditError(Exception):
|
||||
"""Заказ нельзя сохранить — сообщение показывается кассиру."""
|
||||
|
||||
@@ -80,21 +95,31 @@ async def sync_orders_from_crm(session: AsyncSession, crm: CrmClient) -> None:
|
||||
await session.commit()
|
||||
|
||||
|
||||
async def list_orders(session: AsyncSession, *, has_receipt: bool) -> list[Order]:
|
||||
receipt_filter = (
|
||||
Order.receipt_created_at.is_not(None) if has_receipt else Order.receipt_created_at.is_(None)
|
||||
)
|
||||
async def list_orders(session: AsyncSession, *, tab: OrderTab) -> list[Order]:
|
||||
"""Заказы вкладки. Отказ клиента важнее наличия чека: такой заказ показывается
|
||||
только в «Отказах», даже если по нему уже создан ЕТТН-чек."""
|
||||
refused = Order.np_status_code.in_(NP_REFUSAL_STATUS_CODES)
|
||||
not_refused = Order.np_status_code.is_(None) | ~refused
|
||||
tab_filter = {
|
||||
OrderTab.REFUSED: refused,
|
||||
OrderTab.NO_RECEIPT: Order.receipt_created_at.is_(None) & not_refused,
|
||||
OrderTab.HAS_RECEIPT: Order.receipt_created_at.is_not(None) & not_refused,
|
||||
}[tab]
|
||||
result = await session.scalars(
|
||||
select(Order)
|
||||
.where(Order.is_deleted.is_(False))
|
||||
.where(receipt_filter)
|
||||
.where(tab_filter)
|
||||
.order_by(Order.create_date_time.desc())
|
||||
)
|
||||
return list(result)
|
||||
|
||||
|
||||
async def sync_np_statuses(session: AsyncSession, np: NovaPoshtaClient) -> None:
|
||||
"""Обновляет статус ТТН и сумму наложенного платежа для заказов без чека.
|
||||
"""Обновляет статус ТТН, сумму и статус оплаты наложки по заказам в пути.
|
||||
|
||||
Опрашиваются и заказы с уже созданным ЕТТН-чеком: именно после создания
|
||||
чека посылка доезжает и оплачивается, и этот переход кассир должен видеть.
|
||||
Опрос прекращается, когда NP вернул финальный статус "отримано" или отказ.
|
||||
|
||||
Вызывается ARQ worker'ом раз в минуту (см. `app/worker.py`), а не из
|
||||
HTTP-запроса: опрос статусов не должен зависеть от того, открыт ли сейчас
|
||||
@@ -103,8 +128,11 @@ async def sync_np_statuses(session: AsyncSession, np: NovaPoshtaClient) -> None:
|
||||
orders = await session.scalars(
|
||||
select(Order)
|
||||
.where(Order.is_deleted.is_(False))
|
||||
.where(Order.receipt_created_at.is_(None))
|
||||
.where(Order.waybill_number.is_not(None))
|
||||
.where(
|
||||
Order.np_status_code.is_(None)
|
||||
| Order.np_status_code.not_in(_NP_FINAL_STATUS_CODES)
|
||||
)
|
||||
)
|
||||
orders_by_waybill: dict[str, Order] = {order.waybill_number: order for order in orders}
|
||||
if not orders_by_waybill:
|
||||
|
||||
Reference in New Issue
Block a user