EOD and Carryover tickets added
This commit is contained in:
+22
-6
@@ -33,7 +33,7 @@ from app.ui.settings_dialog import SettingsDialog
|
||||
from app.ui.widgets.orders_table import OrdersTableView
|
||||
from app.ui.widgets.dashboard import DashboardWidget
|
||||
from app.ui.widgets.order_detail_dialog import OrderDetailDialog
|
||||
from app.workers import FetchOrdersWorker, load_all_orders, get_dashboard_stats
|
||||
from app.workers import FetchOrdersWorker, load_active_and_done_orders, get_dashboard_stats
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
@@ -58,8 +58,11 @@ class MainWindow(QMainWindow):
|
||||
self.dashboard = DashboardWidget()
|
||||
self.orders_table = OrdersTableView()
|
||||
self.orders_table.order_double_clicked.connect(self._on_order_double_clicked)
|
||||
self.done_table = OrdersTableView()
|
||||
self.done_table.order_double_clicked.connect(self._on_order_double_clicked)
|
||||
self.tabs.addTab(self.dashboard, "Dashboard")
|
||||
self.tabs.addTab(self.orders_table, "All Orders")
|
||||
self.tabs.addTab(self.orders_table, "Active Orders")
|
||||
self.tabs.addTab(self.done_table, "Done")
|
||||
layout.addWidget(self.tabs)
|
||||
|
||||
self.setCentralWidget(central)
|
||||
@@ -81,6 +84,7 @@ class MainWindow(QMainWindow):
|
||||
toolbar.addSeparator()
|
||||
|
||||
export_action = QAction("Export Visible Orders to Odoo CSV", self)
|
||||
export_action.setToolTip("Exports from whichever of Active Orders / Done is open")
|
||||
export_action.triggered.connect(self._on_export_clicked)
|
||||
toolbar.addAction(export_action)
|
||||
|
||||
@@ -194,7 +198,16 @@ class MainWindow(QMainWindow):
|
||||
QMessageBox.critical(self, f"{service_name.title()} import failed", message)
|
||||
|
||||
def _on_export_clicked(self) -> None:
|
||||
orders = self.orders_table.visible_orders()
|
||||
current_tab = self.tabs.currentWidget()
|
||||
if not isinstance(current_tab, OrdersTableView):
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Nothing to export",
|
||||
"Switch to the Active Orders or Done tab first, then export.",
|
||||
)
|
||||
return
|
||||
|
||||
orders = current_tab.visible_orders()
|
||||
if not orders:
|
||||
QMessageBox.information(
|
||||
self, "Nothing to export", "No orders match the current filters."
|
||||
@@ -216,11 +229,14 @@ class MainWindow(QMainWindow):
|
||||
self.status_label.setText(f"Exported {count} order(s) to {filepath}")
|
||||
|
||||
def _refresh_everything(self) -> None:
|
||||
orders = load_all_orders()
|
||||
self.orders_table.set_orders(orders)
|
||||
active_orders, done_orders = load_active_and_done_orders()
|
||||
self.orders_table.set_orders(active_orders)
|
||||
self.done_table.set_orders(done_orders)
|
||||
self.dashboard.update_stats(get_dashboard_stats())
|
||||
if not hasattr(self, "_suppress_ready_status"):
|
||||
self.status_label.setText(f"{len(orders)} order(s) in local database.")
|
||||
self.status_label.setText(
|
||||
f"{len(active_orders)} active order(s), {len(done_orders)} done."
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
+17
-13
@@ -59,28 +59,31 @@ class DashboardWidget(QWidget):
|
||||
heading.setFont(heading_font)
|
||||
outer.addWidget(heading)
|
||||
|
||||
# Top row: overall pipeline health
|
||||
# Top row: active workload health (fulfilled tickets have moved to the Done tab)
|
||||
top_row = QHBoxLayout()
|
||||
self.total_card = StatCard("Total Orders")
|
||||
self.fulfilled_card = StatCard("Fulfilled")
|
||||
self.fulfilled_card.setStyleSheet("QLabel { color: #1a7a1a; }")
|
||||
self.total_card = StatCard("Active Orders")
|
||||
self.cancelled_card = StatCard("Cancelled")
|
||||
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; }")
|
||||
for card in (self.total_card, self.fulfilled_card, self.cancelled_card, self.carryover_card):
|
||||
self.past_cutoff_card = StatCard("Arrived Past Cutoff Today")
|
||||
self.past_cutoff_card.setStyleSheet("QLabel { color: #b06a00; }")
|
||||
for card in (self.total_card, self.cancelled_card, self.carryover_card, self.past_cutoff_card):
|
||||
top_row.addWidget(card)
|
||||
outer.addLayout(top_row)
|
||||
|
||||
# Second row: tracking pull progress, since that's the 5PM task
|
||||
outer.addWidget(self._section_label("Tracking Numbers"))
|
||||
tracking_row = QHBoxLayout()
|
||||
self.tracking_received_card = StatCard("Tickets With Tracking Pulled")
|
||||
tracking_row.addWidget(self.tracking_received_card)
|
||||
outer.addLayout(tracking_row)
|
||||
# Second row: today's completions + tracking pull progress
|
||||
outer.addWidget(self._section_label("Today's Progress"))
|
||||
progress_row = QHBoxLayout()
|
||||
self.fulfilled_today_card = StatCard("Fulfilled Today (moved to Done)")
|
||||
self.fulfilled_today_card.setStyleSheet("QLabel { color: #1a7a1a; }")
|
||||
self.tracking_received_card = StatCard("Active Tickets With Tracking Pulled")
|
||||
progress_row.addWidget(self.fulfilled_today_card)
|
||||
progress_row.addWidget(self.tracking_received_card)
|
||||
outer.addLayout(progress_row)
|
||||
|
||||
# Third row: per-company totals (grid so adding a 3rd company later just works)
|
||||
outer.addWidget(self._section_label("By Company"))
|
||||
outer.addWidget(self._section_label("Active Orders By Company"))
|
||||
self.company_grid = QGridLayout()
|
||||
self.company_cards: dict[str, StatCard] = {}
|
||||
outer.addLayout(self.company_grid)
|
||||
@@ -97,9 +100,10 @@ class DashboardWidget(QWidget):
|
||||
|
||||
def update_stats(self, stats: dict) -> None:
|
||||
self.total_card.set_value(stats.get("total", 0))
|
||||
self.fulfilled_card.set_value(stats.get("fulfilled_count", 0))
|
||||
self.cancelled_card.set_value(stats.get("cancelled_count", 0))
|
||||
self.carryover_card.set_value(stats.get("carryover_count", 0))
|
||||
self.past_cutoff_card.set_value(stats.get("past_cutoff_today_count", 0))
|
||||
self.fulfilled_today_card.set_value(stats.get("fulfilled_today_count", 0))
|
||||
self.tracking_received_card.set_value(stats.get("tracking_received_count", 0))
|
||||
|
||||
by_company = stats.get("by_company", {})
|
||||
|
||||
@@ -28,7 +28,9 @@ from PyQt6.QtWidgets import (
|
||||
|
||||
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.tracking import get_fulfilled_statuses
|
||||
|
||||
CANCELLED_ROW_COLOR = QColor(255, 210, 210)
|
||||
FULFILLED_ROW_COLOR = QColor(210, 240, 210)
|
||||
@@ -40,7 +42,8 @@ COLUMNS = [
|
||||
("summary", "Summary"),
|
||||
("status", "Status"),
|
||||
("tracking_display", "Tracking #"),
|
||||
("source_created_at", "Created"),
|
||||
("created_display", "Created"),
|
||||
("past_cutoff_display", "Past Cutoff"),
|
||||
]
|
||||
|
||||
ALL_COMPANIES = "All Companies"
|
||||
@@ -55,31 +58,27 @@ def _format_tracking(tracking_numbers) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _format_created(source_created_at) -> str:
|
||||
if not source_created_at:
|
||||
return ""
|
||||
return source_created_at.strftime("%b %d, %I:%M %p").replace(" 0", " ")
|
||||
|
||||
|
||||
class OrdersTableModel(QAbstractTableModel):
|
||||
def __init__(self, orders: List[Order] | None = None, parent=None):
|
||||
super().__init__(parent)
|
||||
self._orders: List[Order] = orders or []
|
||||
self._cancelled_statuses: set[str] = set()
|
||||
self._fulfilled_statuses: set[str] = set()
|
||||
self._cutoff_time = None
|
||||
|
||||
def set_orders(self, orders: List[Order]) -> None:
|
||||
# Re-read status lists each refresh, in case Settings changed.
|
||||
# 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._fulfilled_statuses = parse_status_list(
|
||||
",".join(
|
||||
[
|
||||
config.get(
|
||||
"FULFILLED_STATUS_WITH_RETURN", config.DEFAULT_FULFILLED_WITH_RETURN
|
||||
),
|
||||
config.get(
|
||||
"FULFILLED_STATUS_WITHOUT_RETURN",
|
||||
config.DEFAULT_FULFILLED_WITHOUT_RETURN,
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
self._fulfilled_statuses = get_fulfilled_statuses()
|
||||
self._cutoff_time = get_cutoff_time()
|
||||
self.beginResetModel()
|
||||
self._orders = orders
|
||||
self.endResetModel()
|
||||
@@ -119,6 +118,10 @@ class OrdersTableModel(QAbstractTableModel):
|
||||
return ", ".join(order.skus or [])
|
||||
if field_name == "tracking_display":
|
||||
return _format_tracking(order.tracking_numbers)
|
||||
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 ""
|
||||
|
||||
value = getattr(order, field_name)
|
||||
return "" if value is None else str(value)
|
||||
|
||||
Reference in New Issue
Block a user