Permalink
Browse files

Merge branch 'WHISTLE-1217' into v2.00

  • Loading branch information...
2 parents 6e092b0 + ca2977b commit ef239acf1c6153028d80c84ef5d9c1f0cea7e273 @k-anderson k-anderson committed Aug 16, 2012
Showing with 688 additions and 154 deletions.
  1. BIN lib/braintree-1.0.0/ebin/braintree_addon.beam
  2. BIN lib/braintree-1.0.0/ebin/braintree_address.beam
  3. BIN lib/braintree-1.0.0/ebin/braintree_card.beam
  4. BIN lib/braintree-1.0.0/ebin/braintree_customer.beam
  5. BIN lib/braintree-1.0.0/ebin/braintree_discount.beam
  6. BIN lib/braintree-1.0.0/ebin/braintree_request.beam
  7. BIN lib/braintree-1.0.0/ebin/braintree_subscription.beam
  8. BIN lib/braintree-1.0.0/ebin/braintree_transaction.beam
  9. BIN lib/braintree-1.0.0/ebin/braintree_util.beam
  10. +5 −2 lib/braintree-1.0.0/src/braintree_customer.erl
  11. +27 −43 lib/braintree-1.0.0/src/braintree_subscription.erl
  12. +1 −1 lib/rebar.config
  13. +1 −1 lib/whistle_services-1.0.0/priv/couchdb/views/services.json
  14. +1 −0 lib/whistle_services-1.0.0/priv/{example_service_plan.json → example_service_plan_1.json}
  15. +90 −0 lib/whistle_services-1.0.0/priv/example_service_plan_2.json
  16. BIN lib/whistle_services-1.0.0/src/bookkeepers/.wh_bookkeeper_braintree.erl.swp
  17. +29 −7 lib/whistle_services-1.0.0/src/bookkeepers/wh_bookkeeper_braintree.erl
  18. +36 −40 lib/whistle_services-1.0.0/src/wh_service_plan.erl
  19. +44 −11 lib/whistle_services-1.0.0/src/wh_service_plans.erl
  20. +67 −13 lib/whistle_services-1.0.0/src/{wh_service_invoices.erl → wh_service_sync.erl}
  21. +234 −30 lib/whistle_services-1.0.0/src/wh_services.erl
  22. +2 −3 lib/whistle_services-1.0.0/src/whistle_services_sup.erl
  23. +1 −0 whistle_apps/apps/crossbar/src/crossbar_maintenance.erl
  24. +26 −3 whistle_apps/apps/crossbar/src/modules/cb_accounts.erl
  25. +123 −0 whistle_apps/apps/crossbar/src/modules/cb_services.erl
  26. +1 −0 whistle_apps/src/whistle_apps_sup.erl
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -72,9 +72,12 @@ new_subscription(PlanId, Customer) ->
%% Given a cutomer record find (if any) the default payment token
%% @end
%%--------------------------------------------------------------------
--spec default_payment_token/1 :: (#bt_customer{}) -> 'undefined' | ne_binary().
+-spec default_payment_token/1 :: (ne_binary() | #bt_customer{}) -> 'undefined' | ne_binary().
default_payment_token(#bt_customer{}=Customer) ->
- braintree_card:default_payment_token(get_cards(Customer)).
+ braintree_card:default_payment_token(get_cards(Customer));
+default_payment_token(CustomerId) ->
+ default_payment_token(find(CustomerId)).
+
%%--------------------------------------------------------------------
%% @public
@@ -464,53 +464,20 @@ record_to_xml(#bt_subscription{}=Subscription, ToString) ->
,{'add-ons', create_addon_changes(Subscription#bt_subscription.add_ons)}
,{'discounts', create_discount_changes(Subscription#bt_subscription.discounts)}
],
- Conditionals = [fun(#bt_subscription{do_not_inherit=true}, P) ->
- case proplists:get_value('options', P) of
- undefined ->
- [{'options', [{'do-not-inherit-add-ons-or-discounts', true}]}|P];
- Options ->
- [{'options', [{'do-not-inherit-add-ons-or-discounts', true}|Options]}
- |proplists:delete('options', P)
- ]
- end;
- (_, P) -> P
+ Conditionals = [fun(#bt_subscription{do_not_inherit=Value}, P) ->
+ update_options('do-not-inherit-add-ons-or-discounts', Value, P)
end
- ,fun(#bt_subscription{prorate_charges=true}, P) ->
- case proplists:get_value('options', P) of
- undefined ->
- [{'options', [{'prorate-charges', true}]}|P];
- Options ->
- [{'options', [{'prorate-charges', true}|Options]}
- |proplists:delete('options', P)
- ]
- end;
- (_, P) -> P
+ ,fun(#bt_subscription{prorate_charges=Value}, P) ->
+ update_options('prorate-charges', Value, P)
end
- ,fun(#bt_subscription{revert_on_prorate_fail=true}, P) ->
- case proplists:get_value('options', P) of
- undefined ->
- [{'options', [{'revert-subscription-on-proration-failure', true}]}|P];
- Options ->
- [{'options', [{'revert-subscription-on-proration-failure', true}|Options]}
- |proplists:delete('options', P)
- ]
- end;
- (_, P) -> P
+ ,fun(#bt_subscription{revert_on_prorate_fail=Value}, P) ->
+ update_options('revert-subscription-on-proration-failure', Value, P)
end
- ,fun(#bt_subscription{replace_add_ons=true}, P) ->
- case proplists:get_value('options', P) of
- undefined ->
- [{'options', [{'replace-all-add-ons-and-discounts', true}]}|P];
- Options ->
- [{'options', [{'replace-all-add-ons-and-discounts', true}|Options]}
- |proplists:delete('options', P)
- ]
- end;
- (_, P) -> P
+ ,fun(#bt_subscription{replace_add_ons=Value}, P) ->
+ update_options('replace-all-add-ons-and-discounts', Value, P)
end
- ,fun(#bt_subscription{start_immediately=true}, P) ->
- [{'options', [{'start-immediately', true}]}|P];
- (_, P) -> P
+ ,fun(#bt_subscription{start_immediately=Value}, P) ->
+ update_options('start-immediately', Value, P)
end
],
Props1 = lists:foldr(fun(F, P) -> F(Subscription, P) end, Props, Conditionals),
@@ -519,6 +486,23 @@ record_to_xml(#bt_subscription{}=Subscription, ToString) ->
false -> Props1
end.
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Determine the necessary steps to change the add ons
+%% @end
+%%--------------------------------------------------------------------
+-spec update_options/3 :: (_, _, proplist()) -> proplist().
+update_options(Key, Value, Props) ->
+ case proplists:get_value('options', Props) of
+ undefined ->
+ [{'options', [{Key, Value}]}|Props];
+ Options ->
+ [{'options', [{Key, Value}|Options]}
+ |proplists:delete('options', Props)
+ ]
+ end.
+
%%--------------------------------------------------------------------
%% @private
%% @doc
View
@@ -2,6 +2,6 @@
{lib_dirs, ["."]}.
{sub_dirs, ["whistle-1.0.0", "whistle_amqp-1.0.0", "whistle_couch-1.0.0", "whistle_stats-1.0.0"
,"whistle_number_manager-1.0.0", "erlydtl-0.7.0", "kazoo_translator-1.0.0"
- ,"whistle_service-1.0.0"
+ ,"whistle_services-1.0.0"
]}.
{dialyzer_opts, [{warnings, [unmatched_returns, race_conditions, error_handling, underspecs]}]}.
@@ -3,7 +3,7 @@
"language": "javascript",
"views": {
"cascade_quantities": {
- "map": "function (doc) {if (doc.pvt_type != 'service' || doc.pvt_deleted || !doc.quantities) return;for (var key in doc.quantities) {var obj = doc.quantities[key];for (var prop in obj) {if (obj[prop] < 1) continue;if (doc.billing_id == doc._id) {emit([doc._id, key, prop], obj[prop]);} else {for (var idx in doc.pvt_tree) {emit([doc.pvt_tree[idx] || doc._id, key, prop], obj[prop]);}}}}}",
+ "map": "function (doc) {if (doc.pvt_type != 'service' || doc.pvt_deleted || !doc.quantities || doc.billing_id == doc._id) return;for (var key in doc.quantities) {var obj = doc.quantities[key];for (var prop in obj) {if (obj[prop] < 1) continue;for (var idx in doc.pvt_tree) {emit([doc.pvt_tree[idx] || doc._id, key, prop], obj[prop]);}}}}",
"reduce": "_sum"
},
"dirty": {
@@ -1,5 +1,6 @@
{
"_id": "968dc36503bcb05f798d9530016f311f",
+ "_rev": "45-e0a49ed498cd6884dc8262b98cc6188d",
"name": "Direct Web Signups, No Support",
"pvt_type": "service_plan",
"description": "",
@@ -0,0 +1,90 @@
+{
+ "_id": "e748394e1d1b85eeb3f6bec0e0a6d404",
+ "_rev": "8-53977ba6d28c5f5369cc3099d02a9949",
+ "name": "Full Service",
+ "description": "",
+ "plan": {
+ "phone_numbers": {
+ "did_us": {
+ "name": "US DID",
+ "rate": 1,
+ "discount": {
+ "cumulative": {
+ "rates": {
+ "5": 0,
+ "10": 0.25,
+ "20": 0.5
+ }
+ }
+ },
+ "cascade": true
+ },
+ "tollfree_us": {
+ "name": "US Tollfree",
+ "rate": 5,
+ "cascade": true
+ }
+ },
+ "number_services": {
+ },
+ "limits": {
+ "twoway_trunks": {
+ "name": "Two-Way Trunk",
+ "rate": 19.99
+ },
+ "inbound_trunks": {
+ "name": "Inbound Trunk",
+ "rate": 10.99
+ }
+ },
+ "devices": {
+ "_all": {
+ "name": "SIP Device",
+ "as": "sip_devices",
+ "rates": {
+ "5": 0,
+ "20": 24.95,
+ "50": 49.95,
+ "100": 149.95
+ },
+ "cascade": true
+ }
+ }
+ },
+ "bookkeepers": {
+ "braintree": {
+ "phone_numbers": {
+ "did_us": {
+ "plan": "SIP_Services",
+ "addon": "did_us",
+ "discounts": {
+ "cumulative": "discount_did_us"
+ }
+ },
+ "tollfree_us": {
+ "plan": "SIP_Services",
+ "addon": "tollfree_us"
+ }
+ },
+ "number_services": {
+ },
+ "limits": {
+ "twoway_trunks": {
+ "plan": "SIP_Services",
+ "addon": "twoway_trunk"
+ },
+ "inbound_trunks": {
+ "plan": "SIP_Services",
+ "addon": "inbound_trunk"
+ }
+ },
+ "devices": {
+ "sip_devices": {
+ "plan": "SIP_Services",
+ "addon": "sip_device"
+ }
+ }
+ }
+ },
+ "pvt_type": "service_plan"
+}
@@ -10,6 +10,7 @@
-include_lib("whistle_services/src/whistle_services.hrl").
-export([sync/2]).
+-export([is_good_standing/1]).
-record(wh_service_update, {bt_subscription
,plan_id
@@ -19,6 +20,23 @@
,account_id
,bt_customer
}).
+
+%%--------------------------------------------------------------------
+%% @public
+%% @doc
+%%
+%% @end
+%%--------------------------------------------------------------------
+is_good_standing(AccountId) ->
+ try braintree_customer:default_payment_token(AccountId) of
+ _ ->
+ lager:debug("braintree customer ~s is believed to be in good standing", [AccountId]),
+ true
+ catch
+ throw:_R ->
+ lager:debug("braintree customer ~s is not in good standing: ~p", [AccountId, _R]),
+ false
+ end.
%%--------------------------------------------------------------------
%% @public
@@ -27,8 +45,12 @@
%% @end
%%--------------------------------------------------------------------
sync(Items, AccountId) ->
- Customer = fetch_or_create_customer(AccountId),
- sync(wh_service_items:to_list(Items), AccountId, #wh_service_updates{bt_customer=Customer}).
+ ItemList = wh_service_items:to_list(Items),
+ case fetch_bt_customer(AccountId, ItemList =/= []) of
+ undefined -> ok;
+ Customer ->
+ sync(ItemList, AccountId, #wh_service_updates{bt_customer=Customer})
+ end.
sync([], _AccountId, #wh_service_updates{bt_subscriptions=Subscriptions}) ->
_ = [braintree_subscription:update(Subscription)
@@ -103,15 +125,15 @@ handle_cumulative_discounts(ServiceItem, Subscription) ->
%%
%% @end
%%--------------------------------------------------------------------
--spec fetch_or_create_customer/1 :: (ne_binary()) -> braintree_customer:customer().
-fetch_or_create_customer(AccountId) ->
+-spec fetch_bt_customer/2 :: (ne_binary(), boolean()) -> 'undefined' | braintree_customer:customer().
+fetch_bt_customer(AccountId, NewItems) ->
lager:debug("requesting braintree customer ~s", [AccountId]),
try braintree_customer:find(AccountId) of
Customer -> Customer
catch
- throw:{not_found, _} ->
- lager:debug("creating new braintree customer ~s", [AccountId]),
- braintree_customer:create(AccountId)
+ throw:{not_found, Error} when NewItems ->
+ throw({no_payment_token, wh_json:from_list([{<<"no_payment_token">>, Error}])});
+ throw:{not_found, _} -> undefined
end.
%%--------------------------------------------------------------------
@@ -59,48 +59,44 @@ create_items(Category, Item, ServiceItems, ServicePlan, Services) ->
Min;
_ -> ItemQuantity
end,
- case Quantity > 0 of
- false -> ServiceItems;
- true ->
- Rate = get_rate(Quantity, ItemPlan),
- %% allow service plans to re-map item names (IE: softphone items "as" sip_device)
- As = wh_json:get_ne_value(<<"as">>, ItemPlan, Item),
- Routines = [fun(I) -> wh_service_item:set_category(Category, I) end
- ,fun(I) -> wh_service_item:set_item(As, I) end
- ,fun(I) -> wh_service_item:set_quantity(Quantity, I) end
- ,fun(I) -> wh_service_item:set_rate(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_item:set_single_discount_rate(SingleRate, I)
- end
+ Rate = get_rate(Quantity, ItemPlan),
+ %% allow service plans to re-map item names (IE: softphone items "as" sip_device)
+ As = wh_json:get_ne_value(<<"as">>, ItemPlan, Item),
+ Routines = [fun(I) -> wh_service_item:set_category(Category, I) end
+ ,fun(I) -> wh_service_item:set_item(As, I) end
+ ,fun(I) -> wh_service_item:set_quantity(Quantity, I) end
+ ,fun(I) -> wh_service_item:set_rate(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_item:set_single_discount_rate(SingleRate, I)
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 < Quantity ->
- lager:debug("item '~s/~s' quantity ~p exceeds cumulative discount max, using ~p"
- ,[Category, As, Quantity, Max]),
- Max;
- _ -> Quantity
- end,
- CumulativeRate = case get_rate(Quantity, CumulativeDiscount) of
- undefined -> Rate;
- Else -> Else
- end,
- I2 = wh_service_item:set_cumulative_discount(CumulativeQuantity, I),
- wh_service_item:set_cumulative_discount_rate(CumulativeRate, I2)
- 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 < Quantity ->
+ lager:debug("item '~s/~s' quantity ~p exceeds cumulative discount max, using ~p"
+ ,[Category, As, Quantity, Max]),
+ Max;
+ _ -> Quantity
+ end,
+ CumulativeRate = case get_rate(Quantity, CumulativeDiscount) of
+ undefined -> Rate;
+ Else -> Else
+ end,
+ I2 = wh_service_item:set_cumulative_discount(CumulativeQuantity, I),
+ wh_service_item:set_cumulative_discount_rate(CumulativeRate, I2)
end
- ,fun(I) -> wh_service_item:set_bookkeepers(bookkeeper_jobj(Category, As, ServicePlan), I) end
- ],
- ServiceItem = lists:foldl(fun(F, I) -> F(I) end, wh_service_items:find(Category, As, ServiceItems), Routines),
- wh_service_items:update(ServiceItem, ServiceItems)
- end.
+ end
+ ,fun(I) -> wh_service_item:set_bookkeepers(bookkeeper_jobj(Category, As, ServicePlan), I) end
+ ],
+ ServiceItem = lists:foldl(fun(F, I) -> F(I) end, wh_service_items:find(Category, As, ServiceItems), Routines),
+ wh_service_items:update(ServiceItem, ServiceItems).
%%--------------------------------------------------------------------
%% @private
Oops, something went wrong.

0 comments on commit ef239ac

Please sign in to comment.