""" Event publishing service for the notification system. Creates Event records that can trigger notifications. """ from typing import Any, Optional, Dict, Set from django.contrib.contenttypes.models import ContentType from channels.db import database_sync_to_async from core.models.events import Event from core.models.enums import EventTypeChoices from core.services.metadata import MetadataEnricher # Mission-critical events that are ALWAYS created (regardless of notification rules) # These events are essential for audit trails, compliance, and business operations MISSION_CRITICAL_EVENTS: Set[EventTypeChoices] = { # Service delivery (customer-facing) EventTypeChoices.SERVICE_COMPLETED, EventTypeChoices.SERVICE_CANCELLED, EventTypeChoices.SERVICE_SESSION_CLOSED, # Project milestones EventTypeChoices.PROJECT_COMPLETED, EventTypeChoices.PROJECT_CANCELLED, EventTypeChoices.PROJECT_SESSION_CLOSED, # Issues requiring attention EventTypeChoices.ACCOUNT_PUNCHLIST_CREATED, EventTypeChoices.PROJECT_PUNCHLIST_CREATED, EventTypeChoices.PUNCHLIST_STATUS_CHANGED, # Financial events (compliance/audit) EventTypeChoices.INVOICE_GENERATED, EventTypeChoices.INVOICE_SENT, EventTypeChoices.INVOICE_PAID, EventTypeChoices.INVOICE_OVERDUE, EventTypeChoices.INVOICE_CANCELLED, EventTypeChoices.LABOR_RATE_CREATED, EventTypeChoices.LABOR_RATE_UPDATED, EventTypeChoices.LABOR_RATE_DELETED, EventTypeChoices.REVENUE_RATE_CREATED, EventTypeChoices.REVENUE_RATE_UPDATED, EventTypeChoices.REVENUE_RATE_DELETED, # Reporting & documentation (legal/compliance) EventTypeChoices.REPORT_SUBMITTED, EventTypeChoices.REPORT_APPROVED, EventTypeChoices.SESSION_IMAGE_UPLOADED, EventTypeChoices.SESSION_VIDEO_UPLOADED, # Account & access management (security/audit) EventTypeChoices.CUSTOMER_CREATED, EventTypeChoices.CUSTOMER_DELETED, EventTypeChoices.CUSTOMER_STATUS_CHANGED, EventTypeChoices.ACCOUNT_CREATED, EventTypeChoices.ACCOUNT_DELETED, EventTypeChoices.ACCOUNT_STATUS_CHANGED, EventTypeChoices.TEAM_PROFILE_CREATED, EventTypeChoices.TEAM_PROFILE_DELETED, EventTypeChoices.TEAM_PROFILE_ROLE_CHANGED, EventTypeChoices.CUSTOMER_PROFILE_CREATED, EventTypeChoices.CUSTOMER_PROFILE_DELETED, EventTypeChoices.CUSTOMER_PROFILE_ACCESS_GRANTED, EventTypeChoices.CUSTOMER_PROFILE_ACCESS_REVOKED, # Schedule changes (operational impact) EventTypeChoices.SCHEDULE_CREATED, EventTypeChoices.SCHEDULE_DELETED, EventTypeChoices.SCHEDULE_FREQUENCY_CHANGED, EventTypeChoices.SERVICES_BULK_GENERATED, } class EventPublisher: """ Service for publishing events to the event management system. Events are stored in the database and can trigger notifications via Celery tasks. """ @staticmethod async def publish( event_type: EventTypeChoices, entity_type: str, entity_id: str, triggered_by: Optional[Any] = None, metadata: Optional[Dict[str, Any]] = None ) -> Optional[Event]: """ Publish an event to the event management system. For mission-critical events, the event is always created. For non-critical events, the event is only created if there are active notification rules. Args: event_type: Type of event (from EventTypeChoices enum) entity_type: Type of entity (e.g., 'Project', 'Report', 'Invoice') entity_id: UUID of the entity that triggered this event triggered_by: Profile instance (TeamProfile or CustomerProfile) that triggered the event metadata: Additional event metadata (e.g., old_status, new_status, changed_fields) Returns: Event: The created Event instance, or None if event was skipped Example: await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_CREATED, entity_type='Project', entity_id=project.id, triggered_by=team_profile, metadata={'status': 'ACTIVE'}) """ # Check if event is mission-critical is_critical = event_type in MISSION_CRITICAL_EVENTS # For non-critical events, check if there are any active notification rules if not is_critical: from core.models.events import NotificationRule has_rules = await database_sync_to_async( lambda: NotificationRule.objects.filter( is_active=True, event_types__contains=[event_type] ).exists() )() if not has_rules: # Skip event creation - no rules configured for this event type return None # Enrich metadata with domain-appropriate fields enriched_metadata = await MetadataEnricher.enrich( event_type=event_type, entity_type=entity_type, entity_id=entity_id, existing_metadata=metadata ) event_data = { 'event_type': event_type, 'entity_type': entity_type, 'entity_id': entity_id, 'metadata': enriched_metadata, } # Set a generic foreign key for triggered_by if provided if triggered_by: content_type = await database_sync_to_async(ContentType.objects.get_for_model)(triggered_by) event_data['triggered_by_content_type'] = content_type event_data['triggered_by_object_id'] = triggered_by.id # Create the event event = await database_sync_to_async(Event.objects.create)(**event_data) # Queue notification processing task # Note: .delay() is non-blocking and doesn't need sync_to_async wrapping from core.tasks.notifications import process_event_notifications process_event_notifications.delay(str(event.id)) return event @staticmethod def publish_sync( event_type: EventTypeChoices, entity_type: str, entity_id: str, triggered_by: Optional[Any] = None, metadata: Optional[Dict[str, Any]] = None ) -> Optional[Event]: """ Synchronous version of publish() for use in non-async contexts. For mission-critical events, the event is always created. For non-critical events, the event is only created if there are active notification rules. Args: event_type: Type of event (from EventTypeChoices enum) entity_type: Type of entity (e.g., 'Project', 'Report', 'Invoice') entity_id: UUID of the entity that triggered this event triggered_by: Profile instance (TeamProfile or CustomerProfile) that triggered the event metadata: Additional event metadata Returns: Event: The created Event instance, or None if event was skipped """ # Check if event is mission-critical is_critical = event_type in MISSION_CRITICAL_EVENTS # For non-critical events, check if there are any active notification rules if not is_critical: from core.models.events import NotificationRule has_rules = NotificationRule.objects.filter( is_active=True, event_types__contains=[event_type] ).exists() if not has_rules: # Skip event creation - no rules configured for this event type return None event_data = { 'event_type': event_type, 'entity_type': entity_type, 'entity_id': entity_id, 'metadata': metadata or {}, } # Set a generic foreign key for triggered_by if provided if triggered_by: content_type = ContentType.objects.get_for_model(triggered_by) event_data['triggered_by_content_type'] = content_type event_data['triggered_by_object_id'] = triggered_by.id # Create the event event = Event.objects.create(**event_data) # Queue notification processing task from core.tasks.notifications import process_event_notifications process_event_notifications.delay(str(event.id)) return event # ======================================== # Convenience functions for common event types # ======================================== # Customer events async def publish_customer_created(customer_id: str, triggered_by=None, metadata=None): """Publish CUSTOMER_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_CREATED, entity_type='Customer', entity_id=customer_id, triggered_by=triggered_by, metadata=metadata ) async def publish_customer_updated(customer_id: str, triggered_by=None, metadata=None): """Publish CUSTOMER_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_UPDATED, entity_type='Customer', entity_id=customer_id, triggered_by=triggered_by, metadata=metadata ) async def publish_customer_deleted(customer_id: str, triggered_by=None, metadata=None): """Publish CUSTOMER_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_DELETED, entity_type='Customer', entity_id=customer_id, triggered_by=triggered_by, metadata=metadata ) async def publish_customer_status_changed(customer_id: str, old_status: str, new_status: str, triggered_by=None): """Publish CUSTOMER_STATUS_CHANGED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_STATUS_CHANGED, entity_type='Customer', entity_id=customer_id, triggered_by=triggered_by, metadata={'old_status': old_status, 'new_status': new_status} ) # Account events async def publish_account_created(account_id: str, triggered_by=None, metadata=None): """Publish ACCOUNT_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_CREATED, entity_type='Account', entity_id=account_id, triggered_by=triggered_by, metadata=metadata ) async def publish_account_updated(account_id: str, triggered_by=None, metadata=None): """Publish ACCOUNT_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_UPDATED, entity_type='Account', entity_id=account_id, triggered_by=triggered_by, metadata=metadata ) async def publish_account_deleted(account_id: str, triggered_by=None, metadata=None): """Publish ACCOUNT_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_DELETED, entity_type='Account', entity_id=account_id, triggered_by=triggered_by, metadata=metadata ) async def publish_account_status_changed(account_id: str, old_status: str, new_status: str, triggered_by=None): """Publish ACCOUNT_STATUS_CHANGED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_STATUS_CHANGED, entity_type='Account', entity_id=account_id, triggered_by=triggered_by, metadata={'old_status': old_status, 'new_status': new_status} ) # Service events async def publish_service_created(service_id: str, triggered_by=None, metadata=None): """Publish SERVICE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_CREATED, entity_type='Service', entity_id=service_id, triggered_by=triggered_by, metadata=metadata ) async def publish_service_updated(service_id: str, triggered_by=None, metadata=None): """Publish SERVICE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_UPDATED, entity_type='Service', entity_id=service_id, triggered_by=triggered_by, metadata=metadata ) async def publish_service_deleted(service_id: str, triggered_by=None, metadata=None): """Publish SERVICE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_DELETED, entity_type='Service', entity_id=service_id, triggered_by=triggered_by, metadata=metadata ) async def publish_service_status_changed(service_id: str, old_status: str, new_status: str, triggered_by=None): """Publish SERVICE_STATUS_CHANGED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_STATUS_CHANGED, entity_type='Service', entity_id=service_id, triggered_by=triggered_by, metadata={'old_status': old_status, 'new_status': new_status} ) async def publish_service_completed(service_id: str, triggered_by=None, metadata=None): """Publish SERVICE_COMPLETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_COMPLETED, entity_type='Service', entity_id=service_id, triggered_by=triggered_by, metadata=metadata ) async def publish_service_cancelled(service_id: str, triggered_by=None, metadata=None): """Publish SERVICE_CANCELLED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_CANCELLED, entity_type='Service', entity_id=service_id, triggered_by=triggered_by, metadata=metadata ) async def publish_service_team_assigned(service_id: str, team_member_id: str, team_member_name: str, triggered_by=None): """Publish SERVICE_TEAM_ASSIGNED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_TEAM_ASSIGNED, entity_type='Service', entity_id=service_id, triggered_by=triggered_by, metadata={'team_member_id': team_member_id, 'team_member_name': team_member_name} ) async def publish_service_team_unassigned(service_id: str, team_member_id: str, team_member_name: str, triggered_by=None): """Publish SERVICE_TEAM_UNASSIGNED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_TEAM_UNASSIGNED, entity_type='Service', entity_id=service_id, triggered_by=triggered_by, metadata={'team_member_id': team_member_id, 'team_member_name': team_member_name} ) async def publish_service_dispatched(service_id: str, triggered_by=None, metadata=None): """Publish SERVICE_DISPATCHED event (admin assigned to service)""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_DISPATCHED, entity_type='Service', entity_id=service_id, triggered_by=triggered_by, metadata=metadata ) async def publish_services_bulk_generated(account_id: str, count: int, month: str, triggered_by=None): """Publish SERVICES_BULK_GENERATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICES_BULK_GENERATED, entity_type='Account', entity_id=account_id, triggered_by=triggered_by, metadata={'count': count, 'month': month} ) # Schedule events async def publish_schedule_created(schedule_id: str, triggered_by=None, metadata=None): """Publish SCHEDULE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCHEDULE_CREATED, entity_type='Schedule', entity_id=schedule_id, triggered_by=triggered_by, metadata=metadata ) async def publish_schedule_updated(schedule_id: str, triggered_by=None, metadata=None): """Publish SCHEDULE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCHEDULE_UPDATED, entity_type='Schedule', entity_id=schedule_id, triggered_by=triggered_by, metadata=metadata ) async def publish_schedule_deleted(schedule_id: str, triggered_by=None, metadata=None): """Publish SCHEDULE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCHEDULE_DELETED, entity_type='Schedule', entity_id=schedule_id, triggered_by=triggered_by, metadata=metadata ) async def publish_schedule_frequency_changed(schedule_id: str, old_frequency: str, new_frequency: str, triggered_by=None): """Publish SCHEDULE_FREQUENCY_CHANGED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCHEDULE_FREQUENCY_CHANGED, entity_type='Schedule', entity_id=schedule_id, triggered_by=triggered_by, metadata={'old_frequency': old_frequency, 'new_frequency': new_frequency} ) # Project events async def publish_project_created(project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_CREATED, entity_type='Project', entity_id=project_id, triggered_by=triggered_by, metadata=metadata ) async def publish_project_updated(project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_UPDATED, entity_type='Project', entity_id=project_id, triggered_by=triggered_by, metadata=metadata ) async def publish_project_status_changed(project_id: str, old_status: str, new_status: str, triggered_by=None): """Publish PROJECT_STATUS_CHANGED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_STATUS_CHANGED, entity_type='Project', entity_id=project_id, triggered_by=triggered_by, metadata={'old_status': old_status, 'new_status': new_status} ) async def publish_project_completed(project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_COMPLETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_COMPLETED, entity_type='Project', entity_id=project_id, triggered_by=triggered_by, metadata=metadata ) async def publish_project_cancelled(project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_CANCELLED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_CANCELLED, entity_type='Project', entity_id=project_id, triggered_by=triggered_by, metadata=metadata ) async def publish_project_dispatched(project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_DISPATCHED event (admin assigned to project)""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_DISPATCHED, entity_type='Project', entity_id=project_id, triggered_by=triggered_by, metadata=metadata ) async def publish_project_deleted(project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_DELETED, entity_type='Project', entity_id=project_id, triggered_by=triggered_by, metadata=metadata ) # Team profile events async def publish_team_profile_created(profile_id: str, triggered_by=None, metadata=None): """Publish TEAM_PROFILE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TEAM_PROFILE_CREATED, entity_type='TeamProfile', entity_id=profile_id, triggered_by=triggered_by, metadata=metadata ) async def publish_team_profile_updated(profile_id: str, triggered_by=None, metadata=None): """Publish TEAM_PROFILE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TEAM_PROFILE_UPDATED, entity_type='TeamProfile', entity_id=profile_id, triggered_by=triggered_by, metadata=metadata ) async def publish_team_profile_deleted(profile_id: str, triggered_by=None, metadata=None): """Publish TEAM_PROFILE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TEAM_PROFILE_DELETED, entity_type='TeamProfile', entity_id=profile_id, triggered_by=triggered_by, metadata=metadata ) async def publish_team_profile_role_changed(profile_id: str, old_role: str, new_role: str, triggered_by=None): """Publish TEAM_PROFILE_ROLE_CHANGED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TEAM_PROFILE_ROLE_CHANGED, entity_type='TeamProfile', entity_id=profile_id, triggered_by=triggered_by, metadata={'old_role': old_role, 'new_role': new_role} ) # Customer profile events async def publish_customer_profile_created(profile_id: str, triggered_by=None, metadata=None): """Publish CUSTOMER_PROFILE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_PROFILE_CREATED, entity_type='CustomerProfile', entity_id=profile_id, triggered_by=triggered_by, metadata=metadata ) async def publish_customer_profile_updated(profile_id: str, triggered_by=None, metadata=None): """Publish CUSTOMER_PROFILE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_PROFILE_UPDATED, entity_type='CustomerProfile', entity_id=profile_id, triggered_by=triggered_by, metadata=metadata ) async def publish_customer_profile_deleted(profile_id: str, triggered_by=None, metadata=None): """Publish CUSTOMER_PROFILE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_PROFILE_DELETED, entity_type='CustomerProfile', entity_id=profile_id, triggered_by=triggered_by, metadata=metadata ) async def publish_customer_profile_access_granted(profile_id: str, customer_id: str, triggered_by=None): """Publish CUSTOMER_PROFILE_ACCESS_GRANTED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_PROFILE_ACCESS_GRANTED, entity_type='CustomerProfile', entity_id=profile_id, triggered_by=triggered_by, metadata={'customer_id': customer_id} ) async def publish_customer_profile_access_revoked(profile_id: str, customer_id: str, triggered_by=None): """Publish CUSTOMER_PROFILE_ACCESS_REVOKED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_PROFILE_ACCESS_REVOKED, entity_type='CustomerProfile', entity_id=profile_id, triggered_by=triggered_by, metadata={'customer_id': customer_id} ) # Report events async def publish_report_created(report_id: str, triggered_by=None, metadata=None): """Publish REPORT_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.REPORT_CREATED, entity_type='Report', entity_id=report_id, triggered_by=triggered_by, metadata=metadata ) async def publish_report_updated(report_id: str, triggered_by=None, metadata=None): """Publish REPORT_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.REPORT_UPDATED, entity_type='Report', entity_id=report_id, triggered_by=triggered_by, metadata=metadata ) async def publish_report_deleted(report_id: str, triggered_by=None, metadata=None): """Publish REPORT_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.REPORT_DELETED, entity_type='Report', entity_id=report_id, triggered_by=triggered_by, metadata=metadata ) async def publish_report_submitted(report_id: str, triggered_by=None, metadata=None): """Publish REPORT_SUBMITTED event""" return await EventPublisher.publish( event_type=EventTypeChoices.REPORT_SUBMITTED, entity_type='Report', entity_id=report_id, triggered_by=triggered_by, metadata=metadata ) async def publish_report_approved(report_id: str, triggered_by=None, metadata=None): """Publish REPORT_APPROVED event""" return await EventPublisher.publish( event_type=EventTypeChoices.REPORT_APPROVED, entity_type='Report', entity_id=report_id, triggered_by=triggered_by, metadata=metadata ) # Invoice events async def publish_invoice_generated(invoice_id: str, triggered_by=None, metadata=None): """Publish INVOICE_GENERATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.INVOICE_GENERATED, entity_type='Invoice', entity_id=invoice_id, triggered_by=triggered_by, metadata=metadata ) async def publish_invoice_sent(invoice_id: str, triggered_by=None, metadata=None): """Publish INVOICE_SENT event""" return await EventPublisher.publish( event_type=EventTypeChoices.INVOICE_SENT, entity_type='Invoice', entity_id=invoice_id, triggered_by=triggered_by, metadata=metadata ) async def publish_invoice_paid(invoice_id: str, triggered_by=None, metadata=None): """Publish INVOICE_PAID event""" return await EventPublisher.publish( event_type=EventTypeChoices.INVOICE_PAID, entity_type='Invoice', entity_id=invoice_id, triggered_by=triggered_by, metadata=metadata ) async def publish_invoice_overdue(invoice_id: str, triggered_by=None, metadata=None): """Publish INVOICE_OVERDUE event""" return await EventPublisher.publish( event_type=EventTypeChoices.INVOICE_OVERDUE, entity_type='Invoice', entity_id=invoice_id, triggered_by=triggered_by, metadata=metadata ) async def publish_invoice_cancelled(invoice_id: str, triggered_by=None, metadata=None): """Publish INVOICE_CANCELLED event""" return await EventPublisher.publish( event_type=EventTypeChoices.INVOICE_CANCELLED, entity_type='Invoice', entity_id=invoice_id, triggered_by=triggered_by, metadata=metadata ) # Service Session events async def publish_service_session_opened(session_id: str, service_id: str, triggered_by=None): """Publish SERVICE_SESSION_OPENED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_SESSION_OPENED, entity_type='ServiceSession', entity_id=session_id, triggered_by=triggered_by, metadata={'service_id': service_id} ) async def publish_service_session_closed(session_id: str, service_id: str, triggered_by=None, metadata=None): """Publish SERVICE_SESSION_CLOSED event""" event_metadata = {'service_id': service_id} if metadata: event_metadata.update(metadata) return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_SESSION_CLOSED, entity_type='ServiceSession', entity_id=session_id, triggered_by=triggered_by, metadata=event_metadata ) async def publish_service_session_reverted(session_id: str, service_id: str, triggered_by=None): """Publish SERVICE_SESSION_REVERTED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_SESSION_REVERTED, entity_type='ServiceSession', entity_id=session_id, triggered_by=triggered_by, metadata={'service_id': service_id} ) async def publish_service_task_completed(task_id: str, service_id: str, task_name: str, triggered_by=None): """Publish SERVICE_TASK_COMPLETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_TASK_COMPLETED, entity_type='TaskCompletion', entity_id=task_id, triggered_by=triggered_by, metadata={'service_id': service_id, 'task_name': task_name} ) async def publish_service_task_uncompleted(task_id: str, service_id: str, task_name: str, triggered_by=None): """Publish SERVICE_TASK_UNCOMPLETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SERVICE_TASK_UNCOMPLETED, entity_type='TaskCompletion', entity_id=task_id, triggered_by=triggered_by, metadata={'service_id': service_id, 'task_name': task_name} ) # Project Session events async def publish_project_session_opened(session_id: str, project_id: str, triggered_by=None): """Publish PROJECT_SESSION_OPENED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SESSION_OPENED, entity_type='ProjectSession', entity_id=session_id, triggered_by=triggered_by, metadata={'project_id': project_id} ) async def publish_project_session_closed(session_id: str, project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_SESSION_CLOSED event""" event_metadata = {'project_id': project_id} if metadata: event_metadata.update(metadata) return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SESSION_CLOSED, entity_type='ProjectSession', entity_id=session_id, triggered_by=triggered_by, metadata=event_metadata ) async def publish_project_session_reverted(session_id: str, project_id: str, triggered_by=None): """Publish PROJECT_SESSION_REVERTED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SESSION_REVERTED, entity_type='ProjectSession', entity_id=session_id, triggered_by=triggered_by, metadata={'project_id': project_id} ) async def publish_project_task_completed(task_id: str, project_id: str, task_name: str, triggered_by=None): """Publish PROJECT_TASK_COMPLETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_TASK_COMPLETED, entity_type='ProjectTaskCompletion', entity_id=task_id, triggered_by=triggered_by, metadata={'project_id': project_id, 'task_name': task_name} ) async def publish_project_task_uncompleted(task_id: str, project_id: str, task_name: str, triggered_by=None): """Publish PROJECT_TASK_UNCOMPLETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_TASK_UNCOMPLETED, entity_type='ProjectTaskCompletion', entity_id=task_id, triggered_by=triggered_by, metadata={'project_id': project_id, 'task_name': task_name} ) # Punchlist events async def publish_account_punchlist_created(punchlist_id: str, account_id: str, triggered_by=None, metadata=None): """Publish ACCOUNT_PUNCHLIST_CREATED event""" meta = metadata or {} meta['account_id'] = account_id return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_PUNCHLIST_CREATED, entity_type='AccountPunchlist', entity_id=punchlist_id, triggered_by=triggered_by, metadata=meta ) async def publish_account_punchlist_updated(punchlist_id: str, account_id: str, triggered_by=None, metadata=None): """Publish ACCOUNT_PUNCHLIST_UPDATED event""" meta = metadata or {} meta['account_id'] = account_id return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_PUNCHLIST_UPDATED, entity_type='AccountPunchlist', entity_id=punchlist_id, triggered_by=triggered_by, metadata=meta ) async def publish_account_punchlist_deleted(punchlist_id: str, account_id: str, triggered_by=None, metadata=None): """Publish ACCOUNT_PUNCHLIST_DELETED event""" meta = metadata or {} meta['account_id'] = account_id return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_PUNCHLIST_DELETED, entity_type='AccountPunchlist', entity_id=punchlist_id, triggered_by=triggered_by, metadata=meta ) async def publish_project_punchlist_created(punchlist_id: str, project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_PUNCHLIST_CREATED event""" meta = metadata or {} meta['project_id'] = project_id return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_PUNCHLIST_CREATED, entity_type='ProjectPunchlist', entity_id=punchlist_id, triggered_by=triggered_by, metadata=meta ) async def publish_project_punchlist_updated(punchlist_id: str, project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_PUNCHLIST_UPDATED event""" meta = metadata or {} meta['project_id'] = project_id return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_PUNCHLIST_UPDATED, entity_type='ProjectPunchlist', entity_id=punchlist_id, triggered_by=triggered_by, metadata=meta ) async def publish_project_punchlist_deleted(punchlist_id: str, project_id: str, triggered_by=None, metadata=None): """Publish PROJECT_PUNCHLIST_DELETED event""" meta = metadata or {} meta['project_id'] = project_id return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_PUNCHLIST_DELETED, entity_type='ProjectPunchlist', entity_id=punchlist_id, triggered_by=triggered_by, metadata=meta ) async def publish_punchlist_status_changed(punchlist_id: str, entity_type: str, old_status: str, new_status: str, triggered_by=None): """Publish PUNCHLIST_STATUS_CHANGED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PUNCHLIST_STATUS_CHANGED, entity_type=entity_type, entity_id=punchlist_id, triggered_by=triggered_by, metadata={'old_status': old_status, 'new_status': new_status} ) async def publish_punchlist_priority_changed(punchlist_id: str, entity_type: str, old_priority: str, new_priority: str, triggered_by=None): """Publish PUNCHLIST_PRIORITY_CHANGED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PUNCHLIST_PRIORITY_CHANGED, entity_type=entity_type, entity_id=punchlist_id, triggered_by=triggered_by, metadata={'old_priority': old_priority, 'new_priority': new_priority} ) # Session Media events async def publish_session_image_uploaded(image_id: str, session_id: str, is_internal: bool, triggered_by=None): """Publish SESSION_IMAGE_UPLOADED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_IMAGE_UPLOADED, entity_type='SessionImage', entity_id=image_id, triggered_by=triggered_by, metadata={'session_id': session_id, 'is_internal': is_internal} ) async def publish_session_image_updated(image_id: str, session_id: str, triggered_by=None, metadata=None): """Publish SESSION_IMAGE_UPDATED event""" meta = metadata or {} meta['session_id'] = session_id return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_IMAGE_UPDATED, entity_type='SessionImage', entity_id=image_id, triggered_by=triggered_by, metadata=meta ) async def publish_session_image_deleted(image_id: str, session_id: str, triggered_by=None): """Publish SESSION_IMAGE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_IMAGE_DELETED, entity_type='SessionImage', entity_id=image_id, triggered_by=triggered_by, metadata={'session_id': session_id} ) async def publish_session_video_uploaded(video_id: str, session_id: str, is_internal: bool, triggered_by=None): """Publish SESSION_VIDEO_UPLOADED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_VIDEO_UPLOADED, entity_type='SessionVideo', entity_id=video_id, triggered_by=triggered_by, metadata={'session_id': session_id, 'is_internal': is_internal} ) async def publish_session_video_updated(video_id: str, session_id: str, triggered_by=None, metadata=None): """Publish SESSION_VIDEO_UPDATED event""" meta = metadata or {} meta['session_id'] = session_id return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_VIDEO_UPDATED, entity_type='SessionVideo', entity_id=video_id, triggered_by=triggered_by, metadata=meta ) async def publish_session_video_deleted(video_id: str, session_id: str, triggered_by=None): """Publish SESSION_VIDEO_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_VIDEO_DELETED, entity_type='SessionVideo', entity_id=video_id, triggered_by=triggered_by, metadata={'session_id': session_id} ) async def publish_session_media_internal_flagged(media_id: str, media_type: str, session_id: str, triggered_by=None): """Publish SESSION_MEDIA_INTERNAL_FLAGGED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_MEDIA_INTERNAL_FLAGGED, entity_type=media_type, entity_id=media_id, triggered_by=triggered_by, metadata={'session_id': session_id} ) # Session Notes events async def publish_session_note_created(note_id: str, session_id: str, triggered_by=None): """Publish SESSION_NOTE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_NOTE_CREATED, entity_type='SessionNote', entity_id=note_id, triggered_by=triggered_by, metadata={'session_id': session_id} ) async def publish_session_note_updated(note_id: str, session_id: str, triggered_by=None): """Publish SESSION_NOTE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_NOTE_UPDATED, entity_type='SessionNote', entity_id=note_id, triggered_by=triggered_by, metadata={'session_id': session_id} ) async def publish_session_note_deleted(note_id: str, session_id: str, triggered_by=None): """Publish SESSION_NOTE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SESSION_NOTE_DELETED, entity_type='SessionNote', entity_id=note_id, triggered_by=triggered_by, metadata={'session_id': session_id} ) # Customer Address & Contact events async def publish_customer_address_created(address_id: str, customer_id: str, triggered_by=None): """Publish CUSTOMER_ADDRESS_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_ADDRESS_CREATED, entity_type='CustomerAddress', entity_id=address_id, triggered_by=triggered_by, metadata={'customer_id': customer_id} ) async def publish_customer_address_updated(address_id: str, customer_id: str, triggered_by=None): """Publish CUSTOMER_ADDRESS_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_ADDRESS_UPDATED, entity_type='CustomerAddress', entity_id=address_id, triggered_by=triggered_by, metadata={'customer_id': customer_id} ) async def publish_customer_address_deleted(address_id: str, customer_id: str, triggered_by=None): """Publish CUSTOMER_ADDRESS_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_ADDRESS_DELETED, entity_type='CustomerAddress', entity_id=address_id, triggered_by=triggered_by, metadata={'customer_id': customer_id} ) async def publish_customer_contact_created(contact_id: str, customer_id: str, triggered_by=None): """Publish CUSTOMER_CONTACT_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_CONTACT_CREATED, entity_type='CustomerContact', entity_id=contact_id, triggered_by=triggered_by, metadata={'customer_id': customer_id} ) async def publish_customer_contact_updated(contact_id: str, customer_id: str, triggered_by=None): """Publish CUSTOMER_CONTACT_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_CONTACT_UPDATED, entity_type='CustomerContact', entity_id=contact_id, triggered_by=triggered_by, metadata={'customer_id': customer_id} ) async def publish_customer_contact_deleted(contact_id: str, customer_id: str, triggered_by=None): """Publish CUSTOMER_CONTACT_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.CUSTOMER_CONTACT_DELETED, entity_type='CustomerContact', entity_id=contact_id, triggered_by=triggered_by, metadata={'customer_id': customer_id} ) # Account Address & Contact events async def publish_account_address_created(address_id: str, account_id: str, triggered_by=None): """Publish ACCOUNT_ADDRESS_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_ADDRESS_CREATED, entity_type='AccountAddress', entity_id=address_id, triggered_by=triggered_by, metadata={'account_id': account_id} ) async def publish_account_address_updated(address_id: str, account_id: str, triggered_by=None): """Publish ACCOUNT_ADDRESS_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_ADDRESS_UPDATED, entity_type='AccountAddress', entity_id=address_id, triggered_by=triggered_by, metadata={'account_id': account_id} ) async def publish_account_address_deleted(address_id: str, account_id: str, triggered_by=None): """Publish ACCOUNT_ADDRESS_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_ADDRESS_DELETED, entity_type='AccountAddress', entity_id=address_id, triggered_by=triggered_by, metadata={'account_id': account_id} ) async def publish_account_contact_created(contact_id: str, account_id: str, triggered_by=None): """Publish ACCOUNT_CONTACT_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_CONTACT_CREATED, entity_type='AccountContact', entity_id=contact_id, triggered_by=triggered_by, metadata={'account_id': account_id} ) async def publish_account_contact_updated(contact_id: str, account_id: str, triggered_by=None): """Publish ACCOUNT_CONTACT_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_CONTACT_UPDATED, entity_type='AccountContact', entity_id=contact_id, triggered_by=triggered_by, metadata={'account_id': account_id} ) async def publish_account_contact_deleted(contact_id: str, account_id: str, triggered_by=None): """Publish ACCOUNT_CONTACT_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.ACCOUNT_CONTACT_DELETED, entity_type='AccountContact', entity_id=contact_id, triggered_by=triggered_by, metadata={'account_id': account_id} ) # Scope events async def publish_scope_created(scope_id: str, account_id: str, triggered_by=None): """Publish SCOPE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCOPE_CREATED, entity_type='Scope', entity_id=scope_id, triggered_by=triggered_by, metadata={'account_id': account_id} ) async def publish_scope_updated(scope_id: str, account_id: str, triggered_by=None): """Publish SCOPE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCOPE_UPDATED, entity_type='Scope', entity_id=scope_id, triggered_by=triggered_by, metadata={'account_id': account_id} ) async def publish_scope_deleted(scope_id: str, account_id: str, triggered_by=None): """Publish SCOPE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCOPE_DELETED, entity_type='Scope', entity_id=scope_id, triggered_by=triggered_by, metadata={'account_id': account_id} ) async def publish_area_created(area_id: str, scope_id: str, triggered_by=None): """Publish AREA_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.AREA_CREATED, entity_type='Area', entity_id=area_id, triggered_by=triggered_by, metadata={'scope_id': scope_id} ) async def publish_area_updated(area_id: str, scope_id: str, triggered_by=None): """Publish AREA_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.AREA_UPDATED, entity_type='Area', entity_id=area_id, triggered_by=triggered_by, metadata={'scope_id': scope_id} ) async def publish_area_deleted(area_id: str, scope_id: str, triggered_by=None): """Publish AREA_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.AREA_DELETED, entity_type='Area', entity_id=area_id, triggered_by=triggered_by, metadata={'scope_id': scope_id} ) async def publish_task_created(task_id: str, area_id: str, triggered_by=None): """Publish TASK_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TASK_CREATED, entity_type='Task', entity_id=task_id, triggered_by=triggered_by, metadata={'area_id': area_id} ) async def publish_task_updated(task_id: str, area_id: str, triggered_by=None): """Publish TASK_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TASK_UPDATED, entity_type='Task', entity_id=task_id, triggered_by=triggered_by, metadata={'area_id': area_id} ) async def publish_task_deleted(task_id: str, area_id: str, triggered_by=None): """Publish TASK_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TASK_DELETED, entity_type='Task', entity_id=task_id, triggered_by=triggered_by, metadata={'area_id': area_id} ) async def publish_task_completion_recorded(completion_id: str, task_id: str, service_id: str, triggered_by=None): """Publish TASK_COMPLETION_RECORDED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TASK_COMPLETION_RECORDED, entity_type='TaskCompletion', entity_id=completion_id, triggered_by=triggered_by, metadata={'task_id': task_id, 'service_id': service_id} ) # Scope Template events async def publish_scope_template_created(template_id: str, triggered_by=None): """Publish SCOPE_TEMPLATE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCOPE_TEMPLATE_CREATED, entity_type='ScopeTemplate', entity_id=template_id, triggered_by=triggered_by, metadata={} ) async def publish_scope_template_updated(template_id: str, triggered_by=None): """Publish SCOPE_TEMPLATE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCOPE_TEMPLATE_UPDATED, entity_type='ScopeTemplate', entity_id=template_id, triggered_by=triggered_by, metadata={} ) async def publish_scope_template_deleted(template_id: str, triggered_by=None): """Publish SCOPE_TEMPLATE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCOPE_TEMPLATE_DELETED, entity_type='ScopeTemplate', entity_id=template_id, triggered_by=triggered_by, metadata={} ) async def publish_scope_template_instantiated(scope_id: str, template_id: str, account_id: str, triggered_by=None): """Publish SCOPE_TEMPLATE_INSTANTIATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.SCOPE_TEMPLATE_INSTANTIATED, entity_type='Scope', entity_id=scope_id, triggered_by=triggered_by, metadata={'template_id': template_id, 'account_id': account_id} ) async def publish_area_template_created(template_id: str, scope_template_id: str, triggered_by=None): """Publish AREA_TEMPLATE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.AREA_TEMPLATE_CREATED, entity_type='AreaTemplate', entity_id=template_id, triggered_by=triggered_by, metadata={'scope_template_id': scope_template_id} ) async def publish_area_template_updated(template_id: str, scope_template_id: str, triggered_by=None): """Publish AREA_TEMPLATE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.AREA_TEMPLATE_UPDATED, entity_type='AreaTemplate', entity_id=template_id, triggered_by=triggered_by, metadata={'scope_template_id': scope_template_id} ) async def publish_area_template_deleted(template_id: str, scope_template_id: str, triggered_by=None): """Publish AREA_TEMPLATE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.AREA_TEMPLATE_DELETED, entity_type='AreaTemplate', entity_id=template_id, triggered_by=triggered_by, metadata={'scope_template_id': scope_template_id} ) async def publish_task_template_created(template_id: str, area_template_id: str, triggered_by=None): """Publish TASK_TEMPLATE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TASK_TEMPLATE_CREATED, entity_type='TaskTemplate', entity_id=template_id, triggered_by=triggered_by, metadata={'area_template_id': area_template_id} ) async def publish_task_template_updated(template_id: str, area_template_id: str, triggered_by=None): """Publish TASK_TEMPLATE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TASK_TEMPLATE_UPDATED, entity_type='TaskTemplate', entity_id=template_id, triggered_by=triggered_by, metadata={'area_template_id': area_template_id} ) async def publish_task_template_deleted(template_id: str, area_template_id: str, triggered_by=None): """Publish TASK_TEMPLATE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.TASK_TEMPLATE_DELETED, entity_type='TaskTemplate', entity_id=template_id, triggered_by=triggered_by, metadata={'area_template_id': area_template_id} ) # Project Scope events async def publish_project_scope_created(scope_id: str, project_id: str, triggered_by=None): """Publish PROJECT_SCOPE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_CREATED, entity_type='ProjectScope', entity_id=scope_id, triggered_by=triggered_by, metadata={'project_id': project_id} ) async def publish_project_scope_updated(scope_id: str, project_id: str, triggered_by=None): """Publish PROJECT_SCOPE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_UPDATED, entity_type='ProjectScope', entity_id=scope_id, triggered_by=triggered_by, metadata={'project_id': project_id} ) async def publish_project_scope_deleted(scope_id: str, project_id: str, triggered_by=None): """Publish PROJECT_SCOPE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_DELETED, entity_type='ProjectScope', entity_id=scope_id, triggered_by=triggered_by, metadata={'project_id': project_id} ) async def publish_project_scope_category_created(category_id: str, scope_id: str, triggered_by=None): """Publish PROJECT_SCOPE_CATEGORY_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_CATEGORY_CREATED, entity_type='ProjectScopeCategory', entity_id=category_id, triggered_by=triggered_by, metadata={'scope_id': scope_id} ) async def publish_project_scope_category_updated(category_id: str, scope_id: str, triggered_by=None): """Publish PROJECT_SCOPE_CATEGORY_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_CATEGORY_UPDATED, entity_type='ProjectScopeCategory', entity_id=category_id, triggered_by=triggered_by, metadata={'scope_id': scope_id} ) async def publish_project_scope_category_deleted(category_id: str, scope_id: str, triggered_by=None): """Publish PROJECT_SCOPE_CATEGORY_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_CATEGORY_DELETED, entity_type='ProjectScopeCategory', entity_id=category_id, triggered_by=triggered_by, metadata={'scope_id': scope_id} ) async def publish_project_scope_task_created(task_id: str, category_id: str, triggered_by=None): """Publish PROJECT_SCOPE_TASK_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_TASK_CREATED, entity_type='ProjectScopeTask', entity_id=task_id, triggered_by=triggered_by, metadata={'category_id': category_id} ) async def publish_project_scope_task_updated(task_id: str, category_id: str, triggered_by=None): """Publish PROJECT_SCOPE_TASK_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_TASK_UPDATED, entity_type='ProjectScopeTask', entity_id=task_id, triggered_by=triggered_by, metadata={'category_id': category_id} ) async def publish_project_scope_task_deleted(task_id: str, category_id: str, triggered_by=None): """Publish PROJECT_SCOPE_TASK_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_TASK_DELETED, entity_type='ProjectScopeTask', entity_id=task_id, triggered_by=triggered_by, metadata={'category_id': category_id} ) async def publish_project_scope_template_instantiated(scope_id: str, template_id: str, project_id: str, triggered_by=None): """Publish PROJECT_SCOPE_TEMPLATE_INSTANTIATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.PROJECT_SCOPE_TEMPLATE_INSTANTIATED, entity_type='ProjectScope', entity_id=scope_id, triggered_by=triggered_by, metadata={'template_id': template_id, 'project_id': project_id} ) # Labor & Revenue events async def publish_labor_rate_created(rate_id: str, triggered_by=None): """Publish LABOR_RATE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.LABOR_RATE_CREATED, entity_type='LaborRate', entity_id=rate_id, triggered_by=triggered_by, metadata={} ) async def publish_labor_rate_updated(rate_id: str, triggered_by=None): """Publish LABOR_RATE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.LABOR_RATE_UPDATED, entity_type='LaborRate', entity_id=rate_id, triggered_by=triggered_by, metadata={} ) async def publish_labor_rate_deleted(rate_id: str, triggered_by=None): """Publish LABOR_RATE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.LABOR_RATE_DELETED, entity_type='LaborRate', entity_id=rate_id, triggered_by=triggered_by, metadata={} ) async def publish_revenue_rate_created(rate_id: str, triggered_by=None): """Publish REVENUE_RATE_CREATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.REVENUE_RATE_CREATED, entity_type='RevenueRate', entity_id=rate_id, triggered_by=triggered_by, metadata={} ) async def publish_revenue_rate_updated(rate_id: str, triggered_by=None): """Publish REVENUE_RATE_UPDATED event""" return await EventPublisher.publish( event_type=EventTypeChoices.REVENUE_RATE_UPDATED, entity_type='RevenueRate', entity_id=rate_id, triggered_by=triggered_by, metadata={} ) async def publish_revenue_rate_deleted(rate_id: str, triggered_by=None): """Publish REVENUE_RATE_DELETED event""" return await EventPublisher.publish( event_type=EventTypeChoices.REVENUE_RATE_DELETED, entity_type='RevenueRate', entity_id=rate_id, triggered_by=triggered_by, metadata={} )