Skip to content

Commit

Permalink
Add/remove item from refund
Browse files Browse the repository at this point in the history
  • Loading branch information
marlena-b committed Dec 17, 2024
1 parent aebb8c9 commit 119b320
Show file tree
Hide file tree
Showing 17 changed files with 508 additions and 12 deletions.
6 changes: 6 additions & 0 deletions ecommerce/ordering/lib/ordering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
require_relative "ordering/events/order_submitted"
require_relative "ordering/events/order_rejected"
require_relative "ordering/events/draft_refund_created"
require_relative "ordering/events/item_added_to_refund"
require_relative "ordering/events/item_removed_from_refund"
require_relative "ordering/commands/add_item_to_basket"
require_relative "ordering/commands/remove_item_from_basket"
require_relative "ordering/commands/submit_order"
require_relative "ordering/commands/set_order_as_expired"
require_relative "ordering/commands/accept_order"
require_relative "ordering/commands/reject_order"
require_relative "ordering/commands/create_draft_refund"
require_relative "ordering/commands/add_item_to_refund"
require_relative "ordering/commands/remove_item_from_refund"
require_relative "ordering/fake_number_generator"
require_relative "ordering/number_generator"
require_relative "ordering/service"
Expand All @@ -36,6 +40,8 @@ def call(event_store, command_bus)
command_bus.register(AcceptOrder, OnAcceptOrder.new(event_store))
command_bus.register(RejectOrder, OnRejectOrder.new(event_store))
command_bus.register(CreateDraftRefund, OnCreateDraftRefund.new(event_store))
command_bus.register(AddItemToRefund, OnAddItemToRefund.new(event_store))
command_bus.register(RemoveItemFromRefund, OnRemoveItemFromRefund.new(event_store))
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Ordering
class AddItemToRefund < Infra::Command
attribute :refund_id, Infra::Types::UUID
attribute :order_id, Infra::Types::UUID
attribute :product_id, Infra::Types::UUID

alias aggregate_id refund_id
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Ordering
class RemoveItemFromRefund < Infra::Command
attribute :refund_id, Infra::Types::UUID
attribute :order_id, Infra::Types::UUID
attribute :product_id, Infra::Types::UUID

alias aggregate_id refund_id
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Ordering
class ItemAddedToRefund < Infra::Event
attribute :refund_id, Infra::Types::UUID
attribute :order_id, Infra::Types::UUID
attribute :product_id, Infra::Types::UUID
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Ordering
class ItemRemovedFromRefund < Infra::Event
attribute :refund_id, Infra::Types::UUID
attribute :order_id, Infra::Types::UUID
attribute :product_id, Infra::Types::UUID
end
end
41 changes: 41 additions & 0 deletions ecommerce/ordering/lib/ordering/refund.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,57 @@ module Ordering
class Refund
include AggregateRoot

ProductNotFoundError = Class.new(StandardError)

def initialize(id)
@id = id
@refund_items = ItemsList.new
end

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

def add_item(product_id)
apply ItemAddedToRefund.new(data: { refund_id: @id, order_id: @order_id, product_id: product_id })
end

def remove_item(product_id)
raise ProductNotFoundError unless @refund_items.quantity(product_id).positive?
apply ItemRemovedFromRefund.new(data: { refund_id: @id, order_id: @order_id, product_id: product_id })
end

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

on ItemAddedToRefund do |event|
@refund_items.increase_quantity(event.data[:product_id])
end

on ItemRemovedFromRefund do |event|
@refund_items.decrease_quantity(event.data[:product_id])
end
end

class ItemsList
attr_reader :refund_items

def initialize
@refund_items = Hash.new(0)
end

def increase_quantity(product_id)
refund_items[product_id] = quantity(product_id) + 1
end

def decrease_quantity(product_id)
refund_items[product_id] -= 1
refund_items.delete(product_id) if refund_items.fetch(product_id).equal?(0)
end

def quantity(product_id)
refund_items[product_id]
end
end
end
24 changes: 24 additions & 0 deletions ecommerce/ordering/lib/ordering/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,28 @@ def call(command)
end
end
end

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

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

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

def call(command)
@repository.with_aggregate(Refund, command.aggregate_id) do |refund|
refund.remove_item(command.product_id)
end
end
end
end
41 changes: 41 additions & 0 deletions ecommerce/ordering/test/add_item_to_refund_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require_relative "test_helper"

module Ordering
class AddItemToRefundTest < Test
cover "Ordering::OnAddItemToRefund*"

def test_add_item_to_refund
order_id = SecureRandom.uuid
aggregate_id = SecureRandom.uuid
product_id = SecureRandom.uuid
stream = "Ordering::Refund$#{aggregate_id}"

arrange(
CreateDraftRefund.new(
refund_id: aggregate_id,
order_id: order_id
)
)

expected_events = [
ItemAddedToRefund.new(
data: {
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
}
)
]

assert_events(stream, *expected_events) do
act(
AddItemToRefund.new(
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
)
)
end
end
end
end
69 changes: 69 additions & 0 deletions ecommerce/ordering/test/remove_item_from_refund_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
require_relative "test_helper"

module Ordering
class RemoveItemFromRefundTest < Test
cover "Ordering::OnRemoveItemFromRefund*"

def test_removing_items_from_refund
order_id = SecureRandom.uuid
aggregate_id = SecureRandom.uuid
product_id = SecureRandom.uuid
stream = "Ordering::Refund$#{aggregate_id}"

arrange(
CreateDraftRefund.new(
refund_id: aggregate_id,
order_id: order_id
),
AddItemToRefund.new(
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
)
)

expected_events = [
ItemRemovedFromRefund.new(
data: {
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
}
)
]

assert_events(stream, *expected_events) do
act(
RemoveItemFromRefund.new(
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
)
)
end
end

def test_can_remove_only_added_items
order_id = SecureRandom.uuid
aggregate_id = SecureRandom.uuid
product_id = SecureRandom.uuid

arrange(
CreateDraftRefund.new(
refund_id: aggregate_id,
order_id: order_id
)
)

assert_raises(Refund::ProductNotFoundError) do
act(
RemoveItemFromRefund.new(
refund_id: aggregate_id,
order_id: order_id,
product_id: product_id
)
)
end
end
end
end
24 changes: 24 additions & 0 deletions rails_application/app/controllers/refunds_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ def create
redirect_to edit_order_refund_path(refund_id, order_id: params[:order_id])
end

def add_item
add_item_to_refund
end

def remove_item
remove_item_from_refund
end

private

def create_draft_refund_cmd(refund_id)
Expand All @@ -21,4 +29,20 @@ def create_draft_refund_cmd(refund_id)
def create_draft_refund(refund_id)
command_bus.(create_draft_refund_cmd(refund_id))
end

def add_item_to_refund_cmd
Ordering::AddItemToRefund.new(refund_id: params[:id], order_id: params[:order_id], product_id: params[:product_id])
end

def add_item_to_refund
command_bus.(add_item_to_refund_cmd)
end

def remove_item_from_refund_cmd
Ordering::RemoveItemFromRefund.new(refund_id: params[:id], order_id: params[:order_id], product_id: params[:product_id])
end

def remove_item_from_refund
command_bus.(remove_item_from_refund_cmd)
end
end
40 changes: 40 additions & 0 deletions rails_application/app/read_models/refunds/add_item_to_refund.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module Refunds
class AddItemToRefund
def call(event)
refund_id = event.data.fetch(:refund_id)
Refund.find_or_create_by!(uid: refund_id)
product_id = event.data.fetch(:product_id)
item =
find(refund_id, product_id) ||
create(refund_id, product_id)
item.quantity += 1
item.save!
end

private

def event_store
Rails.configuration.event_store
end

def find(refund_id, product_id)
Refund
.find_by_uid(refund_id)
.refund_items
.where(product_uid: product_id)
.first
end

def create(refund_id, product_id)
product = Orders::Product.find_by_uid(product_id)
Refund
.find_by(uid: refund_id)
.refund_items
.create(
product_uid: product_id,
price: product.price,
quantity: 0
)
end
end
end
4 changes: 3 additions & 1 deletion rails_application/app/read_models/refunds/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class Refund < ApplicationRecord

has_many :refund_items,
class_name: "Refunds::RefundItem",
foreign_key: :order_uid,
foreign_key: :refund_uid,
primary_key: :uid
end

Expand All @@ -15,6 +15,8 @@ class RefundItem < ApplicationRecord
class Configuration
def call(event_store)
event_store.subscribe(CreateDraftRefund.new, to: [Ordering::DraftRefundCreated])
event_store.subscribe(AddItemToRefund.new, to: [Ordering::ItemAddedToRefund])
event_store.subscribe(RemoveItemFromRefund.new, to: [Ordering::ItemRemovedFromRefund])
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Refunds
class RemoveItemFromRefund
def call(event)
refund_id = event.data.fetch(:refund_id)
product_id = event.data.fetch(:product_id)
item = find(refund_id, product_id)
item.quantity -= 1
item.quantity > 0 ? item.save! : item.destroy!
end

private
def find(order_uid, product_id)
Refund
.find_by_uid(order_uid)
.refund_items
.where(product_uid: product_id)
.first
end

def event_store
Rails.configuration.event_store
end
end
end
Loading

0 comments on commit 119b320

Please sign in to comment.