feat(store): set trial_ends_at on tenant create
ci / image (pull_request) Has been skipped
ci / shared (pull_request) Successful in 7s
ci / test (pull_request) Successful in 1m55s

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:
2026-05-19 18:18:10 +02:00
parent a37ae1d121
commit ad0b2ef949
4 changed files with 73 additions and 11 deletions
+19 -9
View File
@@ -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