Permalink
Browse files

WHISTLE-1217: populate item discounts

  • Loading branch information...
1 parent f273f08 commit 703af68b92c8dee88387fe89c3f5ce0929e06431 @k-anderson k-anderson committed Aug 2, 2012
@@ -27,57 +27,38 @@
%%--------------------------------------------------------------------
-spec sync/1 :: (ne_binary()) -> #wh_invoice{}.
sync(Account) ->
- Invoice = initialize(Account),
- create_items(Invoice).
+ create(Account).
%%--------------------------------------------------------------------
%% @public
%% @doc
%%
%% @end
%%--------------------------------------------------------------------
--spec initialize/1 :: (ne_binary()) -> #wh_invoice{}.
-initialize(Account) ->
+-spec create/1 :: (ne_binary()) -> #wh_invoice{}.
+create(Account) ->
Routines = [fun(I) -> I#wh_invoice{account_id=wh_util:format_account_id(Account, raw)
,account_db=wh_util:format_account_id(Account, encoded)}
end
,fun(#wh_invoice{account_id=AccountId}=I) ->
- I#wh_invoice{services=wh_services:fetch(AccountId)}
+ I#wh_invoice{services=wh_services:fetch(AccountId)}
end
,fun(#wh_invoice{account_db=AccountDb, account_id=AccountId}=I) ->
case couch_mgr:open_doc(AccountDb, AccountId) of
{ok, JObj} ->
BillingId = wh_json:get_ne_value(<<"billing_id">>, JObj, AccountId),
+ lager:debug("using billing id ~s for account ~s", [BillingId, AccountId]),
I#wh_invoice{billing_id=BillingId};
{error, _R} ->
- error_database(AccountId)
+ lager:debug("unable to open account definition for ~s (using account as billing id): ~p", [AccountId, _R]),
+ I#wh_invoice{billing_id=AccountId}
end
end
,fun(#wh_invoice{account_id=AccountId}=I) ->
I#wh_invoice{service_plans=wh_service_plans:fetch(AccountId)}
end
-
+ ,fun(#wh_invoice{service_plans=ServicePlans, services=Services}=I) ->
+ I#wh_invoice{items=wh_service_plans:create_items(Services, ServicePlans)}
+ end
],
lists:foldl(fun(F, I) -> F(I) end, #wh_invoice{}, Routines).
-
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%%
-%% @end
-%%--------------------------------------------------------------------
--spec create_items/1 :: (#wh_invoice{}) -> #wh_invoice{}.
-create_items(#wh_invoice{service_plans=ServicePlans, services=Services}=Invoice) ->
- Invoice#wh_invoice{items=wh_service_plans:create_items(Services, ServicePlans)}.
-
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%%
-%% @end
-%%--------------------------------------------------------------------
--spec error_database/1 :: (atom()) -> no_return().
-error_database(Reason) ->
- Error = <<"Unable to open document ", (wh_util:to_binary(Reason))/binary>>,
- lager:debug("~s", [Error]),
- throw({database_error, Error}).
@@ -8,8 +8,9 @@
-module(wh_service_items).
-export([empty/0]).
--export([update_quantity/4]).
--export([update_rate/4]).
+-export([update/5]).
+-export([set_single_discount/4]).
+-export([set_cumulative_discount/5]).
-include_lib("whistle_services/src/whistle_services.hrl").
@@ -18,28 +19,75 @@
,quantity = 0
,rate = undefined
,single_discount = false
- ,single_discount_price = 0.00
- ,cumulative_discount = false
- ,cumulative_discount_price = 0.00
- ,minimum = 0
+ ,single_discount_rate = 0.00
+ ,cumulative_discount = 0
+ ,cumulative_discount_rate = 0.00
}).
-type(item() :: #wh_service_item{}).
-type(items() :: [item(),...] | []).
-export_type([item/0]).
-export_type([items/0]).
+%%--------------------------------------------------------------------
+%% @public
+%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec empty/0 :: () -> items().
empty() ->
dict:new().
-update_quantity(Category, Item, Quantity, Items) ->
+%%--------------------------------------------------------------------
+%% @public
+%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec update/5 :: (ne_binary(), ne_binary(), integer(), 'undefined' | float(), items()) -> items().
+update(Category, Item, Quantity, Rate, Items) ->
+ case wh_util:is_empty(Rate) of
+ true -> lager:debug("set item '~s/~s' quantity ~p @ default rate", [Category, Item, Quantity]);
+ false -> lager:debug("set item '~s/~s' quantity ~p @ $~p", [Category, Item, Quantity, Rate])
+ end,
Key = {Category, Item},
- dict:update(Key, fun(#wh_service_item{quantity=ExistingQuantity}=ExistingItem) ->
- ExistingItem#wh_service_item{quantity=ExistingQuantity + Quantity}
- end, #wh_service_item{quantity=Quantity, category=Category, item=Item}, Items).
+ dict:update(Key, fun(#wh_service_item{}=ExistingItem) ->
+ ExistingItem#wh_service_item{quantity=Quantity, rate=Rate}
+ end, #wh_service_item{quantity=Quantity, rate=Rate, category=Category, item=Item}, Items).
-update_rate(Category, Item, Rate, Items) ->
+%%--------------------------------------------------------------------
+%% @public
+%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec set_single_discount/4 :: (ne_binary(), ne_binary(), 'undefined' | float(), items()) -> items().
+set_single_discount(Category, Item, Rate, Items) ->
+ case wh_util:is_empty(Rate) of
+ true -> lager:debug("set item '~s/~s' single discount @ default rate", [Category, Item]);
+ false -> lager:debug("set item '~s/~s' single discount @ $~p", [Category, Item, Rate])
+ end,
Key = {Category, Item},
- dict:update(Key, fun(ExistingItem) ->
- ExistingItem#wh_service_item{rate=Rate}
- end, #wh_service_item{rate=Rate, category=Category, item=Item}, Items).
+ dict:update(Key, fun(#wh_service_item{}=ExistingItem) ->
+ ExistingItem#wh_service_item{single_discount=true, single_discount_rate=Rate}
+ end, #wh_service_item{single_discount=true, single_discount_rate=Rate
+ ,category=Category, item=Item}, Items).
+
+%%--------------------------------------------------------------------
+%% @public
+%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec set_cumulative_discount/5 :: (ne_binary(), ne_binary(), integer(), 'undefined' | float(), items()) -> items().
+set_cumulative_discount(Category, Item, Quantity, Rate, Items) ->
+ case wh_util:is_empty(Rate) of
+ true -> lager:debug("set item '~s/~s' cumulative discount quantity ~p @ default rate", [Category, Item, Quantity]);
+ false -> lager:debug("set item '~s/~s' cumulative discount quantity ~p @ $~p", [Category, Item, Quantity, Rate])
+ end,
+ Key = {Category, Item},
+ dict:update(Key, fun(#wh_service_item{}=ExistingItem) ->
+ ExistingItem#wh_service_item{cumulative_discount=Quantity, cumulative_discount_rate=Rate}
+ end, #wh_service_item{cumulative_discount=Quantity, cumulative_discount_rate=Rate
+ ,category=Category, item=Item}, Items).
@@ -10,7 +10,7 @@
-include_lib("whistle_services/src/whistle_services.hrl").
-export([fetch/3]).
--export([create_items/2]).
+-export([create_items/3]).
%%--------------------------------------------------------------------
%% @public
@@ -21,7 +21,9 @@
-spec fetch/3 :: (ne_binary(), ne_binary(), wh_json:json_object()) -> 'undefined' | wh_json:json_object().
fetch(PlanId, VendorDb, _Overrides) ->
case couch_mgr:open_doc(VendorDb, PlanId) of
- {ok, JObj} -> JObj;
+ {ok, JObj} ->
+ lager:debug("using service plan ~s in vendor db ~s", [PlanId, VendorDb]),
+ JObj;
{error, _R} ->
lager:debug("unable to open service plan ~s in vendor db ~s: ~p", [PlanId, VendorDb, _R]),
undefined
@@ -33,45 +35,76 @@ fetch(PlanId, VendorDb, _Overrides) ->
%%
%% @end
%%--------------------------------------------------------------------
--spec create_items/2 :: (wh_json:json_object(), wh_services:services()) -> item().
--spec create_items/4 :: (ne_binary(), ne_binary(), wh_json:json_object(), wh_services:services()) -> item().
+-spec create_items/3 :: (wh_json:json_object(), wh_service_items:items(), wh_services:services()) -> wh_service_items:items().
+-spec create_items/5 :: (ne_binary(), ne_binary(), wh_json:json_object(), wh_service_items:items(), wh_services:services()) -> wh_service_items:items().
-create_items(ServicePlan, Services) ->
+create_items(ServicePlan, Items, Services) ->
Plan = wh_json:get_value(<<"plan">>, ServicePlan, wh_json:new()),
- [create_items(Category, Item, wh_json:get_value([Category, Item], Plan), Services)
- || Category <- wh_json:get_keys(Plan)
- ,Item <- wh_json:get_keys(Category, Plan)
- ].
+ Plans = [{Category, Item}
+ || Category <- wh_json:get_keys(Plan)
+ ,Item <- wh_json:get_keys(Category, Plan)
+ ],
+ lists:foldl(fun({Category, Item}, I) ->
+ ItemPlan = wh_json:get_value([Category, Item], Plan),
+ create_items(Category, Item, ItemPlan, I, Services)
+ end, Items, Plans).
-create_items(Category, Item, Plan, Services) ->
- io:format("PROCESS ~s/~s: ~p~n", [Category, Item, Plan]),
- Quantity = get_item_quantity(Category, Item, Plan, Services),
- CorrectedQuantity = case wh_json:get_integer_value(<<"minimum">>, Plan, 0) of
- Min when Min > Quantity -> Min;
- _Else -> Quantity
+create_items(Category, Item, ItemPlan, Items, Services) ->
+ Quantity = get_item_quantity(Category, Item, ItemPlan, Services),
+ CorrectedQuantity = case wh_json:get_integer_value(<<"minimum">>, ItemPlan, 0) of
+ Min when Min > Quantity ->
+ lager:debug("minimum '~s/~s' not met with ~p, enforcing quantity ~p", [Category, Item, Quantity, Min]),
+ Min;
+ _ -> Quantity
end,
- #wh_service_item{category = Category
- ,item = wh_json:get_ne_value(<<"as">>, Plan, Item)
- ,quantity = CorrectedQuantity
- ,rate = get_item_rate(CorrectedQuantity, Plan)
- ,single_discount = false
- ,single_discount_price = 0.00
- ,cumulative_discount = false
- ,cumulative_discount_price = 0.00
- }.
+ case CorrectedQuantity > 0 of
+ false -> Items;
+ true ->
+ Rate = get_rate(CorrectedQuantity, ItemPlan),
+ As = wh_json:get_ne_value(<<"as">>, ItemPlan, Item),
+ Routines = [fun(I) -> wh_service_items:update(Category, As, CorrectedQuantity, Rate, I) end
+ ,fun(I) ->
+ case wh_json:get_value([<<"discounts">>, <<"single">>], ItemPlan) of
+ undefined -> I;
+ SingleDiscount ->
+ SingleRate = wh_json:get_float_value(<<"rate">>, SingleDiscount, Rate),
+ wh_service_items:set_single_discount(Category, Item, SingleRate, I)
+ end
+ end
+ ,fun(I) ->
+ case wh_json:get_value([<<"discounts">>, <<"cumulative">>], ItemPlan) of
+ undefined -> I;
+ CumulativeDiscount ->
+ CumulativeQuantity = case wh_json:get_integer_value(<<"maximum">>, CumulativeDiscount, 0) of
+ Max when Max < CorrectedQuantity ->
+ lager:debug("item '~s/~s' quantity ~p exceeds cumulative discount max, using ~p"
+ ,[Category, Item, CorrectedQuantity, Max]),
+ Max;
+ _ -> CorrectedQuantity
+ end,
+ CumulativeRate = case get_rate(CorrectedQuantity, CumulativeDiscount) of
+ undefined -> Rate;
+ Else -> Else
+ end,
+ wh_service_items:set_cumulative_discount(Category, Item, CumulativeQuantity, CumulativeRate, I)
+ end
+ end
+ ],
+ lists:foldl(fun(F, I) -> F(I) end, Items, Routines)
+ end.
%%--------------------------------------------------------------------
%% @private
%% @doc
%%
%% @end
%%--------------------------------------------------------------------
--spec get_item_rate/2 :: (wh_json:json_object(), non_neg_integer()) -> ne_binary().
-get_item_rate(Quantity, Plan) ->
- Rates = wh_json:get_value(<<"rates">>, Plan, wh_json:new()),
+-spec get_rate/2 :: (non_neg_integer(), wh_json:json_object()) -> ne_binary().
+get_rate(Quantity, JObj) ->
+ Rates = wh_json:get_value(<<"rates">>, JObj, wh_json:new()),
L1 = [wh_util:to_integer(K) || K <- wh_json:get_keys(Rates)],
case lists:dropwhile(fun(K) -> Quantity > K end, lists:sort(L1)) of
- [] -> wh_json:get_float_value(<<"rate">>, Plan);
+ [] -> wh_json:get_float_value(<<"rate">>, JObj);
Range -> wh_json:get_float_value(wh_util:to_binary(hd(Range)), Rates)
end.
@@ -82,14 +115,18 @@ get_item_rate(Quantity, Plan) ->
%% @end
%%--------------------------------------------------------------------
-spec get_item_quantity/4 :: (ne_binary(), ne_binary(), wh_json:json_object(), wh_services:services()) -> integer().
-get_item_quantity(Category, <<"_all">>, Plan, Services) ->
- Exceptions = wh_json:get_value(<<"exceptions">>, Plan, []),
- case wh_json:is_true(<<"cascade">>, Plan) of
- true -> wh_services:cascade_category_quantity(Category, Exceptions, Services);
- false -> wh_services:category_quantity(Category, Exceptions, Services)
+get_item_quantity(Category, <<"_all">>, ItemPlan, Services) ->
+ Exceptions = wh_json:get_value(<<"exceptions">>, ItemPlan, []),
+ case wh_json:is_true(<<"cascade">>, ItemPlan) of
+ false -> wh_services:category_quantity(Category, Exceptions, Services);
+ true ->
+ lager:debug("collecting '~s' as a cascaded sum", [Category]),
+ wh_services:cascade_category_quantity(Category, Exceptions, Services)
end;
-get_item_quantity(Category, Item, Plan, Services) ->
- case wh_json:is_true(<<"cascade">>, Plan) of
- true -> wh_services:cascade_quantity(Category, Item, Services);
- false -> wh_services:quantity(Category, Item, Services)
+get_item_quantity(Category, Item, ItemPlan, Services) ->
+ case wh_json:is_true(<<"cascade">>, ItemPlan) of
+ false -> wh_services:quantity(Category, Item, Services);
+ true ->
+ lager:debug("collecting '~s/~s' as a cascaded quantity", [Category, Item]),
+ wh_services:cascade_quantity(Category, Item, Services)
end.
@@ -54,11 +54,13 @@ fetch(AccountId) ->
%%--------------------------------------------------------------------
-spec create_items/2 :: (plans(), wh_services:services()) -> wh_service_items:items().
create_items(Services, ServicePlans) ->
- Items = wh_service_items:empty(),
- [wh_service_plan:create_items(Plan, Services)
- || ServicePlan <- ServicePlans
- ,Plan <- ServicePlan#wh_service_plans.plans
- ].
+ Plans = [Plan
+ || ServicePlan <- ServicePlans
+ ,Plan <- ServicePlan#wh_service_plans.plans
+ ],
+ lists:foldl(fun(Plan, Items) ->
+ wh_service_plan:create_items(Plan, Items, Services)
+ end, wh_service_items:empty(), Plans).
%%--------------------------------------------------------------------
%% @private
@@ -26,7 +26,7 @@
-include_lib("whistle_services/src/whistle_services.hrl").
-include_lib("whistle/include/wh_databases.hrl").
--record(wh_services, {account_id
+-record(wh_services, {account_id = undefined
,dirty = false
,jobj = wh_json:new()
,updates = wh_json:new()
@@ -106,10 +106,11 @@ fetch(Account) ->
AccountDb = wh_util:format_account_id(Account, encoded),
case couch_mgr:open_doc(?WH_SERVICES_DB, AccountId) of
{ok, JObj} ->
+ lager:debug("loaded account service doc ~s", [AccountId]),
#wh_services{account_id=AccountId, jobj=JObj
,cascade_quantities=cascade_quantities(AccountId)};
{error, _R} ->
- lager:debug("unable to open account ~s services doc: ~p", [Account, _R]),
+ lager:debug("unable to open account ~s services doc (creating new): ~p", [Account, _R]),
TStamp = wh_util:current_tstamp(),
New = [{<<"_id">>, AccountId}
,{<<"pvt_created">>, TStamp}
@@ -118,11 +119,11 @@ fetch(Account) ->
,{<<"pvt_vsn">>, <<"1">>}
,{<<"pvt_account_id">>, AccountId}
,{<<"pvt_account_db">>, AccountDb}
- ,{<<"pvt_dirty">>, true}
,{?QUANTITIES, wh_json:new()}
],
#wh_services{account_id=AccountId, jobj=wh_json:from_list(New)
- ,cascade_quantities=cascade_quantities(AccountId)}
+ ,cascade_quantities=cascade_quantities(AccountId)
+ ,dirty=true}
end.
%%--------------------------------------------------------------------
@@ -155,12 +156,15 @@ save(#wh_services{jobj=JObj, updates=UpdatedQuantities, account_id=AccountId, di
UpdatedJObj = wh_json:set_values(Props, JObj),
case couch_mgr:save_doc(?WH_SERVICES_DB, UpdatedJObj) of
{ok, NewJObj} ->
+ lager:debug("saved new account service doc for ~s", [AccountId]),
Services#wh_services{jobj=NewJObj
,cascade_quantities=cascade_quantities(AccountId)};
{error, not_found} ->
+ lager:debug("service database does not exist, attempting to create", []),
true = couch_mgr:db_create(?WH_SERVICES_DB),
save(Services);
{error, conflict} ->
+ lager:debug("account service doc for ~s conflicted, merging changes and retrying", [AccountId]),
{ok, Existing} = couch_mgr:open_doc(?WH_SERVICES_DB, AccountId),
save(Services#wh_services{jobj=Existing})
end.

0 comments on commit 703af68

Please sign in to comment.