More Shipstation integrations(warehouses)
This commit is contained in:
+36
-32
@@ -14,12 +14,11 @@ from typing import List, Tuple, TypedDict
|
||||
from PyQt6.QtCore import QThread, pyqtSignal
|
||||
from sqlalchemy import select
|
||||
|
||||
from app import config
|
||||
from app.database import get_session
|
||||
from app.models import Order
|
||||
from app.schedule import get_cutoff_time, is_past_cutoff
|
||||
from app.services.base import OrderService, NormalizedOrder
|
||||
from app.status_rules import parse_status_list, status_in
|
||||
from app.status_rules import get_active_statuses, get_cancelled_statuses, status_in
|
||||
from app.tracking import get_fulfilled_statuses
|
||||
|
||||
|
||||
@@ -209,63 +208,68 @@ def load_all_orders() -> List[Order]:
|
||||
session.close()
|
||||
|
||||
|
||||
def split_active_and_done(orders: List[Order]) -> Tuple[List[Order], List[Order]]:
|
||||
def split_orders_by_view(orders: List[Order]) -> Tuple[List[Order], List[Order], List[Order]]:
|
||||
"""
|
||||
Active = still being worked (includes cancelled - those still need
|
||||
eyes on them). Done = fulfilled (Waiting For Return / Device Return
|
||||
Not Needed) - archived out of the working view on purpose, per how
|
||||
the team wants to keep the active list focused on what's at hand.
|
||||
Three tabs, allowlist-driven:
|
||||
- Active: status is in ACTIVE_STATUSES (just "Created" by default) -
|
||||
the only tickets that represent real work still to do.
|
||||
- Cancelled: status is in CANCELLED_STATUSES - its own tab so it
|
||||
doesn't clutter Active, but still reviewable on demand.
|
||||
- Done: everything else. This deliberately doesn't enumerate every
|
||||
"finished" status by name - a JIRA-side automation status like
|
||||
"1st Contact Attempt" falls in here automatically just by not
|
||||
being Created or Cancelled, with no code change needed when your
|
||||
JIRA workflow adds another downstream status later.
|
||||
"""
|
||||
fulfilled_statuses = get_fulfilled_statuses()
|
||||
active, done = [], []
|
||||
active_statuses = get_active_statuses()
|
||||
cancelled_statuses = get_cancelled_statuses()
|
||||
active, cancelled, done = [], [], []
|
||||
for order in orders:
|
||||
(done if status_in(order.status, fulfilled_statuses) else active).append(order)
|
||||
return active, done
|
||||
if status_in(order.status, active_statuses):
|
||||
active.append(order)
|
||||
elif status_in(order.status, cancelled_statuses):
|
||||
cancelled.append(order)
|
||||
else:
|
||||
done.append(order)
|
||||
return active, cancelled, done
|
||||
|
||||
|
||||
def load_active_and_done_orders() -> Tuple[List[Order], List[Order]]:
|
||||
return split_active_and_done(load_all_orders())
|
||||
def load_orders_by_view() -> Tuple[List[Order], List[Order], List[Order]]:
|
||||
return split_orders_by_view(load_all_orders())
|
||||
|
||||
|
||||
def get_dashboard_stats() -> dict:
|
||||
"""
|
||||
Counts for the dashboard. "Active Orders" and the company breakdown
|
||||
reflect only the active workload (fulfilled tickets have moved to
|
||||
the Done pile and don't clutter this). Fulfilled-today and
|
||||
past-cutoff-today look across ALL of today's tickets regardless of
|
||||
which pile they're in now, since both are about what happened today.
|
||||
reflect only the Active tab (status in ACTIVE_STATUSES) - the actual
|
||||
at-hand workload. Cancelled counts the Cancelled tab. Fulfilled-today
|
||||
and past-cutoff-today look across ALL of today's tickets regardless
|
||||
of which tab they ended up in, since both are about what happened
|
||||
today specifically.
|
||||
|
||||
Carryover counts any still-open ticket that's already known to spill
|
||||
Carryover counts any Active ticket that's already known to spill
|
||||
into tomorrow - either it's genuinely left over from a prior day, or
|
||||
it arrived today but after the cutoff (same effect, just known a day
|
||||
earlier).
|
||||
earlier). Since we're only looking at the Active bucket, every
|
||||
ticket here is by definition still unresolved - no separate "is it
|
||||
still open" check needed.
|
||||
"""
|
||||
orders = load_all_orders()
|
||||
active_orders, _done_orders = split_active_and_done(orders)
|
||||
active_orders, cancelled_orders, _done_orders = split_orders_by_view(orders)
|
||||
|
||||
cancelled_statuses = parse_status_list(
|
||||
config.get("CANCELLED_STATUSES", config.DEFAULT_CANCELLED_STATUSES)
|
||||
)
|
||||
terminal_statuses = parse_status_list(
|
||||
config.get("JIRA_TERMINAL_STATUSES", config.DEFAULT_TERMINAL_STATUSES)
|
||||
)
|
||||
cutoff = get_cutoff_time()
|
||||
today = dt.date.today()
|
||||
|
||||
by_company: dict[str, int] = {}
|
||||
cancelled_count = 0
|
||||
carryover_count = 0
|
||||
tracking_received_count = 0
|
||||
|
||||
for order in active_orders:
|
||||
by_company[order.company] = by_company.get(order.company, 0) + 1
|
||||
|
||||
if status_in(order.status, cancelled_statuses):
|
||||
cancelled_count += 1
|
||||
if order.tracking_numbers:
|
||||
tracking_received_count += 1
|
||||
|
||||
still_open = not status_in(order.status, terminal_statuses)
|
||||
created_before_today = bool(
|
||||
order.source_created_at and order.source_created_at.date() < today
|
||||
)
|
||||
@@ -277,7 +281,7 @@ def get_dashboard_stats() -> dict:
|
||||
and order.source_created_at.date() == today
|
||||
and is_past_cutoff(order.source_created_at, cutoff)
|
||||
)
|
||||
if still_open and (created_before_today or arrived_past_cutoff_today):
|
||||
if created_before_today or arrived_past_cutoff_today:
|
||||
carryover_count += 1
|
||||
|
||||
fulfilled_today_count = sum(
|
||||
@@ -294,7 +298,7 @@ def get_dashboard_stats() -> dict:
|
||||
return {
|
||||
"total": len(active_orders),
|
||||
"by_company": by_company,
|
||||
"cancelled_count": cancelled_count,
|
||||
"cancelled_count": len(cancelled_orders),
|
||||
"carryover_count": carryover_count,
|
||||
"tracking_received_count": tracking_received_count,
|
||||
"fulfilled_today_count": fulfilled_today_count,
|
||||
|
||||
Reference in New Issue
Block a user