diff --git a/Machine-Backend/.env b/Machine-Backend/.env index 55c51c2..0608171 100644 --- a/Machine-Backend/.env +++ b/Machine-Backend/.env @@ -6,14 +6,14 @@ PAYU_MERCHANT_SALT=DusXSSjqqSMTPSpw32hlFXF6LKY2Zm3y PAYU_SUCCESS_URL=http://localhost:4200/payment-success PAYU_FAILURE_URL=http://localhost:4200/payment-failure -FLASK_ENV=production +FLASK_ENV=development MYSQL_HOST=db MYSQL_USER=vendinguser MYSQL_PASSWORD=vendingpass MYSQL_DATABASE=vending -SQLITE_DB_PATH=machines +SQLITE_DB_PATH=machines.db BREVO_SMTP_EMAIL=smukeshsn2000@gmail.com BREVO_SMTP_KEY=your-brevo-smtp-key diff --git a/Machine-Backend/app/instance/machines.db b/Machine-Backend/app/instance/machines.db index 803bd6c..eef373b 100644 Binary files a/Machine-Backend/app/instance/machines.db and b/Machine-Backend/app/instance/machines.db differ diff --git a/Machine-Backend/app/routes/__pycache__/routes.cpython-313.pyc b/Machine-Backend/app/routes/__pycache__/routes.cpython-313.pyc index 348dfce..4707755 100644 Binary files a/Machine-Backend/app/routes/__pycache__/routes.cpython-313.pyc and b/Machine-Backend/app/routes/__pycache__/routes.cpython-313.pyc differ diff --git a/Machine-Backend/app/routes/routes.py b/Machine-Backend/app/routes/routes.py index 517c569..138e3e9 100644 --- a/Machine-Backend/app/routes/routes.py +++ b/Machine-Backend/app/routes/routes.py @@ -1,5 +1,5 @@ from app import db # Add this if not already there -from sqlalchemy import func # Add this +from sqlalchemy import func, extract # Add this from flask import Blueprint, request, jsonify, send_from_directory from app.services.services import MachineService, UserService, ProductService, serial_service, TransactionService, RoleService from app.models.models import Machine, User, Transaction, Product, VendingSlot, Role @@ -853,6 +853,10 @@ def get_current_user_from_token(): return None +from flask import jsonify, request +from sqlalchemy import func, extract +from datetime import datetime + @bp.route('/dashboard-metrics', methods=['GET']) def get_dashboard_metrics_role_based(): """Get dashboard metrics filtered by user role""" @@ -877,16 +881,12 @@ def get_dashboard_metrics_role_based(): # ROLE-BASED FILTERING if user_role in ['Management', 'SuperAdmin', 'Admin']: - # Show overall system stats response = get_admin_dashboard_metrics(filter_type) elif user_role == 'Client': - # Show only this client's data response = get_Client_dashboard_metrics(current_user.id, filter_type) elif user_role == 'Refiller': - # Show product/warehouse related stats response = get_refiller_dashboard_metrics(filter_type) elif user_role == 'Servicer': - # Show machine maintenance stats response = get_servicer_dashboard_metrics(filter_type) else: return jsonify({'error': 'Invalid role'}), 403 @@ -914,23 +914,52 @@ def get_admin_dashboard_metrics(filter_type): machine_count = Machine.query.count() machine_title = 'All Machines' - # Client count (users with role 'Client') + # Client count clients = User.query.filter_by(roles='Client').count() - # Company users (Admin, SuperAdmin, Management, Refiller, Servicer) + # Company users company_users = User.query.filter( User.roles.in_(['Management', 'SuperAdmin', 'Admin', 'Refiller', 'Servicer']) ).count() + # Active and inactive machines + active_machines = Machine.query.filter_by(operation_status='active').count() + inactive_machines = Machine.query.filter_by(operation_status='inactive').count() + # COUNT TRANSACTIONS AND CALCULATE SALES transactions_count = Transaction.query.count() - # Calculate total sales (sum of all successful transaction amounts) - from sqlalchemy import func + # Calculate total sales total_sales = db.session.query(func.sum(Transaction.amount))\ .filter(Transaction.status == 'Success')\ .scalar() or 0.0 + # PAYMENT BREAKDOWN + payment_breakdown = get_payment_breakdown() + + # MACHINE OPERATION STATUS + machine_operation_status = { + 'Online': Machine.query.filter_by(connection_status='online').count(), + 'Offline': Machine.query.filter_by(connection_status='offline').count(), + 'Down (Planned)': Machine.query.filter_by(operation_status='down_planned').count(), + 'Down': Machine.query.filter_by(operation_status='down').count(), + 'Standby': Machine.query.filter_by(operation_status='standby').count(), + 'Terminated': Machine.query.filter_by(operation_status='terminated').count(), + 'N/A': Machine.query.filter(Machine.operation_status.is_(None)).count() + } + + # MACHINE STOCK STATUS - Calculate based on vending slots + machine_stock_status = calculate_machine_stock_status() + + # PRODUCT SALES YEARLY + product_sales_yearly = get_product_sales_yearly() + + # SALES YEARLY + sales_yearly = get_sales_yearly() + + # TOP SELLING PRODUCTS + top_selling_products = get_top_selling_products() + return { 'machine_title': machine_title, 'machine_count': machine_count, @@ -939,13 +968,21 @@ def get_admin_dashboard_metrics(filter_type): 'client_users': clients, 'transactions': transactions_count, 'sales': f'{total_sales:.2f}', + 'active_machines': active_machines, + 'inactive_machines': inactive_machines, + 'payment_breakdown': payment_breakdown, + 'machine_operation_status': machine_operation_status, + 'machine_stock_status': machine_stock_status, + 'product_sales_yearly': product_sales_yearly, + 'sales_yearly': sales_yearly, + 'top_selling_products': top_selling_products, 'user_role': 'admin', 'user_type': 'Overall System' } def get_Client_dashboard_metrics(user_id, filter_type): - """Get metrics for Client/Client - Only their own data""" + """Get metrics for Client - Only their own data""" # Get only machines belonging to this client if filter_type == 'active': machine_count = Machine.query.filter_by( @@ -963,28 +1000,53 @@ def get_Client_dashboard_metrics(user_id, filter_type): machine_count = Machine.query.filter_by(client_id=user_id).count() machine_title = 'My Machines' - # For Client, clients = 1 (themselves) clients = 1 - - # Company users not relevant for Client company_users = 0 - # GET THIS CLIENT'S TRANSACTIONS AND SALES # Get machine IDs for this client client_machine_ids = [m.machine_id for m in Machine.query.filter_by(client_id=user_id).all()] + # Active and inactive machines for this client + active_machines = Machine.query.filter_by(client_id=user_id, operation_status='active').count() + inactive_machines = Machine.query.filter_by(client_id=user_id, operation_status='inactive').count() + # Count transactions for this client's machines transactions_count = Transaction.query.filter( Transaction.machine_id.in_(client_machine_ids) ).count() if client_machine_ids else 0 # Calculate sales for this client's machines - from sqlalchemy import func total_sales = db.session.query(func.sum(Transaction.amount))\ .filter(Transaction.machine_id.in_(client_machine_ids))\ .filter(Transaction.status == 'Success')\ .scalar() or 0.0 if client_machine_ids else 0.0 + # PAYMENT BREAKDOWN for client's machines + payment_breakdown = get_payment_breakdown(client_machine_ids) + + # MACHINE OPERATION STATUS for client's machines + machine_operation_status = { + 'Online': Machine.query.filter(Machine.client_id == user_id, Machine.connection_status == 'online').count(), + 'Offline': Machine.query.filter(Machine.client_id == user_id, Machine.connection_status == 'offline').count(), + 'Down (Planned)': Machine.query.filter(Machine.client_id == user_id, Machine.operation_status == 'down_planned').count(), + 'Down': Machine.query.filter(Machine.client_id == user_id, Machine.operation_status == 'down').count(), + 'Standby': Machine.query.filter(Machine.client_id == user_id, Machine.operation_status == 'standby').count(), + 'Terminated': Machine.query.filter(Machine.client_id == user_id, Machine.operation_status == 'terminated').count(), + 'N/A': Machine.query.filter(Machine.client_id == user_id, Machine.operation_status.is_(None)).count() + } + + # MACHINE STOCK STATUS for client's machines + machine_stock_status = calculate_machine_stock_status(client_machine_ids) + + # PRODUCT SALES YEARLY for client's machines + product_sales_yearly = get_product_sales_yearly(client_machine_ids) + + # SALES YEARLY for client's machines + sales_yearly = get_sales_yearly(client_machine_ids) + + # TOP SELLING PRODUCTS for client's machines + top_selling_products = get_top_selling_products(client_machine_ids) + return { 'machine_title': machine_title, 'machine_count': machine_count, @@ -993,6 +1055,14 @@ def get_Client_dashboard_metrics(user_id, filter_type): 'client_users': clients, 'transactions': transactions_count, 'sales': f'{total_sales:.2f}', + 'active_machines': active_machines, + 'inactive_machines': inactive_machines, + 'payment_breakdown': payment_breakdown, + 'machine_operation_status': machine_operation_status, + 'machine_stock_status': machine_stock_status, + 'product_sales_yearly': product_sales_yearly, + 'sales_yearly': sales_yearly, + 'top_selling_products': top_selling_products, 'user_role': 'Client', 'user_type': 'My Business' } @@ -1000,21 +1070,32 @@ def get_Client_dashboard_metrics(user_id, filter_type): def get_refiller_dashboard_metrics(filter_type): """Get metrics for Refiller - Product/warehouse focused""" - # Machines they need to service machine_count = Machine.query.count() - - # Products in warehouse product_count = Product.query.count() - - # Transaction count (all) transactions_count = Transaction.query.count() - # Total sales - from sqlalchemy import func total_sales = db.session.query(func.sum(Transaction.amount))\ .filter(Transaction.status == 'Success')\ .scalar() or 0.0 + active_machines = Machine.query.filter_by(operation_status='active').count() + inactive_machines = Machine.query.filter_by(operation_status='inactive').count() + + payment_breakdown = get_payment_breakdown() + machine_operation_status = { + 'Online': Machine.query.filter_by(connection_status='online').count(), + 'Offline': Machine.query.filter_by(connection_status='offline').count(), + 'Down (Planned)': Machine.query.filter_by(operation_status='down_planned').count(), + 'Down': Machine.query.filter_by(operation_status='down').count(), + 'Standby': Machine.query.filter_by(operation_status='standby').count(), + 'Terminated': Machine.query.filter_by(operation_status='terminated').count(), + 'N/A': Machine.query.filter(Machine.operation_status.is_(None)).count() + } + machine_stock_status = calculate_machine_stock_status() + product_sales_yearly = get_product_sales_yearly() + sales_yearly = get_sales_yearly() + top_selling_products = get_top_selling_products() + return { 'machine_title': 'Machines to Service', 'machine_count': machine_count, @@ -1023,7 +1104,15 @@ def get_refiller_dashboard_metrics(filter_type): 'client_users': 0, 'transactions': transactions_count, 'sales': f'{total_sales:.2f}', + 'active_machines': active_machines, + 'inactive_machines': inactive_machines, 'product_count': product_count, + 'payment_breakdown': payment_breakdown, + 'machine_operation_status': machine_operation_status, + 'machine_stock_status': machine_stock_status, + 'product_sales_yearly': product_sales_yearly, + 'sales_yearly': sales_yearly, + 'top_selling_products': top_selling_products, 'user_role': 'refiller', 'user_type': 'Inventory Management' } @@ -1031,7 +1120,6 @@ def get_refiller_dashboard_metrics(filter_type): def get_servicer_dashboard_metrics(filter_type): """Get metrics for Servicer - Machine maintenance focused""" - # All machines they can service if filter_type == 'active': machine_count = Machine.query.filter_by(operation_status='active').count() machine_title = 'Active Machines' @@ -1042,18 +1130,31 @@ def get_servicer_dashboard_metrics(filter_type): machine_count = Machine.query.count() machine_title = 'Total Machines' - # Machines needing maintenance maintenance_count = Machine.query.filter_by(operation_status='maintenance').count() - - # Transaction count (all) transactions_count = Transaction.query.count() - # Total sales - from sqlalchemy import func total_sales = db.session.query(func.sum(Transaction.amount))\ .filter(Transaction.status == 'Success')\ .scalar() or 0.0 + active_machines = Machine.query.filter_by(operation_status='active').count() + inactive_machines = Machine.query.filter_by(operation_status='inactive').count() + + payment_breakdown = get_payment_breakdown() + machine_operation_status = { + 'Online': Machine.query.filter_by(connection_status='online').count(), + 'Offline': Machine.query.filter_by(connection_status='offline').count(), + 'Down (Planned)': Machine.query.filter_by(operation_status='down_planned').count(), + 'Down': Machine.query.filter_by(operation_status='down').count(), + 'Standby': Machine.query.filter_by(operation_status='standby').count(), + 'Terminated': Machine.query.filter_by(operation_status='terminated').count(), + 'N/A': Machine.query.filter(Machine.operation_status.is_(None)).count() + } + machine_stock_status = calculate_machine_stock_status() + product_sales_yearly = get_product_sales_yearly() + sales_yearly = get_sales_yearly() + top_selling_products = get_top_selling_products() + return { 'machine_title': machine_title, 'machine_count': machine_count, @@ -1062,11 +1163,374 @@ def get_servicer_dashboard_metrics(filter_type): 'client_users': 0, 'transactions': transactions_count, 'sales': f'{total_sales:.2f}', + 'active_machines': active_machines, + 'inactive_machines': inactive_machines, 'maintenance_count': maintenance_count, + 'payment_breakdown': payment_breakdown, + 'machine_operation_status': machine_operation_status, + 'machine_stock_status': machine_stock_status, + 'product_sales_yearly': product_sales_yearly, + 'sales_yearly': sales_yearly, + 'top_selling_products': top_selling_products, 'user_role': 'servicer', 'user_type': 'Machine Maintenance' } + +# HELPER FUNCTIONS + +def get_payment_breakdown(machine_ids=None): + """Calculate payment breakdown from transactions""" + query = db.session.query( + Transaction.payment_type, + func.sum(Transaction.amount).label('total') + ) + + if machine_ids: + query = query.filter(Transaction.machine_id.in_(machine_ids)) + + query = query.filter(Transaction.status == 'Success')\ + .group_by(Transaction.payment_type) + + payment_data = query.all() + + breakdown = { + 'cash': 0.0, + 'cashless': 0.0, + 'upi_wallet_card': 0.0, + 'upi_wallet_paytm': 0.0, + 'refund': 0.0, + 'refund_processed': 0.0, + 'total': 0.0 + } + + for payment_type, total in payment_data: + if payment_type and total: + payment_type_lower = payment_type.lower() + if 'cash' in payment_type_lower and 'cashless' not in payment_type_lower: + breakdown['cash'] += float(total) + elif 'cashless' in payment_type_lower: + breakdown['cashless'] += float(total) + elif 'phonepe' in payment_type_lower or 'card' in payment_type_lower: + breakdown['upi_wallet_card'] += float(total) + elif 'paytm' in payment_type_lower: + breakdown['upi_wallet_paytm'] += float(total) + + # Calculate refunds + refund_query = db.session.query(func.sum(Transaction.return_amount)) + if machine_ids: + refund_query = refund_query.filter(Transaction.machine_id.in_(machine_ids)) + + breakdown['refund'] = float(refund_query.filter( + Transaction.amount_receiving_status == 'Dispense failed refund' + ).scalar() or 0.0) + + breakdown['refund_processed'] = float(refund_query.filter( + Transaction.amount_receiving_status == 'Refund processed' + ).scalar() or 0.0) + + breakdown['total'] = sum([ + breakdown['cash'], + breakdown['cashless'], + breakdown['upi_wallet_card'], + breakdown['upi_wallet_paytm'] + ]) + + return breakdown + + +def calculate_machine_stock_status(machine_ids=None): + """Calculate machine stock status based on vending slots""" + query = Machine.query + if machine_ids: + query = query.filter(Machine.machine_id.in_(machine_ids)) + + machines = query.all() + + status_counts = { + '75 - 100%': 0, + '50 - 75%': 0, + '25 - 50%': 0, + '0 - 25%': 0, + 'N/A': 0 + } + + for machine in machines: + slots = VendingSlot.query.filter_by(machine_id=machine.machine_id).all() + if not slots: + status_counts['N/A'] += 1 + continue + + total_slots = len(slots) + filled_slots = sum(1 for slot in slots if slot.units and slot.units > 0) + + if total_slots == 0: + status_counts['N/A'] += 1 + else: + fill_percentage = (filled_slots / total_slots) * 100 + if fill_percentage >= 75: + status_counts['75 - 100%'] += 1 + elif fill_percentage >= 50: + status_counts['50 - 75%'] += 1 + elif fill_percentage >= 25: + status_counts['25 - 50%'] += 1 + elif fill_percentage > 0: + status_counts['0 - 25%'] += 1 + else: + status_counts['0 - 25%'] += 1 + + return status_counts + + +def get_product_sales_yearly(machine_ids=None): + """Get product sales aggregated by year""" + query = db.session.query( + extract('year', Transaction.created_at).label('year'), + func.sum(Transaction.amount).label('total_sales') + ) + + if machine_ids: + query = query.filter(Transaction.machine_id.in_(machine_ids)) + + query = query.filter(Transaction.status == 'Success')\ + .group_by(extract('year', Transaction.created_at))\ + .order_by('year') + + results = query.all() + + return [{'year': str(int(year)), 'amount': float(total_sales)} + for year, total_sales in results if year] + + +def get_sales_yearly(machine_ids=None): + """Get sales aggregated by year""" + return get_product_sales_yearly(machine_ids) + + +def get_top_selling_products(machine_ids=None, limit=10): + """Get top selling products""" + query = db.session.query( + Transaction.product_name, + func.sum(Transaction.quantity).label('total_quantity') + ) + + if machine_ids: + query = query.filter(Transaction.machine_id.in_(machine_ids)) + + query = query.filter(Transaction.status == 'Success')\ + .group_by(Transaction.product_name)\ + .order_by(func.sum(Transaction.quantity).desc())\ + .limit(limit) + + results = query.all() + + return [{'product_name': product_name, 'quantity': int(total_quantity)} + for product_name, total_quantity in results] + +@bp.route('/product-sales', methods=['GET']) +def get_product_sales_filtered(): + """Get product sales data filtered by time range""" + try: + current_user = get_current_user_from_token() + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + time_range = request.args.get('time_range', 'year') + user_role = current_user.roles + + print(f"Product Sales Filter - User: {current_user.username}, Range: {time_range}") + + # Get machine IDs based on role + machine_ids = None + if user_role == 'Client': + machine_ids = [m.machine_id for m in Machine.query.filter_by(client_id=current_user.id).all()] + + # Fetch data based on time range + product_sales = get_sales_data_by_range(time_range, machine_ids) + + return jsonify({'product_sales': product_sales}), 200 + + except Exception as e: + print(f"Error in product sales filter: {str(e)}") + return jsonify({'error': str(e)}), 500 + + +@bp.route('/sales-data', methods=['GET']) +def get_sales_filtered(): + """Get sales data filtered by time range""" + try: + current_user = get_current_user_from_token() + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + time_range = request.args.get('time_range', 'year') + user_role = current_user.roles + + print(f"Sales Filter - User: {current_user.username}, Range: {time_range}") + + # Get machine IDs based on role + machine_ids = None + if user_role == 'Client': + machine_ids = [m.machine_id for m in Machine.query.filter_by(client_id=current_user.id).all()] + + # Fetch data based on time range + sales_data = get_sales_data_by_range(time_range, machine_ids) + + return jsonify({'sales_data': sales_data}), 200 + + except Exception as e: + print(f"Error in sales filter: {str(e)}") + return jsonify({'error': str(e)}), 500 + + +@bp.route('/top-products', methods=['GET']) +def get_top_products_filtered(): + """Get top selling products filtered by time range""" + try: + current_user = get_current_user_from_token() + if not current_user: + return jsonify({'error': 'Authentication required'}), 401 + + time_range = request.args.get('time_range', 'year') + user_role = current_user.roles + + print(f"Top Products Filter - User: {current_user.username}, Range: {time_range}") + + # Get machine IDs based on role + machine_ids = None + if user_role == 'Client': + machine_ids = [m.machine_id for m in Machine.query.filter_by(client_id=current_user.id).all()] + + # Fetch data based on time range + top_products = get_top_products_by_range(time_range, machine_ids) + + return jsonify({'top_products': top_products}), 200 + + except Exception as e: + print(f"Error in top products filter: {str(e)}") + return jsonify({'error': str(e)}), 500 + + +# Helper function to get date filter based on time range +def get_date_filter(time_range): + """Return date filter based on time range""" + now = datetime.now() + + if time_range == 'day': + # Last 24 hours + return now - timedelta(days=1) + elif time_range == 'week': + # Last 7 days + return now - timedelta(weeks=1) + elif time_range == 'month': + # Last 30 days + return now - timedelta(days=30) + elif time_range == 'year': + # All time, grouped by year + return None + else: + return None + + +def get_sales_data_by_range(time_range, machine_ids=None): + """Get sales data aggregated by time range""" + date_filter = get_date_filter(time_range) + + # Base query + query = db.session.query(Transaction.created_at, Transaction.amount)\ + .filter(Transaction.status == 'Success') + + # Apply machine filter if needed + if machine_ids: + query = query.filter(Transaction.machine_id.in_(machine_ids)) + + # Apply date filter + if date_filter: + query = query.filter(Transaction.created_at >= date_filter) + + transactions = query.all() + + # Aggregate based on time range + if time_range == 'day': + # Group by hour + aggregated = {} + for txn in transactions: + hour = txn.created_at.strftime('%H:00') + if hour not in aggregated: + aggregated[hour] = 0 + aggregated[hour] += float(txn.amount) + + return [{'year': k, 'amount': v} for k, v in sorted(aggregated.items())] + + elif time_range == 'week': + # Group by day + aggregated = {} + for txn in transactions: + day = txn.created_at.strftime('%a') # Mon, Tue, etc. + if day not in aggregated: + aggregated[day] = 0 + aggregated[day] += float(txn.amount) + + # Sort by weekday order + days_order = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + return [{'year': day, 'amount': aggregated.get(day, 0)} for day in days_order] + + elif time_range == 'month': + # Group by day + aggregated = {} + for txn in transactions: + day = txn.created_at.strftime('%d') + if day not in aggregated: + aggregated[day] = 0 + aggregated[day] += float(txn.amount) + + return [{'year': k, 'amount': v} for k, v in sorted(aggregated.items())] + + else: # year or all time + # Group by year + query = db.session.query( + extract('year', Transaction.created_at).label('year'), + func.sum(Transaction.amount).label('total_sales') + ).filter(Transaction.status == 'Success') + + if machine_ids: + query = query.filter(Transaction.machine_id.in_(machine_ids)) + + query = query.group_by(extract('year', Transaction.created_at))\ + .order_by('year') + + results = query.all() + + return [{'year': str(int(year)), 'amount': float(total_sales)} + for year, total_sales in results if year] + + +def get_top_products_by_range(time_range, machine_ids=None, limit=10): + """Get top selling products filtered by time range""" + date_filter = get_date_filter(time_range) + + # Base query + query = db.session.query( + Transaction.product_name, + func.sum(Transaction.quantity).label('total_quantity') + ).filter(Transaction.status == 'Success') + + # Apply machine filter if needed + if machine_ids: + query = query.filter(Transaction.machine_id.in_(machine_ids)) + + # Apply date filter + if date_filter: + query = query.filter(Transaction.created_at >= date_filter) + + query = query.group_by(Transaction.product_name)\ + .order_by(func.sum(Transaction.quantity).desc())\ + .limit(limit) + + results = query.all() + + return [{'product_name': product_name, 'quantity': int(total_quantity)} + for product_name, total_quantity in results] # Additional utility routes for PayU integration @bp.route('/verify-payu-hash', methods=['POST']) @@ -1603,7 +2067,7 @@ def get_roles(): return jsonify({'error': 'Authentication required'}), 401 # Only Management and SuperAdmin can view roles - if current_user.roles not in ['Management', 'SuperAdmin']: + if current_user.roles not in ['Management', 'SuperAdmin','Admin']: return jsonify({'error': 'Permission denied'}), 403 roles = RoleService.get_all_roles() @@ -1622,7 +2086,7 @@ def create_role(): return jsonify({'error': 'Authentication required'}), 401 # Only Management and SuperAdmin can create roles - if current_user.roles not in ['Management', 'SuperAdmin']: + if current_user.roles not in ['Management', 'SuperAdmin','Admin']: return jsonify({'error': 'Permission denied'}), 403 data = request.json @@ -1645,7 +2109,7 @@ def update_role(role_id): return jsonify({'error': 'Authentication required'}), 401 # Only Management and SuperAdmin can update roles - if current_user.roles not in ['Management', 'SuperAdmin']: + if current_user.roles not in ['Management', 'SuperAdmin','Admin']: return jsonify({'error': 'Permission denied'}), 403 data = request.json @@ -1668,7 +2132,7 @@ def delete_role(role_id): return jsonify({'error': 'Authentication required'}), 401 # Only Management and SuperAdmin can delete roles - if current_user.roles not in ['Management', 'SuperAdmin']: + if current_user.roles not in ['Management', 'SuperAdmin','Admin']: return jsonify({'error': 'Permission denied'}), 403 RoleService.delete_role(role_id) @@ -1690,7 +2154,7 @@ def get_available_permissions(): return jsonify({'error': 'Authentication required'}), 401 # Only Management and SuperAdmin can view permissions - if current_user.roles not in ['Management', 'SuperAdmin']: + if current_user.roles not in ['Management', 'SuperAdmin','Admin']: return jsonify({'error': 'Permission denied'}), 403 # Define all available permissions diff --git a/Machine-Backend/app/uploads/f736133f7f6b40c3a836747a87567606_download.png b/Machine-Backend/app/uploads/070423cbc97247739946779e8bef485b_download.png similarity index 100% rename from Machine-Backend/app/uploads/f736133f7f6b40c3a836747a87567606_download.png rename to Machine-Backend/app/uploads/070423cbc97247739946779e8bef485b_download.png diff --git a/Machine-Backend/app/uploads/b30aac7345554870812ccc0df4647213_7up.png b/Machine-Backend/app/uploads/a054b74e7a5c4714bf8199c43c8e58e2_7up.png similarity index 100% rename from Machine-Backend/app/uploads/b30aac7345554870812ccc0df4647213_7up.png rename to Machine-Backend/app/uploads/a054b74e7a5c4714bf8199c43c8e58e2_7up.png diff --git a/Machine-Backend/app/uploads/company_logos/30a4d83d8d924884abb79f715941f1e8_social-support.png b/Machine-Backend/app/uploads/company_logos/30a4d83d8d924884abb79f715941f1e8_social-support.png deleted file mode 100644 index c83de0d..0000000 Binary files a/Machine-Backend/app/uploads/company_logos/30a4d83d8d924884abb79f715941f1e8_social-support.png and /dev/null differ diff --git a/Machine-Backend/app/uploads/user_documents/0ffd085d728f4993a0a5a632a9e53d85_sample.pdf b/Machine-Backend/app/uploads/user_documents/0ffd085d728f4993a0a5a632a9e53d85_sample.pdf deleted file mode 100644 index 94d9477..0000000 Binary files a/Machine-Backend/app/uploads/user_documents/0ffd085d728f4993a0a5a632a9e53d85_sample.pdf and /dev/null differ diff --git a/Machine-Backend/app/uploads/user_photos/701adc9c512748cba515c0021c5cd1f1_doctor.jpg b/Machine-Backend/app/uploads/user_photos/aa3622cbfa17457faea2a8a9da4ee99a_doctor.jpg similarity index 100% rename from Machine-Backend/app/uploads/user_photos/701adc9c512748cba515c0021c5cd1f1_doctor.jpg rename to Machine-Backend/app/uploads/user_photos/aa3622cbfa17457faea2a8a9da4ee99a_doctor.jpg diff --git a/Machine-Backend/instance/machines.db b/Machine-Backend/instance/machines.db index 2568e95..6250316 100644 Binary files a/Machine-Backend/instance/machines.db and b/Machine-Backend/instance/machines.db differ diff --git a/Machine-Backend/reset_password.py b/Machine-Backend/reset_password.py new file mode 100644 index 0000000..529dc8e --- /dev/null +++ b/Machine-Backend/reset_password.py @@ -0,0 +1,18 @@ +from app import create_app, db +from app.models.models import User +from werkzeug.security import generate_password_hash + +app = create_app() + +with app.app_context(): + email = "test@example.com" + new_password = "test123" + + user = User.query.filter_by(email=email).first() + if not user: + print("User not found ❌") + else: + # Hash password in the way your model expects + user.password = generate_password_hash(new_password, method='pbkdf2:sha256') + db.session.commit() + print(f"Password reset successfully for {email}. Login with: {new_password}") diff --git a/fuse-starter-v20.0.0/src/app/app.routes.ts b/fuse-starter-v20.0.0/src/app/app.routes.ts index 738a599..428b538 100644 --- a/fuse-starter-v20.0.0/src/app/app.routes.ts +++ b/fuse-starter-v20.0.0/src/app/app.routes.ts @@ -153,7 +153,7 @@ export const appRoutes: Route[] = [ { path: 'role-management', canActivate: [RoleGuard], - data: { roles: ['Management', 'SuperAdmin'] }, + data: { roles: ['Management', 'SuperAdmin', 'Admin'] }, loadChildren: () =>import('app/modules/admin/dashboard/role-management/role-management.routes') }, diff --git a/fuse-starter-v20.0.0/src/app/modules/admin/example/example.component.html b/fuse-starter-v20.0.0/src/app/modules/admin/example/example.component.html index 20b022e..996d99d 100644 --- a/fuse-starter-v20.0.0/src/app/modules/admin/example/example.component.html +++ b/fuse-starter-v20.0.0/src/app/modules/admin/example/example.component.html @@ -1,162 +1,305 @@ +