ShipStation tracking pull, status tracking, dashboard rework

This commit is contained in:
2026-08-26 11:28:42 -05:00
parent ffaaa0c8c8
commit 18be8588f6
16 changed files with 769 additions and 304 deletions
+51 -3
View File
@@ -17,7 +17,9 @@ import requests
from app import config
from app.companies import parse_mapping, resolve_company_for_skus
from app.queries import get_open_ticket_numbers
from app.services.base import OrderService, NormalizedOrder
from app.status_rules import parse_status_list
SEARCH_PAGE_SIZE = 50
REQUEST_TIMEOUT_SECONDS = 30
@@ -58,6 +60,12 @@ class JiraService(OrderService):
settings["COMPANY_SKU_MAP"] or config.DEFAULT_COMPANY_SKU_MAP
)
# Statuses at which we stop re-checking a ticket for changes -
# see fetch_orders() for why we re-check at all.
self.terminal_statuses = parse_status_list(
settings["JIRA_TERMINAL_STATUSES"] or config.DEFAULT_TERMINAL_STATUSES
)
@staticmethod
def _split_field_ids(raw: str) -> List[str]:
return [f.strip() for f in (raw or "").split(",") if f.strip()]
@@ -72,12 +80,51 @@ class JiraService(OrderService):
"JIRA Site URL, Email, API Token, and JQL."
)
issues = self._search_all_issues()
# Your JQL (e.g. "created >= startOfDay()") only catches NEW
# tickets. On its own, that would miss a ticket that gets
# cancelled a day or two after it was created, since it no
# longer matches "created today". So on top of your JQL, we
# also re-check every previously-imported JIRA ticket that
# hasn't reached a terminal status yet (JIRA_TERMINAL_STATUSES,
# default just "Cancelled") - that's how a later cancellation
# gets picked up.
recheck_keys = get_open_ticket_numbers("jira", self.terminal_statuses)
effective_jql = self._build_effective_jql(self.jql, recheck_keys)
issues = self._search_all_issues(effective_jql)
return [self._to_normalized_order(issue) for issue in issues]
# -- internals -----------------------------------------------------
def _search_all_issues(self) -> List[dict]:
@staticmethod
def _build_effective_jql(base_jql: str, recheck_keys: List[str]) -> str:
"""
Combine the configured JQL with "OR key in (...)" for tickets we
want to re-check, being careful to keep any ORDER BY clause at
the very end (JQL requires it there).
Note: as the number of still-open tracked tickets grows, this
"key in (...)" list grows too. If that ever gets unwieldy, add
more statuses to JIRA_TERMINAL_STATUSES (e.g. "Done", once you
know your workflow's real terminal status names) so fulfilled
tickets stop being re-checked and drop out of this list.
"""
if not recheck_keys:
return base_jql
order_by_match = re.search(r"\bORDER BY\b.*$", base_jql, re.IGNORECASE)
if order_by_match:
where_part = base_jql[: order_by_match.start()].strip()
order_by_clause = " " + order_by_match.group(0)
else:
where_part = base_jql.strip()
order_by_clause = ""
keys_clause = "key in (" + ", ".join(recheck_keys) + ")"
combined_where = f"({where_part}) OR {keys_clause}" if where_part else keys_clause
return combined_where + order_by_clause
def _search_all_issues(self, jql: str) -> List[dict]:
url = f"{self.base_url}/rest/api/3/search"
auth = (self.email, self.api_token)
headers = {"Accept": "application/json"}
@@ -92,7 +139,7 @@ class JiraService(OrderService):
while True:
params = {
"jql": self.jql,
"jql": jql,
"startAt": start_at,
"maxResults": SEARCH_PAGE_SIZE,
"fields": fields,
@@ -221,6 +268,7 @@ class JiraService(OrderService):
ticket_number=ticket_number,
company=company,
skus=skus,
tracking_numbers=[],
summary=summary,
status=(fields.get("status") or {}).get("name", ""),
source_created_at=created_at,