Add operator write endpoints for the pricing catalog
CatalogController gains platform-admin-gated writes: create a plan, create a draft version (with its plan items, transactionally), publish a version, create an addon — plus a plan-detail endpoint exposing every version. Pricing stays versioned: create_version always makes a new draft, publish retires the prior active version, existing subscriptions are untouched. The Catalog context functions already existed; this just exposes them over HTTP. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,22 @@ defmodule ArcadiaCloud.Catalog do
|
||||
Repo.get_by(Plan, code: code)
|
||||
end
|
||||
|
||||
@doc "A plan with every version (newest first) + each version's items."
|
||||
def get_plan(id) do
|
||||
case Repo.get(Plan, id) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
plan ->
|
||||
versions =
|
||||
from(v in PlanVersion, where: v.plan_id == ^plan.id, order_by: [desc: v.version])
|
||||
|> Repo.all()
|
||||
|> Repo.preload(:items)
|
||||
|
||||
%{plan | versions: versions}
|
||||
end
|
||||
end
|
||||
|
||||
def create_plan(attrs) do
|
||||
%Plan{}
|
||||
|> Plan.changeset(attrs)
|
||||
@@ -69,6 +85,48 @@ defmodule ArcadiaCloud.Catalog do
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a new draft version of a plan together with its plan items, in
|
||||
one transaction. The version number is assigned automatically. Pricing
|
||||
edits are always a new version — never an in-place mutation — so
|
||||
existing subscriptions keep the terms they signed up on.
|
||||
|
||||
`items` is a list of maps (string or atom keys) shaped like PlanItem.
|
||||
Returns `{:ok, version}` (with items preloaded) or `{:error, changeset}`.
|
||||
"""
|
||||
def create_plan_version(plan_id, attrs, items) when is_list(items) do
|
||||
version_attrs =
|
||||
attrs
|
||||
|> Map.new(fn {k, v} -> {to_string(k), v} end)
|
||||
|> Map.merge(%{
|
||||
"plan_id" => plan_id,
|
||||
"version" => next_version_number(plan_id),
|
||||
"status" => "draft"
|
||||
})
|
||||
|
||||
Repo.transaction(fn ->
|
||||
version =
|
||||
case %PlanVersion{} |> PlanVersion.changeset(version_attrs) |> Repo.insert() do
|
||||
{:ok, v} -> v
|
||||
{:error, cs} -> Repo.rollback(cs)
|
||||
end
|
||||
|
||||
Enum.each(items, fn item ->
|
||||
item_attrs =
|
||||
item
|
||||
|> Map.new(fn {k, v} -> {to_string(k), v} end)
|
||||
|> Map.put("plan_version_id", version.id)
|
||||
|
||||
case %PlanItem{} |> PlanItem.changeset(item_attrs) |> Repo.insert() do
|
||||
{:ok, _} -> :ok
|
||||
{:error, cs} -> Repo.rollback(cs)
|
||||
end
|
||||
end)
|
||||
|
||||
get_version!(version.id)
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Publishes a draft version: retires any currently-active version of the
|
||||
same plan, then flips this one to active.
|
||||
|
||||
Reference in New Issue
Block a user