crm

package
v1.799.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package crm mounts the Hanzo Cloud /v1/crm/* surface: a native-Go, per-org CRM (companies, contacts, opportunities) on Base/SQLite. It is the first slice of the "collapse the business apps into the unified cloud binary" program (universe/docs/architecture/unified-backend-go.md) — a native-Go port of the Twenty CRM core model, NOT a proxy to a NestJS backend.

The three entities are faithful to Twenty's `company` / `person` / `opportunity` standard objects, with Twenty's composite fields (FULL_NAME, EMAILS, CURRENCY, LINKS, ADDRESS) flattened to scalar columns for SQLite.

Tenant isolation is enforced SERVER-SIDE on every request: the org is c.Org() — the value SanitizeIdentity minted from the VALIDATED bearer owner claim (HIP-0026) — and NEVER a client-supplied header. Every store query filters WHERE org=?, so one tenant can never read or mutate another's data.

Surface (all org-scoped; /v1 only):

GET    /v1/crm/summary               per-org row counts (companies/contacts/opps)
GET    /v1/crm/companies             list companies                 -> {data:[…]}
POST   /v1/crm/companies             create a company               -> Company (201)
GET    /v1/crm/companies/:id         company detail                 -> Company
PUT    /v1/crm/companies/:id         update a company               -> Company
DELETE /v1/crm/companies/:id         delete a company (+ clear refs)
GET    /v1/crm/contacts              list contacts (?companyId=)     -> {data:[…]}
POST   /v1/crm/contacts             create a contact               -> Contact (201)
GET    /v1/crm/contacts/:id          contact detail                 -> Contact
PUT    /v1/crm/contacts/:id          update a contact               -> Contact
DELETE /v1/crm/contacts/:id          delete a contact (+ clear refs)
GET    /v1/crm/opportunities         list opportunities (?stage=)    -> {data:[…]}
POST   /v1/crm/opportunities        create an opportunity          -> Opportunity (201)
GET    /v1/crm/opportunities/:id     opportunity detail             -> Opportunity
PUT    /v1/crm/opportunities/:id     update an opportunity          -> Opportunity
DELETE /v1/crm/opportunities/:id     delete an opportunity

Order 131: binds /v1/crm/* before the AI subsystem's /v1/* catch-all (150). serve.go auto-registers GET /v1/crm/health.

Index

Constants

View Source
const (
	StageApplied        = "applied"
	StageScreened       = "screened"
	StageQualified      = "qualified"
	StageCreditsOffered = "credits-offered"
	StageOnboarded      = "onboarded"
	StageRejected       = "rejected"
)

Startup pipeline stages, in order. `rejected` is an off-pipeline terminal.

Variables

This section is empty.

Functions

func Mount

func Mount(app *zip.App, deps cloud.Deps) error

Mount wires the crm surface onto app per HIP-0106. Complex flavour: it keeps a package global (mounted) for Shutdown, so it constructs the Service value directly.

func Shutdown

func Shutdown() error

Shutdown closes the crm store. Idempotent.

Types

type Application added in v1.786.92

type Application struct {
	ID          string         `json:"id"`
	Org         string         `json:"-"`
	Company     string         `json:"company"`
	Website     string         `json:"website"`
	ContactName string         `json:"contactName"`
	Email       string         `json:"email"`
	Role        string         `json:"role"`
	Stage       string         `json:"stage"`
	Tier1       bool           `json:"tier1"`
	Metadata    map[string]any `json:"metadata"`
	Screen      ScreenResult   `json:"screen"`
	Events      []StageEvent   `json:"events"`
	CompanyID   string         `json:"companyId"`
	ContactID   string         `json:"contactId"`
	Reason      string         `json:"reason"`
	CreatedAt   int64          `json:"createdAt"`
	UpdatedAt   int64          `json:"updatedAt"`
}

Application is one startup-program submission plus its AI screen and pipeline state. Metadata carries the FULL submitted payload (all form fields, including arrays like tier1Investors/useCases); the promoted columns are query/display projections. Tier1 is deterministically derived at intake from the submitted fund list (independent of the AI screen's judgement).

type Company

type Company struct {
	ID         string `json:"id"`
	Org        string `json:"-"`
	Name       string `json:"name"`
	DomainName string `json:"domainName"`
	Employees  int64  `json:"employees"`
	City       string `json:"city"`
	Country    string `json:"country"`
	ARR        int64  `json:"arr"`
	Currency   string `json:"currency"`
	ICP        bool   `json:"idealCustomerProfile"`
	Linkedin   string `json:"linkedinLink"`
	XLink      string `json:"xLink"`
	CreatedAt  int64  `json:"createdAt"`
	UpdatedAt  int64  `json:"updatedAt"`
}

Company is an org-scoped account record, faithful to Twenty's `company` standard object (composites flattened to scalar columns for SQLite): ARR is minor units (cents) of Currency; ICP is the ideal-customer-profile flag.

type Contact

type Contact struct {
	ID        string `json:"id"`
	Org       string `json:"-"`
	FirstName string `json:"firstName"`
	LastName  string `json:"lastName"`
	Email     string `json:"email"`
	Phone     string `json:"phone"`
	JobTitle  string `json:"jobTitle"`
	City      string `json:"city"`
	CompanyID string `json:"companyId"`
	Linkedin  string `json:"linkedinLink"`
	XLink     string `json:"xLink"`
	CreatedAt int64  `json:"createdAt"`
	UpdatedAt int64  `json:"updatedAt"`
}

Contact is an org-scoped person record, faithful to Twenty's `person` standard object (FULL_NAME/EMAILS/PHONES composites flattened). CompanyID is an optional in-org relation to a Company.

type Opportunity

type Opportunity struct {
	ID             string `json:"id"`
	Org            string `json:"-"`
	Name           string `json:"name"`
	Amount         int64  `json:"amount"`
	Currency       string `json:"currency"`
	Stage          string `json:"stage"`
	CloseDate      int64  `json:"closeDate"`
	CompanyID      string `json:"companyId"`
	PointOfContact string `json:"pointOfContactId"`
	CreatedAt      int64  `json:"createdAt"`
	UpdatedAt      int64  `json:"updatedAt"`
}

Opportunity is an org-scoped deal record, faithful to Twenty's `opportunity` standard object. Amount is minor units (cents) of Currency; CloseDate is a unix second (0 == unset). Stage is validated against the default pipeline.

type ScreenResult added in v1.786.92

type ScreenResult struct {
	Status           string `json:"status"` // pending | done | failed
	Score            int    `json:"score"`  // 0..100
	Tier1Backed      string `json:"tier1Backed"`
	SuggestedCredits int    `json:"suggestedCredits"` // 0 | 5000 | 25000 | 50000 | 150000
	Summary          string `json:"summary"`
	DraftReply       string `json:"draftReply"`
	Model            string `json:"model"`
	ScreenedAt       int64  `json:"screenedAt"`
	Error            string `json:"error,omitempty"`
}

ScreenResult is the AI screen stored on an application. Status is pending → done | failed; a failed/absent screen never blocks intake.

type StageEvent added in v1.786.92

type StageEvent struct {
	From string `json:"from"`
	To   string `json:"to"`
	At   int64  `json:"at"`
	By   string `json:"by"` // "system" or a staff user id
	Note string `json:"note,omitempty"`
}

StageEvent is one entry in an application's append-only stage-transition log.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the CRM database. ONE SQLite file ({DataDir}/crm.db) holds every org's records; tenant isolation is the `org` column, enforced on EVERY query. This mirrors clients/prompts and clients/eval exactly (the ONE storage pattern). MaxOpenConns(1) serializes writes against the single-writer file.

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database. Idempotent-safe via sql.DB.

func (*Store) CountApplications added in v1.786.92

func (s *Store) CountApplications(ctx context.Context, org string) (int, error)

CountApplications returns the org's application count (overview cards).

func (*Store) Counts

func (s *Store) Counts(ctx context.Context, org string) (companies, contacts, opps int, err error)

Counts returns per-org row counts across the three entities — a real, non-fabricated summary for the CRM module's overview cards.

func (*Store) CreateApplication added in v1.786.92

func (s *Store) CreateApplication(ctx context.Context, a Application) (Application, error)

func (*Store) CreateCompany

func (s *Store) CreateCompany(ctx context.Context, c Company) (Company, error)

func (*Store) CreateContact

func (s *Store) CreateContact(ctx context.Context, c Contact) (Contact, error)

func (*Store) CreateOpportunity

func (s *Store) CreateOpportunity(ctx context.Context, o Opportunity) (Opportunity, error)

func (*Store) DeleteCompany

func (s *Store) DeleteCompany(ctx context.Context, org, id string) (bool, error)

DeleteCompany removes a company and NULLs any dangling contact/opportunity refs to it within the same org (no orphaned foreign refs). One transaction.

func (*Store) DeleteContact

func (s *Store) DeleteContact(ctx context.Context, org, id string) (bool, error)

DeleteContact removes a contact and clears any opportunity point-of-contact refs to it within the org. One transaction.

func (*Store) DeleteOpportunity

func (s *Store) DeleteOpportunity(ctx context.Context, org, id string) (bool, error)

func (*Store) FindApplicationByEmailCompany added in v1.786.92

func (s *Store) FindApplicationByEmailCompany(ctx context.Context, org, email, company string) (Application, error)

FindApplicationByEmailCompany returns the org's application matching a case-insensitive (email, company) pair, or errNotFound. Basis for idempotent intake (a resubmission updates rather than duplicates).

func (*Store) GetApplication added in v1.786.92

func (s *Store) GetApplication(ctx context.Context, org, id string) (Application, error)

func (*Store) GetCompany

func (s *Store) GetCompany(ctx context.Context, org, id string) (Company, error)

func (*Store) GetContact

func (s *Store) GetContact(ctx context.Context, org, id string) (Contact, error)

func (*Store) GetOpportunity

func (s *Store) GetOpportunity(ctx context.Context, org, id string) (Opportunity, error)

func (*Store) ListApplications added in v1.786.92

func (s *Store) ListApplications(ctx context.Context, org, stage string, limit int) ([]Application, error)

ListApplications lists an org's applications, optionally filtered by pipeline stage (stage=="" means all). Newest first.

func (*Store) ListCompanies

func (s *Store) ListCompanies(ctx context.Context, org string, limit int) ([]Company, error)

func (*Store) ListContacts

func (s *Store) ListContacts(ctx context.Context, org, companyID string, limit int) ([]Contact, error)

ListContacts lists the org's contacts, optionally filtered to one company (companyID=="" means all). Most-recently-updated first.

func (*Store) ListOpportunities

func (s *Store) ListOpportunities(ctx context.Context, org, stage string, limit int) ([]Opportunity, error)

ListOpportunities lists the org's opportunities, optionally filtered by stage (stage=="" means all). Most-recently-updated first.

func (*Store) UpdateApplication added in v1.786.92

func (s *Store) UpdateApplication(ctx context.Context, a Application) (Application, error)

UpdateApplication persists the mutable columns (stage, tier1, metadata, screen, events, links, reason). ID/Org/CreatedAt are immutable keys.

func (*Store) UpdateCompany

func (s *Store) UpdateCompany(ctx context.Context, c Company) (Company, error)

func (*Store) UpdateContact

func (s *Store) UpdateContact(ctx context.Context, c Contact) (Contact, error)

func (*Store) UpdateOpportunity

func (s *Store) UpdateOpportunity(ctx context.Context, o Opportunity) (Opportunity, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL