Add the Sales CRM service through collaboration, sync architecture/registry docs, expose crm.torbatyar.ir, and include a production deploy script. Co-authored-by: Cursor <cursoragent@cursor.com>
502 lines
12 KiB
Python
502 lines
12 KiB
Python
"""Phase 6.3 collaboration DTOs."""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.models.types import (
|
|
ActivityPriority,
|
|
ActivityRelatedType,
|
|
ActivityStatus,
|
|
ActivityType,
|
|
CallDirection,
|
|
CollaborationEntityType,
|
|
EmailDeliveryStatus,
|
|
RecurrenceRule,
|
|
TeamRoleCode,
|
|
TimelineEventType,
|
|
)
|
|
from app.schemas.common import ORMBase
|
|
|
|
|
|
class SalesActivityCreate(BaseModel):
|
|
subject: str = Field(max_length=255)
|
|
activity_type: ActivityType
|
|
related_type: ActivityRelatedType | None = None
|
|
related_id: UUID | None = None
|
|
lead_id: UUID | None = None
|
|
contact_id: UUID | None = None
|
|
organization_id: UUID | None = None
|
|
opportunity_id: UUID | None = None
|
|
due_at: datetime | None = None
|
|
owner_user_id: str | None = Field(default=None, max_length=100)
|
|
description: str | None = None
|
|
status: ActivityStatus = ActivityStatus.PLANNED
|
|
priority: ActivityPriority = ActivityPriority.MEDIUM
|
|
custom_type_label: str | None = Field(default=None, max_length=100)
|
|
|
|
|
|
class SalesActivityUpdate(BaseModel):
|
|
subject: str | None = Field(default=None, max_length=255)
|
|
activity_type: ActivityType | None = None
|
|
related_type: ActivityRelatedType | None = None
|
|
related_id: UUID | None = None
|
|
lead_id: UUID | None = None
|
|
contact_id: UUID | None = None
|
|
organization_id: UUID | None = None
|
|
opportunity_id: UUID | None = None
|
|
due_at: datetime | None = None
|
|
owner_user_id: str | None = Field(default=None, max_length=100)
|
|
description: str | None = None
|
|
status: ActivityStatus | None = None
|
|
priority: ActivityPriority | None = None
|
|
custom_type_label: str | None = Field(default=None, max_length=100)
|
|
|
|
|
|
class SalesActivityRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
subject: str
|
|
activity_type: ActivityType
|
|
status: ActivityStatus
|
|
priority: ActivityPriority
|
|
related_type: ActivityRelatedType | None
|
|
related_id: UUID | None
|
|
lead_id: UUID | None
|
|
contact_id: UUID | None
|
|
organization_id: UUID | None
|
|
opportunity_id: UUID | None
|
|
due_at: datetime | None
|
|
completed_at: datetime | None
|
|
owner_user_id: str | None
|
|
description: str | None
|
|
custom_type_label: str | None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class LookupCreate(BaseModel):
|
|
code: str = Field(max_length=50)
|
|
name: str = Field(max_length=150)
|
|
is_active: bool = True
|
|
|
|
|
|
class ActivityStatusLookupCreate(LookupCreate):
|
|
is_terminal: bool = False
|
|
|
|
|
|
class ActivityPriorityLookupCreate(LookupCreate):
|
|
rank: int = 0
|
|
|
|
|
|
class LookupRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
code: str
|
|
name: str
|
|
is_active: bool
|
|
created_at: datetime
|
|
|
|
|
|
class ParticipantCreate(BaseModel):
|
|
activity_id: UUID
|
|
user_id: str | None = None
|
|
contact_id: UUID | None = None
|
|
role_label: str | None = None
|
|
is_required: bool = False
|
|
|
|
|
|
class ParticipantRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
activity_id: UUID
|
|
user_id: str | None
|
|
contact_id: UUID | None
|
|
role_label: str | None
|
|
is_required: bool
|
|
created_at: datetime
|
|
|
|
|
|
class ReminderCreate(BaseModel):
|
|
activity_id: UUID
|
|
remind_at: datetime
|
|
channel: str = "in_app"
|
|
|
|
|
|
class ReminderRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
activity_id: UUID
|
|
remind_at: datetime
|
|
channel: str
|
|
is_sent: bool
|
|
created_at: datetime
|
|
|
|
|
|
class TaskCreate(BaseModel):
|
|
title: str = Field(max_length=255)
|
|
description: str | None = None
|
|
owner_user_id: str | None = None
|
|
assignee_user_id: str | None = None
|
|
priority: str = "medium"
|
|
due_at: datetime | None = None
|
|
recurrence: RecurrenceRule = RecurrenceRule.NONE
|
|
opportunity_id: UUID | None = None
|
|
lead_id: UUID | None = None
|
|
contact_id: UUID | None = None
|
|
organization_id: UUID | None = None
|
|
activity_id: UUID | None = None
|
|
|
|
|
|
class TaskRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
activity_id: UUID | None
|
|
title: str
|
|
description: str | None
|
|
owner_user_id: str | None
|
|
assignee_user_id: str | None
|
|
status: str
|
|
priority: str
|
|
due_at: datetime | None
|
|
completed_at: datetime | None
|
|
recurrence: RecurrenceRule
|
|
opportunity_id: UUID | None
|
|
lead_id: UUID | None
|
|
contact_id: UUID | None
|
|
organization_id: UUID | None
|
|
completion_percent: int
|
|
is_deleted: bool
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ChecklistItemCreate(BaseModel):
|
|
task_id: UUID
|
|
title: str = Field(max_length=255)
|
|
position: int = 0
|
|
|
|
|
|
class ChecklistItemRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
task_id: UUID
|
|
title: str
|
|
position: int
|
|
is_completed: bool
|
|
completed_at: datetime | None
|
|
created_at: datetime
|
|
|
|
|
|
class MeetingCreate(BaseModel):
|
|
subject: str = Field(max_length=255)
|
|
start_at: datetime
|
|
end_at: datetime
|
|
location: str | None = None
|
|
online_meeting_url: str | None = None
|
|
agenda: str | None = None
|
|
notes: str | None = None
|
|
owner_user_id: str | None = None
|
|
opportunity_id: UUID | None = None
|
|
lead_id: UUID | None = None
|
|
contact_id: UUID | None = None
|
|
organization_id: UUID | None = None
|
|
activity_id: UUID | None = None
|
|
|
|
|
|
class MeetingFinishRequest(BaseModel):
|
|
outcome: str | None = None
|
|
notes: str | None = None
|
|
|
|
|
|
class MeetingRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
activity_id: UUID | None
|
|
subject: str
|
|
start_at: datetime
|
|
end_at: datetime
|
|
location: str | None
|
|
online_meeting_url: str | None
|
|
agenda: str | None
|
|
notes: str | None
|
|
outcome: str | None
|
|
owner_user_id: str | None
|
|
opportunity_id: UUID | None
|
|
lead_id: UUID | None
|
|
contact_id: UUID | None
|
|
organization_id: UUID | None
|
|
is_finished: bool
|
|
finished_at: datetime | None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class MeetingAttendeeCreate(BaseModel):
|
|
meeting_id: UUID
|
|
user_id: str | None = None
|
|
contact_id: UUID | None = None
|
|
email: str | None = None
|
|
display_name: str | None = None
|
|
|
|
|
|
class MeetingAttendeeRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
meeting_id: UUID
|
|
user_id: str | None
|
|
contact_id: UUID | None
|
|
email: str | None
|
|
display_name: str | None
|
|
response_status: str
|
|
created_at: datetime
|
|
|
|
|
|
class CallLogCreate(BaseModel):
|
|
direction: CallDirection
|
|
opportunity_id: UUID
|
|
subject: str | None = None
|
|
duration_seconds: int = 0
|
|
outcome_id: UUID | None = None
|
|
outcome_label: str | None = None
|
|
recording_ref: str | None = None
|
|
notes: str | None = None
|
|
owner_user_id: str | None = None
|
|
contact_id: UUID | None = None
|
|
lead_id: UUID | None = None
|
|
organization_id: UUID | None = None
|
|
called_at: datetime | None = None
|
|
activity_id: UUID | None = None
|
|
|
|
|
|
class CallLogRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
activity_id: UUID | None
|
|
direction: CallDirection
|
|
subject: str | None
|
|
duration_seconds: int
|
|
outcome_id: UUID | None
|
|
outcome_label: str | None
|
|
recording_ref: str | None
|
|
notes: str | None
|
|
owner_user_id: str | None
|
|
opportunity_id: UUID | None
|
|
contact_id: UUID | None
|
|
lead_id: UUID | None
|
|
organization_id: UUID | None
|
|
called_at: datetime
|
|
created_at: datetime
|
|
|
|
|
|
class EmailThreadCreate(BaseModel):
|
|
subject: str = Field(max_length=255)
|
|
opportunity_id: UUID | None = None
|
|
contact_id: UUID | None = None
|
|
organization_id: UUID | None = None
|
|
lead_id: UUID | None = None
|
|
|
|
|
|
class EmailThreadRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
subject: str
|
|
opportunity_id: UUID | None
|
|
contact_id: UUID | None
|
|
organization_id: UUID | None
|
|
lead_id: UUID | None
|
|
last_message_at: datetime | None
|
|
created_at: datetime
|
|
|
|
|
|
class EmailMessageCreate(BaseModel):
|
|
thread_id: UUID
|
|
subject: str = Field(max_length=255)
|
|
status: EmailDeliveryStatus = EmailDeliveryStatus.SENT
|
|
opportunity_id: UUID | None = None
|
|
notification_ref: str | None = None
|
|
opened: bool = False
|
|
clicked: bool = False
|
|
|
|
|
|
class EmailMessageRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
thread_id: UUID
|
|
subject: str
|
|
status: EmailDeliveryStatus
|
|
opened: bool
|
|
clicked: bool
|
|
opportunity_id: UUID | None
|
|
notification_ref: str | None
|
|
sent_at: datetime | None
|
|
created_at: datetime
|
|
|
|
|
|
class TimelineEventRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
event_type: TimelineEventType
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
related_opportunity_id: UUID | None
|
|
related_lead_id: UUID | None
|
|
related_contact_id: UUID | None
|
|
related_organization_id: UUID | None
|
|
actor_user_id: str | None
|
|
title: str
|
|
payload: dict | None
|
|
occurred_at: datetime
|
|
is_immutable: bool
|
|
created_at: datetime
|
|
|
|
|
|
class CommentCreate(BaseModel):
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
body: str
|
|
parent_comment_id: UUID | None = None
|
|
body_format: str = "richtext"
|
|
is_pinned: bool = False
|
|
mention_user_ids: list[str] = Field(default_factory=list)
|
|
|
|
|
|
class CommentUpdate(BaseModel):
|
|
body: str | None = None
|
|
is_pinned: bool | None = None
|
|
|
|
|
|
class CommentRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
parent_comment_id: UUID | None
|
|
body: str
|
|
body_format: str
|
|
version: int
|
|
is_pinned: bool
|
|
is_deleted: bool
|
|
created_by: str | None
|
|
updated_by: str | None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class MentionRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
comment_id: UUID | None
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
mentioned_user_id: str
|
|
mentioned_by: str | None
|
|
is_read: bool
|
|
created_at: datetime
|
|
|
|
|
|
class BookmarkCreate(BaseModel):
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
label: str | None = None
|
|
|
|
|
|
class BookmarkRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
user_id: str
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
label: str | None
|
|
created_at: datetime
|
|
|
|
|
|
class FavoriteCreate(BaseModel):
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
|
|
|
|
class FavoriteRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
user_id: str
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
created_at: datetime
|
|
|
|
|
|
class TeamRoleCreate(BaseModel):
|
|
code: str = Field(max_length=50)
|
|
name: str = Field(max_length=150)
|
|
role_kind: TeamRoleCode = TeamRoleCode.CUSTOM
|
|
is_active: bool = True
|
|
|
|
|
|
class TeamRoleRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
code: str
|
|
name: str
|
|
role_kind: TeamRoleCode
|
|
is_active: bool
|
|
created_at: datetime
|
|
|
|
|
|
class TeamMemberCreate(BaseModel):
|
|
user_id: str = Field(max_length=100)
|
|
team_key: str = Field(max_length=100)
|
|
display_name: str | None = None
|
|
role_id: UUID | None = None
|
|
role_code: TeamRoleCode = TeamRoleCode.SALESPERSON
|
|
is_active: bool = True
|
|
|
|
|
|
class TeamMemberRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
user_id: str
|
|
display_name: str | None
|
|
team_key: str
|
|
role_id: UUID | None
|
|
role_code: TeamRoleCode
|
|
is_active: bool
|
|
created_at: datetime
|
|
|
|
|
|
class TeamAssignmentCreate(BaseModel):
|
|
team_key: str
|
|
member_id: UUID
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
|
|
|
|
class TeamAssignmentRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
team_key: str
|
|
member_id: UUID
|
|
entity_type: CollaborationEntityType
|
|
entity_id: UUID
|
|
assigned_by: str | None
|
|
created_at: datetime
|
|
|
|
|
|
class NotificationPreferenceUpsert(BaseModel):
|
|
mentions_enabled: bool = True
|
|
task_reminders_enabled: bool = True
|
|
meeting_reminders_enabled: bool = True
|
|
timeline_digest_enabled: bool = False
|
|
|
|
|
|
class NotificationPreferenceRead(ORMBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
user_id: str
|
|
mentions_enabled: bool
|
|
task_reminders_enabled: bool
|
|
meeting_reminders_enabled: bool
|
|
timeline_digest_enabled: bool
|
|
created_at: datetime
|