Major updates: Setup environment, database, permissions and admin access

- Created virtual environment and installed requirements
- Set up MariaDB database with user 'sa' and database 'recticel'
- Created order_for_labels table for printing functionality
- Implemented role-based permissions system with admin role
- Added admin access to print label modules and etichete functionality
- Fixed template routing issues in main_page_etichete.html
- Updated app to run on port 8781 with network access (0.0.0.0)
- Added systemd service file for background deployment
- Created installation documentation
This commit is contained in:
scheianu ionut
2025-10-07 22:07:06 +03:00
parent 957c8eca4d
commit a8811b94b7
11 changed files with 170 additions and 136 deletions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -2,10 +2,10 @@ import mariadb
# Database connection credentials
DB_CONFIG = {
"user": "trasabilitate",
"password": "Initial01!",
"user": "sa",
"password": "12345678",
"host": "localhost",
"database": "trasabilitate_database"
"database": "recticel"
}
def recreate_order_for_labels_table():
+93 -126
View File
@@ -27,84 +27,8 @@ from .print_module import get_unprinted_orders_data
bp = Blueprint('main', __name__)
warehouse_bp = Blueprint('warehouse', __name__)
def format_cell_data(cell):
"""Helper function to format cell data, especially dates and times"""
# Import date and datetime at the top of the function
from datetime import datetime, date
if isinstance(cell, datetime):
# Format datetime as dd/mm/yyyy
return cell.strftime('%d/%m/%Y')
elif isinstance(cell, date):
# Format date as dd/mm/yyyy
return cell.strftime('%d/%m/%Y')
elif isinstance(cell, timedelta):
# Convert timedelta to HH:MM:SS format
total_seconds = int(cell.total_seconds())
hours, remainder = divmod(total_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
elif isinstance(cell, str):
# Handle string dates in yyyy-mm-dd or yyyy-mm-dd HH:MM:SS
import re
match = re.match(r'^(\d{4})-(\d{2})-(\d{2})(.*)$', cell)
if match:
year, month, day, rest = match.groups()
formatted = f"{day}/{month}/{year}"
if rest.strip():
# If there is a time part, keep it after the date
formatted += rest
return formatted
return cell
else:
return cell
@bp.route('/store_articles')
def store_articles():
return render_template('store_articles.html')
@bp.route('/get_pairing_keys')
def get_pairing_keys():
"""Return all pairing keys as JSON for client selection."""
keys_path = os.path.join(current_app.instance_path, 'pairing_keys.json')
try:
if os.path.exists(keys_path):
with open(keys_path, 'r') as f:
keys = json.load(f)
else:
keys = []
except Exception as e:
print(f"Error loading pairing keys: {e}")
return jsonify([]), 200
return jsonify(keys)
@bp.route('/warehouse_reports')
def warehouse_reports():
return render_template('warehouse_reports.html')
def get_db_connection():
"""Reads the external_server.conf file and returns a MariaDB database connection."""
settings_file = os.path.join(current_app.instance_path, 'external_server.conf')
if not os.path.exists(settings_file):
raise FileNotFoundError("The external_server.conf file is missing in the instance folder.")
# Read settings from the configuration file
settings = {}
with open(settings_file, 'r') as f:
for line in f:
key, value = line.strip().split('=', 1)
settings[key] = value
# Create a database connection
return mariadb.connect(
user=settings['username'],
password=settings['password'],
host=settings['server_domain'],
port=int(settings['port']),
database=settings['database_name']
)
@bp.route('/login', methods=['GET', 'POST'])
@bp.route('/', methods=['GET', 'POST'])
def login():
import sqlite3
if request.method == 'POST':
@@ -190,6 +114,83 @@ def login():
flash('Invalid credentials. Please try again.')
return render_template('login.html')
def format_cell_data(cell):
"""Helper function to format cell data, especially dates and times"""
# Import date and datetime at the top of the function
from datetime import datetime, date
if isinstance(cell, datetime):
# Format datetime as dd/mm/yyyy
return cell.strftime('%d/%m/%Y')
elif isinstance(cell, date):
# Format date as dd/mm/yyyy
return cell.strftime('%d/%m/%Y')
elif isinstance(cell, timedelta):
# Convert timedelta to HH:MM:SS format
total_seconds = int(cell.total_seconds())
hours, remainder = divmod(total_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
elif isinstance(cell, str):
# Handle string dates in yyyy-mm-dd or yyyy-mm-dd HH:MM:SS
import re
match = re.match(r'^(\d{4})-(\d{2})-(\d{2})(.*)$', cell)
if match:
year, month, day, rest = match.groups()
formatted = f"{day}/{month}/{year}"
if rest.strip():
# If there is a time part, keep it after the date
formatted += rest
return formatted
return cell
else:
return cell
@bp.route('/store_articles')
def store_articles():
return render_template('store_articles.html')
@bp.route('/get_pairing_keys')
def get_pairing_keys():
"""Return all pairing keys as JSON for client selection."""
keys_path = os.path.join(current_app.instance_path, 'pairing_keys.json')
try:
if os.path.exists(keys_path):
with open(keys_path, 'r') as f:
keys = json.load(f)
else:
keys = []
except Exception as e:
print(f"Error loading pairing keys: {e}")
return jsonify([]), 200
return jsonify(keys)
@bp.route('/warehouse_reports')
def warehouse_reports():
return render_template('warehouse_reports.html')
def get_db_connection():
"""Reads the external_server.conf file and returns a MariaDB database connection."""
settings_file = os.path.join(current_app.instance_path, 'external_server.conf')
if not os.path.exists(settings_file):
raise FileNotFoundError("The external_server.conf file is missing in the instance folder.")
# Read settings from the configuration file
settings = {}
with open(settings_file, 'r') as f:
for line in f:
key, value = line.strip().split('=', 1)
settings[key] = value
# Create a database connection
return mariadb.connect(
user=settings['username'],
password=settings['password'],
host=settings['server_domain'],
port=int(settings['port']),
database=settings['database_name']
)
@bp.route('/dashboard')
def dashboard():
print("Session user:", session.get('user'), session.get('role'))
@@ -203,21 +204,21 @@ def settings():
@bp.route('/quality')
def quality():
if 'role' not in session or session['role'] not in ['superadmin', 'quality']:
if 'role' not in session or session['role'] not in ['superadmin', 'administrator', 'quality']:
flash('Access denied: Quality users only.')
return redirect(url_for('main.dashboard'))
return render_template('quality.html')
@bp.route('/warehouse')
def warehouse():
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse']:
if 'role' not in session or session['role'] not in ['superadmin', 'administrator', 'admin', 'warehouse']:
flash('Access denied: Warehouse users only.')
return redirect(url_for('main.dashboard'))
return render_template('main_page_warehouse.html')
@bp.route('/scan', methods=['GET', 'POST'])
def scan():
if 'role' not in session or session['role'] not in ['superadmin', 'scan']:
if 'role' not in session or session['role'] not in ['superadmin', 'administrator', 'admin', 'scan']:
flash('Access denied: Scan users only.')
return redirect(url_for('main.dashboard'))
@@ -952,14 +953,14 @@ def test_database():
@bp.route('/etichete')
def etichete():
if 'role' not in session or session['role'] not in ['superadmin', 'etichete']:
if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'etichete']:
flash('Access denied: Etichete users only.')
return redirect(url_for('main.dashboard'))
return render_template('main_page_etichete.html')
@bp.route('/upload_data')
def upload_data():
return render_template('upload_data.html')
return render_template('upload_orders.html')
@bp.route('/print_module')
def print_module():
@@ -1817,7 +1818,7 @@ def create_template():
@bp.route('/get_database_tables', methods=['GET'])
def get_database_tables():
"""Get list of database tables for template creation"""
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']:
return jsonify({'error': 'Access denied'}), 403
try:
@@ -1867,7 +1868,7 @@ def get_database_tables():
@bp.route('/get_table_columns/<table_name>', methods=['GET'])
def get_table_columns(table_name):
"""Get column names and descriptions for a specific table"""
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']:
return jsonify({'error': 'Access denied'}), 403
try:
@@ -2002,41 +2003,7 @@ def generate_pdf():
return jsonify({'message': 'PDF generated successfully!', 'pdf_path': f'/static/label_templates/label_template.pdf'})
# Order Labels Upload Module Routes
@bp.route('/upload_orders', methods=['GET', 'POST'])
def upload_orders():
"""Route for uploading orders CSV files for label generation"""
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse', 'warehouse_manager']:
flash('Access denied: Warehouse management permissions required.')
return redirect(url_for('main.dashboard'))
from app.order_labels import upload_orders_handler
return upload_orders_handler()
@bp.route('/view_orders')
def view_orders():
"""Route for viewing uploaded orders"""
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse', 'warehouse_manager', 'warehouse_worker']:
flash('Access denied: Warehouse access required.')
return redirect(url_for('main.dashboard'))
from app.order_labels import get_orders_from_database
orders = get_orders_from_database(200) # Get last 200 orders
return render_template('view_orders.html', orders=orders)
@bp.route('/print_lost_labels')
def print_lost_labels():
"""Route for printing lost labels - allows searching and reprinting specific orders"""
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse', 'warehouse_manager', 'warehouse_worker']:
flash('Access denied: Warehouse access required.')
return redirect(url_for('main.dashboard'))
from app.order_labels import get_orders_from_database
orders = get_orders_from_database(500) # Get more orders for searching
return render_template('print_lost_labels.html', orders=orders)
@bp.route('/db_test')
def db_test():
"""Simple database test page"""
# ...existing code...
return render_template('db_test.html')
@bp.route('/get_unprinted_orders', methods=['GET'])
@@ -2067,9 +2034,9 @@ def generate_labels_pdf(order_id, paper_saving_mode='true'):
"""Generate PDF labels for a specific order"""
print(f"DEBUG: generate_labels_pdf called for order_id: {order_id}")
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']:
print(f"DEBUG: Access denied for role: {session.get('role')}")
return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403
return jsonify({'error': 'Access denied. Required roles: superadmin, admin, administrator, warehouse_manager, etichete'}), 403
try:
from .pdf_generator import generate_order_labels_pdf, update_order_printed_status
@@ -2148,8 +2115,8 @@ def generate_labels_pdf(order_id, paper_saving_mode='true'):
def generate_label_pdf():
"""Generate a single label PDF for thermal printing via QZ Tray"""
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403
if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']:
return jsonify({'error': 'Access denied. Required roles: superadmin, admin, administrator, warehouse_manager, etichete'}), 403
try:
from .pdf_generator import LabelPDFGenerator
@@ -2197,9 +2164,9 @@ def update_printed_status(order_id):
"""Update printed status for direct printing (without PDF generation)"""
print(f"DEBUG: update_printed_status called for order_id: {order_id}")
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']:
print(f"DEBUG: Access denied for role: {session.get('role')}")
return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403
return jsonify({'error': 'Access denied. Required roles: superadmin, admin, administrator, warehouse_manager, etichete'}), 403
try:
from .pdf_generator import update_order_printed_status
@@ -2312,7 +2279,7 @@ def get_order_data(order_id):
"""Get specific order data for preview"""
print(f"DEBUG: get_order_data called for order_id: {order_id}")
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']:
return jsonify({'error': 'Access denied'}), 403
try:
+3 -3
View File
@@ -14,8 +14,8 @@
<h3>View Orders</h3>
<p>Upload new orders or view existing orders and manage label data for printing.</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="{{ url_for('main.upload_orders') }}" class="btn">Upload Orders</a>
<a href="{{ url_for('main.view_orders') }}" class="btn">View Orders</a>
<a href="{{ url_for('main.upload_data') }}" class="btn">Upload Orders</a>
<a href="{{ url_for('main.get_unprinted_orders') }}" class="btn">View Orders</a>
</div>
</div>
@@ -25,7 +25,7 @@
<p>Access the print module to print labels.</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="{{ url_for('main.print_module') }}" class="btn">Launch Printing Module</a>
<a href="{{ url_for('main.print_lost_labels') }}" class="btn">Launch lost labels printing module</a>
<a href="{{ url_for('main.print_module') }}" class="btn">Launch lost labels printing module</a>
</div>
</div>
+3 -3
View File
@@ -1,5 +1,5 @@
server_domain=localhost
port=3602
database_name=trasabilitate_database
username=trasabilitate
password=Initial01!
database_name=recticel
username=sa
password=12345678
+1 -1
View File
@@ -3,4 +3,4 @@ from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
app.run(debug=True, port=8781, host='0.0.0.0')