Skip to content

Activities

Activities track interactions and work items related to entities and deals. The CRM supports five activity types: tasks, events, emails, notes, and calls.

Base Activity Fields

All activity types share these common fields:

Field Type Default Description
activity_type ActivityType required Discriminator: task, event, email, note, call
subject str required Activity subject/title
description str \| None None Detailed description
related_to_type ActivityRelatedTo \| None required Related record type: Entity or Deal
related_to_id str \| None required ID of the related record
assigned_to User \| None None Assigned user (FK)
due_date datetime \| None None Due date
completed_at datetime \| None None Completion timestamp
is_completed bool False Whether the activity is completed
created_at / updated_at datetime auto Timestamps

The ActivityManager automatically calls select_related('assigned_to') on all queries.

Polymorphic Linking

Activities link to entities or deals using a generic foreign key pattern:

  • related_to_type — an ActivityRelatedTo enum with values ENTITY ('Entity') and DEAL ('Deal')
  • related_to_id — the _object_id of the related record

Activity Types

Task

Tasks represent work items with priority and status tracking.

Field Type Default Description
priority Literal['low', 'medium', 'high'] 'medium' Task priority
status Literal['not_started', 'in_progress', 'waiting', 'completed'] 'not_started' Task status
from amsdal_crm.models.activity import Task, ActivityType, ActivityRelatedTo

task = Task(
    activity_type=ActivityType.TASK,
    subject='Follow up with client',
    related_to_type=ActivityRelatedTo.ENTITY,
    related_to_id=entity._object_id,
    priority='high',
    status='not_started',
)
task.save(force_insert=True)

Event

Events represent meetings or calendar items.

Field Type Default Description
start_time datetime required Event start time
end_time datetime required Event end time
location str \| None None Event location
from datetime import datetime, timezone
from amsdal_crm.models.activity import Event, ActivityType, ActivityRelatedTo

event = Event(
    activity_type=ActivityType.EVENT,
    subject='Quarterly review meeting',
    related_to_type=ActivityRelatedTo.DEAL,
    related_to_id=deal._object_id,
    start_time=datetime(2025, 3, 15, 14, 0, tzinfo=timezone.utc),
    end_time=datetime(2025, 3, 15, 15, 0, tzinfo=timezone.utc),
    location='Conference Room A',
)
event.save(force_insert=True)

EmailActivity

Email records linked to CRM entities or deals.

Field Type Default Description
from_address str required Sender email address
to_addresses list[str] required Recipient email addresses
cc_addresses list[str] \| None None CC email addresses
body str required Email body
is_outbound bool True True if sent, False if received

Note

Notes are simple text records with no additional fields beyond the base activity fields.

from amsdal_crm.models.activity import Note, ActivityType, ActivityRelatedTo

note = Note(
    activity_type=ActivityType.NOTE,
    subject='Client prefers email communication',
    description='Discussed communication preferences during onboarding call.',
    related_to_type=ActivityRelatedTo.ENTITY,
    related_to_id=entity._object_id,
)
note.save(force_insert=True)

Call

Phone call records.

Field Type Default Description
phone_number str required Phone number
duration_seconds int \| None None Call duration in seconds
call_outcome str \| None None Outcome description

ActivityService

get_timeline

Returns a chronological activity feed for a record, sorted by created_at descending (newest first).

from amsdal_crm.services.activity_service import ActivityService
from amsdal_crm.models.activity import ActivityRelatedTo

# Get timeline for an entity
timeline = ActivityService.get_timeline(
    related_to_type=ActivityRelatedTo.ENTITY,
    related_to_id=entity._object_id,
    limit=50,
)

# Async version
timeline = await ActivityService.aget_timeline(
    related_to_type=ActivityRelatedTo.ENTITY,
    related_to_id=entity._object_id,
    limit=50,
)

The default limit is 100, configurable via the AMSDAL_CRM_DEFAULT_ACTIVITY_TIMELINE_LIMIT setting.

EmailService

log_email

Logs an email as an EmailActivity record, running inside a transaction.

from amsdal_crm.services.email_service import EmailService
from amsdal_crm.models.activity import ActivityRelatedTo

email = EmailService.log_email(
    subject='Proposal for Q2',
    body='Please find the attached proposal...',
    from_address='sales@example.com',
    to_addresses=['client@acme.com'],
    cc_addresses=['manager@example.com'],
    related_to_type=ActivityRelatedTo.DEAL,
    related_to_id=deal._object_id,
    is_outbound=True,
)

# Async version
email = await EmailService.alog_email(...)

Attachments

Attachments link files to entities, deals, or activities using the same polymorphic pattern.

Field Type Default Description
file File required AMSDAL File reference
related_to_type Literal['Entity', 'Deal', 'Activity'] required Related record type
related_to_id str required Related record ID
uploaded_by str required Uploader's email address
uploaded_at datetime auto (UTC now) Upload timestamp
description str \| None None File description
from amsdal_crm.models.attachment import Attachment

attachment = Attachment(
    file=my_file,
    related_to_type='Entity',
    related_to_id=entity._object_id,
    uploaded_by='user@example.com',
    description='Signed contract',
)
attachment.save(force_insert=True)

Permissions

Activities use the same owner-based permission model as entities:

  • The user in assigned_to has full access
  • Users with the super_admin scope have full access