CRM Domain Model
This document explains the current CRM concepts and the naming decisions.
Main Concepts
Current model hierarchy:
Organization
-> OrganizationMembership -> User
-> OrganizationSettings
-> EmailAccount
-> OrganizationInvitation
-> Company
-> Contact
-> Deal
-> Task
-> Automation
-> AutomationRun
User
-> UserProfileOrganization
Organization is our own customer's workspace or tenant.
An organization represents one isolated customer workspace in Bubllio CRM.
An organization owns its CRM data:
- companies
- contacts
- deals and sales pipeline
- tasks and follow-ups
- automations
- users and role-based permissions through memberships
- pending invitations to users who are not members yet
- organization settings and encrypted SMTP email accounts
Organization settings also hold the workspace's business identity, regional preferences, and ERP defaults. Business identity includes legal/trading names, tax and registration references, contact details, and postal address. ERP defaults currently prepare currency, fiscal-year start, tax rate, payment terms, and document numbering; they do not create accounting documents by themselves.
Users may opt into one private personal workspace through the workspace
picker. It is a normal tenant for the user's own companies, contacts and
automations, but it is marked is_personal and has a single owner. Personal
workspaces cannot be deleted, invited to, or given additional members.
Team/shared workspaces remain normal organizations: users see them after an
accepted invitation or when they create one for themselves. A workspace
awaiting its nominated owner is hidden from normal CRM endpoints. Membership
roles control access after provisioning.
Company
Company is an external business stored inside the CRM.
A company is an external business that the workspace sells to or works with.
We chose Company instead of Customer because not every company is already a customer.
A company may be:
- lead
- prospect
- customer
- inactive
That status lives in:
Company.lifecycle_stageThe company record also stores the business profile needed during sales and customer servicing: tax/VAT reference, industry, contact channels, postal address, country code, and internal notes. These fields belong to the owning organization and are included in tenant-scoped search.
Each company receives an organization-scoped customer code (CUS-00001, then
CUS-00002, and so on) and may be assigned to one workspace member as its
account owner. Tax/VAT IDs and email addresses are checked case-insensitively
for exact duplicates inside the same organization.
Customer-facing company URLs use this shorter code as their public reference. The API still accepts the original UUID in detail and action routes so existing bookmarks and integrations remain compatible.
Companies are archived and restored for normal lifecycle management. Active lists hide archived records by default, while explicit filters can show archived or all records. Permanent deletion remains an API-level operation for empty records only; a company with contacts cannot be deleted.
CompanyActivity records who created, updated, assigned, archived, or restored
a company. It also records when a contact is added. Activity entries store safe
metadata such as changed field names rather than previous field values.
Contact
Contact is a person inside a company.
A contact is a person who works at one of the stored companies.
Contacts belong to:
- one organization
- one company
The serializer validates that the selected company belongs to the selected organization. Contact email addresses are checked case-insensitively for exact duplicates inside the same organization. Contacts can be searched by name, communication details, department, or job title and filtered by their company. They also track an optional workspace owner, active/former status, one active primary contact per company, archival state, and a tenant-scoped activity log. See the business contact guide for the non-technical workflow.
Deal
Deal is a tenant-owned sales opportunity connected to one company and
optionally one contact and workspace owner. It moves through lead, qualified,
proposal, negotiation, won, or lost stages and stores value, currency,
probability, and expected close date. The entered amount records whether it is
net or VAT-inclusive; the API derives net value, VAT amount, and gross value
without mixing tax into revenue forecasts. Deals also keep a stable position
inside each pipeline stage so drag-and-drop ordering persists. Cross-tenant
company, contact, and owner references are rejected. See the
business pipeline guide.
Why Not Use Customer As The Main Model?
Customer sounds like someone who already bought something.
In CRM systems, we often need to store companies before they become customers.
Better naming:
Company = the external business
Contact = a person at that business
Company.lifecycle_stage = whether the company is a lead, prospect, customer, or inactiveThis lets us model the full CRM lifecycle without renaming things later.
Future Account Concept
We discussed Account, but for now Company carries that role.
Later, if the product needs more advanced account management, we can introduce an Account concept carefully. For now, adding it too early would create naming confusion.
Current Relationships
Organization 1 -> many Companies
Organization 1 -> many Contacts
Organization 1 -> many Deals
Organization 1 -> many Tasks
Organization 1 -> many ServiceCatalogItems
Organization 1 -> many CustomerSubscriptions
Organization 1 -> many Charges
Organization 1 -> many Payments
Organization 1 -> many OrganizationMemberships
User 1 -> many OrganizationMemberships
Company 1 -> many Contacts
Company 1 -> many CompanyActivities
Company 1 -> many Deals
Company 1 -> many Tasks
Company 1 -> many CustomerSubscriptions
User 1 -> many assigned Companies
Organization 1 -> many Automations
Automation 1 -> many AutomationRuns
Organization 1 -> 1 OrganizationSettings
Organization 1 -> many EmailAccounts
User 1 -> 0 or 1 personal Organization
CustomerSubscription 1 -> many Charges
Charge 1 -> many PaymentsTasks and next actions
Task is an organization-owned follow-up anchored to one company and optionally
linked to a contact or deal from that same company. It records a task, call,
email or meeting with an owner, priority, due time, optional reminder time and
notes. Organization ownership always comes from the authorized URL; related
records and assignees are validated against that organization.
Workflow is stored separately as todo, in_progress, waiting or
completed. Completion is recorded with completed_at and completed_by.
Moving to completed fills that audit metadata; moving out of it clears the
metadata. All move, complete and reopen operations use the same row-locking
service so these values cannot drift apart. Overdue remains a derived status
for an incomplete task whose due time has passed, not a mutable database flag.
The earliest incomplete task linked to a deal is its next action in the
frontend. Reminder delivery and the unified customer timeline are not yet
implemented.
Services and billing
ServiceCatalogItem stores reusable commercial defaults such as a service name,
net price, VAT rate and billing cycle. CustomerSubscription is the actual
agreement with one company and keeps a copy of those values so later catalog
changes cannot rewrite customer history.
Creating a subscription creates its first Charge. A charge snapshots net,
VAT and gross amounts for one coverage period. One or more Payment records may
settle it. The payment status is derived from the received and outstanding
amounts; it is not a manually maintained boolean.
When an active recurring subscription with auto-renew enabled is fully paid,
the service creates exactly one next charge. The database uniqueness constraint
on subscription and due date keeps this operation idempotent. Pausing or
cancelling a subscription does not delete its charges or payments. A normal
cancellation enters cancelling state through the current charge's coverage
end, cancels only later open charges, and prevents payment from generating
another charge. It may be resumed before that effective date without duplicating
the next charge. Immediate cancellation remains an explicit exceptional action.
Business Logic
Business logic means rules that belong to the product domain, not just HTTP.
Examples:
- A contact's company must belong to the same organization.
- A company creation can trigger automations.
- An automation only runs inside its own organization.
- An email account may only be accessed through its organization's authorized settings endpoints; automation sending is not connected to these accounts yet.
- An authenticated user may access an organization only through a membership and the capabilities granted by its role.
See docs/architecture/authentication-and-roles.md for the complete tenant and
role rules.
Where business logic should live:
serializers.py (or api/*_serializers.py) -> API input validation
services/*.py -> reusable domain behavior
models.py -> core data constraints and simple model behavior
api/*.py -> HTTP request/response coordinationViews should not become the place where all product rules live.
For the event-to-action path, read Automations. A future
Create contact action would create a CRM record; a Contact created trigger
would react to one. Neither is currently implemented in the automation system.
The workflow roadmap explains this distinction.
Tenant access boundary
Every authenticated user can access normal REST resources only in organizations
where they have an OrganizationMembership with the required capability. Django
staff or superuser status does not bypass this REST boundary; however, a Django
superuser has broad access through /admin/. Installation administration uses
explicit endpoints. A normal pending invitation does not create membership;
provisioning creates a temporary owner membership but keeps the workspace
inaccessible until the nominated owner accepts.
System Overview
This page is the shortest architecture explanation that still lets a new contributor understand how a browser action becomes a database change.
Authentication, Organizations, and Roles
This document describes the implemented security boundary of Bubllio CRM. For the proposed next step and a comparison with other CRM products, see Access-model research.
