Skip to content

Commit

Permalink
Pass refundable_products when initializing a refund
Browse files Browse the repository at this point in the history
  • Loading branch information
marlena-b committed Jan 21, 2025
1 parent c6bf8c1 commit fb29492
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 77 deletions.
2 changes: 1 addition & 1 deletion ecommerce/ordering/lib/ordering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
require_relative "ordering/service"
require_relative "ordering/order"
require_relative "ordering/refund"
require_relative "ordering/product_quantity_available_to_refund"
require_relative "ordering/refundable_products"

module Ordering
class Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@ module Ordering
class DraftRefundCreated < Infra::Event
attribute :refund_id, Infra::Types::UUID
attribute :order_id, Infra::Types::UUID
attribute :refundable_products, Infra::Types::Array.of(
Infra::Types::Hash.schema(
product_id: Infra::Types::UUID,
quantity: Infra::Types::Integer
)
)
end
end

This file was deleted.

18 changes: 12 additions & 6 deletions ecommerce/ordering/lib/ordering/refund.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ def initialize(id)
@refund_items = ItemsList.new
end

def create_draft(order_id)
apply DraftRefundCreated.new(data: { refund_id: @id, order_id: order_id })
def create_draft(order_id, refundable_products)
apply DraftRefundCreated.new(data: { refund_id: @id, order_id: order_id, refundable_products: refundable_products })
end

def add_item(product_id, available_quantity_to_refund)
raise ExceedsOrderQuantityError unless enough_items?(available_quantity_to_refund, product_id)
def add_item(product_id)
raise ExceedsOrderQuantityError unless enough_items?(product_id)
apply ItemAddedToRefund.new(data: { refund_id: @id, order_id: @order_id, product_id: product_id })
end

Expand All @@ -26,6 +26,7 @@ def remove_item(product_id)

on DraftRefundCreated do |event|
@order_id = event.data[:order_id]
@refundable_products = event.data[:refundable_products]
end

on ItemAddedToRefund do |event|
Expand All @@ -38,8 +39,13 @@ def remove_item(product_id)

private

def enough_items?(available_quantity_to_refund, product_id)
@refund_items.quantity(product_id) < available_quantity_to_refund
def enough_items?(product_id)
@refund_items.quantity(product_id) < refundable_quantity(product_id)
end

def refundable_quantity(product_id)
product = @refundable_products.find { |product| product.fetch(:product_id) == product_id }
product.fetch(:quantity)
end
end

Expand Down
32 changes: 32 additions & 0 deletions ecommerce/ordering/lib/ordering/refundable_products.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module Ordering
class RefundableProducts
class << self
def call(order_id)
RubyEventStore::Projection
.from_stream("Ordering::Order$#{order_id}")
.init(-> { [] })
.when(ItemAddedToBasket, -> (state, event) { increase_quantity(state, event.data.fetch(:product_id)) })
.when(ItemRemovedFromBasket, -> (state, event) { decrease_quantity(state, event.data.fetch(:product_id)) })
end

private

def increase_quantity(state, product_id)
prod_quantity = state.find { |prod_quantity| prod_quantity.fetch(:product_id) == product_id }

if prod_quantity
prod_quantity[:quantity] += 1
else
state << { product_id: product_id, quantity: 1 }
end
end

def decrease_quantity(state, product_id)
prod_quantity = state.find { |prod_quantity| prod_quantity.fetch(:product_id) == product_id }

prod_quantity[:quantity] -= 1
state.delete(prod_quantity) if prod_quantity.fetch(:quantity).zero?
end
end
end
end
29 changes: 14 additions & 15 deletions ecommerce/ordering/lib/ordering/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,38 +76,37 @@ def call(command)
class OnCreateDraftRefund
def initialize(event_store)
@repository = Infra::AggregateRootRepository.new(event_store)
@event_store = event_store
end

def call(command)
@repository.with_aggregate(Refund, command.aggregate_id) do |refund|
refund.create_draft(command.order_id)
refund.create_draft(
command.order_id,
refundable_products(command.order_id)
)
end
end

private

def refundable_products(order_id)
RefundableProducts
.call(order_id)
.run(@event_store)
end
end

class OnAddItemToRefund
def initialize(event_store)
@repository = Infra::AggregateRootRepository.new(event_store)
@event_store = event_store
end

def call(command)
@repository.with_aggregate(Refund, command.aggregate_id) do |refund|
refund.add_item(
command.product_id,
available_quantity_to_refund(command.order_id, command.product_id)
)
refund.add_item(command.product_id)
end
end

private

def available_quantity_to_refund(order_id, product_id)
ProductQuantityAvailableToRefund
.call(order_id, product_id)
.run(@event_store)
.fetch(:available)
end
end

class OnRemoveItemFromRefund
Expand Down
23 changes: 15 additions & 8 deletions ecommerce/ordering/test/add_item_to_refund_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,30 @@ class AddItemToRefundTest < Test
def test_add_item_to_refund
order_id = SecureRandom.uuid
aggregate_id = SecureRandom.uuid
product_id = SecureRandom.uuid
product_1_id = SecureRandom.uuid
product_2_id = SecureRandom.uuid
product_3_id = SecureRandom.uuid
stream = "Ordering::Refund$#{aggregate_id}"

arrange(
AddItemToBasket.new(order_id: order_id, product_id: product_id),
CreateDraftRefund.new(
refund_id: aggregate_id,
order_id: order_id
AddItemToBasket.new(order_id: order_id, product_id: product_1_id),
AddItemToBasket.new(order_id: order_id, product_id: product_2_id),
AddItemToBasket.new(order_id: order_id, product_id: product_2_id),
AddItemToBasket.new(order_id: order_id, product_id: product_3_id),
CreateDraftRefund.new(refund_id: aggregate_id, order_id: order_id),
AddItemToRefund.new(
refund_id: aggregate_id,
order_id: order_id,
product_id: product_2_id
)
)
)

expected_events = [
ItemAddedToRefund.new(
data: {
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
product_id: product_2_id
}
)
]
Expand All @@ -33,7 +40,7 @@ def test_add_item_to_refund
AddItemToRefund.new(
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
product_id: product_2_id
)
)
end
Expand Down
3 changes: 2 additions & 1 deletion ecommerce/ordering/test/create_draft_refund_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ def test_draft_refund_created
DraftRefundCreated.new(
data: {
refund_id: aggregate_id,
order_id: order_id
order_id: order_id,
refundable_products: []
}
)
]
Expand Down

This file was deleted.

29 changes: 29 additions & 0 deletions ecommerce/ordering/test/refundable_products_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require_relative "test_helper"

module Ordering
class RefundableProductsTest < Test
cover "Ordering::RefundableProducts"

def test_product_quantity_available_to_refund
order_id = SecureRandom.uuid
product_1_id = SecureRandom.uuid
product_2_id = SecureRandom.uuid
product_3_id = SecureRandom.uuid
stream_name = "Ordering::Order$#{order_id}"
projection = RefundableProducts.call(order_id)

event_store = RubyEventStore::Client.new(repository: RubyEventStore::InMemoryRepository.new)

event_store.publish(ItemAddedToBasket.new(data: { order_id: order_id, product_id: product_3_id }), stream_name: stream_name)
event_store.publish(ItemAddedToBasket.new(data: { order_id: order_id, product_id: product_1_id }), stream_name: stream_name)
event_store.publish(ItemAddedToBasket.new(data: { order_id: order_id, product_id: product_2_id }), stream_name: stream_name)
event_store.publish(ItemAddedToBasket.new(data: { order_id: order_id, product_id: product_2_id }), stream_name: stream_name)
event_store.publish(ItemRemovedFromBasket.new(data: { order_id: order_id, product_id: product_2_id }), stream_name: stream_name)
event_store.publish(ItemRemovedFromBasket.new(data: { order_id: order_id, product_id: product_3_id }), stream_name: stream_name)

refundable_products = projection.run(event_store)

assert_equal([{ product_id: product_1_id, quantity: 1}, {product_id: product_2_id, quantity: 1 }], refundable_products)
end
end
end
15 changes: 10 additions & 5 deletions ecommerce/ordering/test/remove_item_from_refund_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,24 @@ class RemoveItemFromRefundTest < Test
def test_removing_items_from_refund
order_id = SecureRandom.uuid
aggregate_id = SecureRandom.uuid
product_id = SecureRandom.uuid
product_1_id = SecureRandom.uuid
product_2_id = SecureRandom.uuid
product_3_id = SecureRandom.uuid
stream = "Ordering::Refund$#{aggregate_id}"

arrange(
AddItemToBasket.new(order_id: order_id, product_id: product_id),
AddItemToBasket.new(order_id: order_id, product_id: product_1_id),
AddItemToBasket.new(order_id: order_id, product_id: product_2_id),
AddItemToBasket.new(order_id: order_id, product_id: product_2_id),
AddItemToBasket.new(order_id: order_id, product_id: product_3_id),
CreateDraftRefund.new(
refund_id: aggregate_id,
order_id: order_id
),
AddItemToRefund.new(
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
product_id: product_1_id
)
)

Expand All @@ -28,7 +33,7 @@ def test_removing_items_from_refund
data: {
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
product_id: product_1_id
}
)
]
Expand All @@ -38,7 +43,7 @@ def test_removing_items_from_refund
RemoveItemFromRefund.new(
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
product_id: product_1_id
)
)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ def test_remove_item_from_refund
product_id = SecureRandom.uuid
another_product_id = SecureRandom.uuid
order_id = SecureRandom.uuid
create_draft_refund(refund_id, order_id)
refundable_products = [{product_id: product_id, quantity: 1}, {product_id: another_product_id, quantity: 1}]
create_draft_refund(refund_id, order_id, refundable_products)
prepare_product(product_id, 50)
prepare_product(another_product_id, 30)
AddItemToRefund.new.call(item_added_to_refund(refund_id, order_id, product_id))
Expand All @@ -30,8 +31,8 @@ def test_remove_item_from_refund

private

def create_draft_refund(refund_id, order_id)
draft_refund_created = Ordering::DraftRefundCreated.new(data: { refund_id: refund_id, order_id: order_id })
def create_draft_refund(refund_id, order_id, refundable_products)
draft_refund_created = Ordering::DraftRefundCreated.new(data: { refund_id: refund_id, order_id: order_id, refundable_products: refundable_products })
CreateDraftRefund.new.call(draft_refund_created)
end

Expand Down

0 comments on commit fb29492

Please sign in to comment.