""" Warehouse Module Routes """ from flask import Blueprint, render_template, session, redirect, url_for, request, flash, jsonify from app.modules.warehouse.warehouse import ( get_all_locations, add_location, update_location, delete_location, delete_multiple_locations, get_location_by_id, search_box_by_number, search_location_with_boxes, assign_box_to_location, move_box_to_new_location, get_cp_inventory_list, search_cp_code, search_by_box_number, get_cp_details ) from app.modules.warehouse.warehouse_orders import ( get_unassigned_orders, get_orders_by_box, search_orders_by_cp_code, assign_order_to_box, move_order_to_box, unassign_order_from_box, get_all_boxes_summary ) import logging logger = logging.getLogger(__name__) warehouse_bp = Blueprint('warehouse', __name__, url_prefix='/warehouse') @warehouse_bp.route('/', methods=['GET']) def warehouse_index(): """Warehouse module main page - launcher for all warehouse operations""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/warehouse/index.html') @warehouse_bp.route('/set-boxes-locations', methods=['GET', 'POST']) def set_boxes_locations(): """Set boxes locations - add or update articles in warehouse inventory""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/warehouse/set_boxes_locations.html') @warehouse_bp.route('/locations', methods=['GET', 'POST']) def locations(): """Create and manage warehouse locations""" if 'user_id' not in session: return redirect(url_for('main.login')) message = None message_type = 'info' if request.method == 'POST': # Handle edit location if request.form.get('edit_location'): location_id = request.form.get('location_id', '') size = request.form.get('edit_size', '').strip() description = request.form.get('edit_description', '').strip() try: location_id = int(location_id) success, msg = update_location(location_id, None, size if size else None, description if description else None) message = msg message_type = 'success' if success else 'error' except Exception as e: message = f"Error: {str(e)}" message_type = 'error' # Handle delete locations elif request.form.get('delete_locations'): delete_ids_str = request.form.get('delete_ids', '') try: location_ids = [int(id.strip()) for id in delete_ids_str.split(',') if id.strip().isdigit()] success, msg = delete_multiple_locations(location_ids) message = msg message_type = 'success' if success else 'error' except Exception as e: message = f"Error: {str(e)}" message_type = 'error' # Handle add location elif request.form.get('add_location'): location_code = request.form.get('location_code', '').strip() size = request.form.get('size', '').strip() description = request.form.get('description', '').strip() if not location_code: message = "Location code is required" message_type = 'error' else: success, msg = add_location(location_code, size if size else None, description if description else None) message = msg message_type = 'success' if success else 'error' # Get all locations locations_list = get_all_locations() return render_template('modules/warehouse/locations.html', locations=locations_list, message=message, message_type=message_type) @warehouse_bp.route('/boxes', methods=['GET', 'POST']) def boxes(): """Manage boxes and crates in the warehouse""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/warehouse/boxes.html') @warehouse_bp.route('/inventory', methods=['GET']) def inventory(): """View warehouse inventory - products, boxes, and locations""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/warehouse/inventory.html') @warehouse_bp.route('/reports', methods=['GET']) def reports(): """Warehouse activity and inventory reports""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/warehouse/reports.html') @warehouse_bp.route('/set-orders-on-boxes', methods=['GET', 'POST']) def set_orders_on_boxes(): """Set orders on boxes - assign or move orders between boxes""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/warehouse/set_orders_on_boxes.html') @warehouse_bp.route('/test-barcode', methods=['GET']) def test_barcode(): """Test barcode printing functionality""" if 'user_id' not in session: return redirect(url_for('main.login')) return render_template('modules/warehouse/test_barcode.html') # ============================================================================ # API Routes for Set Boxes Locations Feature # ============================================================================ @warehouse_bp.route('/api/search-box', methods=['POST'], endpoint='api_search_box') def api_search_box(): """Search for a box by number""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 data = request.get_json() box_number = data.get('box_number', '').strip() if not box_number: return jsonify({'success': False, 'error': 'Box number is required'}), 400 success, box_data, status_code = search_box_by_number(box_number) if success: return jsonify({'success': True, 'box': box_data}), 200 else: return jsonify({'success': False, 'error': f'Box "{box_number}" not found'}), status_code @warehouse_bp.route('/api/search-location', methods=['POST'], endpoint='api_search_location') def api_search_location(): """Search for a location and get all boxes in it""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 data = request.get_json() location_code = data.get('location_code', '').strip() if not location_code: return jsonify({'success': False, 'error': 'Location code is required'}), 400 success, response_data, status_code = search_location_with_boxes(location_code) if success: return jsonify({'success': True, **response_data}), 200 else: return jsonify({'success': False, 'error': response_data.get('error', 'Not found')}), status_code @warehouse_bp.route('/api/assign-box-to-location', methods=['POST'], endpoint='api_assign_box_to_location') def api_assign_box_to_location(): """Assign a box to a location""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 data = request.get_json() box_id = data.get('box_id') location_code = data.get('location_code', '').strip() if not box_id or not location_code: return jsonify({'success': False, 'error': 'Box ID and location code are required'}), 400 success, message, status_code = assign_box_to_location(box_id, location_code) return jsonify({'success': success, 'message': message}), status_code @warehouse_bp.route('/api/move-box-to-location', methods=['POST'], endpoint='api_move_box_to_location') def api_move_box_to_location(): """Move a box to a new location""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 data = request.get_json() box_id = data.get('box_id') new_location_code = data.get('new_location_code', '').strip() if not box_id or not new_location_code: return jsonify({'success': False, 'error': 'Box ID and new location code are required'}), 400 success, message, status_code = move_box_to_new_location(box_id, new_location_code) return jsonify({'success': success, 'message': message}), status_code @warehouse_bp.route('/api/get-locations', methods=['GET'], endpoint='api_get_locations') def api_get_locations(): """Get all warehouse locations for dropdown""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 locations = get_all_locations() return jsonify({'success': True, 'locations': locations}), 200 # ============================================================================ # API Routes for Orders Management # ============================================================================ @warehouse_bp.route('/api/unassigned-orders', methods=['GET'], endpoint='api_unassigned_orders') def api_unassigned_orders(): """Get all unassigned orders""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 try: limit = request.args.get('limit', 100, type=int) offset = request.args.get('offset', 0, type=int) orders = get_unassigned_orders(limit, offset) return jsonify({'success': True, 'orders': orders, 'count': len(orders)}), 200 except Exception as e: logger.error(f"Error getting unassigned orders: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @warehouse_bp.route('/api/orders-by-box', methods=['POST'], endpoint='api_orders_by_box') def api_orders_by_box(): """Get all orders assigned to a specific box""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 data = request.get_json() box_id = data.get('box_id') if not box_id: return jsonify({'success': False, 'error': 'Box ID is required'}), 400 success, data_resp, status_code = get_orders_by_box(box_id) return jsonify({'success': success, **data_resp}), status_code @warehouse_bp.route('/api/search-orders', methods=['POST'], endpoint='api_search_orders') def api_search_orders(): """Search for orders by CP code""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 data = request.get_json() cp_code = data.get('cp_code', '').strip() if not cp_code: return jsonify({'success': False, 'error': 'CP code is required'}), 400 orders = search_orders_by_cp_code(cp_code) return jsonify({'success': True, 'orders': orders, 'count': len(orders)}), 200 @warehouse_bp.route('/api/assign-order-to-box', methods=['POST'], endpoint='api_assign_order_to_box') def api_assign_order_to_box(): """Assign an order to a box""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 data = request.get_json() order_id = data.get('order_id') box_id = data.get('box_id') if not order_id or not box_id: return jsonify({'success': False, 'error': 'Order ID and box ID are required'}), 400 success, message, status_code = assign_order_to_box(order_id, box_id) return jsonify({'success': success, 'message': message}), status_code @warehouse_bp.route('/api/move-order-to-box', methods=['POST'], endpoint='api_move_order_to_box') def api_move_order_to_box(): """Move an order to a different box""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 data = request.get_json() order_id = data.get('order_id') new_box_id = data.get('new_box_id') if not order_id or not new_box_id: return jsonify({'success': False, 'error': 'Order ID and destination box ID are required'}), 400 success, message, status_code = move_order_to_box(order_id, new_box_id) return jsonify({'success': success, 'message': message}), status_code @warehouse_bp.route('/api/unassign-order', methods=['POST'], endpoint='api_unassign_order') def api_unassign_order(): """Remove an order from its box""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 data = request.get_json() order_id = data.get('order_id') if not order_id: return jsonify({'success': False, 'error': 'Order ID is required'}), 400 success, message, status_code = unassign_order_from_box(order_id) return jsonify({'success': success, 'message': message}), status_code @warehouse_bp.route('/api/boxes-summary', methods=['GET'], endpoint='api_boxes_summary') def api_boxes_summary(): """Get summary of all boxes with order counts""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 boxes = get_all_boxes_summary() return jsonify({'success': True, 'boxes': boxes}), 200 # ============================================================================ # API Routes for CP Inventory View # ============================================================================ @warehouse_bp.route('/api/cp-inventory', methods=['GET'], endpoint='api_cp_inventory') def api_cp_inventory(): """Get CP inventory list - all CP articles with box and location info""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 try: limit = request.args.get('limit', 100, type=int) offset = request.args.get('offset', 0, type=int) # Validate pagination parameters if limit > 1000: limit = 1000 if limit < 1: limit = 50 if offset < 0: offset = 0 inventory = get_cp_inventory_list(limit=limit, offset=offset) return jsonify({ 'success': True, 'inventory': inventory, 'count': len(inventory), 'limit': limit, 'offset': offset }), 200 except Exception as e: logger.error(f"Error getting CP inventory: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @warehouse_bp.route('/api/search-cp', methods=['POST'], endpoint='api_search_cp') def api_search_cp(): """Search for CP code in warehouse inventory""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 try: data = request.get_json() cp_code = data.get('cp_code', '').strip() if not cp_code or len(cp_code) < 2: return jsonify({'success': False, 'error': 'CP code must be at least 2 characters'}), 400 results = search_cp_code(cp_code) return jsonify({ 'success': True, 'results': results, 'count': len(results), 'search_term': cp_code }), 200 except Exception as e: logger.error(f"Error searching CP code: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @warehouse_bp.route('/api/search-cp-box', methods=['POST'], endpoint='api_search_cp_box') def api_search_cp_box(): """Search for box number and get all CP codes in it""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 try: data = request.get_json() box_number = data.get('box_number', '').strip() if not box_number or len(box_number) < 1: return jsonify({'success': False, 'error': 'Box number is required'}), 400 results = search_by_box_number(box_number) return jsonify({ 'success': True, 'results': results, 'count': len(results), 'search_term': box_number }), 200 except Exception as e: logger.error(f"Error searching by box number: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @warehouse_bp.route('/api/cp-details/', methods=['GET'], endpoint='api_cp_details') def api_cp_details(cp_code): """Get detailed information for a CP code and all its variations""" if 'user_id' not in session: return jsonify({'success': False, 'error': 'Unauthorized'}), 401 try: cp_code = cp_code.strip().upper() # Ensure CP code is properly formatted (at least "CP00000001") if not cp_code.startswith('CP') or len(cp_code) < 10: return jsonify({'success': False, 'error': 'Invalid CP code format'}), 400 # Extract base CP code (first 10 characters) cp_base = cp_code[:10] details = get_cp_details(cp_base) return jsonify({ 'success': True, 'cp_code': cp_base, 'details': details, 'count': len(details) }), 200 except Exception as e: logger.error(f"Error getting CP details: {e}") return jsonify({'success': False, 'error': str(e)}), 500