Merge pull request #5 from rbkmoney/ED-181/ft/subtraction_behaviour

ED-181: add subtraction behaviour
This commit is contained in:
Boris 2021-06-23 15:32:40 +03:00 committed by GitHub
commit 886acc0503
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 166 additions and 40 deletions

View File

@ -6,6 +6,8 @@
-export([unmarshal/2]).
-export([marshal_config/1]).
-export([unmarshal_body_type/1]).
-export([unmarshal_op_behaviour/1]).
-export([maybe_apply/2]).
%% Types
@ -17,6 +19,7 @@
-type decoded_value() :: decoded_value(any()).
-type decoded_value(T) :: T.
-spec maybe_apply(any(), function()) -> any().
maybe_apply(undefined, _) ->
undefined;
maybe_apply(Value, Fun) ->
@ -59,9 +62,21 @@ 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) ->
PaymentRefund = maps:get(invoice_payment_refund, OpBehaviour, undefined),
#limiter_config_OperationLimitBehaviour{
invoice_payment_refund = maybe_apply(PaymentRefund, 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 +154,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 +168,24 @@ 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)
}).
-spec unmarshal_op_behaviour(encoded_value()) -> decoded_value().
unmarshal_op_behaviour(OpBehaviour) ->
#limiter_config_OperationLimitBehaviour{
invoice_payment_refund = Refund
} = OpBehaviour,
genlib_map:compact(#{
invoice_payment_refund => maybe_apply(Refund, 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;

View File

@ -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() :: #{
@ -74,9 +76,13 @@
context_type := context_type(),
type => limit_type(),
scope => limit_scope(),
description => description()
description => description(),
op_behaviour => op_behaviour()
}.
-type op_behaviour() :: #{operation_type() := addition | subtraction}.
-type operation_type() :: invoice_payment_refund.
-type lim_id() :: lim_limiter_thrift:'LimitID'().
-type lim_change() :: lim_limiter_thrift:'LimitChange'().
-type limit() :: lim_limiter_thrift:'Limit'().
@ -209,6 +215,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()}.

View File

@ -30,7 +30,8 @@ handle_function_(
name = Name,
description = Description,
started_at = StartedAt,
body_type = BodyType
body_type = BodyType,
op_behaviour = OpBehaviour
}},
LimitContext,
_Opts
@ -39,11 +40,15 @@ handle_function_(
{ok, Config} ->
{ok, LimitConfig} = lim_config_machine:start(
ID,
Config#{
genlib_map:compact(Config#{
description => Description,
started_at => StartedAt,
body_type => lim_config_codec:unmarshal_body_type(BodyType)
},
body_type => lim_config_codec:unmarshal_body_type(BodyType),
op_behaviour => lim_config_codec:maybe_apply(
OpBehaviour,
fun lim_config_codec:unmarshal_op_behaviour/1
)
}),
LimitContext
),
{ok, lim_config_codec:marshal_config(LimitConfig)};
@ -67,7 +72,7 @@ mk_limit_config(<<"ShopDayTurnover">>) ->
processor_type => <<"TurnoverProcessor">>,
type => turnover,
scope => {scope, shop},
shard_size => 12,
shard_size => 7,
context_type => payment_processing,
time_range_type => {calendar, day}
}};

View File

@ -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 = apply_op_behaviour(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 = apply_op_behaviour(PartialPostings0, LimitContext, Config),
FullPostings1 = apply_op_behaviour(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).
apply_op_behaviour(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;
apply_op_behaviour(Body, _LimitContext, _Config) ->
Body.
assert_partial_body(
{cash, #{amount := Partial, currency := Currency}},
{cash, #{amount := Full, currency := Currency}}

View File

@ -0,0 +1,47 @@
-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.

View File

@ -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, <<"ShopDayTurnover">>, 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) ->
@ -315,7 +331,10 @@ prepare_environment(ID, LimitName, _C) ->
name = LimitName,
description = <<"description">>,
started_at = <<"2000-01-01T00:00:00Z">>,
body_type = {cash, #limiter_config_LimitBodyTypeCash{currency = <<"RUB">>}}
body_type = {cash, #limiter_config_LimitBodyTypeCash{currency = <<"RUB">>}},
op_behaviour = #limiter_config_OperationLimitBehaviour{
invoice_payment_refund = {subtraction, #limiter_config_Subtraction{}}
}
},
{ok, LimitConfig} = lim_client:create_config(Params, Client),
#{config => LimitConfig, client => Client}.

View File

@ -35,7 +35,7 @@
{<<"jsx">>,{pkg,<<"jsx">>,<<"3.0.0">>},1},
{<<"limiter_proto">>,
{git,"git@github.com:rbkmoney/limiter-proto.git",
{ref,"d4b40ead589dd4dbd9d442397239c635d2a8382e"}},
{ref,"9c0653ff7281ff443f515d2fddf9673fe837a5be"}},
0},
{<<"machinery">>,
{git,"https://github.com/rbkmoney/machinery.git",