import { SmrtObject } from '@happyvertical/smrt-core'; import { CommissionPlanComponent, CommissionPlanOptions, CommissionPlanStatus } from '../types.js'; /** * Validate a components array. Throws a descriptive error on the first * violation. Exported for reuse by the calculation service's input guards * and by referral-terms builders that assemble component arrays. * * Rules: * - component keys are non-empty and unique within the plan * - `trigger` is a non-empty string (`'*'` matches every event kind) * - `basis` is one of {@link COMMISSION_BASES} * - basis `fixed` requires an integer `fixedAmountCents` * - every other basis requires `rate` in `[0, 1]` * - basis `custom` additionally requires a non-empty `customBasisKey` * - `recurrence.kind` (when present) is `one_time` or `recurring`; * `maxOccurrences` / `windowMonths` (when present) are positive integers */ export declare function validateCommissionPlanComponents(components: CommissionPlanComponent[]): void; export declare class CommissionPlan extends SmrtObject { /** Tenant ID for multi-tenant isolation (nullable → global plans). */ tenantId: string | null; /** Stable plan identity shared by every version of the plan. */ planKey: string; /** Monotonic version within `planKey`. Amendments insert `max + 1`. */ version: number; /** Human-readable plan name. */ name: string; /** Longer human-readable description of the terms. */ description: string; /** * Lifecycle status — see {@link PLAN_STATUS_TRANSITIONS}. Mutate via * {@link activate} / {@link supersede} / {@link retire} (or a legal * single-step assignment; the save-time guard rejects illegal edges). */ status: CommissionPlanStatus; /** When this version takes effect. Frozen once the plan activates. */ effectiveFrom: Date | null; /** ISO 4217 currency the plan's terms are denominated in. */ currency: string; /** * Calculation components as a JSON-string array — see * {@link CommissionPlanComponent}. Use {@link getComponents} / * {@link setComponents} (the setter validates). */ components: string; /** Additional metadata as a JSON string. */ metadata: string; constructor(options?: CommissionPlanOptions); /** * Re-coerce date fields after the framework reapplies raw option values, * record the loaded status for the transition guard, and capture the * frozen snapshot when the row arrived already activated. The snapshot is * captured for every non-draft status (not just `active`) so a superseded * or retired version — history — can't be rewritten either. */ initialize(): Promise; isDraft(): boolean; isActive(): boolean; /** Parse {@link components}; returns `[]` on empty/invalid JSON. */ getComponents(): CommissionPlanComponent[]; /** * Validate and store the components array. Throws on invalid components — * see {@link validateCommissionPlanComponents} for the rules. */ setComponents(components: CommissionPlanComponent[]): void; /** Parse {@link metadata}; returns `{}` on empty/invalid JSON. */ getMetadata(): Record; /** Serialize and store {@link metadata}. */ setMetadata(data: Record): void; /** * Transition `draft → active`. Validates components first so no active * plan can carry malformed terms. */ activate(): void; /** Transition `active → superseded` (a newer version took over). */ supersede(): void; /** Transition `draft | active → retired` (terminal). */ retire(): void; /** * Save with two guards: * * 1. **Status transition** — the about-to-be-written status must be a * legal edge from the authoritative prior persisted status (re-read * from the DB so a `create({ id: , _skipLoad: true })` upsert * can't sidestep the guard — commerce pattern). * 2. **Frozen calculation identity** — once the row has been saved * non-draft, `components` / `currency` / `planKey` / `version` / * `effectiveFrom` must match the captured snapshot. Amend by inserting * a new version instead. * * Activating saves also re-validate components, so an `active` row always * carries well-formed terms regardless of which write path set them. */ save(): Promise; /** * Refuse a save whose `(tenantId, planKey, version)` natural key already * belongs to a DIFFERENT row. The frozen-identity guard above is * instance-local (WeakMap), so a FRESH instance carrying an existing * natural key would otherwise sail through and the conflict-column * upsert would rewrite the persisted terms (and rotate the row id). * Edit drafts by hydrating them; change terms with * `CommissionPlanCollection.createAmendment()`. */ private assertNaturalKeyNotTaken; /** * Resolve the AUTHORITATIVE prior status from the database; fall back to * the loaded-status WeakMap only when the DB is unavailable. `undefined` * means no persisted row exists (genuinely new). */ private resolvePriorStatus; private assertStatusTransition; private assertFrozenIdentityUnchanged; private serializeFrozenSnapshot; private static coerceDate; } export default CommissionPlan; //# sourceMappingURL=CommissionPlan.d.ts.map