This commit is contained in:
Iliyan Angelov
2025-11-21 15:01:24 +02:00
parent 4ab7546de0
commit 9a6190e8ef
889 changed files with 1912 additions and 57 deletions

View File

@@ -0,0 +1,57 @@
#!/usr/bin/env python3
"""
Script to add the 'accountant' role to the database.
Run this script once to create the accountant role if it doesn't exist.
"""
import sys
from pathlib import Path
# Add the Backend directory to the path
backend_dir = Path(__file__).parent
sys.path.insert(0, str(backend_dir))
from src.config.database import SessionLocal
from src.models.role import Role
def add_accountant_role():
"""Add the accountant role to the database if it doesn't exist."""
db = SessionLocal()
try:
# Check if accountant role already exists
existing_role = db.query(Role).filter(Role.name == 'accountant').first()
if existing_role:
print("✓ Accountant role already exists in the database.")
print(f" Role ID: {existing_role.id}")
print(f" Role Name: {existing_role.name}")
return
# Create the accountant role
accountant_role = Role(
name='accountant',
description='Accountant role with access to financial data, payments, and invoices'
)
db.add(accountant_role)
db.commit()
db.refresh(accountant_role)
print("✓ Accountant role created successfully!")
print(f" Role ID: {accountant_role.id}")
print(f" Role Name: {accountant_role.name}")
print(f" Description: {accountant_role.description}")
except Exception as e:
db.rollback()
print(f"✗ Error creating accountant role: {e}")
sys.exit(1)
finally:
db.close()
if __name__ == '__main__':
print("Adding accountant role to the database...")
print("-" * 50)
add_accountant_role()
print("-" * 50)
print("Done!")

View File

@@ -28,8 +28,7 @@ def seed_about_page(db: Session):
"title": "About Luxury Hotel", "title": "About Luxury Hotel",
"subtitle": "Where Excellence Meets Unforgettable Experiences", "subtitle": "Where Excellence Meets Unforgettable Experiences",
"description": "Discover the story behind our commitment to luxury hospitality and exceptional service.", "description": "Discover the story behind our commitment to luxury hospitality and exceptional service.",
"story_content": "story_content": "For over three decades, Luxury Hotel has been a beacon of excellence in the hospitality industry. Founded with a vision to redefine luxury travel, we have grown from a single property to a collection of world-renowned destinations, each offering a unique blend of timeless elegance and modern sophistication. Our journey has been marked by countless awards, memorable moments, and the unwavering trust of our guests who return year after year.",
,
"mission": "To provide unparalleled luxury hospitality experiences that exceed expectations, creating lasting memories for our guests through exceptional service, attention to detail, and genuine care.", "mission": "To provide unparalleled luxury hospitality experiences that exceed expectations, creating lasting memories for our guests through exceptional service, attention to detail, and genuine care.",
"vision": "To be recognized as the world's premier luxury hotel brand, setting the standard for excellence in hospitality while maintaining our commitment to sustainability and community engagement.", "vision": "To be recognized as the world's premier luxury hotel brand, setting the standard for excellence in hospitality while maintaining our commitment to sustainability and community engagement.",
"about_hero_image": "https://images.unsplash.com/photo-1566073771259-6a8506099945?w=1920&h=1080&fit=crop", "about_hero_image": "https://images.unsplash.com/photo-1566073771259-6a8506099945?w=1920&h=1080&fit=crop",

View File

@@ -0,0 +1,156 @@
import sys
import os
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from sqlalchemy.orm import Session
from src.config.database import SessionLocal
from src.models.role import Role
from src.models.room_type import RoomType
from src.models.user import User
import bcrypt
from datetime import datetime
def get_db():
db = SessionLocal()
try:
return db
finally:
pass
def seed_roles(db: Session):
print('=' * 80)
print('SEEDING ROLES')
print('=' * 80)
roles_data = [
{'name': 'admin', 'description': 'Administrator with full access'},
{'name': 'staff', 'description': 'Staff member with limited admin access'},
{'name': 'customer', 'description': 'Regular customer'},
{'name': 'accountant', 'description': 'Accountant role with access to financial data, payments, and invoices'}
]
for role_data in roles_data:
existing = db.query(Role).filter(Role.name == role_data['name']).first()
if existing:
print(f' ✓ Role "{role_data["name"]}" already exists')
else:
role = Role(**role_data)
db.add(role)
print(f' ✓ Created role: {role_data["name"]}')
db.commit()
print('✓ Roles seeded successfully!\n')
def seed_room_types(db: Session):
print('=' * 80)
print('SEEDING ROOM TYPES')
print('=' * 80)
room_types_data = [
{
'name': 'Standard Room',
'description': 'Comfortable and well-appointed standard accommodation',
'base_price': 150.00,
'capacity': 2,
'amenities': ['Free WiFi', 'Air Conditioning', 'TV', 'Minibar', 'Safe']
},
{
'name': 'Superior Room',
'description': 'Spacious room with enhanced amenities and better views',
'base_price': 200.00,
'capacity': 2,
'amenities': ['Free WiFi', 'Air Conditioning', 'Smart TV', 'Minibar', 'Safe', 'Coffee Maker']
},
{
'name': 'Deluxe Room',
'description': 'Luxurious room with premium furnishings and amenities',
'base_price': 280.00,
'capacity': 3,
'amenities': ['Free WiFi', 'Air Conditioning', 'Smart TV', 'Netflix', 'Minibar', 'Safe', 'Coffee Maker', 'Premium Toiletries']
},
{
'name': 'Executive Suite',
'description': 'Elegant suite with separate living area and premium amenities',
'base_price': 400.00,
'capacity': 4,
'amenities': ['Free WiFi', 'Air Conditioning', 'Smart TV', 'Netflix', 'Minibar', 'Safe', 'Espresso Machine', 'Premium Toiletries', 'Bathrobes', 'Work Desk']
},
{
'name': 'Presidential Suite',
'description': 'The ultimate in luxury with expansive space and exclusive amenities',
'base_price': 800.00,
'capacity': 6,
'amenities': ['Free WiFi', 'Air Conditioning', 'Smart TV', 'Netflix', 'Private Bar', 'Jacuzzi', 'Butler Service', 'Premium Toiletries', 'Bathrobes', 'Private Terrace']
}
]
import json
for rt_data in room_types_data:
existing = db.query(RoomType).filter(RoomType.name == rt_data['name']).first()
if existing:
print(f' ✓ Room type "{rt_data["name"]}" already exists')
else:
amenities = rt_data.pop('amenities')
room_type = RoomType(**rt_data, amenities=json.dumps(amenities))
db.add(room_type)
print(f' ✓ Created room type: {rt_data["name"]} (€{rt_data["base_price"]:.2f}/night)')
db.commit()
print('✓ Room types seeded successfully!\n')
def seed_admin_user(db: Session):
print('=' * 80)
print('SEEDING ADMIN USER')
print('=' * 80)
admin_role = db.query(Role).filter(Role.name == 'admin').first()
if not admin_role:
print(' ❌ Admin role not found! Please seed roles first.')
return
admin_email = 'admin@hotel.com'
existing_admin = db.query(User).filter(User.email == admin_email).first()
if existing_admin:
print(f' ✓ Admin user "{admin_email}" already exists')
else:
password = 'admin123' # Default password - should be changed in production
password_bytes = password.encode('utf-8')
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password_bytes, salt).decode('utf-8')
admin_user = User(
email=admin_email,
password=hashed_password,
full_name='Administrator',
role_id=admin_role.id,
is_active=True,
currency='EUR'
)
db.add(admin_user)
db.commit()
print(f' ✓ Created admin user: {admin_email}')
print(f' ⚠️ Default password: admin123 (please change in production!)')
print('✓ Admin user seeded successfully!\n')
def main():
db = get_db()
try:
seed_roles(db)
seed_room_types(db)
seed_admin_user(db)
print('=' * 80)
print('✅ Initial data seeding completed successfully!')
print('=' * 80)
except Exception as e:
print(f'\n❌ Error: {e}')
import traceback
traceback.print_exc()
db.rollback()
finally:
db.close()
if __name__ == '__main__':
main()

165
Backend/seed_users.py Normal file
View File

@@ -0,0 +1,165 @@
import sys
import os
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from sqlalchemy.orm import Session
from src.config.database import SessionLocal
from src.models.role import Role
from src.models.user import User
import bcrypt
from datetime import datetime
def get_db():
db = SessionLocal()
try:
return db
finally:
pass
def seed_users(db: Session):
print('=' * 80)
print('SEEDING USERS')
print('=' * 80)
# Get roles
admin_role = db.query(Role).filter(Role.name == 'admin').first()
staff_role = db.query(Role).filter(Role.name == 'staff').first()
customer_role = db.query(Role).filter(Role.name == 'customer').first()
if not admin_role or not staff_role or not customer_role:
print(' ❌ Roles not found! Please seed roles first.')
return
users_data = [
{
'email': 'gnxsoft@gnxsoft.com',
'password': 'gnxsoft123',
'full_name': 'GNXSoft Admin',
'phone': '+1 (555) 111-2222',
'role': 'admin',
'currency': 'EUR',
'is_active': True
},
{
'email': 'admin@gnxsoft.com',
'password': 'admin123',
'full_name': 'Administrator',
'phone': '+1 (555) 222-3333',
'role': 'admin',
'currency': 'EUR',
'is_active': True
},
{
'email': 'staff@gnxsoft.com',
'password': 'staff123',
'full_name': 'Staff Member',
'phone': '+1 (555) 333-4444',
'role': 'staff',
'currency': 'EUR',
'is_active': True
},
{
'email': 'customer@gnxsoft.com',
'password': 'customer123',
'full_name': 'Customer User',
'phone': '+1 (555) 444-5555',
'role': 'customer',
'currency': 'EUR',
'is_active': True
},
{
'email': 'john.doe@gnxsoft.com',
'password': 'customer123',
'full_name': 'John Doe',
'phone': '+1 (555) 555-6666',
'role': 'customer',
'currency': 'USD',
'is_active': True
},
{
'email': 'jane.smith@gnxsoft.com',
'password': 'customer123',
'full_name': 'Jane Smith',
'phone': '+1 (555) 666-7777',
'role': 'customer',
'currency': 'EUR',
'is_active': True
},
{
'email': 'robert.wilson@gnxsoft.com',
'password': 'customer123',
'full_name': 'Robert Wilson',
'phone': '+1 (555) 777-8888',
'role': 'customer',
'currency': 'GBP',
'is_active': True
},
{
'email': 'maria.garcia@gnxsoft.com',
'password': 'customer123',
'full_name': 'Maria Garcia',
'phone': '+1 (555) 888-9999',
'role': 'customer',
'currency': 'EUR',
'is_active': True
}
]
role_map = {
'admin': admin_role.id,
'staff': staff_role.id,
'customer': customer_role.id
}
created_count = 0
skipped_count = 0
for user_data in users_data:
existing = db.query(User).filter(User.email == user_data['email']).first()
if existing:
print(f' ⚠️ User "{user_data["email"]}" already exists, skipping...')
skipped_count += 1
continue
password = user_data.pop('password')
role_name = user_data.pop('role')
role_id = role_map[role_name]
password_bytes = password.encode('utf-8')
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password_bytes, salt).decode('utf-8')
user = User(
email=user_data['email'],
password=hashed_password,
full_name=user_data['full_name'],
phone=user_data.get('phone'),
role_id=role_id,
currency=user_data.get('currency', 'EUR'),
is_active=user_data.get('is_active', True)
)
db.add(user)
print(f' ✓ Created user: {user_data["email"]} ({role_name}) - Password: {password}')
created_count += 1
db.commit()
print(f'\n✓ Users seeded successfully!')
print(f' - Created: {created_count} user(s)')
print(f' - Skipped: {skipped_count} user(s) (already exist)')
print('=' * 80)
def main():
db = get_db()
try:
seed_users(db)
except Exception as e:
print(f'\n❌ Error: {e}')
import traceback
traceback.print_exc()
db.rollback()
finally:
db.close()
if __name__ == '__main__':
main()

View File

@@ -9,6 +9,7 @@ from ..config.database import get_db
from ..config.settings import settings from ..config.settings import settings
from ..middleware.auth import get_current_user, authorize_roles from ..middleware.auth import get_current_user, authorize_roles
from ..models.user import User from ..models.user import User
from ..models.role import Role
from ..models.booking import Booking, BookingStatus from ..models.booking import Booking, BookingStatus
from ..models.room import Room, RoomStatus from ..models.room import Room, RoomStatus
from ..models.room_type import RoomType from ..models.room_type import RoomType
@@ -142,8 +143,9 @@ async def get_my_bookings(request: Request, current_user: User=Depends(get_curre
@router.post('/') @router.post('/')
async def create_booking(booking_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)): async def create_booking(booking_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
if current_user.role in ['admin', 'staff']: role = db.query(Role).filter(Role.id == current_user.role_id).first()
raise HTTPException(status_code=403, detail='Admin and staff users cannot create bookings') if role and role.name in ['admin', 'staff', 'accountant']:
raise HTTPException(status_code=403, detail='Admin, staff, and accountant users cannot create bookings')
try: try:
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -4,6 +4,7 @@ from sqlalchemy import func
from ..config.database import get_db from ..config.database import get_db
from ..middleware.auth import get_current_user from ..middleware.auth import get_current_user
from ..models.user import User from ..models.user import User
from ..models.role import Role
from ..models.favorite import Favorite from ..models.favorite import Favorite
from ..models.room import Room from ..models.room import Room
from ..models.room_type import RoomType from ..models.room_type import RoomType
@@ -12,8 +13,9 @@ router = APIRouter(prefix='/favorites', tags=['favorites'])
@router.get('/') @router.get('/')
async def get_favorites(current_user: User=Depends(get_current_user), db: Session=Depends(get_db)): async def get_favorites(current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
if current_user.role in ['admin', 'staff']: role = db.query(Role).filter(Role.id == current_user.role_id).first()
raise HTTPException(status_code=403, detail='Admin and staff users cannot have favorites') if role and role.name in ['admin', 'staff', 'accountant']:
raise HTTPException(status_code=403, detail='Admin, staff, and accountant users cannot have favorites')
try: try:
favorites = db.query(Favorite).filter(Favorite.user_id == current_user.id).order_by(Favorite.created_at.desc()).all() favorites = db.query(Favorite).filter(Favorite.user_id == current_user.id).order_by(Favorite.created_at.desc()).all()
result = [] result = []
@@ -33,8 +35,9 @@ async def get_favorites(current_user: User=Depends(get_current_user), db: Sessio
@router.post('/{room_id}') @router.post('/{room_id}')
async def add_favorite(room_id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)): async def add_favorite(room_id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
if current_user.role in ['admin', 'staff']: role = db.query(Role).filter(Role.id == current_user.role_id).first()
raise HTTPException(status_code=403, detail='Admin and staff users cannot add favorites') if role and role.name in ['admin', 'staff', 'accountant']:
raise HTTPException(status_code=403, detail='Admin, staff, and accountant users cannot add favorites')
try: try:
room = db.query(Room).filter(Room.id == room_id).first() room = db.query(Room).filter(Room.id == room_id).first()
if not room: if not room:
@@ -55,8 +58,9 @@ async def add_favorite(room_id: int, current_user: User=Depends(get_current_user
@router.delete('/{room_id}') @router.delete('/{room_id}')
async def remove_favorite(room_id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)): async def remove_favorite(room_id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
if current_user.role in ['admin', 'staff']: role = db.query(Role).filter(Role.id == current_user.role_id).first()
raise HTTPException(status_code=403, detail='Admin and staff users cannot remove favorites') if role and role.name in ['admin', 'staff', 'accountant']:
raise HTTPException(status_code=403, detail='Admin, staff, and accountant users cannot remove favorites')
try: try:
favorite = db.query(Favorite).filter(Favorite.user_id == current_user.id, Favorite.room_id == room_id).first() favorite = db.query(Favorite).filter(Favorite.user_id == current_user.id, Favorite.room_id == room_id).first()
if not favorite: if not favorite:
@@ -72,7 +76,8 @@ async def remove_favorite(room_id: int, current_user: User=Depends(get_current_u
@router.get('/check/{room_id}') @router.get('/check/{room_id}')
async def check_favorite(room_id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)): async def check_favorite(room_id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
if current_user.role in ['admin', 'staff']: role = db.query(Role).filter(Role.id == current_user.role_id).first()
if role and role.name in ['admin', 'staff', 'accountant']:
return {'status': 'success', 'data': {'isFavorited': False}} return {'status': 'success', 'data': {'isFavorited': False}}
try: try:
favorite = db.query(Favorite).filter(Favorite.user_id == current_user.id, Favorite.room_id == room_id).first() favorite = db.query(Favorite).filter(Favorite.user_id == current_user.id, Favorite.room_id == room_id).first()

View File

@@ -13,7 +13,7 @@ router = APIRouter(prefix='/invoices', tags=['invoices'])
@router.get('/') @router.get('/')
async def get_invoices(booking_id: Optional[int]=Query(None), status_filter: Optional[str]=Query(None, alias='status'), page: int=Query(1, ge=1), limit: int=Query(10, ge=1, le=100), current_user: User=Depends(get_current_user), db: Session=Depends(get_db)): async def get_invoices(booking_id: Optional[int]=Query(None), status_filter: Optional[str]=Query(None, alias='status'), page: int=Query(1, ge=1), limit: int=Query(10, ge=1, le=100), current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
try: try:
user_id = None if current_user.role_id == 1 else current_user.id user_id = None if current_user.role_id in [1, 4] else current_user.id # admin and accountant can see all invoices
result = InvoiceService.get_invoices(db=db, user_id=user_id, booking_id=booking_id, status=status_filter, page=page, limit=limit) result = InvoiceService.get_invoices(db=db, user_id=user_id, booking_id=booking_id, status=status_filter, page=page, limit=limit)
return {'status': 'success', 'data': result} return {'status': 'success', 'data': result}
except Exception as e: except Exception as e:
@@ -25,7 +25,7 @@ async def get_invoice_by_id(id: int, current_user: User=Depends(get_current_user
invoice = InvoiceService.get_invoice(id, db) invoice = InvoiceService.get_invoice(id, db)
if not invoice: if not invoice:
raise HTTPException(status_code=404, detail='Invoice not found') raise HTTPException(status_code=404, detail='Invoice not found')
if current_user.role_id != 1 and invoice['user_id'] != current_user.id: if current_user.role_id not in [1, 4] and invoice['user_id'] != current_user.id: # admin and accountant can see all invoices
raise HTTPException(status_code=403, detail='Forbidden') raise HTTPException(status_code=403, detail='Forbidden')
return {'status': 'success', 'data': {'invoice': invoice}} return {'status': 'success', 'data': {'invoice': invoice}}
except HTTPException: except HTTPException:
@@ -36,7 +36,7 @@ async def get_invoice_by_id(id: int, current_user: User=Depends(get_current_user
@router.post('/') @router.post('/')
async def create_invoice(invoice_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)): async def create_invoice(invoice_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
try: try:
if current_user.role_id not in [1, 2]: if current_user.role_id not in [1, 2, 4]: # admin, staff, and accountant can create invoices
raise HTTPException(status_code=403, detail='Forbidden') raise HTTPException(status_code=403, detail='Forbidden')
booking_id = invoice_data.get('booking_id') booking_id = invoice_data.get('booking_id')
if not booking_id: if not booking_id:
@@ -64,7 +64,7 @@ async def create_invoice(invoice_data: dict, current_user: User=Depends(get_curr
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@router.put('/{id}') @router.put('/{id}')
async def update_invoice(id: int, invoice_data: dict, current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)): async def update_invoice(id: int, invoice_data: dict, current_user: User=Depends(authorize_roles('admin', 'staff', 'accountant')), db: Session=Depends(get_db)):
try: try:
invoice = db.query(Invoice).filter(Invoice.id == id).first() invoice = db.query(Invoice).filter(Invoice.id == id).first()
if not invoice: if not invoice:
@@ -79,7 +79,7 @@ async def update_invoice(id: int, invoice_data: dict, current_user: User=Depends
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@router.post('/{id}/mark-paid') @router.post('/{id}/mark-paid')
async def mark_invoice_as_paid(id: int, payment_data: dict, current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)): async def mark_invoice_as_paid(id: int, payment_data: dict, current_user: User=Depends(authorize_roles('admin', 'staff', 'accountant')), db: Session=Depends(get_db)):
try: try:
amount = payment_data.get('amount') amount = payment_data.get('amount')
updated_invoice = InvoiceService.mark_invoice_as_paid(invoice_id=id, db=db, amount=amount, updated_by_id=current_user.id) updated_invoice = InvoiceService.mark_invoice_as_paid(invoice_id=id, db=db, amount=amount, updated_by_id=current_user.id)
@@ -112,7 +112,7 @@ async def get_invoices_by_booking(booking_id: int, current_user: User=Depends(ge
booking = db.query(Booking).filter(Booking.id == booking_id).first() booking = db.query(Booking).filter(Booking.id == booking_id).first()
if not booking: if not booking:
raise HTTPException(status_code=404, detail='Booking not found') raise HTTPException(status_code=404, detail='Booking not found')
if current_user.role_id != 1 and booking.user_id != current_user.id: if current_user.role_id not in [1, 4] and booking.user_id != current_user.id: # admin and accountant can see all invoices
raise HTTPException(status_code=403, detail='Forbidden') raise HTTPException(status_code=403, detail='Forbidden')
result = InvoiceService.get_invoices(db=db, booking_id=booking_id) result = InvoiceService.get_invoices(db=db, booking_id=booking_id)
return {'status': 'success', 'data': result} return {'status': 'success', 'data': result}

View File

@@ -54,7 +54,7 @@ async def get_payments(booking_id: Optional[int]=Query(None), status_filter: Opt
query = query.filter(Payment.payment_status == PaymentStatus(status_filter)) query = query.filter(Payment.payment_status == PaymentStatus(status_filter))
except ValueError: except ValueError:
pass pass
if current_user.role_id != 1: if current_user.role_id not in [1, 4]: # admin and accountant can see all payments
query = query.join(Booking).filter(Booking.user_id == current_user.id) query = query.join(Booking).filter(Booking.user_id == current_user.id)
total = query.count() total = query.count()
query = query.options(selectinload(Payment.booking).selectinload(Booking.user)) query = query.options(selectinload(Payment.booking).selectinload(Booking.user))
@@ -106,7 +106,7 @@ async def get_payment_by_id(id: int, current_user: User=Depends(get_current_user
payment = db.query(Payment).filter(Payment.id == id).first() payment = db.query(Payment).filter(Payment.id == id).first()
if not payment: if not payment:
raise HTTPException(status_code=404, detail='Payment not found') raise HTTPException(status_code=404, detail='Payment not found')
if current_user.role_id != 1: if current_user.role_id not in [1, 4]: # admin and accountant can see all payments
if payment.booking and payment.booking.user_id != current_user.id: if payment.booking and payment.booking.user_id != current_user.id:
raise HTTPException(status_code=403, detail='Forbidden') raise HTTPException(status_code=403, detail='Forbidden')
payment_dict = {'id': payment.id, 'booking_id': payment.booking_id, 'amount': float(payment.amount) if payment.amount else 0.0, 'payment_method': payment.payment_method.value if isinstance(payment.payment_method, PaymentMethod) else payment.payment_method, 'payment_type': payment.payment_type.value if isinstance(payment.payment_type, PaymentType) else payment.payment_type, 'deposit_percentage': payment.deposit_percentage, 'related_payment_id': payment.related_payment_id, 'payment_status': payment.payment_status.value if isinstance(payment.payment_status, PaymentStatus) else payment.payment_status, 'transaction_id': payment.transaction_id, 'payment_date': payment.payment_date.isoformat() if payment.payment_date else None, 'notes': payment.notes, 'created_at': payment.created_at.isoformat() if payment.created_at else None} payment_dict = {'id': payment.id, 'booking_id': payment.booking_id, 'amount': float(payment.amount) if payment.amount else 0.0, 'payment_method': payment.payment_method.value if isinstance(payment.payment_method, PaymentMethod) else payment.payment_method, 'payment_type': payment.payment_type.value if isinstance(payment.payment_type, PaymentType) else payment.payment_type, 'deposit_percentage': payment.deposit_percentage, 'related_payment_id': payment.related_payment_id, 'payment_status': payment.payment_status.value if isinstance(payment.payment_status, PaymentStatus) else payment.payment_status, 'transaction_id': payment.transaction_id, 'payment_date': payment.payment_date.isoformat() if payment.payment_date else None, 'notes': payment.notes, 'created_at': payment.created_at.isoformat() if payment.created_at else None}
@@ -159,8 +159,8 @@ async def create_payment(payment_data: dict, current_user: User=Depends(get_curr
db.rollback() db.rollback()
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@router.put('/{id}/status', dependencies=[Depends(authorize_roles('admin', 'staff'))]) @router.put('/{id}/status', dependencies=[Depends(authorize_roles('admin', 'staff', 'accountant'))])
async def update_payment_status(id: int, status_data: dict, current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)): async def update_payment_status(id: int, status_data: dict, current_user: User=Depends(authorize_roles('admin', 'staff', 'accountant')), db: Session=Depends(get_db)):
try: try:
payment = db.query(Payment).filter(Payment.id == id).first() payment = db.query(Payment).filter(Payment.id == id).first()
if not payment: if not payment:

View File

@@ -14,7 +14,7 @@ from ..models.service import Service
router = APIRouter(prefix='/reports', tags=['reports']) router = APIRouter(prefix='/reports', tags=['reports'])
@router.get('') @router.get('')
async def get_reports(from_date: Optional[str]=Query(None, alias='from'), to_date: Optional[str]=Query(None, alias='to'), type: Optional[str]=Query(None), current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)): async def get_reports(from_date: Optional[str]=Query(None, alias='from'), to_date: Optional[str]=Query(None, alias='to'), type: Optional[str]=Query(None), current_user: User=Depends(authorize_roles('admin', 'staff', 'accountant')), db: Session=Depends(get_db)):
try: try:
start_date = None start_date = None
end_date = None end_date = None
@@ -83,7 +83,7 @@ async def get_reports(from_date: Optional[str]=Query(None, alias='from'), to_dat
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@router.get('/dashboard') @router.get('/dashboard')
async def get_dashboard_stats(current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)): async def get_dashboard_stats(current_user: User=Depends(authorize_roles('admin', 'staff', 'accountant')), db: Session=Depends(get_db)):
try: try:
total_bookings = db.query(Booking).count() total_bookings = db.query(Booking).count()
active_bookings = db.query(Booking).filter(Booking.status.in_([BookingStatus.pending, BookingStatus.confirmed, BookingStatus.checked_in])).count() active_bookings = db.query(Booking).filter(Booking.status.in_([BookingStatus.pending, BookingStatus.confirmed, BookingStatus.checked_in])).count()
@@ -150,7 +150,7 @@ async def get_customer_dashboard_stats(current_user: User=Depends(get_current_us
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@router.get('/revenue') @router.get('/revenue')
async def get_revenue_report(start_date: Optional[str]=Query(None), end_date: Optional[str]=Query(None), current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)): async def get_revenue_report(start_date: Optional[str]=Query(None), end_date: Optional[str]=Query(None), current_user: User=Depends(authorize_roles('admin', 'staff', 'accountant')), db: Session=Depends(get_db)):
try: try:
query = db.query(Payment).filter(Payment.payment_status == PaymentStatus.completed) query = db.query(Payment).filter(Payment.payment_status == PaymentStatus.completed)
if start_date: if start_date:

View File

@@ -17,7 +17,7 @@ async def get_users(search: Optional[str]=Query(None), role: Optional[str]=Query
if search: if search:
query = query.filter(or_(User.full_name.like(f'%{search}%'), User.email.like(f'%{search}%'), User.phone.like(f'%{search}%'))) query = query.filter(or_(User.full_name.like(f'%{search}%'), User.email.like(f'%{search}%'), User.phone.like(f'%{search}%')))
if role: if role:
role_map = {'admin': 1, 'staff': 2, 'customer': 3} role_map = {'admin': 1, 'staff': 2, 'customer': 3, 'accountant': 4}
if role in role_map: if role in role_map:
query = query.filter(User.role_id == role_map[role]) query = query.filter(User.role_id == role_map[role])
if status_filter: if status_filter:
@@ -57,7 +57,7 @@ async def create_user(user_data: dict, current_user: User=Depends(authorize_role
phone_number = user_data.get('phone_number') phone_number = user_data.get('phone_number')
role = user_data.get('role', 'customer') role = user_data.get('role', 'customer')
status = user_data.get('status', 'active') status = user_data.get('status', 'active')
role_map = {'admin': 1, 'staff': 2, 'customer': 3} role_map = {'admin': 1, 'staff': 2, 'customer': 3, 'accountant': 4}
role_id = role_map.get(role, 3) role_id = role_map.get(role, 3)
existing = db.query(User).filter(User.email == email).first() existing = db.query(User).filter(User.email == email).first()
if existing: if existing:
@@ -90,7 +90,7 @@ async def update_user(id: int, user_data: dict, current_user: User=Depends(get_c
existing = db.query(User).filter(User.email == email).first() existing = db.query(User).filter(User.email == email).first()
if existing: if existing:
raise HTTPException(status_code=400, detail='Email already exists') raise HTTPException(status_code=400, detail='Email already exists')
role_map = {'admin': 1, 'staff': 2, 'customer': 3} role_map = {'admin': 1, 'staff': 2, 'customer': 3, 'accountant': 4}
if 'full_name' in user_data: if 'full_name' in user_data:
user.full_name = user_data['full_name'] user.full_name = user_data['full_name']
if 'email' in user_data and current_user.role_id == 1: if 'email' in user_data and current_user.role_id == 1:

Some files were not shown because too many files have changed in this diff Show More