Skip to content

Commit 5ae8615

Browse files
committed
Display time promotion on order page
1 parent 18b3ca0 commit 5ae8615

20 files changed

+492
-137
lines changed

ecommerce/pricing/lib/pricing.rb

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
require_relative "pricing/promotions_calendar"
1212
require_relative "pricing/calculate_order_sub_amounts_value"
1313
require_relative "pricing/calculate_order_total_value"
14+
require_relative "pricing/apply_time_promotion"
1415

1516
module Pricing
1617
def self.command_bus=(value)
@@ -90,7 +91,15 @@ def call(event_store, command_bus)
9091
UseCoupon,
9192
UseCouponHandler.new(event_store)
9293
)
93-
event_store.subscribe(CalculateOrderTotalValue, to: [
94+
command_bus.register(
95+
SetTimePromotionDiscount,
96+
SetTimePromotionDiscountHandler.new(event_store)
97+
)
98+
command_bus.register(
99+
ResetTimePromotionDiscount,
100+
ResetTimePromotionDiscountHandler.new(event_store)
101+
)
102+
event_store.subscribe(ApplyTimePromotion, to: [
94103
PriceItemAdded,
95104
PriceItemRemoved,
96105
PercentageDiscountSet,
@@ -99,14 +108,27 @@ def call(event_store, command_bus)
99108
ProductMadeFreeForOrder,
100109
FreeProductRemovedFromOrder
101110
])
111+
event_store.subscribe(CalculateOrderTotalValue, to: [
112+
PriceItemAdded,
113+
PriceItemRemoved,
114+
PercentageDiscountSet,
115+
PercentageDiscountReset,
116+
PercentageDiscountChanged,
117+
ProductMadeFreeForOrder,
118+
FreeProductRemovedFromOrder,
119+
TimePromotionDiscountSet,
120+
TimePromotionDiscountReset
121+
])
102122
event_store.subscribe(CalculateOrderTotalSubAmountsValue, to: [
103123
PriceItemAdded,
104124
PriceItemRemoved,
105125
PercentageDiscountSet,
106126
PercentageDiscountReset,
107127
PercentageDiscountChanged,
108128
ProductMadeFreeForOrder,
109-
FreeProductRemovedFromOrder
129+
FreeProductRemovedFromOrder,
130+
TimePromotionDiscountSet,
131+
TimePromotionDiscountReset
110132
])
111133
end
112134
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
module Pricing
2+
class ApplyTimePromotion
3+
def call(event)
4+
discount = PromotionsCalendar.new(event_store).current_time_promotions_discount
5+
6+
if discount.exists?
7+
command_bus.(SetTimePromotionDiscount.new(order_id: event.data.fetch(:order_id), amount: discount.value))
8+
else
9+
command_bus.(ResetTimePromotionDiscount.new(order_id: event.data.fetch(:order_id)))
10+
end
11+
end
12+
13+
private
14+
15+
def command_bus
16+
Pricing.command_bus
17+
end
18+
19+
def event_store
20+
Pricing.event_store
21+
end
22+
end
23+
end

ecommerce/pricing/lib/pricing/commands.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ class ResetPercentageDiscount < Infra::Command
4545
alias aggregate_id order_id
4646
end
4747

48+
class SetTimePromotionDiscount < Infra::Command
49+
attribute :order_id, Infra::Types::UUID
50+
attribute :amount, Infra::Types::PercentageDiscount
51+
52+
alias aggregate_id order_id
53+
end
54+
55+
class ResetTimePromotionDiscount < Infra::Command
56+
attribute :order_id, Infra::Types::UUID
57+
58+
alias aggregate_id order_id
59+
end
60+
4861
class RegisterCoupon < Infra::Command
4962
attribute :coupon_id, Infra::Types::UUID
5063
attribute :name, Infra::Types::String

ecommerce/pricing/lib/pricing/discounts.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ def add(other_discount)
3333
PercentageDiscount.new(new_value)
3434
end
3535

36+
def ==(other)
37+
value == other.value
38+
end
39+
3640
def exists?
3741
true
3842
end
@@ -57,7 +61,12 @@ def value
5761
0
5862
end
5963

64+
def ==(other)
65+
value == other.value
66+
end
67+
6068
def exists?
69+
false
6170
end
6271
end
6372
end

ecommerce/pricing/lib/pricing/events.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,16 @@ class PriceItemValueCalculated < Infra::Event
3434

3535
class PercentageDiscountSet < Infra::Event
3636
attribute :order_id, Infra::Types::UUID
37-
attribute :amount, Infra::Types::Price
37+
attribute :amount, Infra::Types::PercentageDiscount
38+
end
39+
40+
class TimePromotionDiscountSet < Infra::Event
41+
attribute :order_id, Infra::Types::UUID
42+
attribute :amount, Infra::Types::PercentageDiscount
43+
end
44+
45+
class TimePromotionDiscountReset < Infra::Event
46+
attribute :order_id, Infra::Types::UUID
3847
end
3948

4049
class PriceItemAdded < Infra::Event

ecommerce/pricing/lib/pricing/offer.rb

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ def initialize(id)
66
@id = id
77
@list = List.new
88
@discount = Discounts::NoPercentageDiscount.new
9+
@time_promotion_discount = Discounts::NoPercentageDiscount.new
910
end
1011

1112
def add_item(product_id)
@@ -36,6 +37,25 @@ def apply_discount(discount)
3637
)
3738
end
3839

40+
def apply_time_promotion_discount(time_promotion)
41+
return if time_promotion == @time_promotion_discount
42+
apply TimePromotionDiscountSet.new(
43+
data: {
44+
order_id: @id,
45+
amount: time_promotion.value
46+
}
47+
)
48+
end
49+
50+
def reset_time_promotion_discount
51+
return unless @time_promotion_discount.exists?
52+
apply TimePromotionDiscountReset.new(
53+
data: {
54+
order_id: @id
55+
}
56+
)
57+
end
58+
3959
def change_discount(discount)
4060
raise NotPossibleToChangeDiscount unless @discount.exists?
4161
apply PercentageDiscountChanged.new(
@@ -75,10 +95,10 @@ def remove_free_product(order_id, product_id)
7595
)
7696
end
7797

78-
def calculate_total_value(pricing_catalog, time_promotion_discount)
98+
def calculate_total_value(pricing_catalog)
7999
total_value = @list.base_sum(pricing_catalog)
80100

81-
discounted_value = @discount.add(time_promotion_discount).apply(total_value)
101+
discounted_value = @discount.add(@time_promotion_discount).apply(total_value)
82102
apply(
83103
OrderTotalValueCalculated.new(
84104
data: {
@@ -90,9 +110,9 @@ def calculate_total_value(pricing_catalog, time_promotion_discount)
90110
)
91111
end
92112

93-
def calculate_sub_amounts(pricing_catalog, time_promotions_discount)
113+
def calculate_sub_amounts(pricing_catalog)
94114
sub_amounts_total = @list.sub_amounts_total(pricing_catalog)
95-
sub_discounts = calculate_total_sub_discounts(pricing_catalog, time_promotions_discount)
115+
sub_discounts = calculate_total_sub_discounts(pricing_catalog, @time_promotion_discount)
96116

97117
products = @list.products
98118
quantities = @list.quantities
@@ -145,6 +165,14 @@ def use_coupon(coupon_id, discount)
145165
@discount = Discounts::PercentageDiscount.new(event.data.fetch(:amount))
146166
end
147167

168+
on TimePromotionDiscountSet do |event|
169+
@time_promotion_discount = Discounts::PercentageDiscount.new(event.data.fetch(:amount))
170+
end
171+
172+
on TimePromotionDiscountReset do |event|
173+
@time_promotion_discount = Discounts::NoPercentageDiscount.new
174+
end
175+
148176
on PercentageDiscountReset do |event|
149177
@discount = Discounts::NoPercentageDiscount.new
150178
end

ecommerce/pricing/lib/pricing/services.rb

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,30 @@ def call(cmd)
5050
end
5151
end
5252

53+
class SetTimePromotionDiscountHandler
54+
def initialize(event_store)
55+
@repository = Infra::AggregateRootRepository.new(event_store)
56+
end
57+
58+
def call(cmd)
59+
@repository.with_aggregate(Offer, cmd.aggregate_id) do |order|
60+
order.apply_time_promotion_discount(Discounts::PercentageDiscount.new(cmd.amount))
61+
end
62+
end
63+
end
64+
65+
class ResetTimePromotionDiscountHandler
66+
def initialize(event_store)
67+
@repository = Infra::AggregateRootRepository.new(event_store)
68+
end
69+
70+
def call(cmd)
71+
@repository.with_aggregate(Offer, cmd.aggregate_id) do |order|
72+
order.reset_time_promotion_discount
73+
end
74+
end
75+
end
76+
5377
class SetPriceHandler
5478
def initialize(event_store)
5579
@repository = Infra::AggregateRootRepository.new(event_store)
@@ -124,27 +148,18 @@ def initialize(event_store)
124148
def call(command)
125149
with_retry do
126150
@repository.with_aggregate(Offer, command.aggregate_id) do |order|
127-
order.calculate_total_value(PricingCatalog.new(@event_store), time_promotions_discount)
151+
order.calculate_total_value(PricingCatalog.new(@event_store))
128152
end
129153
end
130154
end
131155

132-
133-
134156
def calculate_sub_amounts(command)
135157
with_retry do
136158
@repository.with_aggregate(Offer, command.aggregate_id) do |order|
137-
order.calculate_sub_amounts(PricingCatalog.new(@event_store), time_promotions_discount)
159+
order.calculate_sub_amounts(PricingCatalog.new(@event_store))
138160
end
139161
end
140162
end
141-
142-
private
143-
144-
def time_promotions_discount
145-
PromotionsCalendar.new(@event_store).current_time_promotions_discount
146-
end
147-
148163
end
149164

150165
class OnCouponRegister
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
require_relative "test_helper"
2+
3+
module Pricing
4+
class ApplyTimePromotionTest < Test
5+
cover "Pricing*"
6+
7+
def test_applies_biggest_time_promotion_discount
8+
order_id = SecureRandom.uuid
9+
product_id = SecureRandom.uuid
10+
create_inactive_time_promotion(60)
11+
create_active_time_promotion(10)
12+
create_active_time_promotion(50)
13+
create_active_time_promotion(30)
14+
15+
assert_events_contain(stream_name(order_id), time_promotion_discount_set_event(order_id, 50)) do
16+
Pricing::ApplyTimePromotion.new.call(item_added_to_basket_event(order_id, product_id))
17+
end
18+
end
19+
20+
def test_resets_time_promotion_discount
21+
order_id = SecureRandom.uuid
22+
product_id = SecureRandom.uuid
23+
set_time_promotion_discount(order_id, 50)
24+
25+
assert_events_contain(stream_name(order_id), time_promotion_discount_reset_event(order_id)) do
26+
Pricing::ApplyTimePromotion.new.call(item_added_to_basket_event(order_id, product_id))
27+
end
28+
end
29+
30+
private
31+
32+
def create_inactive_time_promotion(discount)
33+
run_command(
34+
Pricing::CreateTimePromotion.new(
35+
time_promotion_id: SecureRandom.uuid,
36+
discount: discount,
37+
start_time: Time.current - 2,
38+
end_time: Time.current - 1,
39+
label: "Past Promotion"
40+
)
41+
)
42+
end
43+
44+
def create_active_time_promotion(discount)
45+
run_command(
46+
Pricing::CreateTimePromotion.new(
47+
time_promotion_id: SecureRandom.uuid,
48+
discount: discount,
49+
start_time: Time.current - 1,
50+
end_time: Time.current + 1,
51+
label: "Last Minute"
52+
)
53+
)
54+
end
55+
56+
def item_added_to_basket_event(order_id, product_id)
57+
Pricing::PriceItemAdded.new(
58+
data: {
59+
product_id: product_id,
60+
order_id: order_id
61+
}
62+
)
63+
end
64+
65+
def set_time_promotion_discount(order_id, discount)
66+
run_command(SetTimePromotionDiscount.new(order_id: order_id, amount: 50))
67+
end
68+
69+
def time_promotion_discount_set_event(order_id, amount)
70+
TimePromotionDiscountSet.new(
71+
data: {
72+
order_id: order_id,
73+
amount: amount
74+
}
75+
)
76+
end
77+
78+
def time_promotion_discount_reset_event(order_id)
79+
TimePromotionDiscountReset.new(
80+
data: {
81+
order_id: order_id
82+
}
83+
)
84+
end
85+
86+
def stream_name(order_id)
87+
"Pricing::Offer$#{order_id}"
88+
end
89+
end
90+
end

0 commit comments

Comments
 (0)