Major crashing bug fixed, program returned to normal operations
This commit is contained in:
@@ -12,9 +12,26 @@ wrong and it defeats the "minimal interactions" requirement entirely.
|
||||
Suggested fields come from app.serial_suggestions, based on keywords in
|
||||
the ticket's line items - a starting point, not a fixed schema. Staff
|
||||
can add any custom field the suggestions miss.
|
||||
|
||||
WORKAROUND NOTE: this dialog (along with Return Label) crashed the
|
||||
whole process on close, confirmed via two full crash dumps (identical
|
||||
fault offset both times) to be caused by Bitdefender Endpoint
|
||||
Security's Advanced Threat Control corrupting a stack frame inside
|
||||
Qt6Core.dll - not a bug in this code. Reusing a persistent instance
|
||||
instead of destroying/recreating it per ticket did NOT resolve it -
|
||||
the crash recurred at the same offset regardless, ruling out object
|
||||
destruction timing as the cause. The current mitigation is in
|
||||
main_window.py: this dialog is shown via show() (non-modal) instead of
|
||||
exec() (modal), since exec() runs a nested event loop that disables
|
||||
and re-enables the parent window - a different, more involved Windows
|
||||
API sequence than a plain show/hide. This dialog still supports being
|
||||
reused via set_order() regardless, since avoiding unnecessary
|
||||
construction/destruction is sound practice either way.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QDialog,
|
||||
QVBoxLayout,
|
||||
@@ -36,33 +53,19 @@ from app.serial_suggestions import suggest_serial_fields
|
||||
class PackTicketDialog(QDialog):
|
||||
def __init__(self, order: Order, parent=None):
|
||||
super().__init__(parent)
|
||||
self.order = order
|
||||
self.setWindowTitle(f"Pack Ticket - {order.ticket_number or order.external_id}")
|
||||
self.resize(520, 600)
|
||||
|
||||
self.order: Optional[Order] = None
|
||||
self._field_rows: list[tuple[QLineEdit, QLineEdit]] = [] # (label_edit, value_edit)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
info = order.shipping_info or {}
|
||||
kit_text = ", ".join(
|
||||
f"{item.get('sku', '')}: {item.get('item_name', '')}" for item in (order.line_items or [])
|
||||
) or ", ".join(order.skus or [])
|
||||
summary_lines = [
|
||||
f"Ticket: {order.ticket_number or order.external_id} Company: {order.company}",
|
||||
f"Customer: {info.get('name') or '(missing)'}",
|
||||
f"Kit: {kit_text or '(none)'}",
|
||||
]
|
||||
layout.addWidget(QLabel("\n".join(summary_lines)))
|
||||
self.summary_label = QLabel()
|
||||
layout.addWidget(self.summary_label)
|
||||
|
||||
layout.addWidget(QLabel("Serial numbers (scan or type; Enter moves to the next field):"))
|
||||
|
||||
scroll_area = QScrollArea()
|
||||
scroll_area.setWidgetResizable(True)
|
||||
self._fields_widget = QWidget()
|
||||
self._fields_layout = QFormLayout(self._fields_widget)
|
||||
scroll_area.setWidget(self._fields_widget)
|
||||
layout.addWidget(scroll_area, stretch=1)
|
||||
self._scroll_area = QScrollArea()
|
||||
self._scroll_area.setWidgetResizable(True)
|
||||
layout.addWidget(self._scroll_area, stretch=1)
|
||||
|
||||
add_field_row = QHBoxLayout()
|
||||
self.new_field_label_input = QLineEdit()
|
||||
@@ -75,7 +78,6 @@ class PackTicketDialog(QDialog):
|
||||
layout.addLayout(add_field_row)
|
||||
|
||||
self.packed_checkbox = QCheckBox("Packed / ready to ship")
|
||||
self.packed_checkbox.setChecked(bool(order.packed))
|
||||
layout.addWidget(self.packed_checkbox)
|
||||
|
||||
button_box = QDialogButtonBox()
|
||||
@@ -85,6 +87,44 @@ class PackTicketDialog(QDialog):
|
||||
button_box.rejected.connect(self.reject)
|
||||
layout.addWidget(button_box)
|
||||
|
||||
self.set_order(order)
|
||||
|
||||
def set_order(self, order: Order) -> None:
|
||||
"""
|
||||
Re-initializes this dialog for a different ticket, in place -
|
||||
this is what lets main_window.py reuse a single persistent
|
||||
instance instead of constructing (and eventually destroying) a
|
||||
new one per ticket. See the module docstring for why that
|
||||
matters here specifically.
|
||||
"""
|
||||
self.order = order
|
||||
self.setWindowTitle(f"Pack Ticket - {order.ticket_number or order.external_id}")
|
||||
self.resize(520, 600)
|
||||
|
||||
info = order.shipping_info or {}
|
||||
kit_text = ", ".join(
|
||||
f"{item.get('sku', '')}: {item.get('item_name', '')}" for item in (order.line_items or [])
|
||||
) or ", ".join(order.skus or [])
|
||||
summary_lines = [
|
||||
f"Ticket: {order.ticket_number or order.external_id} Company: {order.company}",
|
||||
f"Customer: {info.get('name') or '(missing)'}",
|
||||
f"Kit: {kit_text or '(none)'}",
|
||||
]
|
||||
self.summary_label.setText("\n".join(summary_lines))
|
||||
|
||||
# Swap in a fresh fields widget rather than trying to clear rows
|
||||
# out of the existing QFormLayout - simpler, and the old one is
|
||||
# only deleteLater()'d, not force-destroyed immediately.
|
||||
old_fields_widget = self._scroll_area.takeWidget()
|
||||
if old_fields_widget is not None:
|
||||
old_fields_widget.deleteLater()
|
||||
self._fields_widget = QWidget()
|
||||
self._fields_layout = QFormLayout(self._fields_widget)
|
||||
self._scroll_area.setWidget(self._fields_widget)
|
||||
self._field_rows = []
|
||||
|
||||
self.packed_checkbox.setChecked(bool(order.packed))
|
||||
|
||||
self._populate_initial_fields()
|
||||
|
||||
def _populate_initial_fields(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user