TD-821: Add support for deposit negative amount (#78)

* added support for deposit negative amount

* fixed

* added adj tests
This commit is contained in:
Артем 2023-11-30 20:59:06 +03:00 committed by GitHub
parent fe96a2148b
commit 26f859d9f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 341 additions and 40 deletions

View File

@ -18,7 +18,7 @@ marshal_deposit_state(DepositState, Context) ->
Adjustments = ff_deposit:adjustments(DepositState),
#deposit_DepositState{
id = marshal(id, ff_deposit:id(DepositState)),
body = marshal(cash, ff_deposit:body(DepositState)),
body = marshal(cash, ff_deposit:negative_body(DepositState)),
status = maybe_marshal(status, ff_deposit:status(DepositState)),
wallet_id = marshal(id, ff_deposit:wallet_id(DepositState)),
source_id = marshal(id, ff_deposit:source_id(DepositState)),

View File

@ -56,7 +56,7 @@ marshal(revert_state, Revert) ->
wallet_id = marshal(id, ff_deposit_revert:wallet_id(Revert)),
source_id = marshal(id, ff_deposit_revert:source_id(Revert)),
status = marshal(status, ff_deposit_revert:status(Revert)),
body = marshal(cash, ff_deposit_revert:body(Revert)),
body = marshal(cash, ff_deposit_revert:negative_body(Revert)),
created_at = marshal(timestamp_ms, ff_deposit_revert:created_at(Revert)),
domain_revision = marshal(domain_revision, ff_deposit_revert:domain_revision(Revert)),
party_revision = marshal(party_revision, ff_deposit_revert:party_revision(Revert)),

View File

@ -30,13 +30,16 @@
-export([create_source_notfound_test/1]).
-export([create_wallet_notfound_test/1]).
-export([create_ok_test/1]).
-export([create_negative_ok_test/1]).
-export([unknown_test/1]).
-export([get_context_test/1]).
-export([get_events_test/1]).
-export([create_adjustment_ok_test/1]).
-export([create_negative_adjustment_ok_test/1]).
-export([create_adjustment_unavailable_status_error_test/1]).
-export([create_adjustment_already_has_status_error_test/1]).
-export([create_revert_ok_test/1]).
-export([create_negative_revert_ok_test/1]).
-export([create_revert_inconsistent_revert_currency_error_test/1]).
-export([create_revert_insufficient_deposit_amount_error_test/1]).
-export([create_revert_invalid_revert_amount_error_test/1]).
@ -68,13 +71,16 @@ groups() ->
create_source_notfound_test,
create_wallet_notfound_test,
create_ok_test,
create_negative_ok_test,
unknown_test,
get_context_test,
get_events_test,
create_adjustment_ok_test,
create_negative_adjustment_ok_test,
create_adjustment_unavailable_status_error_test,
create_adjustment_already_has_status_error_test,
create_revert_ok_test,
create_negative_revert_ok_test,
create_revert_inconsistent_revert_currency_error_test,
create_revert_insufficient_deposit_amount_error_test,
create_revert_invalid_revert_amount_error_test,
@ -238,6 +244,35 @@ create_ok_test(C) ->
ff_codec:unmarshal(timestamp_ms, DepositState#deposit_DepositState.created_at)
).
-spec create_negative_ok_test(config()) -> test_return().
create_negative_ok_test(C) ->
EnvBody = make_cash({100, <<"RUB">>}),
#{
wallet_id := WalletID,
source_id := SourceID
} = prepare_standard_environment(EnvBody, C),
_ = process_deposit(WalletID, SourceID, EnvBody),
Body = make_cash({-100, <<"RUB">>}),
{DepositState, DepositID, ExternalID, _} = process_deposit(WalletID, SourceID, Body),
Expected = get_deposit(DepositID),
?assertEqual(DepositID, DepositState#deposit_DepositState.id),
?assertEqual(WalletID, DepositState#deposit_DepositState.wallet_id),
?assertEqual(SourceID, DepositState#deposit_DepositState.source_id),
?assertEqual(ExternalID, DepositState#deposit_DepositState.external_id),
?assertEqual(Body, DepositState#deposit_DepositState.body),
?assertEqual(
ff_deposit:domain_revision(Expected),
DepositState#deposit_DepositState.domain_revision
),
?assertEqual(
ff_deposit:party_revision(Expected),
DepositState#deposit_DepositState.party_revision
),
?assertEqual(
ff_deposit:created_at(Expected),
ff_codec:unmarshal(timestamp_ms, DepositState#deposit_DepositState.created_at)
).
-spec unknown_test(config()) -> test_return().
unknown_test(_C) ->
DepositID = <<"unknown_deposit">>,
@ -303,6 +338,45 @@ create_adjustment_ok_test(C) ->
AdjustmentState#deposit_adj_AdjustmentState.changes_plan
).
-spec create_negative_adjustment_ok_test(config()) -> test_return().
create_negative_adjustment_ok_test(C) ->
#{
wallet_id := WalletID,
source_id := SourceID
} = prepare_standard_environment_with_deposit(C),
{_, DepositID, _, _} = process_deposit(WalletID, SourceID, make_cash({-50, <<"RUB">>})),
AdjustmentID = generate_id(),
ExternalID = generate_id(),
Params = #deposit_adj_AdjustmentParams{
id = AdjustmentID,
change =
{change_status, #deposit_adj_ChangeStatusRequest{
new_status = {failed, #deposit_status_Failed{failure = #'fistful_base_Failure'{code = <<"Ooops">>}}}
}},
external_id = ExternalID
},
{ok, AdjustmentState} = call_deposit('CreateAdjustment', {DepositID, Params}),
ExpectedAdjustment = get_adjustment(DepositID, AdjustmentID),
?assertEqual(AdjustmentID, AdjustmentState#deposit_adj_AdjustmentState.id),
?assertEqual(ExternalID, AdjustmentState#deposit_adj_AdjustmentState.external_id),
?assertEqual(
ff_adjustment:created_at(ExpectedAdjustment),
ff_codec:unmarshal(timestamp_ms, AdjustmentState#deposit_adj_AdjustmentState.created_at)
),
?assertEqual(
ff_adjustment:domain_revision(ExpectedAdjustment),
AdjustmentState#deposit_adj_AdjustmentState.domain_revision
),
?assertEqual(
ff_adjustment:party_revision(ExpectedAdjustment),
AdjustmentState#deposit_adj_AdjustmentState.party_revision
),
?assertEqual(
ff_deposit_adjustment_codec:marshal(changes_plan, ff_adjustment:changes_plan(ExpectedAdjustment)),
AdjustmentState#deposit_adj_AdjustmentState.changes_plan
).
-spec create_adjustment_unavailable_status_error_test(config()) -> test_return().
create_adjustment_unavailable_status_error_test(C) ->
#{
@ -374,6 +448,44 @@ create_revert_ok_test(C) ->
RevertState#deposit_revert_RevertState.party_revision
).
-spec create_negative_revert_ok_test(config()) -> test_return().
create_negative_revert_ok_test(C) ->
#{
wallet_id := WalletID,
source_id := SourceID
} = prepare_standard_environment_with_deposit(make_cash({10000, <<"RUB">>}), C),
Body = make_cash({-5000, <<"RUB">>}),
{_, DepositID, _, _} = process_deposit(WalletID, SourceID, Body),
RevertID = generate_id(),
ExternalID1 = generate_id(),
Reason = generate_id(),
RevertParams = #deposit_revert_RevertParams{
id = RevertID,
body = Body,
external_id = ExternalID1,
reason = Reason
},
{ok, RevertState} = call_deposit('CreateRevert', {DepositID, RevertParams}),
succeeded = await_final_revert_status(DepositID, RevertID),
Expected = get_revert(DepositID, RevertID),
?assertEqual(RevertID, RevertState#deposit_revert_RevertState.id),
?assertEqual(ExternalID1, RevertState#deposit_revert_RevertState.external_id),
?assertEqual(Body, RevertState#deposit_revert_RevertState.body),
?assertEqual(Reason, RevertState#deposit_revert_RevertState.reason),
?assertEqual(
ff_deposit_revert:created_at(Expected),
ff_codec:unmarshal(timestamp_ms, RevertState#deposit_revert_RevertState.created_at)
),
?assertEqual(
ff_deposit_revert:domain_revision(Expected),
RevertState#deposit_revert_RevertState.domain_revision
),
?assertEqual(
ff_deposit_revert:party_revision(Expected),
RevertState#deposit_revert_RevertState.party_revision
).
-spec create_revert_inconsistent_revert_currency_error_test(config()) -> test_return().
create_revert_inconsistent_revert_currency_error_test(C) ->
#{
@ -586,19 +698,7 @@ prepare_standard_environment_with_deposit(Body, C) ->
wallet_id := WalletID,
source_id := SourceID
} = Env = prepare_standard_environment(Body, C),
DepositID = generate_id(),
ExternalID = generate_id(),
Context = #{<<"NS">> => #{generate_id() => generate_id()}},
EncodedContext = ff_entity_context_codec:marshal(Context),
Params = #deposit_DepositParams{
id = DepositID,
wallet_id = WalletID,
source_id = SourceID,
body = Body,
external_id = ExternalID
},
{ok, _DepositState} = call_deposit('Create', {Params, EncodedContext}),
succeeded = await_final_deposit_status(DepositID),
{_, DepositID, ExternalID, Context} = process_deposit(WalletID, SourceID, Body),
Env#{
deposit_id => DepositID,
external_id => ExternalID,
@ -631,6 +731,22 @@ prepare_standard_environment_with_revert(Body, C) ->
reason => Reason
}.
process_deposit(WalletID, SourceID, Body) ->
DepositID = generate_id(),
ExternalID = generate_id(),
Context = #{<<"NS">> => #{generate_id() => generate_id()}},
EncodedContext = ff_entity_context_codec:marshal(Context),
Params = #deposit_DepositParams{
id = DepositID,
wallet_id = WalletID,
source_id = SourceID,
body = Body,
external_id = ExternalID
},
{ok, DepositState} = call_deposit('Create', {Params, EncodedContext}),
succeeded = await_final_deposit_status(DepositID),
{DepositState, DepositID, ExternalID, Context}.
get_deposit(DepositID) ->
{ok, Machine} = ff_deposit_machine:get(DepositID),
ff_deposit_machine:deposit(Machine).

View File

@ -12,6 +12,7 @@
id := id(),
transfer_type := deposit,
body := body(),
is_negative := is_negative(),
params := transfer_params(),
party_revision => party_revision(),
domain_revision => domain_revision(),
@ -148,6 +149,8 @@
-export([source_id/1]).
-export([id/1]).
-export([body/1]).
-export([negative_body/1]).
-export([is_negative/1]).
-export([status/1]).
-export([external_id/1]).
-export([party_revision/1]).
@ -192,6 +195,7 @@
-type revert() :: ff_deposit_revert:revert().
-type revert_id() :: ff_deposit_revert:id().
-type body() :: ff_accounting:body().
-type is_negative() :: boolean().
-type cash() :: ff_cash:cash().
-type cash_range() :: ff_range:range(cash()).
-type action() :: machinery:action() | undefined.
@ -250,6 +254,18 @@ source_id(T) ->
body(#{body := V}) ->
V.
-spec negative_body(deposit_state()) -> body().
negative_body(#{body := {Amount, Currency}, is_negative := true}) ->
{-1 * Amount, Currency};
negative_body(T) ->
body(T).
-spec is_negative(deposit_state()) -> is_negative().
is_negative(#{is_negative := V}) ->
V;
is_negative(_T) ->
false.
-spec status(deposit_state()) -> status() | undefined.
status(Deposit) ->
maps:get(status, Deposit, undefined).
@ -424,7 +440,7 @@ apply_event(Ev, T0) ->
-spec apply_event_(event(), deposit_state() | undefined) -> deposit_state().
apply_event_({created, T}, undefined) ->
T;
apply_negative_body(T);
apply_event_({status_changed, S}, T) ->
maps:put(status, S, T);
apply_event_({limit_check, Details}, T) ->
@ -436,6 +452,11 @@ apply_event_({revert, _Ev} = Event, T) ->
apply_event_({adjustment, _Ev} = Event, T) ->
apply_adjustment_event(Event, T).
apply_negative_body(T = #{body := {Amount, Currency}}) when Amount < 0 ->
T#{body => {-1 * Amount, Currency}, is_negative => true};
apply_negative_body(T) ->
T.
%% Internals
-spec do_start_revert(revert_params(), deposit_state()) ->
@ -537,7 +558,7 @@ do_process_transfer(stop, _Deposit) ->
-spec create_p_transfer(deposit_state()) -> process_result().
create_p_transfer(Deposit) ->
FinalCashFlow = make_final_cash_flow(wallet_id(Deposit), source_id(Deposit), body(Deposit)),
FinalCashFlow = make_final_cash_flow(Deposit),
PTransferID = construct_p_transfer_id(id(Deposit)),
{ok, PostingsTransferEvents} = ff_postings_transfer:create(PTransferID, FinalCashFlow),
{continue, [{p_transfer, Ev} || Ev <- PostingsTransferEvents]}.
@ -585,8 +606,11 @@ process_transfer_fail(limit_check, Deposit) ->
Failure = build_failure(limit_check, Deposit),
{undefined, [{status_changed, {failed, Failure}}]}.
-spec make_final_cash_flow(wallet_id(), source_id(), body()) -> final_cash_flow().
make_final_cash_flow(WalletID, SourceID, Body) ->
-spec make_final_cash_flow(deposit_state()) -> final_cash_flow().
make_final_cash_flow(Deposit) ->
WalletID = wallet_id(Deposit),
SourceID = source_id(Deposit),
Body = body(Deposit),
{ok, WalletMachine} = ff_wallet_machine:get(WalletID),
WalletAccount = ff_wallet:account(ff_wallet_machine:wallet(WalletMachine)),
{ok, SourceMachine} = ff_source_machine:get(SourceID),
@ -595,10 +619,19 @@ make_final_cash_flow(WalletID, SourceID, Body) ->
Constants = #{
operation_amount => Body
},
Accounts = #{
{wallet, sender_source} => SourceAccount,
{wallet, receiver_settlement} => WalletAccount
},
Accounts =
case is_negative(Deposit) of
true ->
#{
{wallet, sender_source} => WalletAccount,
{wallet, receiver_settlement} => SourceAccount
};
false ->
#{
{wallet, sender_source} => SourceAccount,
{wallet, receiver_settlement} => WalletAccount
}
end,
CashFlowPlan = #{
postings => [
#{
@ -777,7 +810,7 @@ validate_revert_start(Params, Deposit) ->
validate_revert_body(Params, Deposit) ->
do(fun() ->
valid = unwrap(validate_revert_currency(Params, Deposit)),
valid = unwrap(validate_revert_amount(Params)),
valid = unwrap(validate_revert_amount(Params, Deposit)),
valid = unwrap(validate_unreverted_amount(Params, Deposit))
end).
@ -820,13 +853,15 @@ validate_unreverted_amount(Params, Deposit) ->
{error, {insufficient_deposit_amount, {RevertBody, Unreverted}}}
end.
-spec validate_revert_amount(revert_params()) ->
-spec validate_revert_amount(revert_params(), deposit_state()) ->
{ok, valid}
| {error, {invalid_revert_amount, Revert :: body()}}.
validate_revert_amount(Params) ->
validate_revert_amount(Params, Desposit) ->
#{body := {RevertAmount, _Currency} = RevertBody} = Params,
case RevertAmount of
Good when Good > 0 ->
case {RevertAmount, is_negative(Desposit)} of
{Good, false} when Good > 0 ->
{ok, valid};
{Good, true} when Good < 0 ->
{ok, valid};
_Other ->
{error, {invalid_revert_amount, RevertBody}}
@ -976,7 +1011,7 @@ make_change_status_params(succeeded, {failed, _} = NewStatus, Deposit) ->
};
make_change_status_params({failed, _}, succeeded = NewStatus, Deposit) ->
CurrentCashFlow = effective_final_cash_flow(Deposit),
NewCashFlow = make_final_cash_flow(wallet_id(Deposit), source_id(Deposit), body(Deposit)),
NewCashFlow = make_final_cash_flow(Deposit),
#{
new_status => #{
new_status => NewStatus

View File

@ -13,6 +13,7 @@
version := ?ACTUAL_FORMAT_VERSION,
id := id(),
body := body(),
is_negative := is_negative(),
wallet_id := wallet_id(),
source_id := source_id(),
status := status(),
@ -102,6 +103,8 @@
-export([wallet_id/1]).
-export([source_id/1]).
-export([body/1]).
-export([negative_body/1]).
-export([is_negative/1]).
-export([status/1]).
-export([reason/1]).
-export([external_id/1]).
@ -135,6 +138,7 @@
-type source_id() :: ff_source:id().
-type p_transfer() :: ff_postings_transfer:transfer().
-type body() :: ff_accounting:body().
-type is_negative() :: boolean().
-type action() :: machinery:action() | undefined.
-type process_result() :: {action(), [event()]}.
-type legacy_event() :: any().
@ -191,6 +195,18 @@ source_id(#{source_id := V}) ->
body(#{body := V}) ->
V.
-spec negative_body(revert()) -> body().
negative_body(#{body := {Amount, Currency}, is_negative := true}) ->
{-1 * Amount, Currency};
negative_body(T) ->
body(T).
-spec is_negative(revert()) -> is_negative().
is_negative(#{is_negative := V}) ->
V;
is_negative(_T) ->
false.
-spec status(revert()) -> status().
status(#{status := V}) ->
V.
@ -311,7 +327,7 @@ apply_event(Ev, T0) ->
-spec apply_event_(event(), revert() | undefined) -> revert().
apply_event_({created, T}, undefined) ->
T;
apply_negative_body(T);
apply_event_({status_changed, S}, T) ->
T#{status => S};
apply_event_({limit_check, Details}, T) ->
@ -321,6 +337,11 @@ apply_event_({p_transfer, Ev}, T) ->
apply_event_({adjustment, _Ev} = Event, T) ->
apply_adjustment_event(Event, T).
apply_negative_body(T = #{body := {Amount, Currency}}) when Amount < 0 ->
T#{body => {-1 * Amount, Currency}, is_negative => true};
apply_negative_body(T) ->
T.
-spec maybe_migrate(event() | legacy_event()) -> event().
% Actual events
maybe_migrate(Ev = {limit_check, {wallet_receiver, _Details}}) ->
@ -397,7 +418,7 @@ do_process_transfer(adjustment, Revert) ->
-spec create_p_transfer(revert()) -> process_result().
create_p_transfer(Revert) ->
FinalCashFlow = make_final_cash_flow(wallet_id(Revert), source_id(Revert), body(Revert)),
FinalCashFlow = make_final_cash_flow(Revert),
PTransferID = construct_p_transfer_id(id(Revert)),
{ok, PostingsTransferEvents} = ff_postings_transfer:create(PTransferID, FinalCashFlow),
{continue, [{p_transfer, Ev} || Ev <- PostingsTransferEvents]}.
@ -447,8 +468,11 @@ process_transfer_fail(limit_check, Revert) ->
Failure = build_failure(limit_check, Revert),
{undefined, [{status_changed, {failed, Failure}}]}.
-spec make_final_cash_flow(wallet_id(), source_id(), body()) -> final_cash_flow().
make_final_cash_flow(WalletID, SourceID, Body) ->
-spec make_final_cash_flow(revert()) -> final_cash_flow().
make_final_cash_flow(Revert) ->
WalletID = wallet_id(Revert),
SourceID = source_id(Revert),
Body = body(Revert),
{ok, WalletMachine} = ff_wallet_machine:get(WalletID),
WalletAccount = ff_wallet:account(ff_wallet_machine:wallet(WalletMachine)),
{ok, SourceMachine} = ff_source_machine:get(SourceID),
@ -457,10 +481,19 @@ make_final_cash_flow(WalletID, SourceID, Body) ->
Constants = #{
operation_amount => Body
},
Accounts = #{
{wallet, sender_source} => SourceAccount,
{wallet, receiver_settlement} => WalletAccount
},
Accounts =
case is_negative(Revert) of
true ->
#{
{wallet, sender_source} => WalletAccount,
{wallet, receiver_settlement} => SourceAccount
};
false ->
#{
{wallet, sender_source} => SourceAccount,
{wallet, receiver_settlement} => WalletAccount
}
end,
CashFlowPlan = #{
postings => [
#{
@ -608,7 +641,7 @@ make_change_status_params(succeeded, {failed, _} = NewStatus, Revert) ->
};
make_change_status_params({failed, _}, succeeded = NewStatus, Revert) ->
CurrentCashFlow = effective_final_cash_flow(Revert),
NewCashFlow = make_final_cash_flow(wallet_id(Revert), source_id(Revert), body(Revert)),
NewCashFlow = make_final_cash_flow(Revert),
#{
new_status => #{
new_status => NewStatus

View File

@ -19,6 +19,7 @@
-export([limit_check_fail_test/1]).
-export([create_bad_amount_test/1]).
-export([create_negative_amount_test/1]).
-export([create_currency_validation_error_test/1]).
-export([create_source_notfound_test/1]).
-export([create_wallet_notfound_test/1]).
@ -45,6 +46,7 @@ groups() ->
{default, [parallel], [
limit_check_fail_test,
create_bad_amount_test,
create_negative_amount_test,
create_currency_validation_error_test,
create_source_notfound_test,
create_wallet_notfound_test,
@ -136,6 +138,35 @@ create_bad_amount_test(C) ->
Result = ff_deposit_machine:create(DepositParams, ff_entity_context:new()),
?assertMatch({error, {bad_deposit_amount, {0, <<"RUB">>}}}, Result).
-spec create_negative_amount_test(config()) -> test_return().
create_negative_amount_test(C) ->
#{
wallet_id := WalletID,
source_id := SourceID
} = prepare_standard_environment(<<"RUB">>, C),
DepositID0 = generate_id(),
DepositCash0 = {100, <<"RUB">>},
DepositParams0 = #{
id => DepositID0,
body => DepositCash0,
source_id => SourceID,
wallet_id => WalletID,
external_id => generate_id()
},
ok = ff_deposit_machine:create(DepositParams0, ff_entity_context:new()),
succeeded = await_final_deposit_status(DepositID0),
ok = await_wallet_balance(DepositCash0, WalletID),
DepositID1 = generate_id(),
DepositCash1 = {-100, <<"RUB">>},
DepositParams1 = DepositParams0#{
id => DepositID1,
body => DepositCash1,
external_id => generate_id()
},
ok = ff_deposit_machine:create(DepositParams1, ff_entity_context:new()),
succeeded = await_final_deposit_status(DepositID1),
ok = await_wallet_balance({0, <<"RUB">>}, WalletID).
-spec create_currency_validation_error_test(config()) -> test_return().
create_currency_validation_error_test(C) ->
#{

View File

@ -16,8 +16,10 @@
%% Tests
-export([adjustment_can_change_status_to_failed_test/1]).
-export([negative_adjustment_can_change_status_to_failed_test/1]).
-export([adjustment_can_change_failure_test/1]).
-export([adjustment_can_change_status_to_succeeded_test/1]).
-export([negative_adjustment_can_change_status_to_succeeded_test/1]).
-export([adjustment_can_not_change_status_to_pending_test/1]).
-export([adjustment_can_not_change_status_to_same/1]).
-export([adjustment_sequence_test/1]).
@ -48,8 +50,10 @@ groups() ->
[
{default, [parallel], [
adjustment_can_change_status_to_failed_test,
negative_adjustment_can_change_status_to_failed_test,
adjustment_can_change_failure_test,
adjustment_can_change_status_to_succeeded_test,
negative_adjustment_can_change_status_to_succeeded_test,
adjustment_can_not_change_status_to_pending_test,
adjustment_can_not_change_status_to_same,
adjustment_sequence_test,
@ -120,6 +124,32 @@ adjustment_can_change_status_to_failed_test(C) ->
?assertEqual(?FINAL_BALANCE(0, <<"RUB">>), get_wallet_balance(WalletID)),
?assertEqual(?FINAL_BALANCE(0, <<"RUB">>), get_source_balance(SourceID)).
-spec negative_adjustment_can_change_status_to_failed_test(config()) -> test_return().
negative_adjustment_can_change_status_to_failed_test(C) ->
#{
wallet_id := WalletID,
source_id := SourceID
} = prepare_standard_environment({100, <<"RUB">>}, C),
DepositID = process_deposit(#{
source_id => SourceID,
wallet_id => WalletID,
body => {-50, <<"RUB">>}
}),
?assertEqual(?FINAL_BALANCE(50, <<"RUB">>), get_wallet_balance(WalletID)),
?assertEqual(?FINAL_BALANCE(-50, <<"RUB">>), get_source_balance(SourceID)),
Failure = #{code => <<"test">>},
AdjustmentID = process_adjustment(DepositID, #{
change => {change_status, {failed, Failure}},
external_id => <<"true_unique_id">>
}),
?assertMatch(succeeded, get_adjustment_status(DepositID, AdjustmentID)),
ExternalID = ff_adjustment:external_id(get_adjustment(DepositID, AdjustmentID)),
?assertEqual(<<"true_unique_id">>, ExternalID),
?assertEqual({failed, Failure}, get_deposit_status(DepositID)),
assert_adjustment_same_revisions(DepositID, AdjustmentID),
?assertEqual(?FINAL_BALANCE(100, <<"RUB">>), get_wallet_balance(WalletID)),
?assertEqual(?FINAL_BALANCE(-100, <<"RUB">>), get_source_balance(SourceID)).
-spec adjustment_can_change_failure_test(config()) -> test_return().
adjustment_can_change_failure_test(C) ->
#{
@ -172,6 +202,32 @@ adjustment_can_change_status_to_succeeded_test(C) ->
?assertEqual(?FINAL_BALANCE(5000100, <<"RUB">>), get_wallet_balance(WalletID)),
?assertEqual(?FINAL_BALANCE(-5000100, <<"RUB">>), get_source_balance(SourceID)).
-spec negative_adjustment_can_change_status_to_succeeded_test(config()) -> test_return().
negative_adjustment_can_change_status_to_succeeded_test(C) ->
#{
wallet_id := WalletID,
source_id := SourceID
} = prepare_standard_environment({5000000, <<"RUB">>}, C),
?assertEqual(?FINAL_BALANCE(5000000, <<"RUB">>), get_wallet_balance(WalletID)),
?assertEqual(?FINAL_BALANCE(-5000000, <<"RUB">>), get_source_balance(SourceID)),
DepositID = generate_id(),
Params = #{
id => DepositID,
wallet_id => WalletID,
source_id => SourceID,
body => {-6000000, <<"RUB">>}
},
ok = ff_deposit_machine:create(Params, ff_entity_context:new()),
?assertMatch({failed, _}, await_final_deposit_status(DepositID)),
AdjustmentID = process_adjustment(DepositID, #{
change => {change_status, succeeded}
}),
?assertMatch(succeeded, get_adjustment_status(DepositID, AdjustmentID)),
?assertMatch(succeeded, get_deposit_status(DepositID)),
assert_adjustment_same_revisions(DepositID, AdjustmentID),
?assertEqual(?FINAL_BALANCE(-1000000, <<"RUB">>), get_wallet_balance(WalletID)),
?assertEqual(?FINAL_BALANCE(1000000, <<"RUB">>), get_source_balance(SourceID)).
-spec adjustment_can_not_change_status_to_pending_test(config()) -> test_return().
adjustment_can_not_change_status_to_pending_test(C) ->
#{

View File

@ -16,6 +16,7 @@
%% Tests
-export([revert_ok_test/1]).
-export([negative_revert_ok_test/1]).
-export([multiple_reverts_ok_test/1]).
-export([multiple_parallel_reverts_ok_test/1]).
-export([idempotency_test/1]).
@ -50,6 +51,7 @@ groups() ->
[
{default, [parallel], [
revert_ok_test,
negative_revert_ok_test,
multiple_reverts_ok_test,
multiple_parallel_reverts_ok_test,
idempotency_test,
@ -126,6 +128,34 @@ revert_ok_test(C) ->
{ok, PartyRevision} = ff_party:get_revision(PartyID),
?assertEqual(PartyRevision, ff_deposit_revert:party_revision(Revert)).
-spec negative_revert_ok_test(config()) -> test_return().
negative_revert_ok_test(C) ->
#{
party_id := PartyID,
wallet_id := WalletID,
source_id := SourceID
} = prepare_standard_environment({10000, <<"RUB">>}, C),
DepositID1 = process_deposit(#{
source_id => SourceID,
wallet_id => WalletID,
body => {-5000, <<"RUB">>}
}),
RevertID = process_revert(DepositID1, #{
body => {-5000, <<"RUB">>}
}),
?assertEqual(?FINAL_BALANCE(10000, <<"RUB">>), get_wallet_balance(WalletID)),
?assertEqual(?FINAL_BALANCE(-10000, <<"RUB">>), get_source_balance(SourceID)),
Revert = get_revert(RevertID, DepositID1),
?assertEqual(undefined, ff_deposit_revert:reason(Revert)),
?assertEqual(undefined, ff_deposit_revert:external_id(Revert)),
?assertEqual({-5000, <<"RUB">>}, ff_deposit_revert:negative_body(Revert)),
?assertEqual(SourceID, ff_deposit_revert:source_id(Revert)),
?assertEqual(WalletID, ff_deposit_revert:wallet_id(Revert)),
DomainRevision = ff_domain_config:head(),
?assertEqual(DomainRevision, ff_deposit_revert:domain_revision(Revert)),
{ok, PartyRevision} = ff_party:get_revision(PartyID),
?assertEqual(PartyRevision, ff_deposit_revert:party_revision(Revert)).
-spec multiple_reverts_ok_test(config()) -> test_return().
multiple_reverts_ok_test(C) ->
#{

View File

@ -259,7 +259,7 @@ deposit_via_admin_amount_fails(C) ->
source = SrcID,
destination = WalID,
body = #'fistful_base_Cash'{
amount = -1,
amount = 0,
currency = #'fistful_base_CurrencyRef'{symbolic_code = <<"RUB">>}
}
}

View File

@ -390,7 +390,7 @@ validate_withdrawal_creation(Terms, {_, CurrencyID} = Cash, Method) ->
-spec validate_deposit_creation(terms(), cash()) -> Result when
Result :: {ok, valid} | {error, Error},
Error :: validate_deposit_creation_error().
validate_deposit_creation(_Terms, {Amount, _Currency} = Cash) when Amount < 1 ->
validate_deposit_creation(_Terms, {Amount, _Currency} = Cash) when Amount == 0 ->
{error, {bad_deposit_amount, Cash}};
validate_deposit_creation(Terms, {_Amount, CurrencyID} = _Cash) ->
do(fun() ->