from QT import QtWidgets, QtGui, QT from _With_Table_Widget_ import _With_Table_Widget_ from App_State import App_State from Action_List import Action_Dialog import schema import subprocess import threading import time import pathlib class Order_List (_With_Table_Widget_) : settings_group = "order-list" Header = "Aufträge" Columns = \ ("Warenkorb", "Status", "Bearbeiter", "Kommission", "Kunde", "Linie") State_Map = \ { 0 : "neu" , 10 : "in Bearbeitung" , 20 : "im CAD geöffnet" , 25 : "CNC Erzeugung läuft" , 90 : "Odoo" , 95 : "Zurückgesetzt" , 99 : "Storno" } def __init__ (self, parent = None) : super ().__init__ (parent) self._view.setContextMenuPolicy (QT.CustomContextMenu) self._view.customContextMenuRequested.connect (self._show_context_menu) ACT = QtGui.QAction self._open_cad = ACT (self.tr ("Im CAD öffnen")) self._recreate_cnc = ACT (self.tr ("CNC Programm neu erzeugen")) self._reset_order = ACT (self.tr ("Auftrag zurücksetzen")) self._cancel = ACT (self.tr ("Auftrag stronieren")) self._send_to_odoo = ACT (self.tr ("Auftrag ans Odoo schicken")) self._actions = ACT (self.tr ("Aktionsliste")) self._reset_state = ACT (self.tr ("Status zurücksetzen")) self._open_cad.triggered.connect (self._open_order_in_cad) self._recreate_cnc.triggered.connect (self._start_recreate_cnc) self._reset_order.triggered.connect (self._reset_order_in_webshop) self._cancel.triggered.connect (self._cancel_order) self._send_to_odoo.triggered.connect (self._send_oder_to_odoo) self._actions.triggered.connect (self._show_actions) self._reset_state.triggered.connect (self._reset_order_state) self._cad_processes = {} # end def __init__ def _show_context_menu (self, pos) : row_data = self._view.selectedItems () if row_data : bno = row_data [0].text () with schema.orm.db_session () : order = schema.Order.get (basket_no = bno, active = True) if order.user and order.user.id != App_State.user_id : r = QtWidgets.QMessageBox.question \ ( self, self.tr ("Fehler") , self.tr ( "Dieser Auftrag wird bereits von jemand anderes " "bearbeitet.\n" "Wollen Sie den Auftrag als Bearbeiter übernehmen?" ) ) if r == QtWidgets.QMessageBox.No : return App_State.Create_Action \ ( 10 , f"User changed from {order.user.id} to {App_State.user_id}" , order ) order.user = schema.User [App_State.user_id] self.update_order (order, False, row_data [0].row ()) state = order.state cm = QtWidgets.QMenu () if state < 20 : cm.addAction (self._open_cad) cm.addAction (self._recreate_cnc) cm.addSeparator () cm.addAction (self._send_to_odoo) cm.addSeparator () cm.addAction (self._reset_order) cm.addAction (self._cancel) cm.addSeparator () cm.addAction (self._actions) cm.addSeparator () cm.addAction (self._reset_state) cm.exec (self._view.mapToGlobal (pos)) # end def _show_context_menu def update_order (self, order, new, row = None) : if order.active : state = self.State_Map [order.state] if new : row = self._view.rowCount () self._view.setRowCount (row + 1) self._add_read_only_string (row, 0, str (order.basket_no)) self._add_read_only_string (row, 1, state) if order.user : self._add_read_only_string (row, 2, order.user.username) self._add_read_only_string (row, 3, order.commission) self._add_read_only_string (row, 4, order.imos_user) self._add_read_only_string (row, 5, str (order.production_line)) else : if row is None : for row in range (self._view.rowCount ()) : if self._view.item (row, 0).text () == order.basket_no : break else : App_State.L.error \ ("Could not find row for order %s", order.basket_no) return self._update_read_only_string (row, 1, state) self._update_read_only_string (row, 2, order.user.username) else : for row in range (self._view.rowCount ()) : if self._view.item (row, 0).text () == order.basket_no : self._view.removeRow (row) return order.basket_no # end def update_order def remove_orders (self, orders) : for bno in orders : for row in range (self._view.rowCount ()) : if self._view.item (row, 0).text () == bno : self._view.removeRow (row) break # end def remove_orders def _open_order_in_cad (self) : row_data = self._view.selectedItems () if row_data : bno = row_data [0].text () with schema.orm.db_session () : state = schema.State.get () order = schema.Order.get (basket_no = bno, active = True) if order.state < 20 : order.state = 20 self.update_order (order, False, row_data [0].row ()) order_dwg = r"%s\ImOrder\%s\%s.dwg" \ % (state.imos_factory_root, bno, bno) self._cad_processes [bno] = p = subprocess.Popen \ ( ( order_dwg, ), shell = True ) threading.Thread \ ( target = self._wait_for_cad_close , args = (bno, ) , daemon = True ).start () App_State.Create_Action \ ( 20 , f"IMOS CAD started {order.basket_no}" , order ) # end def _open_order_in_cad def _wait_for_cad_close (self, basket_no) : App_State.L.info ("Auftrag %s im CAD geöffnet", basket_no) p = self._cad_processes [basket_no] while p.poll () is None : time.sleep (.5) del self._cad_processes [basket_no] with schema.orm.db_session () : order = schema.Order.get (basket_no = basket_no, active = True) order.state = 10 self.update_order (order, False) App_State.Create_Action \ ( 25 , f"IMOS CAD closed {order.basket_no}: Error code {p.poll ()}" , order ) App_State.L.info ("CAD für Auftrag %s beendet", basket_no) # end def _wait_for_cad_close def _start_recreate_cnc (self) : row_data = self._view.selectedItems () if row_data : bno = row_data [0].text () xml = f""" {bno} STANDARD IMOSADMIN """ with schema.orm.db_session () : state = schema.State.get () order = schema.Order.get (basket_no = bno, active = True) job_file_name = ( pathlib.Path (state.imos_sim_root) / App_State.sim_batches / f"{bno}.xml" ) txt_file = ( pathlib.Path (state.imos_factory_root) / App_State.imos_done_dir / f"{bno}.txt" ) order.state = 25 with job_file_name.open ("w") as f : f.write (xml) if txt_file.exists () : txt_file.unlink () self.update_order (order, False, row_data [0].row ()) threading.Thread \ ( target = self._wait_for_cnc_done , args = (bno, ) , daemon = True ).start () App_State.Create_Action \ ( 30, f"Start CNC generation for {bno}", order) # end def _start_recreate_cnc def _wait_for_cnc_done (self, basket_no) : with schema.orm.db_session () : state = schema.State.get () txt_file = ( pathlib.Path (state.imos_factory_root) / App_State.imos_done_dir / f"{basket_no}.txt" ) err_file_name = ( pathlib.Path (state.imos_sim_root) / App_State.sim_batches / "Error" / f"{basket_no}.xml" ) while not txt_file.exists () and not err_file_name.exists (): time.sleep (0.5) App_State.L.debug (f"wait for {txt_file}/{err_file_name}") if err_file_name.exists () : text = f"Fehler bei der CNC Erzeugung for {basket_no}" else : text = f"CNC Erzeugung für {basket_no} abgeschlossen" with schema.orm.db_session () : order = schema.Order.get (basket_no = basket_no, active = True) order.state = 10 self.update_order (order, False) App_State.Create_Action (35, text, order) # end def _wait_for_cnc_done def _reset_order_in_webshop (self) : row_data = self._view.selectedItems () if row_data : bno = row_data [0].text () App_State.L.error ("Reset order in webshop %s", bno) # end def _reset_order_in_webshop def _cancel_order (self) : row_data = self._view.selectedItems () if row_data : bno = row_data [0].text () App_State.L.error ("Cancel Order %s", bno) # end def _cancel_order def _send_oder_to_odoo (self) : row_data = self._view.selectedItems () if row_data : bno = row_data [0].text () with schema.orm.db_session () : state = schema.State.get () order = schema.Order.get (basket_no = bno, active = True) factory = pathlib.Path (state.imos_factory_root) for ext in ".xml", ".txt" : sf = factory / App_State.imos_done_dir / f"{bno}{ext}" df = factory / App_State.pgiq_dir / f"{bno}{ext}" sf.rename (df) App_State.L.info ("Auftrag %s für Odoo freigegeben", bno) App_State.Create_Action \ (40, f"Auftrag {bno} für Odoo freigegeben", order) order.active = False self.update_order (order, False) # end def _send_oder_to_odoo def _show_actions (self) : row_data = self._view.selectedItems () if row_data : bno = row_data [0].text () with schema.orm.db_session () : order = schema.Order.get (basket_no = bno, active = True) Action_Dialog \ (order.actions.order_by (schema.orm.desc (schema.Action.date))).exec () # end def _show_actions def _reset_order_state (self) : row_data = self._view.selectedItems () if row_data : bno = row_data [0].text () r = QtWidgets.QMessageBox.question \ ( self, self.tr ("Fehler") , self.tr ( "Soll der Status wirklich auf In Bearbeitung " "gesetzt werden?" ) ) if r == QtWidgets.QMessageBox.No : return with schema.orm.db_session () : order = schema.Order.get (basket_no = bno, active = True) order.state = 10 self.update_order (order, False) App_State.Create_Action \ ( 15 , f"Status wird auf 10 gesetzt" , order ) # end def _reset_order_state # end class Order_List class Order_Detail (_With_Table_Widget_) : settings_group = "order-detail" Header = "Auftrags Detail" Columns = \ ("Position", "Artikel", "Preis") # end class Order_Detail class Order_Display (QtWidgets.QSplitter) : def __init__ (self, parent) : super ().__init__ (parent) self._order_list = Order_List () self._order_detail = Order_Detail () self.addWidget (self._order_list) self.addWidget (self._order_detail) self.orders = {} # end def __init__ def update_orders (self) : orders = set (self.orders.keys ()) with schema.orm.db_session : for o in schema.Order.select (active = True) : db_lc = o.actions.order_by (schema.Action.date).first ().date if o.basket_no not in self.orders : self._order_list.update_order (o, True) self.orders [o.basket_no] = o o.last_changed = db_lc else : if db_lc > self.orders [o.basket_no].last_changed : self._order_list.update_order (o, False) self.orders [o.basket_no].last_changed = db_lc orders.discard (o.basket_no) self._order_list.remove_orders (orders) for o in orders : self.orders.pop (o, None) # end def update_orders def save_settings (self, settings) : settings.beginGroup ("order-display") settings.setValue ("geometry", self.saveGeometry ()) settings.setValue ("windowState", self.saveState ()) settings.endGroup () self._order_list .save_settings (settings) self._order_detail.save_settings (settings) # end def save_settings def restore_settings (self, settings) : settings.beginGroup ("order-display") self.restoreGeometry (settings.value ("geometry" )) self.restoreState (settings.value ("windowState")) settings.endGroup () self._order_list .restore_settings (settings) self._order_detail.restore_settings (settings) # end def restore_settings # end class Order_Display