@@ -11,6 +11,7 @@ class InvoiceGeneration
1111 )
1212
1313 def act
14+ calculate_sub_amounts
1415 end
1516
1617 private
@@ -20,11 +21,138 @@ def fetch_id(event)
2021 end
2122
2223 def apply ( event )
24+ @order_id = event . data . fetch ( :order_id )
25+ case event
26+ when Pricing ::PriceItemAdded
27+ apply_price_item_added ( event )
28+ when Pricing ::PriceItemRemoved
29+ apply_price_item_removed ( event )
30+ when Pricing ::PercentageDiscountSet
31+ apply_percentage_discount_set ( event )
32+ when Pricing ::PercentageDiscountChanged
33+ apply_percentage_discount_changed ( event )
34+ when Pricing ::PercentageDiscountRemoved
35+ apply_percentage_discount_removed ( event )
36+ else
37+ state
38+ end
39+ end
40+
41+ def calculate_sub_amounts
42+ sub_amounts_total = state . sub_amounts_total
43+
44+ sub_amounts_total . each_pair do |product_id , h |
45+ publish_invoice_item_value_calculated (
46+ product_id : product_id ,
47+ quantity : h . fetch ( :quantity ) ,
48+ amount : h . fetch ( :base_amount ) ,
49+ discounted_amount : h . fetch ( :amount )
50+ )
51+ end
52+ end
53+
54+ def publish_invoice_item_value_calculated ( product_id :, quantity :, amount :, discounted_amount :)
55+ event_store . publish (
56+ InvoiceItemValueCalculated . new (
57+ data : {
58+ order_id : @order_id ,
59+ product_id : product_id ,
60+ quantity : quantity ,
61+ amount : amount ,
62+ discounted_amount : discounted_amount
63+ }
64+ ) ,
65+ stream_name : "Processes::InvoiceGeneration$#{ @order_id } "
66+ )
67+ end
68+
69+ def apply_price_item_added ( event )
70+ product_id = event . data . fetch ( :product_id )
71+ base_price = event . data . fetch ( :base_price )
72+ price = event . data . fetch ( :price )
73+ lines = ( state . lines + [ { product_id :, base_price :, price : } ] )
74+ state . with ( lines :)
75+ end
76+
77+ def apply_price_item_removed ( event )
78+ product_id = event . data . fetch ( :product_id )
79+ lines = state . lines . dup
80+ index_to_remove = lines . find_index { |line | line . fetch ( :product_id ) == product_id }
81+ lines . delete_at ( index_to_remove )
82+ state . with ( lines :)
83+ end
84+
85+ def apply_percentage_discount_set ( event )
86+ discount_type = event . data . fetch ( :type )
87+ discount_amount = event . data . fetch ( :amount )
88+ discounts = state . discounts . reject { |d | d . fetch ( :type ) == discount_type }
89+ discounts = discounts + [ { type : discount_type , amount : discount_amount } ]
90+ new_state = state . with ( discounts :)
91+ apply_discounts_to_existing_lines ( new_state )
92+ end
93+
94+ def apply_percentage_discount_changed ( event )
95+ discount_type = event . data . fetch ( :type )
96+ discount_amount = event . data . fetch ( :amount )
97+ discounts = state . discounts . reject { |d | d . fetch ( :type ) == discount_type }
98+ discounts = discounts + [ { amount : discount_amount } ]
99+ new_state = state . with ( discounts : discounts )
100+ apply_discounts_to_existing_lines ( new_state )
101+ end
102+
103+ def apply_percentage_discount_removed ( event )
104+ discount_type = event . data . fetch ( :type )
105+ discounts = state . discounts . reject { |d | d . fetch ( :type ) == discount_type }
106+ new_state = state . with ( discounts :)
107+ apply_discounts_to_existing_lines ( new_state )
108+ end
109+
110+ def apply_discounts_to_existing_lines ( new_state )
111+ total_discount_percentage = new_state . total_discount_percentage
112+ final_discount_percentage = [ total_discount_percentage , 100 ] . min
113+ discount_multiplier = ( 1 - final_discount_percentage / 100.0 )
114+
115+ updated_lines = new_state . lines . map do |line |
116+ base_price = line . fetch ( :base_price )
117+ discounted_price = base_price * discount_multiplier
118+ line . merge ( price : discounted_price )
119+ end
120+
121+ new_state . with ( lines : updated_lines )
23122 end
24123
25124 end
26125
27- Invoice = Data . define do
126+ Invoice = Data . define ( :lines , :discounts ) do
127+ def initialize ( lines : [ ] , discounts : [ ] )
128+ super ( lines : lines . freeze , discounts : discounts . freeze )
129+ end
130+
131+ def sub_amounts_total
132+ lines . each_with_object ( { } ) do |line , memo |
133+ product_id = line . fetch ( :product_id )
134+ memo [ product_id ] ||= { base_amount : 0 , amount : 0 , quantity : 0 }
135+ memo [ product_id ] [ :base_amount ] += line . fetch ( :base_price )
136+ memo [ product_id ] [ :amount ] += line . fetch ( :price )
137+ memo [ product_id ] [ :quantity ] += 1
138+ end
139+ end
140+
141+ def subtotal
142+ lines . sum { |line | line . fetch ( :price ) }
143+ end
144+
145+ def total_discount_percentage
146+ discounts . sum { |discount | discount . fetch ( :amount ) }
147+ end
148+
149+ def final_discount_percentage
150+ [ total_discount_percentage , 100 ] . min
151+ end
152+
153+ def discounted_value
154+ subtotal * ( 1 - final_discount_percentage / 100.0 )
155+ end
28156 end
29157
30158end
0 commit comments