GUI updates and misc fixes for the Cancel cutoff
This commit is contained in:
@@ -38,6 +38,7 @@ from app.workers import (
|
||||
SendToShipStationWorker,
|
||||
load_orders_by_view,
|
||||
get_dashboard_stats,
|
||||
mark_shipstation_sent,
|
||||
)
|
||||
|
||||
|
||||
@@ -284,17 +285,18 @@ class MainWindow(QMainWindow):
|
||||
self._send_via_csv(order)
|
||||
|
||||
def _send_via_api(self, order) -> None:
|
||||
self.status_label.setText(
|
||||
f"Sending {order.ticket_number or order.external_id} to ShipStation..."
|
||||
)
|
||||
ticket_number = order.ticket_number or order.external_id
|
||||
self.status_label.setText(f"Sending {ticket_number} to ShipStation...")
|
||||
worker = SendToShipStationWorker(order)
|
||||
worker.finished_ok.connect(self._on_emergency_send_ok)
|
||||
worker.finished_ok.connect(lambda result: self._on_emergency_send_ok(ticket_number, result))
|
||||
worker.failed.connect(self._on_emergency_send_failed)
|
||||
self._send_worker = worker # keep a reference so it isn't garbage collected
|
||||
worker.start()
|
||||
|
||||
def _on_emergency_send_ok(self, result: dict) -> None:
|
||||
def _on_emergency_send_ok(self, ticket_number: str, result: dict) -> None:
|
||||
shipment_id = result.get("shipment_id", "?")
|
||||
mark_shipstation_sent(ticket_number)
|
||||
self._refresh_everything()
|
||||
self.status_label.setText(f"Sent to ShipStation - shipment {shipment_id} created.")
|
||||
QMessageBox.information(
|
||||
self,
|
||||
|
||||
@@ -62,7 +62,7 @@ class DashboardWidget(QWidget):
|
||||
# Top row: active workload health (fulfilled tickets have moved to the Done tab)
|
||||
top_row = QHBoxLayout()
|
||||
self.total_card = StatCard("Active Orders")
|
||||
self.cancelled_card = StatCard("Cancelled")
|
||||
self.cancelled_card = StatCard("Cancelled Today")
|
||||
self.cancelled_card.setStyleSheet("QLabel { color: #b00020; }")
|
||||
self.carryover_card = StatCard("Carryover (still open from an earlier day)")
|
||||
self.carryover_card.setStyleSheet("QLabel { color: #b06a00; }")
|
||||
|
||||
@@ -26,35 +26,37 @@ from PyQt6.QtWidgets import (
|
||||
QLabel,
|
||||
)
|
||||
|
||||
from app import config
|
||||
from app.models import Order
|
||||
from app.schedule import get_cutoff_time, is_past_cutoff
|
||||
from app.status_rules import parse_status_list, status_in
|
||||
from app.schedule import get_cutoff_time, is_past_cutoff_today
|
||||
from app.status_rules import get_cancelled_statuses, status_in
|
||||
from app.tracking import get_fulfilled_statuses
|
||||
|
||||
CANCELLED_ROW_COLOR = QColor(255, 210, 210)
|
||||
CANCELLED_TEXT_COLOR = QColor(180, 0, 0)
|
||||
FULFILLED_ROW_COLOR = QColor(210, 240, 210)
|
||||
CHECK_MARK = "\u2713"
|
||||
|
||||
# Order here is display order, left to right.
|
||||
COLUMNS = [
|
||||
("company", "Company"),
|
||||
("ticket_number", "Ticket #"),
|
||||
("status", "Status"),
|
||||
("skus_display", "SKUs"),
|
||||
("summary", "Summary"),
|
||||
("status", "Status"),
|
||||
("tracking_display", "Tracking #"),
|
||||
("created_display", "Created"),
|
||||
("outgoing_tracking_display", "Outgoing Tracking"),
|
||||
("return_tracking_display", "Return Tracking"),
|
||||
("uploaded_display", "Uploaded"),
|
||||
("past_cutoff_display", "Past Cutoff"),
|
||||
("created_display", "Created"),
|
||||
]
|
||||
|
||||
ALL_COMPANIES = "All Companies"
|
||||
|
||||
|
||||
def _format_tracking(tracking_numbers) -> str:
|
||||
def _format_tracking_numbers(tracking_numbers, is_return: bool) -> str:
|
||||
if not tracking_numbers:
|
||||
return ""
|
||||
return ", ".join(
|
||||
f"{t.get('number')} ({'return' if t.get('is_return') else 'out'})"
|
||||
for t in tracking_numbers
|
||||
t.get("number", "") for t in tracking_numbers if bool(t.get("is_return")) == is_return
|
||||
)
|
||||
|
||||
|
||||
@@ -74,9 +76,7 @@ class OrdersTableModel(QAbstractTableModel):
|
||||
|
||||
def set_orders(self, orders: List[Order]) -> None:
|
||||
# Re-read status lists and cutoff time each refresh, in case Settings changed.
|
||||
self._cancelled_statuses = parse_status_list(
|
||||
config.get("CANCELLED_STATUSES", config.DEFAULT_CANCELLED_STATUSES)
|
||||
)
|
||||
self._cancelled_statuses = get_cancelled_statuses()
|
||||
self._fulfilled_statuses = get_fulfilled_statuses()
|
||||
self._cutoff_time = get_cutoff_time()
|
||||
self.beginResetModel()
|
||||
@@ -101,11 +101,17 @@ class OrdersTableModel(QAbstractTableModel):
|
||||
return None
|
||||
|
||||
order = self._orders[index.row()]
|
||||
is_cancelled = status_in(order.status, self._cancelled_statuses)
|
||||
|
||||
if role == Qt.ItemDataRole.ForegroundRole:
|
||||
return CANCELLED_TEXT_COLOR if is_cancelled else None
|
||||
|
||||
if role == Qt.ItemDataRole.BackgroundRole:
|
||||
if status_in(order.status, self._cancelled_statuses):
|
||||
return CANCELLED_ROW_COLOR
|
||||
if status_in(order.status, self._fulfilled_statuses):
|
||||
# Cancelled rows use red TEXT (above) instead of a background,
|
||||
# so it stays readable regardless of theme. Fulfilled rows
|
||||
# still get a background tint since there's no text-color
|
||||
# signal already in use for them.
|
||||
if not is_cancelled and status_in(order.status, self._fulfilled_statuses):
|
||||
return FULFILLED_ROW_COLOR
|
||||
return None
|
||||
|
||||
@@ -116,12 +122,16 @@ class OrdersTableModel(QAbstractTableModel):
|
||||
|
||||
if field_name == "skus_display":
|
||||
return ", ".join(order.skus or [])
|
||||
if field_name == "tracking_display":
|
||||
return _format_tracking(order.tracking_numbers)
|
||||
if field_name == "outgoing_tracking_display":
|
||||
return _format_tracking_numbers(order.tracking_numbers, is_return=False)
|
||||
if field_name == "return_tracking_display":
|
||||
return _format_tracking_numbers(order.tracking_numbers, is_return=True)
|
||||
if field_name == "uploaded_display":
|
||||
return CHECK_MARK if order.shipstation_sent_at else ""
|
||||
if field_name == "created_display":
|
||||
return _format_created(order.source_created_at)
|
||||
if field_name == "past_cutoff_display":
|
||||
return "Yes" if is_past_cutoff(order.source_created_at, self._cutoff_time) else ""
|
||||
return CHECK_MARK if is_past_cutoff_today(order.source_created_at, self._cutoff_time) else ""
|
||||
|
||||
value = getattr(order, field_name)
|
||||
return "" if value is None else str(value)
|
||||
@@ -154,13 +164,16 @@ class OrdersFilterProxyModel(QSortFilterProxyModel):
|
||||
return False
|
||||
|
||||
if self.search_text:
|
||||
tracking_text = " ".join(
|
||||
t.get("number", "") for t in (order.tracking_numbers or [])
|
||||
)
|
||||
haystack = " ".join(
|
||||
[
|
||||
order.ticket_number or "",
|
||||
order.summary or "",
|
||||
order.status or "",
|
||||
" ".join(order.skus or []),
|
||||
_format_tracking(order.tracking_numbers),
|
||||
tracking_text,
|
||||
]
|
||||
).lower()
|
||||
if self.search_text not in haystack:
|
||||
@@ -204,8 +217,11 @@ class OrdersTableView(QWidget):
|
||||
self.table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
self.table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
|
||||
self.table.setSortingEnabled(True)
|
||||
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
||||
self.table.horizontalHeader().setStretchLastSection(False)
|
||||
# Interactive (not Stretch) so columns size to their own content and
|
||||
# stay individually resizable by dragging, rather than every column
|
||||
# being force-stretched to equal widths regardless of content.
|
||||
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Interactive)
|
||||
self.table.horizontalHeader().setStretchLastSection(True)
|
||||
self.table.doubleClicked.connect(self._on_row_double_clicked)
|
||||
layout.addWidget(self.table)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user