Created
June 6, 2020 07:23
-
-
Save gorkaio/fa0f93de3e8f139eb6c47a5cb47fc350 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
-module(billing). | |
-export([print/1]). | |
-import(catalog, [find/1]). | |
-define(WIDTH, 30). | |
-define(STORE_NAME, "Erlang Stores"). | |
-define(PRECISSION, "~.2f"). | |
print(Cart) -> | |
Bill = format_bill(Cart), | |
lists:map(fun(L) -> io:format("~s~n", [L]) end, Bill). | |
format_bill({cart, CartLines}) -> | |
BillingLines = billing_lines(CartLines), | |
Header = header(?STORE_NAME, ?WIDTH), | |
Lines = lists:map(fun(L) -> format_line(L, ?WIDTH) end, BillingLines), | |
Footer = footer(BillingLines, ?WIDTH), | |
Header ++ Lines ++ Footer. | |
% Generate header | |
header(StoreName, Width) -> | |
[string:centre(StoreName, Width), []]. | |
% Generate footer | |
footer(BillingLines, Width) -> | |
Total = total(BillingLines), | |
[[], format_total(Total, Width)]. | |
% Transform cart lines into bill lines | |
billing_lines(CartLines) -> | |
BillLines = lists:map( | |
fun({_, Code, Quantity}) -> | |
case catalog:find(Code) of | |
nil -> nil; % Ignore any cart line not existing on DB | |
{_, _, Desc, Price} -> {Desc, Quantity, Price} | |
end | |
end, | |
CartLines | |
), | |
lists:filter(fun(X) -> X /= nil end, BillLines). | |
% Bill line formatter | |
format_line({Desc, Quantity, Price}, Width) -> | |
PriceString = io_lib:format(?PRECISSION, [float(Price)]), | |
ItemString = integer_to_list(Quantity) ++ "x " ++ Desc, | |
ItemString ++ string:right(PriceString, Width - length(ItemString), $.). | |
format_total(Total, Width) -> | |
PriceString = io_lib:format(?PRECISSION, [float(Total)]), | |
"Total" ++ string:right(PriceString, Width - length("Total"), $.). | |
% Calculate bill totals | |
total(BillingLines) -> | |
lists:foldl(fun({_, Quantity, Price}, Sum) -> Sum + (Quantity * Price) end, 0.0, BillingLines). |
This file contains hidden or 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
-module(cart). | |
-export( | |
[ | |
new/0, | |
add_item/2, add_item/3, add_promo/2, add_promo/3, | |
remove_item/2, remove_item/3, remove_promo/2, remove_promo/3, | |
has_item/2, has_promo/2 | |
] | |
). | |
-export([test/0]). | |
% TODO: Add method to remove all quantities of given promo | |
% Create new cart | |
new() -> new([]). | |
new(Lines) -> {cart, Lines}. | |
% Create new line | |
new_line(Type, Code, Quantity) -> | |
case Type of | |
item -> {item, Code, Quantity}; | |
promo -> {promo, Code, Quantity} | |
end. | |
new_item(Code, Quantity) -> new_line(item, Code, Quantity). | |
new_promo(Code, Quantity) -> new_line(promo, Code, Quantity). | |
% Add item to cart | |
add_item({cart, _Lines} = Cart, Code) -> | |
add_item(Cart, Code, 1). | |
add_item(Cart, _, Quantity) when Quantity == 0 -> Cart; | |
add_item(Cart, Code, Quantity) when Quantity > 0 -> | |
add(Cart, new_item(Code, Quantity)). | |
add_promo({cart, _Lines} = Cart, Code) -> | |
add_promo(Cart, Code, 1). | |
add_promo(Cart, _, Quantity) when Quantity == 0 -> Cart; | |
add_promo(Cart, Code, Quantity) when Quantity > 0 -> | |
add(Cart, new_promo(Code, Quantity)). | |
add({cart, Lines} = Cart, {Type, Code, Quantity} = NewLine) -> | |
NewLines = case has(Cart, Code, Type) of | |
false -> Lines ++ [NewLine]; | |
{Type, Code, OldQuantity} = FoundLine -> (Lines -- [FoundLine]) ++ [new_line(Type, Code, OldQuantity + Quantity)] | |
end, | |
new(NewLines). | |
add_test() -> | |
{cart, [{item, 1234, 1}]} = add_item(new(), 1234), | |
{cart, [{promo, 1234, 1}]} = add_promo(new(), 1234), | |
{cart, [{item, 1234, 2}]} = add_item(new(), 1234, 2), | |
{cart, [{promo, 1234, 2}]} = add_promo(new(), 1234, 2), | |
{cart, [{item, 1234, 3}]} = add_item(new([new_item(1234, 1)]), 1234, 2), | |
{cart, [{promo, 1234, 3}]} = add_promo(new([new_promo(1234, 1)]), 1234, 2), | |
{cart, [{promo, 1234, 1}, {item, 1234, 3}]} = add_item(new([new_item(1234, 1), new_promo(1234, 1)]), 1234, 2), | |
{cart, [{item, 1234, 1}, {promo, 1234, 3}]} = add_promo(new([new_item(1234, 1), new_promo(1234, 1)]), 1234, 2), | |
passed. | |
% Remove item from cart | |
remove_item({cart, _Lines} = Cart, Code) -> | |
remove_item(Cart, Code, 1). | |
remove_item({cart, _Lines} = Cart, Code, all) -> | |
remove_all(Cart, item, Code); | |
remove_item(Cart, _, Quantity) when Quantity == 0 -> Cart; | |
remove_item(Cart, Code, Quantity) when Quantity > 0 -> | |
remove(Cart, new_item(Code, Quantity)). | |
% Remove promo from cart | |
remove_promo({cart, _Lines} = Cart, Code) -> | |
remove_promo(Cart, Code, 1). | |
remove_promo({cart, _Lines} = Cart, Code, all) -> | |
remove_all(Cart, promo, Code); | |
remove_promo(Cart, _, Quantity) when Quantity == 0 -> Cart; | |
remove_promo(Cart, Code, Quantity) when Quantity > 0 -> | |
remove(Cart, new_promo(Code, Quantity)). | |
remove_all({cart, Lines}, Type, Code) -> | |
NewLines = lists:filter(fun({T, C, _Q}) -> not((T == Type) and (C == Code)) end, Lines), | |
new(NewLines). | |
remove({cart, Lines} = Cart, {Type, Code, Quantity}) -> | |
NewLines = case has(Cart, Code, Type) of | |
false -> Lines; | |
{Type, Code, OldQuantity} = FoundLine -> (Lines -- [FoundLine]) ++ [new_line(Type, Code, OldQuantity - Quantity)] | |
end, | |
LinesWithQuantity = lists:filter(fun({_, _, Q}) -> Q > 0 end, NewLines), | |
new(LinesWithQuantity). | |
remove_test() -> | |
{cart, []} = remove_item(new(), 1234), | |
{cart, []} = remove_promo(new(), 1234), | |
{cart, [{item, 1, 1}]} = remove_item(new([new_item(1, 2)]), 1, 1), | |
{cart, [{promo, 1, 1}]} = remove_promo(new([new_promo(1, 2)]), 1, 1), | |
{cart, [{item, 1, 1}]} = remove_item(new([new_item(1, 3)]), 1, 2), | |
{cart, [{promo, 1, 1}]} = remove_promo(new([new_promo(1, 3)]), 1, 2), | |
{cart, [{item, 2, 1}, {item, 1, 1}]} = remove_item(new([new_item(1, 2), new_item(2, 1)]), 1, 1), | |
{cart, [{promo, 2, 1}, {promo, 1, 1}]} = remove_promo(new([new_promo(1, 2), new_promo(2, 1)]), 1, 1), | |
{cart, []} = remove_item(new([new_item(1234, 1)]), 1234, 1), | |
{cart, []} = remove_promo(new([new_promo(1234, 1)]), 1234, 1), | |
{cart, [{item, 1, 3}, {promo, 1, 1}]} = remove_promo(new([new_promo(1, 2), new_item(1, 3)]), 1, 1), | |
{cart, [{promo, 1, 2}, {item, 1, 2}]} = remove_item(new([new_promo(1, 2), new_item(1, 3)]), 1, 1), | |
{cart, [{promo, 3, 2}, {item, 1, 3}]} = remove_promo(new([new_promo(1, 2), new_promo(3, 2), new_item(1, 3)]), 1, all), | |
{cart, [{promo, 1, 2}, {item, 3, 2}]} = remove_item(new([new_promo(1, 2), new_item(3, 2), new_item(1, 3)]), 1, all), | |
passed. | |
% Determine if line with code is in cart | |
has_item(Cart, Code) -> has(Cart, Code, item). | |
has_promo(Cart, Code) -> has(Cart, Code, promo). | |
has({cart, []}, _, _) -> false; | |
has({cart, [{T, C, _Q} = Line | _]}, Code, Type) when T == Type, C == Code -> | |
Line; | |
has({cart, [_ | ILs]}, Code, Type) -> | |
has({cart, ILs}, Code, Type). | |
has_test() -> | |
false = has_item(new(), 1), | |
false = has_promo(new(), 1), | |
{item, 2, 3} = has_item(new([new_item(2, 3), new_item(5,6)]), 2), | |
{promo, 2, 3} = has_promo(new([new_promo(2, 3), new_item(5,6)]), 2), | |
{promo, 2, 4} = has_promo(new([new_item(2, 3), new_promo(2,4)]), 2), | |
passed. | |
test() -> | |
add_test(), | |
remove_test(), | |
has_test(), | |
passed. |
This file contains hidden or 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
-module(catalog). | |
-export([find/1]). | |
-export([test/0]). | |
% find product in database | |
find(Code) -> find(database(), Code). | |
find([], _) -> nil; | |
find([{C, _, _, _} = Item|_], Code) when C == Code -> | |
Item; | |
find([_|Is], Code) -> | |
find(Is, Code). | |
find_test() -> | |
nil = find([], 1), | |
nil = find([{1, item, "TEST_1", 23.45}], 2), | |
{1, item, "TEST_1", 23.45} = find([{1, item, "TEST_1", 23.45}], 1), | |
{1, item, "TEST_1", 23.45} = find([{1, item, "TEST_1", 23.45}, {2, item, "TEST_2", -1.23}], 1), | |
{2, item, "TEST_2", -1.23} = find([{1, item, "TEST_1", 23.45}, {2, item, "TEST_2", -1.23}], 2), | |
passed. | |
% Fake database | |
database() -> | |
[ | |
{4719, item, "Fish Fingers" , 121}, | |
{5643, item, "Nappies" , 1010}, | |
{3814, item, "Orange Jelly", 56}, | |
{1111, item, "Hula Hoops", 21}, | |
{1112, item, "Hula Hoops (Giant)", 133}, | |
{1234, item, "Dry Sherry, 1lt", 540}, | |
{9999, promo, "2x1 Dry Sherry", -540} | |
]. | |
test() -> | |
find_test(), | |
passed. |
This file contains hidden or 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
-module(promotions). | |
-import(cart,[add_promo/2, remove_promo/3, has_item/2]). | |
-import(cart,[new/0, add_item/2, has_promo/2]). % Used for testing | |
-export([apply_promos/1]). | |
-export([test/0]). | |
-define(SHERRY_CODE, 1234). | |
-define(SHERRY2X1_CODE, 9999). | |
apply_promos(Cart) -> | |
Promos = lists:filter(fun({_, Cond, _}) -> Cond(Cart) end, promotions()), | |
apply_promos(Cart, Promos). | |
apply_promos(Cart, []) -> Cart; | |
apply_promos(Cart, [{_, _, F}|Ps]) -> | |
apply_promos(F(Cart), Ps). | |
apply_test() -> | |
{promo, ?SHERRY2X1_CODE, 1} = cart:has_promo(apply_promos(cart:add_item(cart:new(), ?SHERRY_CODE, 3)), ?SHERRY2X1_CODE), | |
{promo, ?SHERRY2X1_CODE, 2} = cart:has_promo(apply_promos(cart:add_item(cart:new(), ?SHERRY_CODE, 4)), ?SHERRY2X1_CODE), | |
false = cart:has_promo(apply_promos(cart:add_item(cart:new(), ?SHERRY_CODE, 1)), ?SHERRY2X1_CODE), | |
false = cart:has_promo(apply_promos(cart:new()), ?SHERRY2X1_CODE), | |
passed. | |
test() -> | |
apply_test(), | |
passed. | |
promotions() -> [ | |
{ | |
?SHERRY2X1_CODE, | |
fun(Cart) -> cart:has_item(Cart, ?SHERRY_CODE) /= false end, | |
fun(Cart) -> | |
case cart:has_item(Cart, ?SHERRY_CODE) of | |
false -> Cart; | |
{item, ?SHERRY_CODE, Quantity} -> | |
cart:remove_promo(Cart, ?SHERRY2X1_CODE, all), | |
cart:add_promo(Cart, ?SHERRY2X1_CODE, Quantity div 2) | |
end | |
end | |
} | |
]. |
This file contains hidden or 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
-module(supermarket). | |
-export([new/0, add/2, add/3, remove/2, remove/3, bill/1]). | |
%% Create new cart | |
new() -> | |
cart:new(). | |
%% Add items to cart | |
add(Cart, Code) -> add(Cart, Code, 1). | |
add(Cart, Code, Quantity) -> | |
NewCart = cart:add_item(Cart, Code, Quantity), | |
update(NewCart). | |
%% Remove items from cart | |
remove(Cart, Code) -> remove(Cart, Code, 1). | |
remove(Cart, Code, Quantity) -> | |
NewCart = cart:add_item(Cart, Code, Quantity), | |
update(NewCart). | |
%% Update cart: apply promotions, etc... | |
update(Cart) -> | |
promotions:apply_promos(Cart). | |
%% Print bill for given cart | |
bill(Cart) -> | |
billing:print(Cart). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment