- Add boxes_crates database table with BIGINT IDs and 8-digit auto-numbered box_numbers - Implement boxes CRUD operations (add, edit, update, delete, delete_multiple) - Create boxes route handlers with POST actions for all operations - Add boxes.html template with 3-panel layout matching warehouse locations module - Implement barcode generation and printing with JsBarcode and QZ Tray integration - Add browser print fallback for when QZ Tray is not available - Simplify create box form to single button with auto-generation - Fix JavaScript null reference errors with proper element validation - Convert tuple data to dictionaries for Jinja2 template compatibility - Register boxes blueprint in Flask app initialization
188 lines
5.5 KiB
Python
188 lines
5.5 KiB
Python
"""
|
|
Quality App v2 - Flask Application Factory
|
|
Robust, modular application with login, dashboard, and multiple modules
|
|
"""
|
|
from flask import Flask
|
|
from datetime import datetime, timedelta
|
|
import os
|
|
import logging
|
|
from logging.handlers import RotatingFileHandler
|
|
|
|
|
|
def create_app(config=None):
|
|
"""
|
|
Application factory function
|
|
Creates and configures the Flask application
|
|
"""
|
|
app = Flask(__name__)
|
|
|
|
# Load configuration
|
|
if config is None:
|
|
from app.config import Config
|
|
config = Config
|
|
|
|
app.config.from_object(config)
|
|
|
|
# Setup logging
|
|
setup_logging(app)
|
|
logger = logging.getLogger(__name__)
|
|
logger.info("=" * 80)
|
|
logger.info("Flask App Initialization Started")
|
|
logger.info("=" * 80)
|
|
|
|
# Configure session
|
|
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=8)
|
|
app.config['SESSION_COOKIE_SECURE'] = False # Set True in production with HTTPS
|
|
app.config['SESSION_COOKIE_HTTPONLY'] = True
|
|
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
|
|
|
# Initialize database connection
|
|
logger.info("Initializing database connection...")
|
|
from app.database import init_db, close_db
|
|
init_db(app)
|
|
app.teardown_appcontext(close_db)
|
|
|
|
# Register blueprints
|
|
logger.info("Registering blueprints...")
|
|
register_blueprints(app)
|
|
|
|
# Register error handlers
|
|
logger.info("Registering error handlers...")
|
|
register_error_handlers(app)
|
|
|
|
# Add template globals
|
|
app.jinja_env.globals['now'] = datetime.now
|
|
|
|
# Add context processor for app name
|
|
@app.context_processor
|
|
def inject_app_settings():
|
|
"""Inject app settings into all templates"""
|
|
try:
|
|
from app.database import get_db
|
|
conn = get_db()
|
|
cursor = conn.cursor()
|
|
cursor.execute(
|
|
"SELECT setting_value FROM application_settings WHERE setting_key = %s",
|
|
('app_name',)
|
|
)
|
|
result = cursor.fetchone()
|
|
cursor.close()
|
|
app_name = result[0] if result else 'Quality App v2'
|
|
except:
|
|
app_name = 'Quality App v2'
|
|
|
|
return {'app_name': app_name}
|
|
|
|
# Add before_request handlers
|
|
register_request_handlers(app)
|
|
|
|
# Initialize backup scheduler
|
|
logger.info("Initializing backup scheduler...")
|
|
try:
|
|
from app.scheduler import init_scheduler
|
|
init_scheduler(app)
|
|
except Exception as e:
|
|
logger.error(f"Failed to initialize backup scheduler: {e}")
|
|
|
|
logger.info("=" * 80)
|
|
logger.info("Flask App Initialization Completed Successfully")
|
|
logger.info("=" * 80)
|
|
|
|
return app
|
|
|
|
|
|
|
|
def setup_logging(app):
|
|
"""Configure application logging"""
|
|
log_dir = app.config.get('LOG_DIR', '/app/data/logs')
|
|
|
|
# Create log directory if it doesn't exist
|
|
if not os.path.exists(log_dir):
|
|
os.makedirs(log_dir, exist_ok=True)
|
|
|
|
# Configure rotating file handler
|
|
log_file = os.path.join(log_dir, 'app.log')
|
|
handler = RotatingFileHandler(
|
|
log_file,
|
|
maxBytes=10485760, # 10MB
|
|
backupCount=10
|
|
)
|
|
|
|
# Create formatter
|
|
formatter = logging.Formatter(
|
|
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
handler.setFormatter(formatter)
|
|
|
|
# Set logging level
|
|
log_level = app.config.get('LOG_LEVEL', 'INFO')
|
|
handler.setLevel(getattr(logging, log_level))
|
|
|
|
# Add handler to app logger
|
|
app.logger.addHandler(handler)
|
|
app.logger.setLevel(getattr(logging, log_level))
|
|
|
|
|
|
def register_blueprints(app):
|
|
"""Register application blueprints"""
|
|
from app.routes import main_bp
|
|
from app.modules.quality.routes import quality_bp
|
|
from app.modules.settings.routes import settings_bp
|
|
from app.modules.warehouse.routes import warehouse_bp
|
|
from app.modules.warehouse.boxes_routes import boxes_bp
|
|
|
|
app.register_blueprint(main_bp)
|
|
app.register_blueprint(quality_bp, url_prefix='/quality')
|
|
app.register_blueprint(settings_bp, url_prefix='/settings')
|
|
app.register_blueprint(warehouse_bp, url_prefix='/warehouse')
|
|
app.register_blueprint(boxes_bp)
|
|
|
|
app.logger.info("Blueprints registered: main, quality, settings, warehouse, boxes")
|
|
|
|
|
|
def register_error_handlers(app):
|
|
"""Register error handlers"""
|
|
|
|
@app.errorhandler(404)
|
|
def page_not_found(e):
|
|
from flask import render_template
|
|
return render_template('errors/404.html'), 404
|
|
|
|
@app.errorhandler(500)
|
|
def internal_error(e):
|
|
from flask import render_template
|
|
app.logger.error(f"Internal error: {e}")
|
|
return render_template('errors/500.html'), 500
|
|
|
|
@app.errorhandler(403)
|
|
def forbidden(e):
|
|
from flask import render_template
|
|
return render_template('errors/403.html'), 403
|
|
|
|
|
|
def register_request_handlers(app):
|
|
"""Register before/after request handlers"""
|
|
|
|
@app.before_request
|
|
def before_request():
|
|
"""Handle pre-request logic"""
|
|
from flask import session, request, redirect, url_for
|
|
|
|
# Skip authentication check for login and static files
|
|
if request.endpoint and (
|
|
request.endpoint in ['static', 'main.login', 'main.index'] or
|
|
request.path.startswith('/static/')
|
|
):
|
|
return None
|
|
|
|
# Check if user is logged in
|
|
if 'user_id' not in session:
|
|
return redirect(url_for('main.login'))
|
|
|
|
return None
|
|
|
|
@app.after_request
|
|
def after_request(response):
|
|
"""Handle post-request logic"""
|
|
return response
|