Skip to content

Commit 1c99fe0

Browse files
authored
TD-933: Adds support for deviation direction for amount randomization (#137)
* TD-933: Adds support for deviation direction for amount randomization * Bumps damsel * Fixes arg naming
1 parent 47b4b8e commit 1c99fe0

File tree

2 files changed

+86
-31
lines changed

2 files changed

+86
-31
lines changed

apps/hellgate/src/hg_invoice_mutation.erl

Lines changed: 85 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ validate_mutations(_Mutations, _Details) ->
4545

4646
validate_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

7070
apply_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
7676
apply_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

8792
update_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(
114119
make_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+
122138
calc_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].
179223
apply_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

rebar.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
{<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},2},
2222
{<<"damsel">>,
2323
{git,"https://github.com/valitydev/damsel.git",
24-
{ref,"c170117e5fde4ebdc6878e75dcd37ca2779dfb82"}},
24+
{ref,"e5d3c83a240ba716348c6fbd816ea8e4d864472f"}},
2525
0},
2626
{<<"dmt_client">>,
2727
{git,"https://github.com/valitydev/dmt-client.git",

0 commit comments

Comments
 (0)