Add Nova Poshta tracking: TTN status, COD amount, payment status
Adds NpTrackingClient (Protocol + real/stub impls) and an ARQ worker that polls Nova Poshta every minute for orders without a receipt, writing status, net COD amount (Контроль оплати), and payment status onto the order. Surfaced in the orders table and detail modal. Marks plan stages 3-4 done in README. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -229,6 +229,11 @@
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.orders-status--danger {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.orders-status--processing {
|
||||
background: rgba(217, 119, 6, 0.18);
|
||||
@@ -249,6 +254,11 @@
|
||||
background: rgba(22, 163, 74, 0.18);
|
||||
color: #86efac;
|
||||
}
|
||||
|
||||
.orders-status--danger {
|
||||
background: rgba(220, 38, 38, 0.18);
|
||||
color: #fca5a5;
|
||||
}
|
||||
}
|
||||
|
||||
.orders-actions-cell {
|
||||
|
||||
@@ -21,6 +21,20 @@ const TABS: { key: Tab; label: string }[] = [
|
||||
{ key: 'has_receipt', label: 'Выписаны чеки' },
|
||||
]
|
||||
|
||||
function npStatusTone(order: Order): 'delivered' | 'processing' | 'danger' | 'new' {
|
||||
const text = (order.np_status ?? '').toLowerCase()
|
||||
if (text.includes('видано') || text.includes('отримано')) return 'delivered'
|
||||
if (text.includes('відмова') || text.includes('не забра') || text.includes('поверн')) return 'danger'
|
||||
if (text) return 'processing'
|
||||
return 'new'
|
||||
}
|
||||
|
||||
function paymentBadge(order: Order): { label: string; tone: 'delivered' | 'processing' } | null {
|
||||
if (order.np_payment_status === 'Payed') return { label: 'Оплачено', tone: 'delivered' }
|
||||
if (order.np_payment_status === 'NeedPayment') return { label: 'Не оплачено', tone: 'processing' }
|
||||
return null
|
||||
}
|
||||
|
||||
export function DashboardPage() {
|
||||
const { user, logout } = useAuth()
|
||||
const [tab, setTab] = useState<Tab>('no_receipt')
|
||||
@@ -134,29 +148,33 @@ export function DashboardPage() {
|
||||
<th>Номер ТТН</th>
|
||||
<th>Клиент</th>
|
||||
<th>Сумма</th>
|
||||
<th>Наложка</th>
|
||||
<th>Оплачено</th>
|
||||
<th>Предоплата</th>
|
||||
<th>Статус</th>
|
||||
<th>Статус ТТН</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{isLoading && (
|
||||
<tr>
|
||||
<td colSpan={8} className="orders-empty">
|
||||
<td colSpan={10} className="orders-empty">
|
||||
Загрузка…
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{isError && (
|
||||
<tr>
|
||||
<td colSpan={8} className="orders-empty">
|
||||
<td colSpan={10} className="orders-empty">
|
||||
Не удалось загрузить заказы из CRM
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{!isLoading &&
|
||||
!isError &&
|
||||
filtered.map((order) => (
|
||||
filtered.map((order) => {
|
||||
const badge = paymentBadge(order)
|
||||
return (
|
||||
<tr key={order.id}>
|
||||
<td>
|
||||
<input
|
||||
@@ -170,11 +188,21 @@ export function DashboardPage() {
|
||||
<td>{order.waybill_number || '—'}</td>
|
||||
<td>{order.recipient_name || '—'}</td>
|
||||
<td>{order.total_amount} ₴</td>
|
||||
<td>{order.np_cod_amount ? `${order.np_cod_amount} ₴` : '—'}</td>
|
||||
<td>
|
||||
{badge ? (
|
||||
<span className={`orders-status orders-status--${badge.tone}`}>{badge.label}</span>
|
||||
) : (
|
||||
'—'
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" className="orders-prepayment-input" placeholder="0%" />
|
||||
</td>
|
||||
<td>
|
||||
<span className="orders-status orders-status--approved">Одобрен</span>
|
||||
<span className={`orders-status orders-status--${npStatusTone(order)}`}>
|
||||
{order.np_status || 'Нет данных'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="orders-actions-cell">
|
||||
<button type="button" className="orders-view-btn" onClick={() => setViewingOrder(order)}>
|
||||
@@ -195,10 +223,11 @@ export function DashboardPage() {
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
{!isLoading && !isError && filtered.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={8} className="orders-empty">
|
||||
<td colSpan={10} className="orders-empty">
|
||||
Ничего не найдено
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user