Files
orion/app/modules/tenancy/services/team_service.py
Samir Boulahtit d7a0ff8818 refactor: complete module-driven architecture migration
This commit completes the migration to a fully module-driven architecture:

## Models Migration
- Moved all domain models from models/database/ to their respective modules:
  - tenancy: User, Admin, Vendor, Company, Platform, VendorDomain, etc.
  - cms: MediaFile, VendorTheme
  - messaging: Email, VendorEmailSettings, VendorEmailTemplate
  - core: AdminMenuConfig
- models/database/ now only contains Base and TimestampMixin (infrastructure)

## Schemas Migration
- Moved all domain schemas from models/schema/ to their respective modules:
  - tenancy: company, vendor, admin, team, vendor_domain
  - cms: media, image, vendor_theme
  - messaging: email
- models/schema/ now only contains base.py and auth.py (infrastructure)

## Routes Migration
- Moved admin routes from app/api/v1/admin/ to modules:
  - menu_config.py -> core module
  - modules.py -> tenancy module
  - module_config.py -> tenancy module
- app/api/v1/admin/ now only aggregates auto-discovered module routes

## Menu System
- Implemented module-driven menu system with MenuDiscoveryService
- Extended FrontendType enum: PLATFORM, ADMIN, VENDOR, STOREFRONT
- Added MenuItemDefinition and MenuSectionDefinition dataclasses
- Each module now defines its own menu items in definition.py
- MenuService integrates with MenuDiscoveryService for template rendering

## Documentation
- Updated docs/architecture/models-structure.md
- Updated docs/architecture/menu-management.md
- Updated architecture validation rules for new exceptions

## Architecture Validation
- Updated MOD-019 rule to allow base.py in models/schema/
- Created core module exceptions.py and schemas/ directory
- All validation errors resolved (only warnings remain)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 21:02:56 +01:00

218 lines
6.1 KiB
Python

# app/modules/tenancy/services/team_service.py
"""
Team service for vendor team management.
This module provides:
- Team member invitation
- Role management
- Team member CRUD operations
"""
import logging
from datetime import UTC, datetime
from typing import Any
from sqlalchemy.orm import Session
from app.exceptions import ValidationException
from app.modules.tenancy.models import User
from app.modules.tenancy.models import Role, VendorUser
logger = logging.getLogger(__name__)
class TeamService:
"""Service for team management operations."""
def get_team_members(
self, db: Session, vendor_id: int, current_user: User
) -> list[dict[str, Any]]:
"""
Get all team members for vendor.
Args:
db: Database session
vendor_id: Vendor ID
current_user: Current user
Returns:
List of team members
"""
try:
vendor_users = (
db.query(VendorUser)
.filter(VendorUser.vendor_id == vendor_id, VendorUser.is_active == True)
.all()
)
members = []
for vu in vendor_users:
members.append(
{
"id": vu.user_id,
"email": vu.user.email,
"first_name": vu.user.first_name,
"last_name": vu.user.last_name,
"role": vu.role.name,
"role_id": vu.role_id,
"is_active": vu.is_active,
"joined_at": vu.created_at,
}
)
return members
except Exception as e:
logger.error(f"Error getting team members: {str(e)}")
raise ValidationException("Failed to retrieve team members")
def invite_team_member(
self, db: Session, vendor_id: int, invitation_data: dict, current_user: User
) -> dict[str, Any]:
"""
Invite a new team member.
Args:
db: Database session
vendor_id: Vendor ID
invitation_data: Invitation details
current_user: Current user
Returns:
Invitation result
"""
try:
# TODO: Implement full invitation flow with email
# For now, return placeholder
return {
"message": "Team invitation feature coming soon",
"email": invitation_data.get("email"),
"role": invitation_data.get("role"),
}
except Exception as e:
logger.error(f"Error inviting team member: {str(e)}")
raise ValidationException("Failed to invite team member")
def update_team_member(
self,
db: Session,
vendor_id: int,
user_id: int,
update_data: dict,
current_user: User,
) -> dict[str, Any]:
"""
Update team member role or status.
Args:
db: Database session
vendor_id: Vendor ID
user_id: User ID to update
update_data: Update data
current_user: Current user
Returns:
Updated member info
"""
try:
vendor_user = (
db.query(VendorUser)
.filter(
VendorUser.vendor_id == vendor_id, VendorUser.user_id == user_id
)
.first()
)
if not vendor_user:
raise ValidationException("Team member not found")
# Update fields
if "role_id" in update_data:
vendor_user.role_id = update_data["role_id"]
if "is_active" in update_data:
vendor_user.is_active = update_data["is_active"]
vendor_user.updated_at = datetime.now(UTC)
db.flush()
db.refresh(vendor_user)
return {
"message": "Team member updated successfully",
"user_id": user_id,
}
except Exception as e:
logger.error(f"Error updating team member: {str(e)}")
raise ValidationException("Failed to update team member")
def remove_team_member(
self, db: Session, vendor_id: int, user_id: int, current_user: User
) -> bool:
"""
Remove team member from vendor.
Args:
db: Database session
vendor_id: Vendor ID
user_id: User ID to remove
current_user: Current user
Returns:
True if removed
"""
try:
vendor_user = (
db.query(VendorUser)
.filter(
VendorUser.vendor_id == vendor_id, VendorUser.user_id == user_id
)
.first()
)
if not vendor_user:
raise ValidationException("Team member not found")
# Soft delete
vendor_user.is_active = False
vendor_user.updated_at = datetime.now(UTC)
logger.info(f"Removed user {user_id} from vendor {vendor_id}")
return True
except Exception as e:
logger.error(f"Error removing team member: {str(e)}")
raise ValidationException("Failed to remove team member")
def get_vendor_roles(self, db: Session, vendor_id: int) -> list[dict[str, Any]]:
"""
Get available roles for vendor.
Args:
db: Database session
vendor_id: Vendor ID
Returns:
List of roles
"""
try:
roles = db.query(Role).filter(Role.vendor_id == vendor_id).all()
return [
{
"id": role.id,
"name": role.name,
"permissions": role.permissions,
}
for role in roles
]
except Exception as e:
logger.error(f"Error getting vendor roles: {str(e)}")
raise ValidationException("Failed to retrieve roles")
# Create service instance
team_service = TeamService()