add multiple ammo groups at one time

This commit is contained in:
2022-02-24 00:16:23 -05:00
parent d79d0fa179
commit a6b2c6181e
13 changed files with 257 additions and 54 deletions

View File

@ -6,7 +6,9 @@ defmodule Cannery.Ammo do
import Ecto.Query, warn: false
alias Cannery.{Accounts.User, Containers, Repo}
alias Cannery.Ammo.{AmmoGroup, AmmoType}
alias Ecto.Changeset
alias Ecto.{Changeset, Multi}
@ammo_group_create_limit 10_000
@doc """
Returns the list of ammo_types.
@ -327,36 +329,63 @@ defmodule Cannery.Ammo do
end
@doc """
Creates a ammo_group.
Creates multiple ammo_groups at once.
## Examples
iex> create_ammo_group(%{field: value}, %User{id: 123})
{:ok, %AmmoGroup{}}
iex> create_ammo_groups(%{field: value}, 3, %User{id: 123})
{:ok, {3, [%AmmoGroup{}]}}
iex> create_ammo_group(%{field: bad_value}, %User{id: 123})
iex> create_ammo_groups(%{field: bad_value}, 3, %User{id: 123})
{:error, %Changeset{}}
"""
@spec create_ammo_group(attrs :: map(), User.t()) ::
{:ok, AmmoGroup.t()} | {:error, Changeset.t(AmmoGroup.new_ammo_group())}
def create_ammo_group(
@spec create_ammo_groups(attrs :: map(), multiplier :: non_neg_integer(), User.t()) ::
{:ok, {count :: non_neg_integer(), [AmmoGroup.t()] | nil}}
| {:error, Changeset.t(AmmoGroup.new_ammo_group()) | nil}
def create_ammo_groups(
%{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs,
multiplier,
%User{id: user_id} = user
) do
)
when multiplier >= 1 and multiplier <= @ammo_group_create_limit do
# validate ammo type and container ids belong to user
_valid_ammo_type = get_ammo_type!(ammo_type_id, user)
_valid_container = Containers.get_container!(container_id, user)
%AmmoGroup{}
|> AmmoGroup.create_changeset(attrs |> Map.put("user_id", user_id))
|> Repo.insert()
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
changesets =
Enum.map(1..multiplier, fn _count ->
%AmmoGroup{} |> AmmoGroup.create_changeset(attrs |> Map.put("user_id", user_id))
end)
if changesets |> Enum.all?(fn %{valid?: valid} -> valid end) do
Multi.new()
|> Multi.insert_all(
:create_ammo_groups,
AmmoGroup,
changesets
|> Enum.map(fn changeset ->
changeset
|> Map.get(:changes)
|> Map.merge(%{inserted_at: now, updated_at: now})
end),
returning: true
)
|> Repo.transaction()
|> case do
{:ok, %{create_ammo_groups: {count, ammo_groups}}} -> {:ok, {count, ammo_groups}}
{:error, :create_ammo_groups, changeset, _changes_so_far} -> {:error, changeset}
{:error, _other_transaction, _value, _changes_so_far} -> {:error, nil}
end
else
{:error, changesets |> List.first()}
end
end
def create_ammo_group(invalid_attrs, _user) do
%AmmoGroup{}
|> AmmoGroup.create_changeset(invalid_attrs |> Map.put("user_id", "-1"))
|> Repo.insert()
def create_ammo_groups(invalid_attrs, _multiplier, _user) do
{:error, %AmmoGroup{} |> AmmoGroup.create_changeset(invalid_attrs)}
end
@doc """

View File

@ -9,6 +9,8 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
alias Ecto.Changeset
alias Phoenix.LiveView.Socket
@ammo_group_create_limit 10_000
@impl true
@spec update(
%{:ammo_group => AmmoGroup.t(), :current_user => User.t(), optional(any) => any},
@ -22,6 +24,7 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
def update(%{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket) do
socket =
socket
|> assign(:ammo_group_create_limit, @ammo_group_create_limit)
|> assign(:changeset, Ammo.change_ammo_group(ammo_group))
|> assign(:ammo_types, Ammo.list_ammo_types(current_user))
|> assign_new(:containers, fn -> Containers.list_containers(current_user) end)
@ -80,20 +83,68 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
end
defp save_ammo_group(
%{assigns: %{current_user: current_user, return_to: return_to}} = socket,
%{assigns: %{changeset: changeset}} = socket,
:new,
ammo_group_params
%{"multiplier" => multiplier_str} = ammo_group_params
) do
socket =
case Ammo.create_ammo_group(ammo_group_params, current_user) do
{:ok, _ammo_group} ->
prompt = dgettext("prompts", "Ammo group created successfully")
socket |> put_flash(:info, prompt) |> push_redirect(to: return_to)
case multiplier_str |> Integer.parse() do
{multiplier, _remainder}
when multiplier >= 1 and multiplier <= @ammo_group_create_limit ->
socket |> create_multiple(ammo_group_params, multiplier)
{:error, %Changeset{} = changeset} ->
socket |> assign(changeset: changeset)
{multiplier, _remainder} ->
error_msg =
dgettext(
"errors",
"Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
max: @ammo_group_create_limit,
multiplier: multiplier
)
{:error, changeset} =
changeset
|> Changeset.add_error(:multiplier, error_msg)
|> Changeset.apply_action(:insert)
socket |> assign(:changeset, changeset)
:error ->
error_msg = dgettext("errors", "Could not parse number of copies")
{:error, changeset} =
changeset
|> Changeset.add_error(:multiplier, error_msg)
|> Changeset.apply_action(:insert)
socket |> assign(:changeset, changeset)
end
{:noreply, socket}
end
defp create_multiple(
%{assigns: %{current_user: current_user, return_to: return_to}} = socket,
ammo_group_params,
multiplier
) do
case Ammo.create_ammo_groups(ammo_group_params, multiplier, current_user) do
{:ok, {count, _ammo_groups}} ->
prompt =
dngettext(
"prompts",
"Ammo group created successfully",
"Ammo groups created successfully",
count
)
socket |> put_flash(:info, prompt) |> push_redirect(to: return_to)
{:error, %Changeset{} = changeset} ->
socket |> assign(changeset: changeset)
{:error, nil} ->
socket
end
end
end

View File

@ -33,7 +33,7 @@
<%= label(f, :price_paid, gettext("Price paid"), class: "title text-lg text-primary-600") %>
<%= number_input(f, :price_paid,
step: "0.01",
step: 0.01,
class: "text-center col-span-2 input input-primary"
) %>
<%= error_tag(f, :price_paid, "col-span-3 text-center") %>
@ -51,9 +51,29 @@
) %>
<%= error_tag(f, :container_id, "col-span-3 text-center") %>
<%= submit(dgettext("actions", "Save"),
phx_disable_with: dgettext("prompts", "Saving..."),
class: "mx-auto col-span-3 btn btn-primary"
) %>
<%= case @action do %>
<% :new -> %>
<hr class="hr col-span-3" />
<%= label(f, :multiplier, gettext("Copies"), class: "title text-lg text-primary-600") %>
<%= number_input(f, :multiplier,
max: @ammo_group_create_limit,
class: "text-center input input-primary",
value: 1,
phx_update: "ignore"
) %>
<%= submit(dgettext("actions", "Create"),
phx_disable_with: dgettext("prompts", "Creating..."),
class: "mx-auto btn btn-primary"
) %>
<%= error_tag(f, :multiplier, "col-span-3 text-center") %>
<% :edit -> %>
<%= submit(dgettext("actions", "Save"),
phx_disable_with: dgettext("prompts", "Saving..."),
class: "mx-auto col-span-3 btn btn-primary"
) %>
<% end %>
</.form>
</div>