mirror of
https://github.com/valitydev/limiter.git
synced 2024-11-06 00:55:22 +00:00
add subtraction behaviour in limit amount computation
This commit is contained in:
parent
9bf3e29636
commit
4beba47b5c
@ -30,7 +30,7 @@ get_body(BodyType, Config = #{body_type := {cash, ConfigCurrency}}, LimitContext
|
||||
case get_body_for_operation(BodyType, Operation, Config, LimitContext) of
|
||||
{ok, {cash, #{currency := ConfigCurrency}}} = Result ->
|
||||
Result;
|
||||
{ok, {cash, #{amount := Amount, currency := Currency}}} ->
|
||||
{ok, {cash, #{amount := Amount, currency := Currency}} = _Body} ->
|
||||
case lim_rates:get_converted_amount({Amount, Currency}, Config, LimitContext) of
|
||||
{ok, ConvertedAmount} ->
|
||||
{ok, create_body_from_cash(ConvertedAmount, ConfigCurrency)};
|
||||
@ -57,7 +57,7 @@ get_body_for_operation(full, invoice_payment_adjustment, Config, LimitContext) -
|
||||
lim_context:get_from_context(ContextType, cost, invoice_payment, LimitContext);
|
||||
get_body_for_operation(full, invoice_payment_refund, Config, LimitContext) ->
|
||||
ContextType = lim_config_machine:context_type(Config),
|
||||
lim_context:get_from_context(ContextType, cost, invoice_payment, LimitContext);
|
||||
lim_context:get_from_context(ContextType, cost, invoice_payment_refund, LimitContext);
|
||||
get_body_for_operation(full, invoice_payment_chargeback = Operation, Config, LimitContext) ->
|
||||
ContextType = lim_config_machine:context_type(Config),
|
||||
lim_context:get_from_context(ContextType, body, Operation, LimitContext);
|
||||
|
@ -59,9 +59,31 @@ marshal_config(Config) ->
|
||||
time_range_type = marshal_time_range_type(lim_config_machine:time_range_type(Config)),
|
||||
context_type = marshal_context_type(lim_config_machine:context_type(Config)),
|
||||
type = maybe_apply(lim_config_machine:type(Config), fun marshal_type/1),
|
||||
scope = maybe_apply(lim_config_machine:scope(Config), fun marshal_scope/1)
|
||||
scope = maybe_apply(lim_config_machine:scope(Config), fun marshal_scope/1),
|
||||
op_behaviour = maybe_apply(lim_config_machine:op_behaviour(Config), fun marshal_op_behaviour/1)
|
||||
}.
|
||||
|
||||
marshal_op_behaviour(OpBehaviour) ->
|
||||
Invoice = maps:get(invoice, OpBehaviour, undefined),
|
||||
Payment = maps:get(invoice_payment, OpBehaviour, undefined),
|
||||
Adjustment = maps:get(invoice_adjustment, OpBehaviour, undefined),
|
||||
PaymentAdjustment = maps:get(invoice_payment_adjustment, OpBehaviour, undefined),
|
||||
PaymentRefund = maps:get(invoice_payment_refund, OpBehaviour, undefined),
|
||||
PaymentChargeback = maps:get(invoice_payment_adjustment, OpBehaviour, undefined),
|
||||
#limiter_config_OperationLimitBehaviour{
|
||||
invoice = maybe_apply(Invoice, fun marshal_behaviour/1),
|
||||
invoice_adjustment = maybe_apply(Adjustment, fun marshal_behaviour/1),
|
||||
invoice_payment = maybe_apply(Payment, fun marshal_behaviour/1),
|
||||
invoice_payment_adjustment = maybe_apply(PaymentAdjustment, fun marshal_behaviour/1),
|
||||
invoice_payment_refund = maybe_apply(PaymentRefund, fun marshal_behaviour/1),
|
||||
invoice_payment_chargeback = maybe_apply(PaymentChargeback, fun marshal_behaviour/1)
|
||||
}.
|
||||
|
||||
marshal_behaviour(subtraction) ->
|
||||
{subtraction, #limiter_config_Subtraction{}};
|
||||
marshal_behaviour(addition) ->
|
||||
{addition, #limiter_config_Addition{}}.
|
||||
|
||||
marshal_body_type(amount) ->
|
||||
{amount, #limiter_config_LimitBodyTypeAmount{}};
|
||||
marshal_body_type({cash, Currency}) ->
|
||||
@ -139,7 +161,8 @@ unmarshal_config(#limiter_config_LimitConfig{
|
||||
time_range_type = TimeRangeType,
|
||||
context_type = ContextType,
|
||||
type = Type,
|
||||
scope = Scope
|
||||
scope = Scope,
|
||||
op_behaviour = OpBehaviour
|
||||
}) ->
|
||||
genlib_map:compact(#{
|
||||
id => ID,
|
||||
@ -152,9 +175,33 @@ unmarshal_config(#limiter_config_LimitConfig{
|
||||
context_type => unmarshal_context_type(ContextType),
|
||||
type => maybe_apply(Type, fun unmarshal_type/1),
|
||||
scope => maybe_apply(Scope, fun unmarshal_scope/1),
|
||||
description => Description
|
||||
description => Description,
|
||||
op_behaviour => maybe_apply(OpBehaviour, fun unmarshal_op_behaviour/1)
|
||||
}).
|
||||
|
||||
unmarshal_op_behaviour(OpBehaviour) ->
|
||||
#limiter_config_OperationLimitBehaviour{
|
||||
invoice = Invoice,
|
||||
invoice_adjustment = Adjustment,
|
||||
invoice_payment = Payment,
|
||||
invoice_payment_adjustment = PaymentAdjustment,
|
||||
invoice_payment_refund = Refund,
|
||||
invoice_payment_chargeback = Chargeback
|
||||
} = OpBehaviour,
|
||||
genlib_map:compact(#{
|
||||
invoice => maybe_apply(Invoice, fun unmarshal_behaviour/1),
|
||||
invoice_adjustment => maybe_apply(Adjustment, fun unmarshal_behaviour/1),
|
||||
invoice_payment => maybe_apply(Payment, fun unmarshal_behaviour/1),
|
||||
invoice_payment_adjustment => maybe_apply(PaymentAdjustment, fun unmarshal_behaviour/1),
|
||||
invoice_payment_refund => maybe_apply(Refund, fun unmarshal_behaviour/1),
|
||||
invoice_payment_chargeback => maybe_apply(Chargeback, fun unmarshal_behaviour/1)
|
||||
}).
|
||||
|
||||
unmarshal_behaviour({subtraction, #limiter_config_Subtraction{}}) ->
|
||||
subtraction;
|
||||
unmarshal_behaviour({addition, #limiter_config_Addition{}}) ->
|
||||
addition.
|
||||
|
||||
-spec unmarshal_body_type(encoded_value()) -> decoded_value().
|
||||
unmarshal_body_type({amount, #limiter_config_LimitBodyTypeAmount{}}) ->
|
||||
amount;
|
||||
|
@ -16,6 +16,7 @@
|
||||
-export([type/1]).
|
||||
-export([scope/1]).
|
||||
-export([context_type/1]).
|
||||
-export([op_behaviour/1]).
|
||||
|
||||
%% API
|
||||
|
||||
@ -62,7 +63,8 @@
|
||||
context_type := context_type(),
|
||||
type => limit_type(),
|
||||
scope => limit_scope(),
|
||||
description => description()
|
||||
description => description(),
|
||||
op_behaviour => op_behaviour()
|
||||
}.
|
||||
|
||||
-type create_params() :: #{
|
||||
@ -77,6 +79,14 @@
|
||||
description => description()
|
||||
}.
|
||||
|
||||
-type op_behaviour() :: #{operation_type() := addition | subtraction}.
|
||||
-type operation_type() :: invoice
|
||||
| invoice_adjustment
|
||||
| invoice_payment
|
||||
| invoice_payment_adjustment
|
||||
| invoice_payment_refund
|
||||
| invoice_payment_chargeback.
|
||||
|
||||
-type lim_id() :: lim_limiter_thrift:'LimitID'().
|
||||
-type lim_change() :: lim_limiter_thrift:'LimitChange'().
|
||||
-type limit() :: lim_limiter_thrift:'Limit'().
|
||||
@ -209,6 +219,12 @@ scope(_) ->
|
||||
context_type(#{context_type := Value}) ->
|
||||
Value.
|
||||
|
||||
-spec op_behaviour(config()) -> lim_maybe:maybe(op_behaviour()).
|
||||
op_behaviour(#{op_behaviour := Value}) ->
|
||||
Value;
|
||||
op_behaviour(_) ->
|
||||
undefined.
|
||||
|
||||
%%
|
||||
|
||||
-spec start(lim_id(), create_params(), lim_context()) -> {ok, config()}.
|
||||
|
@ -69,7 +69,8 @@ mk_limit_config(<<"ShopMonthTurnover">>) ->
|
||||
scope => {scope, shop},
|
||||
shard_size => 12,
|
||||
context_type => payment_processing,
|
||||
time_range_type => {calendar, month}
|
||||
time_range_type => {calendar, month},
|
||||
op_behaviour => #{invoice_payment_refund => subtraction}
|
||||
}};
|
||||
mk_limit_config(<<"PartyMonthTurnover">>) ->
|
||||
{ok, #{
|
||||
|
@ -84,7 +84,8 @@ hold(LimitChange = #limiter_LimitChange{id = LimitID}, Config = #{body_type := B
|
||||
{ok, #{account_id_from := AccountIDFrom, account_id_to := AccountIDTo}} =
|
||||
lim_range_machine:ensure_range_exist_in_state(TimeRange, LimitRangeState, LimitContext),
|
||||
Postings = lim_p_transfer:construct_postings(AccountIDFrom, AccountIDTo, Body),
|
||||
lim_accounting:hold(construct_plan_id(LimitChange), {1, Postings}, LimitContext)
|
||||
Postings1 = maybe_inverse_posting(Postings, LimitContext, Config),
|
||||
lim_accounting:hold(construct_plan_id(LimitChange), {1, Postings1}, LimitContext)
|
||||
end).
|
||||
|
||||
-spec commit(lim_change(), config(), lim_context()) -> ok | {error, commit_error()}.
|
||||
@ -130,16 +131,27 @@ partial_commit(PartialBody, LimitChange = #limiter_LimitChange{id = LimitID}, Co
|
||||
TimeRange = lim_config_machine:calculate_time_range(Timestamp, Config),
|
||||
{ok, #{account_id_from := AccountIDFrom, account_id_to := AccountIDTo}} =
|
||||
lim_range_machine:get_range(TimeRange, LimitRangeState),
|
||||
|
||||
PartialPostings = lim_p_transfer:construct_postings(AccountIDFrom, AccountIDTo, PartialBody),
|
||||
FullPostings = lim_p_transfer:construct_postings(AccountIDFrom, AccountIDTo, FullBody),
|
||||
NewBatchList = [{2, lim_p_transfer:reverse_postings(FullPostings)} | [{3, PartialPostings}]],
|
||||
|
||||
PartialPostings0 = lim_p_transfer:construct_postings(AccountIDFrom, AccountIDTo, PartialBody),
|
||||
FullPostings0 = lim_p_transfer:construct_postings(AccountIDFrom, AccountIDTo, FullBody),
|
||||
PartialPostings1 = maybe_inverse_posting(PartialPostings0, LimitContext, Config),
|
||||
FullPostings1 = maybe_inverse_posting(FullPostings0, LimitContext, Config),
|
||||
NewBatchList = [{2, lim_p_transfer:reverse_postings(FullPostings1)} | [{3, PartialPostings1}]],
|
||||
PlanID = construct_plan_id(LimitChange),
|
||||
unwrap(lim_accounting:plan(PlanID, NewBatchList, LimitContext)),
|
||||
unwrap(lim_accounting:commit(PlanID, [{1, FullPostings} | NewBatchList], LimitContext))
|
||||
unwrap(lim_accounting:commit(PlanID, [{1, FullPostings1} | NewBatchList], LimitContext))
|
||||
end).
|
||||
|
||||
maybe_inverse_posting(Posting, LimitContext, #{op_behaviour := ComputationConfig}) ->
|
||||
{ok, Operation} = lim_context:get_operation(payment_processing, LimitContext),
|
||||
case maps:get(Operation, ComputationConfig, undefined) of
|
||||
subtraction ->
|
||||
lim_p_transfer:reverse_postings(Posting);
|
||||
Type when Type =:= undefined orelse Type =:= additional ->
|
||||
Posting
|
||||
end;
|
||||
maybe_inverse_posting(Body, _LimitContext, _Config) ->
|
||||
Body.
|
||||
|
||||
assert_partial_body(
|
||||
{cash, #{amount := Partial, currency := Currency}},
|
||||
{cash, #{amount := Full, currency := Currency}}
|
||||
|
49
apps/limiter/test/lim_ct_helper.hrl
Normal file
49
apps/limiter/test/lim_ct_helper.hrl
Normal file
@ -0,0 +1,49 @@
|
||||
-ifndef(__limiter_ct_helper__).
|
||||
-define(__limiter_ct_helper__, 42).
|
||||
|
||||
-include_lib("limiter_proto/include/lim_configurator_thrift.hrl").
|
||||
|
||||
-define(cash(Amount), #limiter_base_Cash{
|
||||
amount = Amount,
|
||||
currency = #limiter_base_CurrencyRef{symbolic_code = <<"RUB">>}
|
||||
}).
|
||||
|
||||
|
||||
-define(ctx_invoice_payment(Cost, CaptureCost), ?ctx_invoice_payment(undefined, undefined, Cost, CaptureCost)).
|
||||
|
||||
-define(ctx_invoice_payment(OwnerID, ShopID, Cost, CaptureCost), #limiter_context_LimitContext{
|
||||
payment_processing = #limiter_context_ContextPaymentProcessing{
|
||||
op = {invoice_payment, #limiter_context_PaymentProcessingOperationInvoicePayment{}},
|
||||
invoice = #limiter_context_Invoice{
|
||||
owner_id = OwnerID,
|
||||
shop_id = ShopID,
|
||||
effective_payment = #limiter_context_InvoicePayment{
|
||||
created_at = <<"2000-01-01T00:00:00Z">>,
|
||||
cost = Cost,
|
||||
capture_cost = CaptureCost
|
||||
}
|
||||
}
|
||||
}
|
||||
}).
|
||||
|
||||
-define(ctx_invoice_payment_refund(OwnerID, ShopID, Cost, CaptureCost, RefundCost), #limiter_context_LimitContext{
|
||||
payment_processing = #limiter_context_ContextPaymentProcessing{
|
||||
op = {invoice_payment_refund, #limiter_context_PaymentProcessingOperationInvoicePaymentRefund{}},
|
||||
invoice = #limiter_context_Invoice{
|
||||
owner_id = OwnerID,
|
||||
shop_id = ShopID,
|
||||
effective_payment = #limiter_context_InvoicePayment{
|
||||
created_at = <<"2000-01-01T00:00:00Z">>,
|
||||
cost = Cost,
|
||||
capture_cost = CaptureCost,
|
||||
effective_refund = #limiter_context_InvoicePaymentRefund{
|
||||
cost = RefundCost,
|
||||
created_at = <<"2000-01-01T00:00:00Z">>
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).
|
||||
|
||||
-endif.
|
@ -2,6 +2,7 @@
|
||||
|
||||
-include_lib("stdlib/include/assert.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("lim_ct_helper.hrl").
|
||||
|
||||
-include_lib("limiter_proto/include/lim_configurator_thrift.hrl").
|
||||
-include_lib("xrates_proto/include/xrates_rate_thrift.hrl").
|
||||
@ -22,6 +23,7 @@
|
||||
-export([hold_ok/1]).
|
||||
-export([commit_ok/1]).
|
||||
-export([rollback_ok/1]).
|
||||
-export([refund_ok/1]).
|
||||
-export([get_config_ok/1]).
|
||||
|
||||
-type test_case_name() :: atom().
|
||||
@ -48,7 +50,8 @@ groups() ->
|
||||
hold_ok,
|
||||
commit_ok,
|
||||
rollback_ok,
|
||||
get_config_ok
|
||||
get_config_ok,
|
||||
refund_ok
|
||||
]}
|
||||
].
|
||||
|
||||
@ -266,32 +269,45 @@ commit_ok(C) ->
|
||||
rollback_ok(C) ->
|
||||
ID = <<"ID">>,
|
||||
#{client := Client} = prepare_environment(ID, <<"GlobalMonthTurnover">>, C),
|
||||
Context = #limiter_context_LimitContext{
|
||||
payment_processing = #limiter_context_ContextPaymentProcessing{
|
||||
op = {invoice_payment, #limiter_context_PaymentProcessingOperationInvoicePayment{}},
|
||||
invoice = #limiter_context_Invoice{
|
||||
effective_payment = #limiter_context_InvoicePayment{
|
||||
created_at = <<"2000-01-01T00:00:00Z">>,
|
||||
cost = #limiter_base_Cash{
|
||||
amount = 10,
|
||||
currency = #limiter_base_CurrencyRef{symbolic_code = <<"RUB">>}
|
||||
},
|
||||
capture_cost = #limiter_base_Cash{
|
||||
amount = 0,
|
||||
currency = #limiter_base_CurrencyRef{symbolic_code = <<"RUB">>}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Context0 = ?ctx_invoice_payment(?cash(10), ?cash(10)),
|
||||
Context1 = ?ctx_invoice_payment(?cash(10), ?cash(0)),
|
||||
|
||||
Timestamp = lim_time:to_rfc3339(lim_time:now()),
|
||||
LimitChangeID = <<Timestamp/binary, "Rollback">>,
|
||||
Change = #limiter_LimitChange{
|
||||
id = ID,
|
||||
change_id = LimitChangeID
|
||||
},
|
||||
{ok, {vector, _}} = hold_and_commit(Change, Context, Client),
|
||||
{ok, #limiter_Limit{}} = lim_client:get(ID, Context, Client).
|
||||
{ok, {vector, _}} = lim_client:hold(Change, Context0, Client),
|
||||
{ok, {vector, _}} = lim_client:commit(Change, Context1, Client).
|
||||
|
||||
-spec refund_ok(config()) -> _.
|
||||
refund_ok(C) ->
|
||||
ID = lim_time:to_rfc3339(lim_time:now()),
|
||||
OwnerID = <<"WWWcool Ltd">>,
|
||||
ShopID = <<"shop">>,
|
||||
#{client := Client} = _LimitConfig = prepare_environment(ID, <<"ShopMonthTurnover">>, C),
|
||||
Context0 = ?ctx_invoice_payment(OwnerID, ShopID, ?cash(15), ?cash(15)),
|
||||
RefundContext1 = ?ctx_invoice_payment_refund(OwnerID, ShopID, ?cash(10), ?cash(10), ?cash(10)),
|
||||
Timestamp = lim_time:to_rfc3339(lim_time:now()),
|
||||
LimitChangeID = <<Timestamp/binary, "Payment">>,
|
||||
|
||||
Change = #limiter_LimitChange{
|
||||
id = ID,
|
||||
change_id = LimitChangeID
|
||||
},
|
||||
{ok, {vector, _}} = hold_and_commit(Change, Context0, Client),
|
||||
|
||||
Timestamp2 = lim_time:to_rfc3339(lim_time:now()),
|
||||
LimitChangeID2 = <<Timestamp2/binary, "Refund">>,
|
||||
Change2 = #limiter_LimitChange{
|
||||
id = ID,
|
||||
change_id = LimitChangeID2
|
||||
},
|
||||
|
||||
{ok, {vector, _}} = hold_and_commit(Change2, RefundContext1, Client),
|
||||
{ok, #limiter_Limit{} = Limit2} = lim_client:get(ID, RefundContext1, Client),
|
||||
?assertEqual(Limit2#limiter_Limit.amount, 5).
|
||||
|
||||
-spec get_config_ok(config()) -> _.
|
||||
get_config_ok(C) ->
|
||||
|
@ -33,7 +33,8 @@
|
||||
},
|
||||
{limiter_proto,
|
||||
{git, "git@github.com:rbkmoney/limiter-proto.git",
|
||||
{branch, "master"}
|
||||
% {branch, "master"}
|
||||
{branch, "ED-181/ft/add_computation_type_in_config"}
|
||||
}
|
||||
},
|
||||
{xrates_proto,
|
||||
|
@ -35,7 +35,7 @@
|
||||
{<<"jsx">>,{pkg,<<"jsx">>,<<"3.0.0">>},1},
|
||||
{<<"limiter_proto">>,
|
||||
{git,"git@github.com:rbkmoney/limiter-proto.git",
|
||||
{ref,"84cc6b7355aa838c2a91dfab64d000f57ff63bf7"}},
|
||||
{ref,"a53f50853fc888898ca25dbdc0089a4851213be6"}},
|
||||
0},
|
||||
{<<"machinery">>,
|
||||
{git,"https://github.com/rbkmoney/machinery.git",
|
||||
|
Loading…
Reference in New Issue
Block a user