Initial commit - production deployment

This commit is contained in:
2026-01-21 17:17:16 +01:00
commit c23d00dd92
2289 changed files with 638440 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
"""
Notification Service Models Package
Import all models to ensure they are registered with SQLAlchemy Base.
"""
# Import AuditLog model for this service
from shared.security import create_audit_log_model
from shared.database.base import Base
# Create audit log model for this service
AuditLog = create_audit_log_model(Base)
# Import all models to register them with the Base metadata
from .notifications import (
Notification,
NotificationTemplate,
NotificationType,
NotificationStatus,
NotificationPriority,
NotificationPreference,
NotificationLog,
)
from .templates import (
EmailTemplate,
)
from .whatsapp_messages import (
WhatsAppTemplate,
WhatsAppMessage,
WhatsAppMessageStatus,
WhatsAppMessageType,
)
# List all models for easier access
__all__ = [
"Notification",
"NotificationTemplate",
"NotificationType",
"NotificationStatus",
"NotificationPriority",
"NotificationPreference",
"NotificationLog",
"EmailTemplate",
"WhatsAppTemplate",
"WhatsAppMessage",
"WhatsAppMessageStatus",
"WhatsAppMessageType",
"AuditLog",
]

View File

@@ -0,0 +1,184 @@
# ================================================================
# services/notification/app/models/notifications.py
# ================================================================
"""
Notification models for the notification service
"""
from sqlalchemy import Column, String, Text, Boolean, DateTime, JSON, Integer, Enum
from sqlalchemy.dialects.postgresql import UUID
from datetime import datetime
import uuid
import enum
from shared.database.base import Base
class NotificationType(enum.Enum):
"""Notification types supported by the service"""
EMAIL = "email"
WHATSAPP = "whatsapp"
PUSH = "push"
SMS = "sms"
class NotificationStatus(enum.Enum):
"""Notification delivery status"""
PENDING = "pending"
SENT = "sent"
DELIVERED = "delivered"
FAILED = "failed"
CANCELLED = "cancelled"
class NotificationPriority(enum.Enum):
"""Notification priority levels"""
LOW = "low"
NORMAL = "normal"
HIGH = "high"
URGENT = "urgent"
class Notification(Base):
"""Main notification record"""
__tablename__ = "notifications"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
sender_id = Column(UUID(as_uuid=True), nullable=False)
recipient_id = Column(UUID(as_uuid=True), nullable=True) # Null for broadcast
# Notification details
type = Column(Enum(NotificationType), nullable=False)
status = Column(Enum(NotificationStatus), default=NotificationStatus.PENDING, index=True)
priority = Column(Enum(NotificationPriority), default=NotificationPriority.NORMAL)
# Content
subject = Column(String(255), nullable=True)
message = Column(Text, nullable=False)
html_content = Column(Text, nullable=True)
template_id = Column(String(100), nullable=True)
template_data = Column(JSON, nullable=True)
# Delivery details
recipient_email = Column(String(255), nullable=True)
recipient_phone = Column(String(20), nullable=True)
delivery_channel = Column(String(50), nullable=True)
# Scheduling
scheduled_at = Column(DateTime, nullable=True)
sent_at = Column(DateTime, nullable=True)
delivered_at = Column(DateTime, nullable=True)
# Metadata
log_metadata = Column(JSON, nullable=True)
error_message = Column(Text, nullable=True)
retry_count = Column(Integer, default=0)
max_retries = Column(Integer, default=3)
# Tracking
broadcast = Column(Boolean, default=False)
read = Column(Boolean, default=False)
read_at = Column(DateTime, nullable=True)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, index=True)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class NotificationTemplate(Base):
"""Email and notification templates"""
__tablename__ = "notification_templates"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=True, index=True) # Null for system templates
# Template identification
template_key = Column(String(100), nullable=False, unique=True)
name = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
category = Column(String(50), nullable=False) # alert, marketing, transactional
# Template content
type = Column(Enum(NotificationType), nullable=False)
subject_template = Column(String(255), nullable=True)
body_template = Column(Text, nullable=False)
html_template = Column(Text, nullable=True)
# Configuration
language = Column(String(2), default="es")
is_active = Column(Boolean, default=True)
is_system = Column(Boolean, default=False) # System templates can't be deleted
# Metadata
default_priority = Column(Enum(NotificationPriority), default=NotificationPriority.NORMAL)
required_variables = Column(JSON, nullable=True) # List of required template variables
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class NotificationPreference(Base):
"""User notification preferences"""
__tablename__ = "notification_preferences"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, unique=True, index=True)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Email preferences
email_enabled = Column(Boolean, default=True)
email_alerts = Column(Boolean, default=True)
email_marketing = Column(Boolean, default=False)
email_reports = Column(Boolean, default=True)
# WhatsApp preferences
whatsapp_enabled = Column(Boolean, default=False)
whatsapp_alerts = Column(Boolean, default=False)
whatsapp_reports = Column(Boolean, default=False)
# Push notification preferences
push_enabled = Column(Boolean, default=True)
push_alerts = Column(Boolean, default=True)
push_reports = Column(Boolean, default=False)
# Timing preferences
quiet_hours_start = Column(String(5), default="22:00") # HH:MM format
quiet_hours_end = Column(String(5), default="08:00")
timezone = Column(String(50), default="Europe/Madrid")
# Frequency preferences
digest_frequency = Column(String(20), default="daily") # none, daily, weekly
max_emails_per_day = Column(Integer, default=10)
# Language preference
language = Column(String(2), default="es")
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class NotificationLog(Base):
"""Detailed logging for notification delivery attempts"""
__tablename__ = "notification_logs"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
notification_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Attempt details
attempt_number = Column(Integer, nullable=False)
status = Column(Enum(NotificationStatus), nullable=False)
# Provider details
provider = Column(String(50), nullable=True) # e.g., "gmail", "twilio"
provider_message_id = Column(String(255), nullable=True)
provider_response = Column(JSON, nullable=True)
# Timing
attempted_at = Column(DateTime, default=datetime.utcnow)
response_time_ms = Column(Integer, nullable=True)
# Error details
error_code = Column(String(50), nullable=True)
error_message = Column(Text, nullable=True)
# Additional metadata
log_metadata = Column(JSON, nullable=True)

View File

@@ -0,0 +1,84 @@
# ================================================================
# services/notification/app/models/templates.py
# ================================================================
"""
Template-specific models for email and WhatsApp templates
"""
from sqlalchemy import Column, String, Text, Boolean, DateTime, JSON, Integer
from sqlalchemy.dialects.postgresql import UUID
from datetime import datetime
import uuid
from shared.database.base import Base
class EmailTemplate(Base):
"""Email-specific templates with HTML support"""
__tablename__ = "email_templates"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=True, index=True)
# Template identification
template_key = Column(String(100), nullable=False, unique=True)
name = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
# Email-specific content
subject = Column(String(255), nullable=False)
html_body = Column(Text, nullable=False)
text_body = Column(Text, nullable=True) # Plain text fallback
# Email settings
from_email = Column(String(255), nullable=True)
from_name = Column(String(255), nullable=True)
reply_to = Column(String(255), nullable=True)
# Template variables
variables = Column(JSON, nullable=True) # Expected variables and their types
sample_data = Column(JSON, nullable=True) # Sample data for preview
# Configuration
language = Column(String(2), default="es")
is_active = Column(Boolean, default=True)
is_system = Column(Boolean, default=False)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# NOTE: WhatsAppTemplate has been moved to app/models/whatsapp_messages.py
# This old definition is commented out to avoid duplicate table definition errors
# class WhatsAppTemplate(Base):
# """WhatsApp-specific templates"""
# __tablename__ = "whatsapp_templates"
#
# id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
# tenant_id = Column(UUID(as_uuid=True), nullable=True, index=True)
#
# # Template identification
# template_key = Column(String(100), nullable=False, unique=True)
# name = Column(String(255), nullable=False)
#
# # WhatsApp template details
# whatsapp_template_name = Column(String(255), nullable=False) # Template name in WhatsApp Business API
# whatsapp_template_id = Column(String(255), nullable=True)
# language_code = Column(String(10), default="es")
#
# # Template content
# header_text = Column(String(60), nullable=True) # WhatsApp header limit
# body_text = Column(Text, nullable=False)
# footer_text = Column(String(60), nullable=True) # WhatsApp footer limit
#
# # Template parameters
# parameter_count = Column(Integer, default=0)
# parameters = Column(JSON, nullable=True) # Parameter definitions
#
# # Status
# approval_status = Column(String(20), default="pending") # pending, approved, rejected
# is_active = Column(Boolean, default=True)
#
# # Timestamps
# created_at = Column(DateTime, default=datetime.utcnow)
# updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

View File

@@ -0,0 +1,135 @@
# ================================================================
# services/notification/app/models/whatsapp_messages.py
# ================================================================
"""
WhatsApp message tracking models for WhatsApp Business Cloud API
"""
from sqlalchemy import Column, String, Text, Boolean, DateTime, JSON, Enum, Integer
from sqlalchemy.dialects.postgresql import UUID
from datetime import datetime
import uuid
import enum
from shared.database.base import Base
class WhatsAppMessageStatus(enum.Enum):
"""WhatsApp message delivery status"""
PENDING = "pending"
SENT = "sent"
DELIVERED = "delivered"
READ = "read"
FAILED = "failed"
class WhatsAppMessageType(enum.Enum):
"""WhatsApp message types"""
TEMPLATE = "template"
TEXT = "text"
IMAGE = "image"
DOCUMENT = "document"
INTERACTIVE = "interactive"
class WhatsAppMessage(Base):
"""Track WhatsApp messages sent via Cloud API"""
__tablename__ = "whatsapp_messages"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
notification_id = Column(UUID(as_uuid=True), nullable=True, index=True) # Link to notification if exists
# Message identification
whatsapp_message_id = Column(String(255), nullable=True, index=True) # WhatsApp's message ID
# Recipient details
recipient_phone = Column(String(20), nullable=False, index=True) # E.164 format
recipient_name = Column(String(255), nullable=True)
# Message details
message_type = Column(Enum(WhatsAppMessageType), nullable=False)
status = Column(Enum(WhatsAppMessageStatus), default=WhatsAppMessageStatus.PENDING, index=True)
# Template details (for template messages)
template_name = Column(String(255), nullable=True)
template_language = Column(String(10), default="es")
template_parameters = Column(JSON, nullable=True) # Template variable values
# Message content (for non-template messages)
message_body = Column(Text, nullable=True)
media_url = Column(String(512), nullable=True)
# Delivery tracking
sent_at = Column(DateTime, nullable=True)
delivered_at = Column(DateTime, nullable=True)
read_at = Column(DateTime, nullable=True)
failed_at = Column(DateTime, nullable=True)
# Error tracking
error_code = Column(String(50), nullable=True)
error_message = Column(Text, nullable=True)
# Provider response
provider_response = Column(JSON, nullable=True)
# Additional data (renamed from metadata to avoid SQLAlchemy reserved word)
additional_data = Column(JSON, nullable=True) # Additional context (PO number, order ID, etc.)
# Conversation tracking
conversation_id = Column(String(255), nullable=True, index=True) # WhatsApp conversation ID
conversation_category = Column(String(50), nullable=True) # business_initiated, user_initiated
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, index=True)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class WhatsAppTemplate(Base):
"""Store WhatsApp message templates metadata"""
__tablename__ = "whatsapp_templates"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=True, index=True) # Null for system templates
# Template identification
template_name = Column(String(255), nullable=False, index=True) # Name in WhatsApp
template_key = Column(String(100), nullable=False, unique=True) # Internal key
display_name = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
category = Column(String(50), nullable=False) # MARKETING, UTILITY, AUTHENTICATION
# Template configuration
language = Column(String(10), default="es")
status = Column(String(20), default="PENDING") # PENDING, APPROVED, REJECTED
# Template structure
header_type = Column(String(20), nullable=True) # TEXT, IMAGE, DOCUMENT, VIDEO
header_text = Column(String(60), nullable=True)
body_text = Column(Text, nullable=False)
footer_text = Column(String(60), nullable=True)
# Parameters
parameters = Column(JSON, nullable=True) # List of parameter definitions
parameter_count = Column(Integer, default=0)
# Buttons (for interactive templates)
buttons = Column(JSON, nullable=True)
# Metadata
is_active = Column(Boolean, default=True)
is_system = Column(Boolean, default=False)
# Usage tracking
sent_count = Column(Integer, default=0)
last_used_at = Column(DateTime, nullable=True)
# WhatsApp metadata
whatsapp_template_id = Column(String(255), nullable=True)
approved_at = Column(DateTime, nullable=True)
rejected_at = Column(DateTime, nullable=True)
rejection_reason = Column(Text, nullable=True)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)