@@ -45,7 +45,7 @@ validate_mutations(_Mutations, _Details) ->
4545
4646validate_mutations_w_cart (Mutations , # domain_InvoiceCart {lines = Lines }) ->
4747 Mutations1 = genlib :define (Mutations , []),
48- amount_mutation_is_present (Mutations1 ) andalso cart_is_valid_for_mutation (Lines ) andalso
48+ amount_mutation_is_present (Mutations1 ) andalso cart_is_not_valid_for_mutation (Lines ) andalso
4949 throw (# base_InvalidRequest {
5050 errors = [<<" Amount mutation with multiline cart or multiple items in a line is not allowed" >>]
5151 }),
@@ -60,12 +60,12 @@ amount_mutation_is_present(Mutations) ->
6060 Mutations
6161 ).
6262
63- cart_is_valid_for_mutation (Lines ) ->
63+ cart_is_not_valid_for_mutation (Lines ) ->
6464 length (Lines ) > 1 orelse (hd (Lines ))# domain_InvoiceLine .quantity =/= 1 .
6565
66- - spec apply_mutations ([mutation_params ()] | undefined , Invoice ) -> Invoice when Invoice :: hg_invoice :invoice ().
67- apply_mutations (MutationsParams , Invoice ) ->
68- lists :foldl (fun apply_mutation /2 , Invoice , genlib :define (MutationsParams , [])).
66+ - spec apply_mutations ([mutation ()] | undefined , Invoice ) -> Invoice when Invoice :: hg_invoice :invoice ().
67+ apply_mutations (Mutations , Invoice ) ->
68+ lists :foldl (fun apply_mutation /2 , Invoice , genlib :define (Mutations , [])).
6969
7070apply_mutation (Mutation = {amount , # domain_InvoiceAmountMutation {mutated = NewAmount }}, Invoice ) ->
7171 # domain_Invoice {cost = Cost , mutations = Mutations } = Invoice ,
@@ -76,13 +76,18 @@ apply_mutation(Mutation = {amount, #domain_InvoiceAmountMutation{mutated = NewAm
7676apply_mutation (_ , Invoice ) ->
7777 Invoice .
7878
79- update_invoice_details_price (NewAmount , Invoice ) ->
80- # domain_Invoice {details = Details } = Invoice ,
81- # domain_InvoiceDetails {cart = Cart } = Details ,
82- # domain_InvoiceCart {lines = [Line ]} = Cart ,
83- NewLines = [update_invoice_line_price (NewAmount , Line )],
84- NewCart = Cart # domain_InvoiceCart {lines = NewLines },
85- Invoice # domain_Invoice {details = Details # domain_InvoiceDetails {cart = NewCart }}.
79+ update_invoice_details_price (NewAmount , Invoice = # domain_Invoice {details = Details }) ->
80+ case Details # domain_InvoiceDetails .cart of
81+ Cart = # domain_InvoiceCart {lines = [Line ]} ->
82+ NewCart = Cart # domain_InvoiceCart {
83+ lines = [update_invoice_line_price (NewAmount , Line )]
84+ },
85+ Invoice # domain_Invoice {
86+ details = Details # domain_InvoiceDetails {cart = NewCart }
87+ };
88+ _ ->
89+ Invoice
90+ end .
8691
8792update_invoice_line_price (NewAmount , Line = # domain_InvoiceLine {price = Price }) ->
8893 Line # domain_InvoiceLine {price = Price # domain_Cash {amount = NewAmount }}.
@@ -114,11 +119,22 @@ make_mutation(
114119make_mutation (_ , {Mutations , Context }) ->
115120 {Mutations , Context }.
116121
117- calc_new_amount (Amount , # domain_RandomizationMutationParams {deviation = MaxDeviation , precision = Precision }) ->
122+ calc_new_amount (Amount , # domain_RandomizationMutationParams {
123+ deviation = MaxDeviation ,
124+ precision = Precision ,
125+ direction = Direction
126+ }) ->
118127 Deviation = calc_deviation (MaxDeviation , trunc (math :pow (10 , Precision ))),
119- Sign = trunc ( math : pow ( - 1 , rand : uniform ( 2 )) ),
128+ Sign = calc_sign ( Direction ),
120129 Amount + Sign * Deviation .
121130
131+ calc_sign (upward ) ->
132+ 1 ;
133+ calc_sign (downward ) ->
134+ - 1 ;
135+ calc_sign (both ) ->
136+ trunc (math :pow (- 1 , rand :uniform (2 ))).
137+
122138calc_deviation (MaxDeviation , PrecisionFactor ) ->
123139 Deviation0 = rand :uniform (MaxDeviation + 1 ) - 1 ,
124140 erlang :round (Deviation0 / PrecisionFactor ) * PrecisionFactor .
@@ -130,11 +146,12 @@ calc_deviation(MaxDeviation, PrecisionFactor) ->
130146
131147- spec test () -> _ .
132148
133- - define (mutations (Deviation , Precision , Min , Max , Multiplicity ), [
149+ - define (mutations (Deviation , Precision , Direction , Min , Max , Multiplicity ), [
134150 {amount ,
135151 {randomization , # domain_RandomizationMutationParams {
136152 deviation = Deviation ,
137153 precision = Precision ,
154+ direction = Direction ,
138155 min_amount_condition = Min ,
139156 max_amount_condition = Max ,
140157 amount_multiplicity_condition = Multiplicity
@@ -145,7 +162,7 @@ calc_deviation(MaxDeviation, PrecisionFactor) ->
145162
146163- define (currency (), # domain_CurrencyRef {symbolic_code = <<" RUB" >>}).
147164
148- - define (invoice (Amount , Lines , Mutations ), # domain_Invoice {
165+ - define (invoice_w_cart (Amount , Cart , Mutations ), # domain_Invoice {
149166 id = <<" invoice" >>,
150167 shop_id = <<" shop_id" >>,
151168 owner_id = <<" owner_id" >>,
@@ -155,34 +172,61 @@ calc_deviation(MaxDeviation, PrecisionFactor) ->
155172 due = <<" 1970-01-01T00:00:00Z" >>,
156173 details = # domain_InvoiceDetails {
157174 product = <<" rubberduck" >>,
158- cart = # domain_InvoiceCart { lines = Lines }
175+ cart = Cart
159176 },
160177 mutations = Mutations
161178}).
162179
180+ - define (invoice (Amount , Lines , Mutations ),
181+ ? invoice_w_cart (Amount , # domain_InvoiceCart {lines = Lines }, Mutations )
182+ ).
183+
184+ - define (invoice_wo_cart (Amount , Mutations ), ? invoice_w_cart (Amount , undefined , Mutations )).
185+
163186- define (mutated_invoice (OriginalAmount , MutatedAmount , Lines ),
164187 ? invoice (MutatedAmount , Lines , [
165188 {amount , # domain_InvoiceAmountMutation {original = OriginalAmount , mutated = MutatedAmount }}
166189 ])
167190).
168191
192+ - define (mutated_invoice_wo_cart (OriginalAmount , MutatedAmount ),
193+ ? invoice_wo_cart (MutatedAmount , [
194+ {amount , # domain_InvoiceAmountMutation {original = OriginalAmount , mutated = MutatedAmount }}
195+ ])
196+ ).
197+
169198- define (not_mutated_invoice (Amount , Lines ), ? invoice (Amount , Lines , undefined )).
170199
200+ - define (not_mutated_invoice_wo_cart (Amount ), ? invoice_wo_cart (Amount , undefined )).
201+
171202- define (cart_line (Price ), # domain_InvoiceLine {
172203 product = <<" product" >>,
173204 quantity = 1 ,
174205 price = ? cash (Price ),
175206 metadata = #{}
176207}).
177208
209+ % % Assert helper
210+
211+ - define (_assertInvoiceMutation (Amount , From , To , Multiplicity , Mutations ),
212+ ? _assertMatch (
213+ ? mutated_invoice (Amount , A , [? cart_line (A )]) when
214+ A >= From andalso A =< To andalso A rem Multiplicity =:= 0 ,
215+ apply_mutations (
216+ make_mutations (Mutations , #{cost => ? cash (Amount )}),
217+ ? not_mutated_invoice (Amount , [? cart_line (Amount )])
218+ )
219+ )
220+ ).
221+
178222- spec apply_mutations_test_ () -> [_TestGen ].
179223apply_mutations_test_ () ->
180224 lists :flatten ([
181225 % % Didn't mutate because of conditions
182226 ? _assertEqual (
183227 ? not_mutated_invoice (1000_00 , [? cart_line (1000_00 )]),
184228 apply_mutations (
185- make_mutations (? mutations (100_00 , 2 , 0 , 100_00 , 1_00 ), #{
229+ make_mutations (? mutations (100_00 , 2 , both , 0 , 100_00 , 1_00 ), #{
186230 cost => ? cash (1000_00 )
187231 }),
188232 ? not_mutated_invoice (1000_00 , [? cart_line (1000_00 )])
@@ -191,7 +235,7 @@ apply_mutations_test_() ->
191235 ? _assertEqual (
192236 ? not_mutated_invoice (1234_00 , [? cart_line (1234_00 )]),
193237 apply_mutations (
194- make_mutations (? mutations (100_00 , 2 , 0 , 1000_00 , 7_00 ), #{
238+ make_mutations (? mutations (100_00 , 2 , both , 0 , 1000_00 , 7_00 ), #{
195239 cost => ? cash (1234_00 )
196240 }),
197241 ? not_mutated_invoice (1234_00 , [? cart_line (1234_00 )])
@@ -202,7 +246,7 @@ apply_mutations_test_() ->
202246 ? _assertEqual (
203247 ? mutated_invoice (100_00 , 100_00 , [? cart_line (100_00 )]),
204248 apply_mutations (
205- make_mutations (? mutations (0 , 2 , 0 , 1000_00 , 1_00 ), #{
249+ make_mutations (? mutations (0 , 2 , both , 0 , 1000_00 , 1_00 ), #{
206250 cost => ? cash (100_00 )
207251 }),
208252 ? not_mutated_invoice (100_00 , [? cart_line (100_00 )])
@@ -219,20 +263,31 @@ apply_mutations_test_() ->
219263 ? not_mutated_invoice (100_00 , [? cart_line (100_00 )])
220264 )
221265 )
222- || Mutations <- lists :duplicate (10 , ? mutations (100_00 , 4 , 0 , 1000_00 , 1_00 ))
266+ || Mutations <- lists :duplicate (10 , ? mutations (100_00 , 4 , both , 0 , 1000_00 , 1_00 ))
223267 ],
224268
225269 % % Deviate in segment [900_00, 1100_00] without minor units
226270 [
227- ? _assertMatch (
228- ? mutated_invoice (1000_00 , A , [? cart_line (A )]) when
229- A >= 900_00 andalso A =< 1100_00 andalso A rem 100 =:= 0 ,
230- apply_mutations (
231- make_mutations (Mutations , #{cost => ? cash (1000_00 )}),
232- ? not_mutated_invoice (1000_00 , [? cart_line (1000_00 )])
233- )
234- )
235- || Mutations <- lists :duplicate (10 , ? mutations (100_00 , 2 , 0 , 1000_00 , 1_00 ))
271+ ? _assertInvoiceMutation (1000_00 , 900_00 , 1100_00 , 100 , Mutations )
272+ || Mutations <- lists :duplicate (10 , ? mutations (100_00 , 2 , both , 0 , 1000_00 , 1_00 ))
273+ ],
274+
275+ % % Deviate a bigger segment but w/o cart details
276+ [
277+ ? _assertInvoiceMutation (20_000_00 , 18_000_00 , 22_000_00 , 1000 , Mutations )
278+ || Mutations <- lists :duplicate (10 , ? mutations (2_000_00 , 4 , both , 0 , 100_000_00 , 1_00 ))
279+ ],
280+
281+ % % Deviate only upward
282+ [
283+ ? _assertInvoiceMutation (1000_00 , 1000_00 , 1100_00 , 100 , Mutations )
284+ || Mutations <- lists :duplicate (10 , ? mutations (100_00 , 2 , upward , 0 , 1000_00 , 1_00 ))
285+ ],
286+
287+ % % Deviate only downward
288+ [
289+ ? _assertInvoiceMutation (1000_00 , 900_00 , 1000_00 , 100 , Mutations )
290+ || Mutations <- lists :duplicate (10 , ? mutations (100_00 , 2 , downward , 0 , 1000_00 , 1_00 ))
236291 ]
237292 ]).
238293
0 commit comments