GUI updates and misc fixes for the Cancel cutoff
This commit is contained in:
+42
-18
@@ -16,7 +16,7 @@ from sqlalchemy import select
|
||||
|
||||
from app.database import get_session
|
||||
from app.models import Order
|
||||
from app.schedule import get_cutoff_time, is_past_cutoff
|
||||
from app.schedule import get_cutoff_time, is_past_cutoff_today
|
||||
from app.services.base import OrderService, NormalizedOrder
|
||||
from app.status_rules import get_active_statuses, get_cancelled_statuses, status_in
|
||||
from app.tracking import get_fulfilled_statuses
|
||||
@@ -99,7 +99,9 @@ def save_orders(orders: List[NormalizedOrder]) -> SaveResult:
|
||||
Whenever a JIRA ticket's status transitions INTO a fulfilled status
|
||||
(Waiting For Return / Device Return Not Needed), fulfilled_at is
|
||||
stamped - that's what lets the dashboard show "fulfilled today" and
|
||||
what moves it into the Done pile.
|
||||
what moves it into the Done pile. cancelled_at works the same way for
|
||||
CANCELLED_STATUSES - it's what limits the Cancelled tab to "cancelled
|
||||
today" rather than showing every cancelled ticket ever.
|
||||
"""
|
||||
session = get_session()
|
||||
new_count = 0
|
||||
@@ -108,6 +110,7 @@ def save_orders(orders: List[NormalizedOrder]) -> SaveResult:
|
||||
status_changes: List[StatusChange] = []
|
||||
unmatched_tracking_tickets: List[str] = []
|
||||
fulfilled_statuses = get_fulfilled_statuses()
|
||||
cancelled_statuses = get_cancelled_statuses()
|
||||
|
||||
try:
|
||||
for order in orders:
|
||||
@@ -148,6 +151,7 @@ def save_orders(orders: List[NormalizedOrder]) -> SaveResult:
|
||||
line_items=order.get("line_items", []),
|
||||
shipping_info=order.get("shipping_info", {}),
|
||||
creator=order.get("creator"),
|
||||
tracking_numbers=order.get("tracking_numbers", []),
|
||||
summary=order["summary"],
|
||||
status=new_status,
|
||||
source_created_at=order["source_created_at"],
|
||||
@@ -155,6 +159,9 @@ def save_orders(orders: List[NormalizedOrder]) -> SaveResult:
|
||||
fulfilled_at=(
|
||||
dt.datetime.utcnow() if status_in(new_status, fulfilled_statuses) else None
|
||||
),
|
||||
cancelled_at=(
|
||||
dt.datetime.utcnow() if status_in(new_status, cancelled_statuses) else None
|
||||
),
|
||||
)
|
||||
)
|
||||
new_count += 1
|
||||
@@ -172,6 +179,10 @@ def save_orders(orders: List[NormalizedOrder]) -> SaveResult:
|
||||
old_status, fulfilled_statuses
|
||||
):
|
||||
existing.fulfilled_at = dt.datetime.utcnow()
|
||||
if status_in(new_status, cancelled_statuses) and not status_in(
|
||||
old_status, cancelled_statuses
|
||||
):
|
||||
existing.cancelled_at = dt.datetime.utcnow()
|
||||
|
||||
existing.ticket_number = order.get("ticket_number")
|
||||
existing.company = order.get("company", "Unknown")
|
||||
@@ -213,21 +224,28 @@ def split_orders_by_view(orders: List[Order]) -> Tuple[List[Order], List[Order],
|
||||
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.
|
||||
- Cancelled: status is in CANCELLED_STATUSES AND it was cancelled
|
||||
TODAY. A ticket cancelled on a prior day falls through to Done
|
||||
instead - the Cancelled tab is meant to be reviewed same-day and
|
||||
then filed away, not accumulate forever.
|
||||
- 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.
|
||||
being Created or (today's) Cancelled, with no code change needed
|
||||
when your JIRA workflow adds another downstream status later.
|
||||
"""
|
||||
active_statuses = get_active_statuses()
|
||||
cancelled_statuses = get_cancelled_statuses()
|
||||
today = dt.date.today()
|
||||
active, cancelled, done = [], [], []
|
||||
for order in orders:
|
||||
if status_in(order.status, active_statuses):
|
||||
active.append(order)
|
||||
elif status_in(order.status, cancelled_statuses):
|
||||
elif (
|
||||
status_in(order.status, cancelled_statuses)
|
||||
and order.cancelled_at
|
||||
and order.cancelled_at.date() == today
|
||||
):
|
||||
cancelled.append(order)
|
||||
else:
|
||||
done.append(order)
|
||||
@@ -238,6 +256,21 @@ def load_orders_by_view() -> Tuple[List[Order], List[Order], List[Order]]:
|
||||
return split_orders_by_view(load_all_orders())
|
||||
|
||||
|
||||
def mark_shipstation_sent(ticket_number: str) -> None:
|
||||
"""Stamps shipstation_sent_at after a CONFIRMED emergency API send -
|
||||
called once ShipStation's own response confirms creation succeeded."""
|
||||
session = get_session()
|
||||
try:
|
||||
order = session.execute(
|
||||
select(Order).where(Order.source == "jira", Order.ticket_number == ticket_number)
|
||||
).scalar_one_or_none()
|
||||
if order is not None:
|
||||
order.shipstation_sent_at = dt.datetime.utcnow()
|
||||
session.commit()
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def get_dashboard_stats() -> dict:
|
||||
"""
|
||||
Counts for the dashboard. "Active Orders" and the company breakdown
|
||||
@@ -276,23 +309,14 @@ def get_dashboard_stats() -> dict:
|
||||
# A ticket that arrived today but after the cutoff won't get worked
|
||||
# today either - it's known carryover before the date even rolls
|
||||
# over, not just once tomorrow arrives.
|
||||
arrived_past_cutoff_today = bool(
|
||||
order.source_created_at
|
||||
and order.source_created_at.date() == today
|
||||
and is_past_cutoff(order.source_created_at, cutoff)
|
||||
)
|
||||
if created_before_today or arrived_past_cutoff_today:
|
||||
if created_before_today or is_past_cutoff_today(order.source_created_at, cutoff):
|
||||
carryover_count += 1
|
||||
|
||||
fulfilled_today_count = sum(
|
||||
1 for o in orders if o.fulfilled_at and o.fulfilled_at.date() == today
|
||||
)
|
||||
past_cutoff_today_count = sum(
|
||||
1
|
||||
for o in orders
|
||||
if o.source_created_at
|
||||
and o.source_created_at.date() == today
|
||||
and is_past_cutoff(o.source_created_at, cutoff)
|
||||
1 for o in orders if is_past_cutoff_today(o.source_created_at, cutoff)
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user