Files
baiyue-logistics-chat/logistics_chat/customer_order_service.py
T

75 lines
2.9 KiB
Python

"""Read-only customer order browsing; scope comes exclusively from verified sessions."""
import sqlite3
from contextlib import contextmanager
from .customer_service import effective_tenants
from .db import connect
from .order_data import OrderDataUnavailable, build_card
from .visitor_service import customer_tenant_ids
PAGE_SIZE = 20
SUMMARY_FIELDS = ('id', 'order_no', 'inbound_code', 'status', 'country_code',
'warehouse_code', 'transport_mode')
STATUS_GROUPS = {
'pending': ('DRAFT', 'SUBMITTED', 'APPROVED', 'ACCEPTED'),
'processing': ('PROCESSING',), 'completed': ('COMPLETED',), 'cancelled': ('CANCELLED',),
}
FILTERS = ('all', *STATUS_GROUPS, 'other')
@contextmanager
def order_connection():
connection = None
try:
connection = connect()
connection.row_factory = sqlite3.Row
# Summary and list must describe the same snapshot, including during imports.
connection.execute('BEGIN')
yield connection
except sqlite3.Error:
raise OrderDataUnavailable from None
finally:
if connection is not None:
connection.close()
def scoped_rows(connection, columns, suffix='', values=()):
tenants = effective_tenants(customer_tenant_ids())
if not tenants:
return []
placeholders = ','.join('?' for _ in tenants)
return [dict(row) for row in connection.execute(
f'SELECT {columns} FROM order_test_data WHERE tenant_id IN ({placeholders}) '+suffix,
(*tenants, *values))]
def list_orders(before=None, status='all'):
if status not in FILTERS:
raise ValueError('Invalid order status filter')
summary = dict.fromkeys(('total', *STATUS_GROUPS, 'other'), 0)
suffix, values = '', ()
if status in STATUS_GROUPS or status == 'other':
codes = STATUS_GROUPS.get(status, tuple(code for codes in STATUS_GROUPS.values() for code in codes))
suffix = 'AND status '+('NOT IN' if status == 'other' else 'IN')+' ('+','.join('?' for _ in codes)+') '
values = codes
if before is not None:
suffix += 'AND id<? '
values += (before,)
with order_connection() as connection:
for row in scoped_rows(connection, 'status,COUNT(*) AS count', 'GROUP BY status'):
group = next((key for key, codes in STATUS_GROUPS.items() if row['status'] in codes), 'other')
summary[group] += row['count']
summary['total'] += row['count']
rows = scoped_rows(connection, ','.join(SUMMARY_FIELDS), suffix+'ORDER BY id DESC LIMIT ?', values+(PAGE_SIZE+1,))
return {'ok': True, 'orders': rows[:PAGE_SIZE], 'summary': summary,
'next_before': rows[PAGE_SIZE-1]['id'] if len(rows) > PAGE_SIZE else None}
def order_detail(order_id):
if not 0 < order_id <= 9223372036854775807:
return None
with order_connection() as connection:
rows = scoped_rows(connection, '*', 'AND id=? LIMIT 1', (order_id,))
return build_card(rows[0]) if rows else None