75 lines
2.9 KiB
Python
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
|