feat(store): set trial_ends_at on tenant create
CreateTenant now defaults trial_ends_at to NOW() + 14 days when the new tenant lands in status='trial'. Demo-kind tenants get status='demo' (per PLATFORM_ARCHITECTURE.md §5d) and trial_ends_at stays NULL — those flow through the M13.2 demo-provisioning path. Both store implementations (Memory + Postgres) updated; tests assert the 14-day window for customers and the absent end for demo kind. Unblocks M12.1 (portal trial banner can render a real countdown). Refs: M4.1 + M12.1
This commit is contained in:
@@ -69,16 +69,26 @@ func (m *Memory) CreateTenant(_ context.Context, in TenantCreate) (*Tenant, erro
|
||||
return nil, ErrConflict
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
kind := firstNonEmpty(in.Kind, "customer")
|
||||
status := "trial"
|
||||
var trialEnds *time.Time
|
||||
if kind == "demo" {
|
||||
status = "demo"
|
||||
} else {
|
||||
end := now.Add(14 * 24 * time.Hour)
|
||||
trialEnds = &end
|
||||
}
|
||||
t := &Tenant{
|
||||
ID: uuid.NewString(),
|
||||
Slug: in.Slug,
|
||||
Name: in.Name,
|
||||
Status: "trial",
|
||||
Kind: firstNonEmpty(in.Kind, "customer"),
|
||||
Plan: firstNonEmpty(in.Plan, "starter"),
|
||||
SalesOwner: in.SalesOwner,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
ID: uuid.NewString(),
|
||||
Slug: in.Slug,
|
||||
Name: in.Name,
|
||||
Status: status,
|
||||
Kind: kind,
|
||||
Plan: firstNonEmpty(in.Plan, "starter"),
|
||||
SalesOwner: in.SalesOwner,
|
||||
TrialEndsAt: trialEnds,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
m.tenants[t.ID] = t
|
||||
m.bySlug[t.Slug] = t.ID
|
||||
|
||||
@@ -90,9 +90,20 @@ func scanTenant(row pgx.Row) (*Tenant, error) {
|
||||
func (p *Postgres) CreateTenant(ctx context.Context, in TenantCreate) (*Tenant, error) {
|
||||
kind := firstNonEmpty(in.Kind, "customer")
|
||||
plan := firstNonEmpty(in.Plan, "starter")
|
||||
// Default status = 'trial'; set trial_ends_at = NOW() + 14 days so the
|
||||
// portal's trial banner has a real countdown to render. Demo tenants
|
||||
// (kind=demo) get status='demo' and no trial_ends_at — that's set by
|
||||
// the M13.2 demo provisioning path.
|
||||
row := p.pool.QueryRow(ctx,
|
||||
`INSERT INTO tenants (slug, name, kind, plan, sales_owner)
|
||||
VALUES ($1, $2, $3::tenant_kind, $4, NULLIF($5, ''))
|
||||
`INSERT INTO tenants (slug, name, kind, plan, status, sales_owner, trial_ends_at)
|
||||
VALUES (
|
||||
$1, $2, $3::tenant_kind, $4,
|
||||
CASE WHEN $3::tenant_kind = 'demo' THEN 'demo'::tenant_status
|
||||
ELSE 'trial'::tenant_status END,
|
||||
NULLIF($5, ''),
|
||||
CASE WHEN $3::tenant_kind = 'demo' THEN NULL
|
||||
ELSE NOW() + INTERVAL '14 days' END
|
||||
)
|
||||
RETURNING id::text, slug, name, status::text, kind::text, plan,
|
||||
COALESCE(erp_customer_id,''), COALESCE(stripe_cust_id,''),
|
||||
trial_ends_at, contract_start, contract_end, COALESCE(sales_owner,''),
|
||||
|
||||
Reference in New Issue
Block a user