-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feature] added the checkout resource
- Loading branch information
1 parent
044e43f
commit cbb9f71
Showing
14 changed files
with
373 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
defmodule StoreCore.Checkouts do | ||
alias StoreCore.Promotions | ||
alias StoreCore.Products | ||
alias StoreCore.CheckoutProduct | ||
|
||
defp to_checkout_products(products) do | ||
products = products | ||
|> Enum.map(fn params -> | ||
checkout_product =%CheckoutProduct{} | ||
|> CheckoutProduct.changeset(params) | ||
|
||
if checkout_product.valid?() do | ||
CheckoutProduct.apply(checkout_product) | ||
else | ||
[:errors, checkout_product.errors] | ||
end | ||
end) | ||
|
||
errors = Enum.filter(products, fn product -> | ||
case product do | ||
[:errors, errors] -> errors | ||
_ -> false | ||
end | ||
end) | ||
|
||
if length(errors) != 0 do | ||
{:errors, errors} | ||
else | ||
{:ok, products} | ||
end | ||
end | ||
|
||
|
||
def retrieve_products(products) do | ||
products = Enum.map(products, fn checkout_product -> | ||
case Products.get(checkout_product.id) do | ||
{:ok, product} -> | ||
Map.put(checkout_product, :price, product.price) | ||
error -> error | ||
end | ||
end) | ||
|
||
IO.puts "verifica em caso de erro" | ||
Enum.filter(products, fn product -> | ||
case product do | ||
{:error, reason} -> true | ||
_ -> false | ||
end | ||
end) | ||
|> IO.inspect | ||
|
||
|
||
{:ok, products} | ||
end | ||
|
||
def preview(products) do | ||
with {:ok, products} <- to_checkout_products(products), | ||
{:ok, products} <- retrieve_products(products) do | ||
total = | ||
products | ||
|> Enum.reduce(0, fn product, acc -> | ||
price = | ||
product | ||
|> promotion_descount | ||
|
||
acc + price | ||
end) | ||
|
||
{:ok, [total: total, products: products]} | ||
end | ||
end | ||
|
||
defp promotion_descount(product) do | ||
if (Map.has_key?(product, :promotion_id) and product.promotion_id == nil) or | ||
not Map.has_key?(product, :promotion_id) do | ||
product.price * product.quantity | ||
else | ||
{:ok, promo} = Promotions.get(product.promotion_id) | ||
descount_from(product, promo.name) | ||
end | ||
end | ||
|
||
def descount_from(%{price: price, quantity: quantity}, "Pague 1 Leve 2") do | ||
numberOfProductsNotIncluded = rem(quantity, 2) | ||
quantity = div(quantity - numberOfProductsNotIncluded, 2) | ||
price * (quantity + numberOfProductsNotIncluded) | ||
end | ||
|
||
def descount_from(%{price: price, quantity: quantity}, "3 por 10 reais") do | ||
numberOfProductsNotIncluded = rem(quantity, 3) | ||
quantity = div(quantity - numberOfProductsNotIncluded, 3) | ||
10 * quantity + numberOfProductsNotIncluded * price | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,22 @@ | ||
defmodule StoreCore.Promotions do | ||
alias StoreCore.{Promotion, Repo} | ||
|
||
def list, do: Promotion |> Repo.all | ||
def list, do: Promotion |> Repo.all() | ||
|
||
def get(id) do | ||
case Repo.get(Promotion, id) do | ||
nil -> {:error, :not_found} | ||
promotion -> {:ok, promotion} | ||
end | ||
end | ||
|
||
def filter_by_name(name) do | ||
[promotion] = | ||
list() | ||
|> Enum.filter(fn promotion -> | ||
promotion.name === name | ||
end) | ||
|
||
promotion | ||
end | ||
end |
21 changes: 21 additions & 0 deletions
21
apps/store_core/lib/store_core/schemas/checkout_product.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
defmodule StoreCore.CheckoutProduct do | ||
use Ecto.Schema | ||
|
||
import Ecto.Changeset | ||
|
||
embedded_schema do | ||
field :quantity, :integer | ||
field :promotion_id, Ecto.UUID | ||
end | ||
|
||
@required [:id, :quantity] | ||
@accepted [:promotion_id | @required] | ||
|
||
def changeset(checkout_product, params \\ %{}) do | ||
checkout_product | ||
|> cast(params, @accepted) | ||
|> validate_required(@required) | ||
end | ||
|
||
def apply(changeset), do: apply_changes(changeset) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
apps/store_core/test/store_core/contexts/checkouts_test.exs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
defmodule StoreCore.CheckoutsTest do | ||
use StoreCore.DataCase | ||
|
||
alias StoreCore.{Checkouts, Product, Products, Promotions} | ||
|
||
test "calculate a checkout without promo code" do | ||
products = | ||
[ | ||
%{name: "Lápis", price: 200, quantity: 1}, | ||
%{name: "Borracha", price: 100, quantity: 1} | ||
] | ||
|> Enum.map(fn product -> | ||
{:ok, created_product} = Products.create(product) | ||
|
||
product = %{product | name: nil} | ||
product = %{product | price: nil} | ||
Map.put(product, :id, created_product.id) | ||
end) | ||
|
||
assert [total: 300, products: ^products] = Checkouts.preview(products) | ||
end | ||
|
||
describe "calculate Pague 1 Leve 2 with" do | ||
test "with one group" do | ||
total = | ||
[ | ||
%{name: "Lápis", price: 200, quantity: 2, promotion_id: pague_1_leve_2()}, | ||
%{name: "Borracha", price: 100, quantity: 1} | ||
] | ||
|> checkout_total | ||
|
||
assert total === 300 | ||
end | ||
|
||
test "with one group and one out from promotion" do | ||
total = | ||
[ | ||
%{name: "Lápis", price: 200, quantity: 3, promotion_id: pague_1_leve_2()}, | ||
%{name: "Borracha", price: 100, quantity: 1} | ||
] | ||
|> checkout_total | ||
|
||
assert total === 500 | ||
end | ||
|
||
test "with many groups" do | ||
total = | ||
[ | ||
%{name: "Lápis", price: 200, quantity: 7, promotion_id: pague_1_leve_2()}, | ||
%{name: "Borracha", price: 150, quantity: 3, promotion_id: pague_1_leve_2()} | ||
] | ||
|> checkout_total | ||
|
||
assert total === 1100 | ||
end | ||
end | ||
|
||
describe "calculate 3 por 10 reais with" do | ||
test "with one group" do | ||
total = | ||
[%{name: "Lápis", price: 200, quantity: 3, promotion_id: tres_por_10()}] | ||
|> checkout_total | ||
|
||
assert total === 10 | ||
end | ||
|
||
test "with two groups" do | ||
total = | ||
[%{name: "Lápis", price: 200, quantity: 6, promotion_id: tres_por_10()}] | ||
|> checkout_total | ||
|
||
assert total === 20 | ||
end | ||
|
||
test "with two groups and one out from promotion" do | ||
total = | ||
[%{name: "Lápis", price: 200, quantity: 7, promotion_id: tres_por_10()}] | ||
|> checkout_total | ||
|
||
assert total === 220 | ||
end | ||
end | ||
|
||
test "a mixed checkout" do | ||
total = | ||
[ | ||
# 700 | ||
%{name: "Ponta de lápis", price: 100, quantity: 7}, | ||
# 30 | ||
%{name: "Caixa de lápis", price: 650, quantity: 9, promotion_id: tres_por_10()}, | ||
# 300 | ||
%{name: "Borracha", price: 75, quantity: 7, promotion_id: pague_1_leve_2()}, | ||
# 320 | ||
%{name: "Pasta colegial", price: 300, quantity: 7, promotion_id: tres_por_10()}, | ||
# 200 | ||
%{name: "Caneta", price: 100, quantity: 3, promotion_id: pague_1_leve_2()}, | ||
# 1400 | ||
%{name: "Lápis", price: 200, quantity: 7} | ||
] | ||
|> checkout_total | ||
|
||
assert total === 700 + 30 + 300 + 320 + 200 + 1400 | ||
end | ||
|
||
defp tres_por_10, do: Promotions.filter_by_name("3 por 10 reais").id | ||
|
||
defp pague_1_leve_2, do: Promotions.filter_by_name("Pague 1 Leve 2").id | ||
|
||
defp checkout_total(products) do | ||
[total: total, products: _] = | ||
products | ||
|> Enum.map(fn product -> | ||
{:ok, created_product} = Products.create(product) | ||
# ensure that the price it is retrieved from the backend | ||
product = %{product | name: nil} | ||
product = %{product | price: nil} | ||
Map.put(product, :id, created_product.id) | ||
end) | ||
|> Checkouts.preview() | ||
|
||
total | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
apps/store_web/lib/store_web/controllers/checkout_controller.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
defmodule StoreWeb.CheckoutController do | ||
use StoreWeb, :controller | ||
|
||
alias StoreCore.Checkouts | ||
|
||
action_fallback StoreWeb.FallbackController | ||
|
||
def create(conn, %{"products" => products}) do | ||
products = Enum.map(products, &convert_to_atom(&1)) | ||
|
||
with {:ok, checkout} <- Checkouts.preview(products) do | ||
conn | ||
|> put_status(:accepted) | ||
|> render("show.json", checkout: checkout) | ||
end | ||
end | ||
|
||
defp convert_to_atom(map) do | ||
for {key, val} <- map, into: %{}, do: {String.to_atom(key), val} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
defmodule StoreWeb.CheckoutView do | ||
use StoreWeb, :view | ||
|
||
alias StoreWeb.CheckoutView | ||
|
||
def render("show.json", %{checkout: checkout}) do | ||
%{data: render_one(checkout, CheckoutView, "checkout.json")} | ||
end | ||
|
||
def render("checkout.json", %{checkout: [total: total, products: products]}) do | ||
%{ | ||
total: total, | ||
products: products_to_view(products) | ||
} | ||
end | ||
|
||
defp products_to_view(products) do | ||
products | ||
|> Enum.map(fn product -> | ||
%{ | ||
id: product.id, | ||
price: product.price, | ||
quantity: product.quantity | ||
} | ||
end) | ||
end | ||
end |
Oops, something went wrong.