From b9bbef633cded3118cb30358088a9ff3212b340c Mon Sep 17 00:00:00 2001 From: ttt161 Date: Mon, 3 Aug 2026 13:46:59 +0300 Subject: [PATCH 1/3] fix product cashflow calculation --- apps/hellgate/src/hg_cashflow.erl | 40 ++++++++++++++------ apps/hellgate/test/hg_invoice_dummy_data.erl | 7 +++- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/apps/hellgate/src/hg_cashflow.erl b/apps/hellgate/src/hg_cashflow.erl index 3cbfd39b..b8b90727 100644 --- a/apps/hellgate/src/hg_cashflow.erl +++ b/apps/hellgate/src/hg_cashflow.erl @@ -85,7 +85,10 @@ compute_postings(CF, Context, AccountMap, Opts) -> ?final_posting( construct_final_account(Source, AccountMap), construct_final_account(Destination, AccountMap), - hg_currency_converter:maybe_reverse_convert_cash(ExchangeContext, compute_volume(Volume, Context)), + hg_currency_converter:maybe_reverse_convert_cash( + ExchangeContext, + compute_volume(Volume, Context, ExchangeContext) + ), Details, ExchangeContext ) @@ -172,14 +175,22 @@ revert_details(Details) -> -define(rational(P, Q), #base_Rational{p = P, q = Q}). -spec compute_volume(cash_volume(), context()) -> cash() | no_return(). -compute_volume(?fixed(Cash), _Context) -> - Cash; -compute_volume(?share(P, Q, Of, RoundingMethod), Context) -> - compute_parts_of(P, Q, resolve_constant(Of, Context), RoundingMethod); -compute_volume(?product(Fun, CVs) = CV0, Context) -> +compute_volume(Volume, Context) -> + compute_volume(Volume, Context, undefined). + +%% convert volume calculation result into the terminal currency +%% for correct calculation of product post +compute_volume(?fixed(Cash), _Context, ExchangeContext) -> + hg_currency_converter:maybe_convert_cash(ExchangeContext, Cash); +compute_volume(?share(P, Q, Of, RoundingMethod), Context, ExchangeContext) -> + hg_currency_converter:maybe_convert_cash( + ExchangeContext, + compute_parts_of(P, Q, resolve_constant(Of, Context), RoundingMethod) + ); +compute_volume(?product(Fun, CVs) = CV0, Context, ExchangeContext) -> case ordsets:size(CVs) of N when N > 0 -> - compute_product(Fun, ordsets:to_list(CVs), CV0, Context); + compute_product(Fun, ordsets:to_list(CVs), CV0, Context, ExchangeContext); 0 -> error({misconfiguration, {'Cash volume product over empty set', CV0}}) end. @@ -195,15 +206,22 @@ compute_parts_of(P, Q, #domain_Cash{amount = Amount} = Cash, RoundingMethod) -> ) }. -compute_product(Fun, [CV | CVRest], CV0, Context) -> +compute_product(Fun, [CV | CVRest], CV0, Context, ExchangeContext) -> lists:foldl( - fun(CVN, CVMin) -> compute_product(Fun, CVN, CVMin, CV0, Context) end, + fun(CVN, CVMin) -> compute_product(Fun, CVN, CVMin, CV0, Context, ExchangeContext) end, compute_volume(CV, Context), CVRest ). -compute_product(Fun, CV, #domain_Cash{amount = AmountMin, currency = Currency} = CVMin, CV0, Context) -> - case compute_volume(CV, Context) of +compute_product( + Fun, + CV, + #domain_Cash{amount = AmountMin, currency = Currency} = CVMin, + CV0, + Context, + ExchangeContext +) -> + case compute_volume(CV, Context, ExchangeContext) of #domain_Cash{amount = Amount, currency = Currency} -> CVMin#domain_Cash{amount = compute_product_fun(Fun, AmountMin, Amount)}; _ -> diff --git a/apps/hellgate/test/hg_invoice_dummy_data.erl b/apps/hellgate/test/hg_invoice_dummy_data.erl index 60bc1120..fd71f7f1 100644 --- a/apps/hellgate/test/hg_invoice_dummy_data.erl +++ b/apps/hellgate/test/hg_invoice_dummy_data.erl @@ -242,7 +242,12 @@ payment_provision_terms(Currency, Category) -> ?cfpost( {system, settlement}, {provider, settlement}, - ?fixed(10, Currency) + {product, + {min_of, + ?ordset([ + ?share(60, 1000, operation_amount), + ?fixed(10, Currency) + ])}} ) ] }, From 73cfdb2da77fedb4c2f3a0f867ea9bdd90372b92 Mon Sep 17 00:00:00 2001 From: ttt161 Date: Mon, 3 Aug 2026 16:33:00 +0300 Subject: [PATCH 2/3] fix issues --- apps/hellgate/src/hg_cashflow.erl | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/apps/hellgate/src/hg_cashflow.erl b/apps/hellgate/src/hg_cashflow.erl index b8b90727..94dac138 100644 --- a/apps/hellgate/src/hg_cashflow.erl +++ b/apps/hellgate/src/hg_cashflow.erl @@ -85,10 +85,7 @@ compute_postings(CF, Context, AccountMap, Opts) -> ?final_posting( construct_final_account(Source, AccountMap), construct_final_account(Destination, AccountMap), - hg_currency_converter:maybe_reverse_convert_cash( - ExchangeContext, - compute_volume(Volume, Context, ExchangeContext) - ), + compute_volume(Volume, Context, ExchangeContext), Details, ExchangeContext ) @@ -178,15 +175,12 @@ revert_details(Details) -> compute_volume(Volume, Context) -> compute_volume(Volume, Context, undefined). -%% convert volume calculation result into the terminal currency -%% for correct calculation of product post compute_volume(?fixed(Cash), _Context, ExchangeContext) -> - hg_currency_converter:maybe_convert_cash(ExchangeContext, Cash); -compute_volume(?share(P, Q, Of, RoundingMethod), Context, ExchangeContext) -> - hg_currency_converter:maybe_convert_cash( - ExchangeContext, - compute_parts_of(P, Q, resolve_constant(Of, Context), RoundingMethod) - ); + %% if posting currency differs from payment currency + %% needs re-convert it into payment currency + hg_currency_converter:maybe_reverse_convert_cash(ExchangeContext, Cash); +compute_volume(?share(P, Q, Of, RoundingMethod), Context, _ExchangeContext) -> + compute_parts_of(P, Q, resolve_constant(Of, Context), RoundingMethod); compute_volume(?product(Fun, CVs) = CV0, Context, ExchangeContext) -> case ordsets:size(CVs) of N when N > 0 -> @@ -209,7 +203,7 @@ compute_parts_of(P, Q, #domain_Cash{amount = Amount} = Cash, RoundingMethod) -> compute_product(Fun, [CV | CVRest], CV0, Context, ExchangeContext) -> lists:foldl( fun(CVN, CVMin) -> compute_product(Fun, CVN, CVMin, CV0, Context, ExchangeContext) end, - compute_volume(CV, Context), + compute_volume(CV, Context, ExchangeContext), CVRest ). From 5d0db49b262311fb84d0b6410f24c1ead54454c3 Mon Sep 17 00:00:00 2001 From: ttt161 Date: Wed, 5 Aug 2026 13:36:22 +0300 Subject: [PATCH 3/3] fix issue --- apps/hellgate/src/hg_cashflow.erl | 15 +++++++++------ .../src/hg_invoice_payment_chargeback.erl | 6 +++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/hellgate/src/hg_cashflow.erl b/apps/hellgate/src/hg_cashflow.erl index 94dac138..1e5c0fd9 100644 --- a/apps/hellgate/src/hg_cashflow.erl +++ b/apps/hellgate/src/hg_cashflow.erl @@ -32,8 +32,9 @@ -type shop_config_ref() :: dmsl_domain_thrift:'ShopConfigRef'(). -type party_config_ref() :: dmsl_domain_thrift:'PartyConfigRef'(). -type route() :: hg_route:payment_route(). +-type exchange_context() :: hg_invoice_payment:exchange_context(). -type options() :: #{ - exchange_context => hg_invoice_payment:exchange_context() + exchange_context => exchange_context() }. %% @@ -43,6 +44,7 @@ -export([revert/1]). -export([compute_volume/2]). +-export([compute_volume/3]). -export([get_partial_remainders/1]). @@ -175,6 +177,7 @@ revert_details(Details) -> compute_volume(Volume, Context) -> compute_volume(Volume, Context, undefined). +-spec compute_volume(cash_volume(), context(), exchange_context() | undefined) -> cash() | no_return(). compute_volume(?fixed(Cash), _Context, ExchangeContext) -> %% if posting currency differs from payment currency %% needs re-convert it into payment currency @@ -285,22 +288,22 @@ modify_remainder(#domain_FinalCashFlowAccount{account_type = AccountType}, ?cash compute_volume_test() -> Cash = ?cash(100, <<"RUB">>), - ?assertEqual(Cash, compute_volume(?fixed(Cash), #{})), + ?assertEqual(Cash, compute_volume(?fixed(Cash), #{}, undefined)), ?assertEqual( ?cash(1, <<"RUB">>), - compute_volume(?share(1, 100, operation_amount, undefined), #{operation_amount => Cash}) + compute_volume(?share(1, 100, operation_amount, undefined), #{operation_amount => Cash}, undefined) ), ?assertEqual( Cash, - compute_volume(?product(min_of, [?fixed(Cash), ?fixed(?cash(200, <<"RUB">>))]), #{}) + compute_volume(?product(min_of, [?fixed(Cash), ?fixed(?cash(200, <<"RUB">>))]), #{}, undefined) ), ?assertEqual( Cash, - compute_volume(?product(max_of, [?fixed(Cash), ?fixed(?cash(50, <<"RUB">>))]), #{}) + compute_volume(?product(max_of, [?fixed(Cash), ?fixed(?cash(50, <<"RUB">>))]), #{}, undefined) ), ?assertEqual( ?cash(200, <<"RUB">>), - compute_volume(?product(sum_of, [?fixed(Cash), ?fixed(Cash)]), #{}) + compute_volume(?product(sum_of, [?fixed(Cash), ?fixed(Cash)]), #{}, undefined) ). -endif. diff --git a/apps/hellgate/src/hg_invoice_payment_chargeback.erl b/apps/hellgate/src/hg_invoice_payment_chargeback.erl index 4fa7f5da..4de8b1cd 100644 --- a/apps/hellgate/src/hg_invoice_payment_chargeback.erl +++ b/apps/hellgate/src/hg_invoice_payment_chargeback.erl @@ -449,7 +449,7 @@ build_chargeback_final_cash_flow(State, Opts) -> }, AccountMap = hg_accounting:collect_account_map(CollectAccountContext), ServiceContext = build_service_cash_flow_context(State), - ProviderContext = build_provider_cash_flow_context(State, ProviderFees), + ProviderContext = build_provider_cash_flow_context(State, ProviderFees, ExchangeContext), ServiceFinalCF = hg_cashflow:finalize(ServiceCashFlow, ServiceContext, AccountMap), ProviderFinalCF = hg_cashflow:finalize(ProviderCashFlow, ProviderContext, AccountMap, ProviderOpts), ServiceFinalCF ++ ProviderFinalCF. @@ -457,9 +457,9 @@ build_chargeback_final_cash_flow(State, Opts) -> build_service_cash_flow_context(State) -> #{operation_amount => get_body(State), surplus => get_levy(State)}. -build_provider_cash_flow_context(State, Fees) -> +build_provider_cash_flow_context(State, Fees, ExchangeContext) -> FeesContext = #{operation_amount => get_body(State)}, - ComputedFees = maps:map(fun(_K, V) -> hg_cashflow:compute_volume(V, FeesContext) end, Fees), + ComputedFees = maps:map(fun(_K, V) -> hg_cashflow:compute_volume(V, FeesContext, ExchangeContext) end, Fees), case get_target_status(State) of ?chargeback_status_rejected() -> ?cash(_Amount, SymCode) = get_body(State),