import { SmrtObject } from '@happyvertical/smrt-core'; import { LeadOptions, LeadStatus } from '../types.js'; /** * Lead is an identified prospect: a person/organization that showed intent * but has not yet been qualified into an Opportunity. * * Acquisition provenance is generic — `sourceKind`/`sourceId` name where the * lead came from (a campaign, an import, a referral, a form, …) without this * module knowing referral semantics; `acquisitionContext` preserves the raw * acquisition payload as JSON, and audited merges append the loser's context * under a `mergedSources` array so no acquisition history is ever lost. * * Leads are never deleted (audit trail): duplicates are merged via * `LeadCollection.mergeLeads()` — the loser becomes terminal `merged` with * `mergedIntoId` pointing at the winner, and its activities stay attached. * * @example * ```typescript * const leads = await LeadCollection.create({ db }); * const lead = await leads.create({ * name: 'Acme rooftop retrofit', * email: 'facilities@acme.test', * sourceKind: 'campaign', * sourceId: 'spring-2026', * }); * const opportunity = await leads.qualify({ leadId: lead.id ?? '' }); * ``` */ export declare class Lead extends SmrtObject { /** * Tenant ID for multi-tenant isolation. * Nullable to support both tenant-scoped and global leads. */ tenantId: string | null; /** Short descriptive name of the prospect/deal-to-be. Required. */ name: string; /** Primary human contact name. */ contactName: string; /** Primary contact email. */ email: string; /** Primary contact phone. */ phone: string; /** Prospect organization/company name. */ organizationName: string; /** * Optional identity link once the prospect is a known Profile — * cross-package string reference to smrt-profiles. */ profileId: string; /** Owning sales rep (assignment). Empty while unassigned. */ ownerRepId: string; /** Lifecycle status; transitions are save-guarded (see module map). */ status: LeadStatus; /** * Generic acquisition-source kind (open string): `'campaign'`, * `'referral'`, `'import'`, `'web_form'`, … CRM stores the pointer without * interpreting it — referral attribution lives in the referrals module. */ sourceKind: string; /** Identifier within the `sourceKind` namespace. */ sourceId: string; /** * Preserved acquisition history as a JSON object string. Use * {@link getAcquisitionContext}/{@link setAcquisitionContext}. Merges append * the losing lead's context under a `mergedSources` array — both sides' * histories survive. */ acquisitionContext: string; /** * Generic external intake-draft reference (open string) — e.g. the id of a * form submission, imported CSV row, or referral intake draft that this * lead was materialized from. */ intakeRef: string; /** * Winner lead this row was merged into. Set (with status `merged`) by * `LeadCollection.mergeLeads()`; empty for live leads. Self-referencing FK. */ mergedIntoId: string; /** When the lead was qualified into an opportunity. */ qualifiedAt: Date | null; /** * Free-form JSON object stored as a string. Use * {@link getMetadata}/{@link setMetadata} instead of parsing manually. */ metadata: string; constructor(options?: LeadOptions); /** Whether the lead has been folded into another lead (terminal). */ isMerged(): boolean; /** Whether the lead has been qualified into an opportunity. */ isQualified(): boolean; /** Parse the acquisition-context JSON string; `{}` on malformed content. */ getAcquisitionContext(): Record; /** Serialize and store the acquisition-context object. */ setAcquisitionContext(context: Record): void; /** Parse the metadata JSON string; returns `{}` on malformed content. */ getMetadata(): Record; /** Serialize and store the metadata object. */ setMetadata(metadata: Record): void; /** * Capture the status the row was loaded with so the save-time transition * guard can reject illegal status flips made via raw field assignment * (mass-assignment on the generated update route, a stale caller, etc.). * Only persisted rows carry a prior status. */ initialize(): Promise; /** * Validate the status transition before persisting, then save. A forged * `status` (e.g. un-merging a merged lead) is rejected here regardless of * how the instance was constructed. */ save(): Promise; /** * Reject an illegal status flip done via raw assignment. No-op transitions * and brand-new rows are always allowed. */ protected assertLeadStatusTransition(prior: LeadStatus | undefined): void; /** * Resolve the AUTHORITATIVE prior status (commerce Contract pattern). The * WeakMap is only populated when {@link initialize} loaded the row from the * DB; it is empty for an instance built via * `collection.create({ id: , _skipLoad: true })` — the upsert * path that writes onto an existing row without hydrating it. Trusting an * empty WeakMap there would treat the write as a brand-new row and skip the * guard entirely (a poisonable prior-state). * * So when this instance carries an `id`, read the persisted row straight * from the database and treat its `status` as the prior. Only when no row * exists (truly new) do we fall back to the WeakMap. The raw row's `status` * column is single-word (no snake_case transform). */ protected resolvePriorStatus(): Promise; } export default Lead; //# sourceMappingURL=Lead.d.ts.map