Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Kazoo-154 #11

Merged
merged 3 commits into from

2 participants

@k-anderson
Owner

Created a low balance alert system, utilizing the notify account crawler. If an account is found with more than the configurable threshold of credit (default $5.00) and then it falls bellow that amount an email will be sent. If the account belongs to the master reseller the email is sent to all admins of the account, otherwise the email is sent to all admins of the accounts reseller.

@macpie macpie merged commit aa3a263 into from
@k-anderson k-anderson deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 2, 2013
  1. @k-anderson

    KAZOO-154: update account crawler to conditionally check the balance …

    k-anderson authored
    …of an account and start notify template processor for low balance emails
  2. @k-anderson
  3. @k-anderson
This page is out of date. Refresh to see the latest.
View
6 whistle_apps/apps/notify/priv/notify_low_balance.config
@@ -1,4 +1,4 @@
-{default_text_template, <<"Low balance in prepay for account \"{{account.name}}\".\nThe account will not be able to make or receive per-minute calls until this balance is increased.\nThe current balance is: {{current_balance}}\n\nAccount ID: {{account.id}}">>}.
-{default_html_template, <<"<html><body><h2>Low Balance in \"{{account.name}}\"</h2><p>Current Balance: {{current_balance}}</p><p>The account will not be able to make or receive per-minute calls until this balance is increased.</body></html>">>}.
-{default_subject_template, <<"Low Balance ({{current_balance}}) for {{account.name}}">>}.
+{default_text_template, <<"The account \"{{account.name}}\" has less than {{threshold}} of credit remaining.\nIf the account runs out of credit it will not be able to make or receive per-minute calls.\nThe current balance is: {{current_balance}}\n\nAccount ID: {{account.id}}">>}.
+{default_html_template, <<"<html><body><h2>The account \"{{account.name}}\" has less than {{threshold}} of credit remaining.</h2><p>Current Balance: {{current_balance}}</p><p>If the account runs out of credit it will not be able to make or receive per-minute calls.</body></html>">>}.
+{default_subject_template, <<"Account {{account.name}} is running out of credit">>}.
View
220 whistle_apps/apps/notify/src/notify_account_crawler.erl
@@ -10,6 +10,8 @@
-behaviour(gen_server).
-export([start_link/0]).
+-export([check/1]).
+-export([low_balance_threshold/1]).
-export([init/1
,handle_call/3
,handle_cast/2
@@ -39,6 +41,19 @@
start_link() ->
gen_server:start_link(?MODULE, [], []).
+check(Account) when is_binary(Account) ->
+ AccountId = wh_util:format_account_id(Account, 'raw'),
+ AccountDb = wh_util:format_account_id(Account, 'encoded'),
+ case couch_mgr:open_doc(?WH_ACCOUNTS_DB, AccountId) of
+ {'ok', JObj} ->
+ AccountDb = wh_json:get_value(<<"pvt_account_db">>, JObj),
+ process_account(AccountId, AccountDb, JObj);
+ {'error', _R} ->
+ lager:warning("unable to open account definition for ~s: ~p", [AccountId, _R])
+ end;
+check(Account) ->
+ check(wh_util:to_binary(Account)).
+
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
@@ -58,8 +73,8 @@ init([]) ->
%% The other modules are init'd because they are
%% responders for the gen_listener...
notify_first_occurrence:init(),
- self() ! crawl_accounts,
- {ok, #state{}}.
+ self() ! 'crawl_accounts',
+ {'ok', #state{}}.
%%--------------------------------------------------------------------
%% @private
@@ -76,7 +91,7 @@ init([]) ->
%% @end
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
- {reply, {error, not_implemented}, State}.
+ {'reply', {'error', 'not_implemented'}, State}.
%%--------------------------------------------------------------------
%% @private
@@ -89,7 +104,7 @@ handle_call(_Request, _From, State) ->
%% @end
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
- {noreply, State}.
+ {'noreply', State}.
%%--------------------------------------------------------------------
%% @private
@@ -101,39 +116,39 @@ handle_cast(_Msg, State) ->
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
-handle_info(next_account, []) ->
+handle_info('next_account', []) ->
Cycle = whapps_config:get_integer(?MOD_CONFIG_CAT, <<"cycle_delay_time">>, 300000),
- erlang:send_after(Cycle, self(), crawl_accounts),
- {noreply, []};
-handle_info(next_account, [Account|Accounts]) ->
+ erlang:send_after(Cycle, self(), 'crawl_accounts'),
+ {'noreply', []};
+handle_info('next_account', [Account|Accounts]) ->
_ = case wh_json:get_value(<<"id">>, Account) of
- <<"_design", _/binary>> -> ok;
+ <<"_design", _/binary>> -> 'ok';
AccountId ->
%% do not open the account def in the account db or we will
%% be wasting bigcouch's file descriptors
case couch_mgr:open_doc(?WH_ACCOUNTS_DB, AccountId) of
- {ok, JObj} ->
+ {'ok', JObj} ->
AccountDb = wh_json:get_value(<<"pvt_account_db">>, JObj),
process_account(AccountId, AccountDb, JObj);
- {error, _R} ->
+ {'error', _R} ->
lager:warning("unable to open account definition for ~s: ~p", [AccountId, _R])
end
end,
Cycle = whapps_config:get_integer(?MOD_CONFIG_CAT, <<"interaccount_delay">>, 10000),
- erlang:send_after(Cycle, self(), next_account),
- {noreply, Accounts};
-handle_info(crawl_accounts, _) ->
+ erlang:send_after(Cycle, self(), 'next_account'),
+ {'noreply', Accounts};
+handle_info('crawl_accounts', _) ->
_ = case couch_mgr:all_docs(?WH_ACCOUNTS_DB) of
- {ok, JObjs} ->
- self() ! next_account,
- {noreply, wh_util:shuffle_list(JObjs)};
- {error, _R} ->
+ {'ok', JObjs} ->
+ self() ! 'next_account',
+ {'noreply', wh_util:shuffle_list(JObjs)};
+ {'error', _R} ->
lager:warning("unable to list all docs in ~s: ~p", [?WH_ACCOUNTS_DB, _R]),
- self() ! next_account,
- {noreply, []}
+ self() ! 'next_account',
+ {'noreply', []}
end;
handle_info(_Info, State) ->
- {noreply, State}.
+ {'noreply', State}.
%%--------------------------------------------------------------------
%% @private
@@ -158,41 +173,42 @@ terminate(_Reason, _State) ->
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
+ {'ok', State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
process_account(AccountId, AccountDb, JObj) ->
- maybe_test_for_initial_occurrences(AccountId, AccountDb, JObj).
+ lager:debug("notify crawler processing account ~s", [AccountId]),
+ _ = maybe_test_for_initial_occurrences(AccountId, AccountDb, JObj),
+ _ = maybe_test_for_low_balance(AccountId, AccountDb, JObj),
+ 'ok'.
--spec maybe_test_for_initial_occurrences/3 ::(ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
+-spec maybe_test_for_initial_occurrences(ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
maybe_test_for_initial_occurrences(AccountId, AccountDb, JObj) ->
- ConfigCat = <<(?NOTIFY_CONFIG_CAT)/binary, ".first_occurrence">>,
- case whapps_config:get_is_true(ConfigCat, <<"crawl_for_first_occurrence">>, true) of
- false -> ok;
- true ->
+ case whapps_config:get_is_true(?MOD_CONFIG_CAT, <<"crawl_for_first_occurrence">>, 'true') of
+ 'false' -> 'ok';
+ 'true' ->
test_for_initial_occurrences(AccountId, AccountDb, JObj)
end.
--spec test_for_initial_occurrences/3 :: (ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
+-spec test_for_initial_occurrences(ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
test_for_initial_occurrences(AccountId, AccountDb, JObj) ->
_ = maybe_test_for_registrations(AccountId, AccountDb, JObj),
maybe_test_for_initial_call(AccountId, AccountDb, JObj).
--spec maybe_test_for_registrations/3 :: (ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
+-spec maybe_test_for_registrations(ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
maybe_test_for_registrations(AccountId, AccountDb, JObj) ->
Realm = wh_json:get_ne_value(<<"realm">>, JObj),
case wh_json:is_true([<<"notifications">>, <<"first_occurrence">>, <<"sent_initial_registration">>], JObj)
- orelse Realm =:= undefined
+ orelse Realm =:= 'undefined'
of
- true -> ok;
- false ->
- lager:debug("testing for initial registration in account ~s", [AccountId]),
+ 'true' -> 'ok';
+ 'false' ->
test_for_registrations(AccountId, AccountDb, Realm)
end.
--spec test_for_registrations/3 :: (ne_binary(), ne_binary(), ne_binary()) -> 'ok'.
+-spec test_for_registrations(ne_binary(), ne_binary(), ne_binary()) -> 'ok'.
test_for_registrations(AccountId, AccountDb, Realm) ->
Req = [{<<"Realm">>, Realm}
,{<<"Fields">>, [<<"Account-ID">>]}
@@ -205,71 +221,151 @@ test_for_registrations(AccountId, AccountDb, Realm) ->
,2000
),
case ReqResp of
- {error, _R} ->
- lager:debug("unable to find any registrations for ~s: ~p", [Realm, _R]),
- ok;
- {ok, _J} ->
+ {'error', _} -> 'ok';
+ {'ok', _} ->
lager:debug("found initial registration for account ~s (~s)", [AccountId, Realm]),
handle_initial_registration(AccountId, AccountDb)
end.
--spec handle_initial_registration/2 :: (ne_binary(), ne_binary()) -> 'ok'.
+-spec handle_initial_registration(ne_binary(), ne_binary()) -> 'ok'.
handle_initial_registration(AccountId, AccountDb) ->
case couch_mgr:open_doc(AccountDb, AccountId) of
- {ok, JObj} ->
+ {'ok', JObj} ->
notify_initial_registration(AccountDb, JObj);
- _E -> ok
+ _E -> 'ok'
end.
--spec notify_initial_registration/2 :: (ne_binary(), wh_json:object()) -> 'ok'.
+-spec notify_initial_registration(ne_binary(), wh_json:object()) -> 'ok'.
notify_initial_registration(AccountDb, JObj) ->
Account = wh_json:set_value([<<"notifications">>, <<"first_occurrence">>, <<"sent_initial_registration">>]
- ,true
+ ,'true'
,JObj),
case couch_mgr:save_doc(AccountDb, Account) of
- {ok, _} ->
+ {'ok', _} ->
couch_mgr:ensure_saved(?WH_ACCOUNTS_DB, Account),
notify_first_occurrence:send(<<"registration">>, Account);
- _E -> ok
+ _E -> 'ok'
end.
--spec maybe_test_for_initial_call/3 :: (ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
+-spec maybe_test_for_initial_call(ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
maybe_test_for_initial_call(AccountId, AccountDb, JObj) ->
case wh_json:is_true([<<"notifications">>, <<"first_occurrence">>, <<"sent_initial_call">>], JObj) of
- true -> ok;
- false ->
- lager:debug("testing for initial call in account ~s", [AccountId]),
+ 'true' -> 'ok';
+ 'false' ->
+ lager:debug("looking for initial call in account ~s", [AccountId]),
test_for_initial_call(AccountId, AccountDb)
end.
--spec test_for_initial_call/2 :: (ne_binary(), ne_binary()) -> 'ok'.
+-spec test_for_initial_call(ne_binary(), ne_binary()) -> 'ok'.
test_for_initial_call(AccountId, AccountDb) ->
- ViewOptions = [{key, <<"cdr">>}
- ,{limit, <<"1">>}
+ ViewOptions = [{'key', <<"cdr">>}
+ ,{'limit', <<"1">>}
],
case couch_mgr:get_results(AccountDb, <<"maintenance/listing_by_type">>, ViewOptions) of
- {ok, [_|_]} ->
+ {'ok', [_|_]} ->
lager:debug("found initial call in account ~s", [AccountId]),
handle_initial_call(AccountId, AccountDb);
- _Else -> ok
+ _Else -> 'ok'
end.
--spec handle_initial_call/2 :: (ne_binary(), ne_binary()) -> 'ok'.
+-spec handle_initial_call(ne_binary(), ne_binary()) -> 'ok'.
handle_initial_call(AccountId, AccountDb) ->
case couch_mgr:open_doc(AccountDb, AccountId) of
- {ok, JObj} ->
+ {'ok', JObj} ->
notify_initial_call(AccountDb, JObj);
- _ -> ok
+ _ -> 'ok'
end.
--spec notify_initial_call/2 :: (ne_binary(), wh_json:object()) -> any().
+-spec notify_initial_call(ne_binary(), wh_json:object()) -> any().
notify_initial_call(AccountDb, JObj) ->
Account = wh_json:set_value([<<"notifications">>, <<"first_occurrence">>, <<"sent_initial_call">>]
- ,true
+ ,'true'
,JObj),
case couch_mgr:save_doc(AccountDb, Account) of
- {ok, _} ->
+ {'ok', _} ->
couch_mgr:ensure_saved(?WH_ACCOUNTS_DB, Account),
notify_first_occurrence:send(<<"call">>, Account);
- _ -> ok
+ _ -> 'ok'
+ end.
+
+-spec maybe_test_for_low_balance(ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
+maybe_test_for_low_balance(AccountId, AccountDb, JObj) ->
+ case whapps_config:get_is_true(?MOD_CONFIG_CAT, <<"crawl_for_low_balance">>, 'true') of
+ 'false' -> 'ok';
+ 'true' ->
+ test_for_low_balance(AccountId, AccountDb, JObj)
+ end.
+
+-spec test_for_low_balance(ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
+test_for_low_balance(AccountId, AccountDb, JObj) ->
+ Threshold = low_balance_threshold(AccountDb),
+ CurrentBalance = wht_util:current_balance(AccountId),
+ lager:debug("checking if account ~s balance is bellow $~w", [AccountId, Threshold]),
+ case CurrentBalance < wht_util:dollars_to_units(Threshold) of
+ 'false' ->
+ maybe_reset_low_balance(AccountId, AccountDb, JObj);
+ 'true' ->
+ maybe_handle_low_balance(CurrentBalance, AccountId, AccountDb, JObj)
+ end.
+
+-spec maybe_reset_low_balance(ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
+maybe_reset_low_balance(AccountId, AccountDb, JObj) ->
+ case wh_json:is_true([<<"notifications">>, <<"low_balance">>, <<"sent_low_balance">>], JObj, 'true') of
+ 'false' -> 'ok';
+ 'true' ->
+ reset_low_balance(AccountId, AccountDb)
+ end.
+
+-spec reset_low_balance(ne_binary(), ne_binary()) -> 'ok'.
+reset_low_balance(AccountId, AccountDb) ->
+ case couch_mgr:open_doc(AccountDb, AccountId) of
+ {'ok', JObj} ->
+ lager:debug("reseting low balance sent flag for account ~s", [AccountId]),
+ Account = wh_json:set_value([<<"notifications">>, <<"low_balance">>, <<"sent_low_balance">>], 'false', JObj),
+ case couch_mgr:save_doc(AccountDb, Account) of
+ {'ok', _} ->
+ couch_mgr:ensure_saved(?WH_ACCOUNTS_DB, Account),
+ 'ok';
+ _E ->
+ lager:info("unable to reset low balance flag for accounnt ~s: ~p", [AccountId, _E])
+ end;
+ {'error', _} ->
+ 'ok'
+ end.
+
+-spec maybe_handle_low_balance(integer(), ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
+maybe_handle_low_balance(CurrentBalance, AccountId, AccountDb, JObj) ->
+ case wh_json:is_true([<<"notifications">>, <<"low_balance">>, <<"sent_low_balance">>], JObj)
+ orelse wh_json:get_value([<<"notifications">>, <<"low_balance">>, <<"sent_low_balance">>], JObj) =:= 'undefined'
+ of
+ 'true' -> 'ok';
+ 'false' ->
+ handle_low_balance(CurrentBalance, AccountId, AccountDb)
+ end.
+
+-spec handle_low_balance(integer(), ne_binary(), ne_binary()) -> 'ok'.
+handle_low_balance(CurrentBalance, AccountId, AccountDb) ->
+ case couch_mgr:open_doc(AccountDb, AccountId) of
+ {'ok', JObj} ->
+ notify_low_balance(CurrentBalance, AccountId, AccountDb, JObj);
+ _ -> 'ok'
end.
+
+-spec notify_low_balance(integer(), ne_binary(), ne_binary(), wh_json:object()) -> 'ok'.
+notify_low_balance(CurrentBalance, AccountId, AccountDb, JObj) ->
+ Account = wh_json:set_value([<<"notifications">>, <<"low_balance">>, <<"sent_low_balance">>]
+ ,'true'
+ ,JObj),
+ case couch_mgr:save_doc(AccountDb, Account) of
+ {'ok', _} ->
+ couch_mgr:ensure_saved(?WH_ACCOUNTS_DB, Account),
+ notify_low_balance:send(CurrentBalance, Account);
+ _E ->
+ lager:debug("unable to update low balance flag for account ~s: ~p~n", [AccountId, _E]),
+ 'ok'
+ end.
+
+-spec low_balance_threshold(ne_binary()) -> float().
+low_balance_threshold(_AccountDb) ->
+ ConfigCat = <<(?NOTIFY_CONFIG_CAT)/binary, ".low_balance">>,
+ whapps_config:get_float(ConfigCat, <<"threshold">>, 5.00).
View
130 whistle_apps/apps/notify/src/notify_low_balance.erl
@@ -2,7 +2,7 @@
%%% @copyright (C) 2011, VoIP INC
%%% @doc
%%% Renders a custom account email template, or the system default,
-%%% and sends the email to the account admin
+%%% and sends the email with voicemail attachment to the user.
%%% @end
%%%
%%% @contributors
@@ -13,7 +13,9 @@
%%%-------------------------------------------------------------------
-module(notify_low_balance).
--export([init/0, handle_req/2]).
+-export([init/0, send/2]).
+-export([collect_recipients/1]).
+
-include("notify.hrl").
@@ -43,31 +45,26 @@ init() ->
%% process the AMQP requests
%% @end
%%--------------------------------------------------------------------
--spec handle_req(wh_json:object(), wh_proplist()) -> any().
-handle_req(JObj, _Props) ->
- true = wapi_notifications:low_balance_v(JObj),
- _ = whapps_util:put_callid(JObj),
-
- lager:debug("account has a low balance, sending email notification"),
-
- {ok, Account} = notify_util:get_account_doc(JObj),
+-spec send(integer(), wh_json:object()) -> any().
+send(CurrentBalance, Account) ->
+ AccountId = wh_json:get_value(<<"_id">>, Account),
+ case collect_recipients(AccountId) of
+ [] -> 'ok';
+ To ->
+ lager:debug("sending low balance alert for account ~s", [AccountId]),
+ Props = create_template_props(CurrentBalance, Account),
- lager:debug("creating low_balance notice"),
-
- Props = create_template_props(JObj, Account),
+ CustomTxtTemplate = wh_json:get_value([<<"notifications">>, <<"low_balance">>, <<"email_text_template">>], Account),
+ {ok, TxtBody} = notify_util:render_template(CustomTxtTemplate, ?DEFAULT_TEXT_TMPL, Props),
- CustomTxtTemplate = wh_json:get_value([<<"notifications">>, <<"low_balance">>, <<"email_text_template">>], Account),
- {ok, TxtBody} = notify_util:render_template(CustomTxtTemplate, ?DEFAULT_TEXT_TMPL, Props),
+ CustomHtmlTemplate = wh_json:get_value([<<"notifications">>, <<"low_balance">>, <<"email_html_template">>], Account),
+ {ok, HTMLBody} = notify_util:render_template(CustomHtmlTemplate, ?DEFAULT_HTML_TMPL, Props),
- CustomHtmlTemplate = wh_json:get_value([<<"notifications">>, <<"low_balance">>, <<"email_html_template">>], Account),
- {ok, HTMLBody} = notify_util:render_template(CustomHtmlTemplate, ?DEFAULT_HTML_TMPL, Props),
+ CustomSubjectTemplate = wh_json:get_value([<<"notifications">>, <<"low_balance">>, <<"email_subject_template">>], Account),
+ {ok, Subject} = notify_util:render_template(CustomSubjectTemplate, ?DEFAULT_SUBJ_TMPL, Props),
- CustomSubjectTemplate = wh_json:get_value([<<"notifications">>, <<"low_balance">>, <<"email_subject_template">>], Account),
- {ok, Subject} = notify_util:render_template(CustomSubjectTemplate, ?DEFAULT_SUBJ_TMPL, Props),
-
- To = wh_json:get_value([<<"notifications">>, <<"low_balance">>, <<"send_to">>], Account
- ,whapps_config:get(?MOD_CONFIG_CAT, <<"default_to">>, <<"">>)),
- build_and_send_email(TxtBody, HTMLBody, Subject, To, Props).
+ build_and_send_email(TxtBody, HTMLBody, Subject, To, Props)
+ end.
%%--------------------------------------------------------------------
%% @private
@@ -75,15 +72,29 @@ handle_req(JObj, _Props) ->
%% create the props used by the template render function
%% @end
%%--------------------------------------------------------------------
--spec create_template_props(wh_json:object(), wh_json:objects()) -> wh_proplist().
-create_template_props(Event, Account) ->
- [{<<"current_balance">>, notify_util:json_to_template_props(Event)}
- ,{<<"account">>, notify_util:json_to_template_props(Account)}
+-spec create_template_props(integer(), wh_json:object()) -> wh_proplist().
+create_template_props(CurrentBalance, Account) ->
+ AccountDb = wh_util:format_account_id(wh_json:get_value(<<"_id">>, Account), 'encoded'),
+ Threshold = notify_account_crawler:low_balance_threshold(AccountDb),
+ [{<<"account">>, notify_util:json_to_template_props(Account)}
+ ,{<<"service">>, notify_util:get_service_props(wh_json:new(), Account, ?MOD_CONFIG_CAT)}
+ ,{<<"current_balance">>, pretty_print_dollars(wht_util:units_to_dollars(CurrentBalance))}
+ ,{<<"threshold">>, pretty_print_dollars(Threshold)}
].
%%--------------------------------------------------------------------
%% @private
%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec pretty_print_dollars(float()) -> ne_binary().
+pretty_print_dollars(Amount) ->
+ wh_util:to_binary(io_lib:format("$~.2f", [Amount])).
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
%% process the AMQP requests
%% @end
%%--------------------------------------------------------------------
@@ -108,3 +119,68 @@ build_and_send_email(TxtBody, HTMLBody, Subject, To, Props) ->
]
},
notify_util:send_email(From, To, Email).
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec collect_recipients(ne_binary()) -> ne_binaries().
+collect_recipients(AccountId) ->
+ Routines = [fun(T) -> maybe_send_to_account_admins(AccountId, T) end
+ ,fun(T) -> maybe_send_to_master_support(T) end
+ ],
+ To = lists:foldl(fun(F, T) -> F(T) end, [], Routines),
+ sets:to_list(sets:from_list(To)).
+
+-spec maybe_send_to_account_admins(ne_binary(), ne_binaries()) -> ne_binaries().
+maybe_send_to_account_admins(AccountId, To) ->
+ {'ok', MasterAccountId} = whapps_util:get_master_account_id(),
+ maybe_send_to_account_admins(MasterAccountId, AccountId, To).
+
+-spec maybe_send_to_account_admins(ne_binary(), ne_binary(), ne_binaries()) -> ne_binaries().
+maybe_send_to_account_admins(MasterAccountId, AccountId, To) ->
+ ResellerId = wh_services:find_reseller_id(AccountId),
+ maybe_send_to_account_admins(ResellerId, MasterAccountId, AccountId, To).
+
+-spec maybe_send_to_account_admins(ne_binary(), ne_binary(), ne_binary(), ne_binaries()) -> ne_binaries().
+maybe_send_to_account_admins(ResellerId, ResellerId, AccountId, To) ->
+ send_to_account_admins(AccountId, To);
+maybe_send_to_account_admins(ResellerId, MasterAccountId, AccountId, To) ->
+ lager:debug("following reseller tree for ~s to ~s", [AccountId, ResellerId]),
+ maybe_send_to_account_admins(MasterAccountId, ResellerId, To).
+
+-spec send_to_account_admins(ne_binary(), ne_binaries()) -> ne_binaries().
+send_to_account_admins(AccountId, To) ->
+ AccountDb = wh_util:format_account_id(AccountId, 'encoded'),
+ ViewOptions = [{'key', <<"user">>}
+ ,'include_docs'
+ ],
+ case couch_mgr:get_results(AccountDb, <<"maintenance/listing_by_type">>, ViewOptions) of
+ {'ok', JObjs} ->
+ find_account_admins(JObjs, To);
+ {'error', _R} ->
+ lager:debug("faild to find users in ~s: ~p", [AccountId, _R]),
+ To
+ end.
+
+-spec find_account_admins(wh_json:objects(), ne_binaries()) -> ne_binaries().
+find_account_admins([], To) ->
+ To;
+find_account_admins([JObj|JObjs], To) ->
+ Email = wh_json:get_ne_value([<<"doc">>, <<"email">>], JObj),
+ case wh_json:get_value([<<"doc">>, <<"priv_level">>], JObj) =:= <<"admin">>
+ andalso Email =/= 'undefined'
+ of
+ 'false' -> find_account_admins(JObjs, To);
+ 'true' ->
+ find_account_admins(JObjs, [Email|To])
+ end.
+
+-spec maybe_send_to_master_support(ne_binaries()) -> ne_binaries().
+maybe_send_to_master_support(To) ->
+ case whapps_config:get(?MOD_CONFIG_CAT, <<"default_to">>, <<"">>) of
+ 'undefined' -> To;
+ DefaultTo -> [DefaultTo|To]
+ end.
Something went wrong with that request. Please try again.