feat: Implement Smart Router workflow with User Input and Duplicate Logic (v0.18.0)
Major update to the scanning engine to support "Pause & Resume" workflows. The system can now halt execution to ask for user input (e.g. Weight) and resume processing seamlessly. Key Changes: - Backend (Global Actions): Added `OPEN_FORM` action type to pause pipeline and request manual input. - Backend (Routes): Updated `scan_lot` to handle `extra_data` payloads, allowing the pipeline to resume after user input. - Backend (Logic): Implemented `confirm_duplicate` gatekeeper to handle "Warn vs Block" logic dynamically. - Frontend (JS): Added `processSmartScan` to handle router signals (Open Modal, Warn Duplicate). - Frontend (JS): Added `saveSmartScanData` to send original barcode + new form data back to the engine. - UI: Fixed modal ID/Name conflicts (forcing use of `name` attribute for DB compatibility). - UI: Restored missing "Cancel" button to Details Modal. - Config: Added "User Input" rule type to the Rule Editor. Ver: 0.18.0
This commit is contained in:
@@ -321,45 +321,63 @@ class ModuleManager:
|
||||
return {'success': True, 'message': f'Module {module["name"]} deactivated'}
|
||||
|
||||
def load_active_modules(self, app):
|
||||
"""
|
||||
Load all active modules and register their blueprints with Flask app.
|
||||
Called during app startup.
|
||||
"""
|
||||
modules = self.scan_available_modules()
|
||||
active_modules = [m for m in modules if m['is_installed'] and m['is_active']]
|
||||
|
||||
print(f"\n🔌 Loading {len(active_modules)} active module(s)...")
|
||||
|
||||
for module in active_modules:
|
||||
try:
|
||||
# Import module's __init__.py
|
||||
init_path = Path(module['path']) / '__init__.py'
|
||||
if not init_path.exists():
|
||||
print(f" ⚠️ {module['name']}: Missing __init__.py")
|
||||
continue
|
||||
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
f"modules.{module['module_key']}",
|
||||
init_path
|
||||
)
|
||||
module_package = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module_package)
|
||||
|
||||
# Get blueprint from create_blueprint()
|
||||
if hasattr(module_package, 'create_blueprint'):
|
||||
blueprint = module_package.create_blueprint()
|
||||
app.register_blueprint(blueprint)
|
||||
print(f" ✅ {module['name']} loaded at {module.get('routes_prefix', '/unknown')}")
|
||||
else:
|
||||
print(f" ⚠️ {module['name']}: Missing create_blueprint() function")
|
||||
"""
|
||||
Load all active modules, run their migrations, and register blueprints.
|
||||
Called during app startup.
|
||||
"""
|
||||
modules = self.scan_available_modules()
|
||||
active_modules = [m for m in modules if m['is_installed'] and m['is_active']]
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Failed to load {module['name']}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print("✅ Module loading complete\n")
|
||||
print(f"\n🔌 Loading {len(active_modules)} active module(s)...")
|
||||
|
||||
for module in active_modules:
|
||||
try:
|
||||
# --- NEW: Run Migrations on Startup ---
|
||||
migrations_path = Path(module['path']) / 'migrations.py'
|
||||
if migrations_path.exists():
|
||||
# 1. Dynamically load the migrations.py file
|
||||
spec_mig = importlib.util.spec_from_file_location(f"{module['module_key']}_mig", migrations_path)
|
||||
mig_mod = importlib.util.module_from_spec(spec_mig)
|
||||
spec_mig.loader.exec_module(mig_mod)
|
||||
|
||||
# 2. Run the migrations
|
||||
if hasattr(mig_mod, 'get_migrations'):
|
||||
conn = get_db()
|
||||
for version, name, func in mig_mod.get_migrations():
|
||||
# Your migrations are written safely (checking IF EXISTS),
|
||||
# so running them on every boot is the correct Dev workflow.
|
||||
func(conn)
|
||||
conn.commit() # <--- CRITICAL: Saves the changes to the DB
|
||||
conn.close()
|
||||
# --------------------------------------
|
||||
|
||||
# Import module's __init__.py
|
||||
init_path = Path(module['path']) / '__init__.py'
|
||||
if not init_path.exists():
|
||||
print(f" ⚠️ {module['name']}: Missing __init__.py")
|
||||
continue
|
||||
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
f"modules.{module['module_key']}",
|
||||
init_path
|
||||
)
|
||||
module_package = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module_package)
|
||||
|
||||
# Get blueprint from create_blueprint()
|
||||
if hasattr(module_package, 'create_blueprint'):
|
||||
blueprint = module_package.create_blueprint()
|
||||
app.register_blueprint(blueprint)
|
||||
print(f" ✅ {module['name']} loaded at {module.get('routes_prefix', '/unknown')}")
|
||||
else:
|
||||
print(f" ⚠️ {module['name']}: Missing create_blueprint() function")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Failed to load {module['name']}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print("✅ Module loading complete\n")
|
||||
|
||||
# Global instance
|
||||
manager = ModuleManager()
|
||||
|
||||
Reference in New Issue
Block a user