kaui 0.15.0 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/app/assets/stylesheets/kaui/common.less +0 -6
  4. data/app/assets/stylesheets/kaui/datatable.less +14 -0
  5. data/app/assets/stylesheets/kaui/home.less +4 -0
  6. data/app/controllers/kaui/accounts_controller.rb +37 -0
  7. data/app/controllers/kaui/admin_allowed_users_controller.rb +2 -2
  8. data/app/controllers/kaui/admin_controller.rb +27 -0
  9. data/app/controllers/kaui/admin_tenants_controller.rb +8 -2
  10. data/app/controllers/kaui/engine_controller.rb +2 -2
  11. data/app/controllers/kaui/engine_controller_util.rb +1 -1
  12. data/app/controllers/kaui/home_controller.rb +3 -1
  13. data/app/controllers/kaui/invoices_controller.rb +10 -0
  14. data/app/controllers/kaui/payments_controller.rb +31 -7
  15. data/app/controllers/kaui/queues_controller.rb +11 -0
  16. data/app/controllers/kaui/subscriptions_controller.rb +20 -2
  17. data/app/controllers/kaui/tags_controller.rb +11 -0
  18. data/app/controllers/kaui/transactions_controller.rb +27 -4
  19. data/app/helpers/kaui/account_helper.rb +1 -1
  20. data/app/helpers/kaui/application_helper.rb +5 -0
  21. data/app/helpers/kaui/date_helper.rb +5 -1
  22. data/app/helpers/kaui/payment_helper.rb +9 -0
  23. data/app/models/kaui/admin.rb +75 -0
  24. data/app/models/kaui/invoice_payment.rb +26 -9
  25. data/app/models/kaui/payment.rb +2 -0
  26. data/app/models/kaui/payment_method.rb +18 -5
  27. data/app/models/kaui/transaction.rb +8 -0
  28. data/app/models/kaui/user.rb +4 -0
  29. data/app/views/kaui/accounts/_account_info.html.erb +23 -2
  30. data/app/views/kaui/accounts/_billing_info.html.erb +21 -0
  31. data/app/views/kaui/accounts/_form.html.erb +14 -8
  32. data/app/views/kaui/accounts/index.html.erb +5 -2
  33. data/app/views/kaui/admin/index.html.erb +39 -0
  34. data/app/views/kaui/admin/index.js.erb +3 -0
  35. data/app/views/kaui/admin_allowed_users/show.html.erb +2 -2
  36. data/app/views/kaui/admin_tenants/_tenant_details.html.erb +15 -0
  37. data/app/views/kaui/admin_tenants/_useful_links.html.erb +18 -0
  38. data/app/views/kaui/admin_tenants/new_catalog.html.erb +0 -1
  39. data/app/views/kaui/admin_tenants/new_overdue_config.html.erb +0 -1
  40. data/app/views/kaui/admin_tenants/show.html.erb +42 -12
  41. data/app/views/kaui/home/index.html.erb +8 -1
  42. data/app/views/kaui/invoices/_invoice_table.html.erb +1 -1
  43. data/app/views/kaui/invoices/index.html.erb +13 -3
  44. data/app/views/kaui/invoices/show.html.erb +11 -1
  45. data/app/views/kaui/layouts/kaui_account_navbar.html.erb +4 -0
  46. data/app/views/kaui/layouts/kaui_navbar.html.erb +19 -12
  47. data/app/views/kaui/payments/_payment_table.html.erb +60 -50
  48. data/app/views/kaui/payments/index.html.erb +13 -3
  49. data/app/views/kaui/payments/show.html.erb +6 -0
  50. data/app/views/kaui/queues/index.html.erb +124 -0
  51. data/app/views/kaui/subscriptions/_edit_form.html.erb +4 -4
  52. data/app/views/kaui/subscriptions/_form.html.erb +3 -3
  53. data/app/views/kaui/subscriptions/_subscriptions_table.html.erb +25 -22
  54. data/app/views/kaui/subscriptions/edit_bcd.erb +29 -0
  55. data/app/views/kaui/tags/index.html.erb +7 -1
  56. data/app/views/kaui/transactions/_form.html.erb +16 -6
  57. data/config/routes.rb +23 -4
  58. data/kaui.gemspec +1 -1
  59. data/lib/kaui/version.rb +1 -1
  60. data/test/fixtures/catalog-v1.xml +0 -1
  61. metadata +161 -151
@@ -3,7 +3,7 @@ module Kaui
3
3
 
4
4
  def pretty_account_identifier
5
5
  return nil if @account.nil?
6
- @account.name.presence || @account.email.presence || @account.external_key
6
+ @account.name.presence || @account.email.presence || truncate_uuid(@account.external_key)
7
7
  end
8
8
  end
9
9
  end
@@ -4,5 +4,10 @@ module Kaui
4
4
  def tenant_selected?
5
5
  session[:kb_tenant_id].present?
6
6
  end
7
+
8
+ def truncate_class_name(klass, with_abbr = false)
9
+ splitted = klass.split('.')
10
+ with_abbr ? splitted.each_with_index.map { |k, idx| idx == splitted.size - 1 ? k : k[0]+'.' }.join : splitted[-1]
11
+ end
7
12
  end
8
13
  end
@@ -15,5 +15,9 @@ module Kaui
15
15
  parsed_date = DateTime.parse(date.to_s).in_time_zone(timezone)
16
16
  parsed_date.to_s(:date_only)
17
17
  end
18
+
19
+ def truncate_millis(date_s)
20
+ DateTime.parse(date_s).strftime('%FT%T')
21
+ end
18
22
  end
19
- end
23
+ end
@@ -0,0 +1,9 @@
1
+ module Kaui
2
+ module PaymentHelper
3
+
4
+ def transaction_statuses
5
+ Kaui::Payment::TRANSACTION_STATUSES
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,75 @@
1
+ class Kaui::Admin < KillBillClient::Model::Resource
2
+
3
+ KILLBILL_API_ADMIN_PREFIX = "#{KILLBILL_API_PREFIX}/admin"
4
+ KILLBILL_API_CLOCK_PREFIX = "#{KILLBILL_API_PREFIX}/test/clock"
5
+ KILLBILL_API_QUEUES_PREFIX = "#{KILLBILL_API_ADMIN_PREFIX}/queues"
6
+
7
+ class << self
8
+
9
+ def get_queues_entries(account_id, options = {})
10
+ res = KillBillClient::API.get KILLBILL_API_QUEUES_PREFIX,
11
+ {
12
+ :accountId => account_id,
13
+ :withHistory => options[:withHistory],
14
+ :minDate => options[:minDate]
15
+ },
16
+ options
17
+ JSON.parse res.body
18
+ end
19
+
20
+ def fix_transaction_state(payment_id, transaction_id, transaction_status, user = nil, reason = nil, comment = nil, options = {})
21
+ res = KillBillClient::API.put "#{KILLBILL_API_ADMIN_PREFIX}/payments/#{payment_id}/transactions/#{transaction_id}",
22
+ {:transactionStatus => transaction_status}.to_json,
23
+ {},
24
+ {
25
+ :user => user,
26
+ :reason => reason,
27
+ :comment => comment,
28
+ }.merge(options)
29
+ end
30
+
31
+ def get_clock(time_zone, options)
32
+ params = {}
33
+ params[:timeZone] = time_zone unless time_zone.nil?
34
+
35
+ res = KillBillClient::API.get KILLBILL_API_CLOCK_PREFIX,
36
+ params,
37
+ options
38
+ JSON.parse res.body
39
+ end
40
+
41
+ def set_clock(requested_date, time_zone, options)
42
+ params = {}
43
+ params[:requestedDate] = requested_date unless requested_date.nil?
44
+ params[:timeZone] = time_zone unless time_zone.nil?
45
+
46
+ # The default 5s is not always enough
47
+ params[:timeoutSec] ||= 10
48
+
49
+ res = KillBillClient::API.post KILLBILL_API_CLOCK_PREFIX,
50
+ {},
51
+ params,
52
+ {}.merge(options)
53
+ JSON.parse res.body
54
+ end
55
+
56
+ def increment_kb_clock(days, weeks, months, years, time_zone, options)
57
+ params = {}
58
+ params[:days] = days unless days.nil?
59
+ params[:weeks] = weeks unless weeks.nil?
60
+ params[:months] = months unless months.nil?
61
+ params[:years] = years unless years.nil?
62
+ params[:timeZone] = time_zone unless time_zone.nil?
63
+
64
+ # The default 5s is not always enough
65
+ params[:timeoutSec] ||= 10
66
+
67
+ res = KillBillClient::API.put KILLBILL_API_CLOCK_PREFIX,
68
+ {},
69
+ params,
70
+ {}.merge(options)
71
+
72
+ JSON.parse res.body
73
+ end
74
+ end
75
+ end
@@ -7,6 +7,14 @@ class Kaui::InvoicePayment < KillBillClient::Model::InvoicePayment
7
7
 
8
8
  class << self
9
9
 
10
+ def find_safely_by_id(id, options = {})
11
+ Kaui::InvoicePayment.find_by_id(id, true, true, options)
12
+ rescue => e
13
+ # Maybe the plugin is not registered or the plugin threw an exception
14
+ Rails.logger.warn(e)
15
+ Kaui::InvoicePayment.find_by_id(id, false, true, options)
16
+ end
17
+
10
18
  def build_from_raw_payment(raw_payment)
11
19
 
12
20
  return nil if raw_payment.nil?
@@ -39,16 +47,25 @@ class Kaui::InvoicePayment < KillBillClient::Model::InvoicePayment
39
47
 
40
48
  # For each payment transaction, compute next_retry date by joining with payment attempts
41
49
  def build_transactions_next_retry_date!
42
- (transactions || []).each do |transaction|
43
- # Filter attempts matching that transaction and SCHEDULED for retry
44
- transaction.next_retry_date = (payment_attempts || []).select do |attempt|
45
- ((attempt.transaction_id && attempt.transaction_id == transaction.transaction_id) ||
46
- (attempt.transaction_external_key && attempt.transaction_external_key == transaction.transaction_external_key)) &&
47
- attempt.state_name == 'SCHEDULED'
48
- end.map do |attempt|
49
- attempt.effective_date
50
- end.first
50
+
51
+ # Filter scheduled attempts: We could have several in parallel when multiple independent transactions occur at the same time
52
+ # (They would have different transaction_external_key)
53
+ scheduled_attempts = (payment_attempts || []).select do |a|
54
+ a.state_name == 'SCHEDULED'
55
+ end
56
+
57
+ # Look for latest transaction associated with each such scheduled attempt and set retry date accordingly
58
+ scheduled_attempts.each do |a|
59
+
60
+ last_transaction_for_attempt = (transactions || []).select do |t|
61
+ t.transaction_external_key == a.transaction_external_key
62
+ end.sort_by do |t|
63
+ t.effective_date
64
+ end.last
65
+
66
+ last_transaction_for_attempt.next_retry_date = a.effective_date if last_transaction_for_attempt
51
67
  end
68
+
52
69
  end
53
70
 
54
71
  end
@@ -4,6 +4,8 @@ class Kaui::Payment < KillBillClient::Model::Payment
4
4
 
5
5
  attr_accessor :payment_date, :target_invoice_id
6
6
 
7
+ TRANSACTION_STATUSES = %w(SUCCESS PENDING PAYMENT_FAILURE PLUGIN_FAILURE UNKNOWN)
8
+
7
9
  SAMPLE_REASON_CODES = ['600 - Alt payment method',
8
10
  '699 - OTHER']
9
11
 
@@ -8,13 +8,26 @@ class Kaui::PaymentMethod < KillBillClient::Model::PaymentMethod
8
8
  end
9
9
  end
10
10
 
11
+ def self.find_safely_by_id(id, options = {})
12
+ Kaui::PaymentMethod.find_by_id(id, true, options)
13
+ rescue => e
14
+ # Maybe the plugin is not registered or the plugin threw an exception
15
+ Rails.logger.warn(e)
16
+ Kaui::PaymentMethod.find_by_id(id, false, options) rescue nil # deleted pm?
17
+ end
18
+
11
19
  def self.find_all_safely_by_account_id(account_id, options = {})
12
- begin
13
- Kaui::PaymentMethod.find_all_by_account_id(account_id, true, options)
14
- rescue KillBillClient::API::BadRequest
15
- # Maybe the plugin(s) are not registered?
16
- Kaui::PaymentMethod.find_all_by_account_id(account_id, false, options)
20
+ pms = Kaui::PaymentMethod.find_all_by_account_id(account_id, false, options)
21
+
22
+ pms.each_with_index do |pm, i|
23
+ begin
24
+ pms[i] = Kaui::PaymentMethod.find_by_id(pm.payment_method_id, true, options)
25
+ rescue => e
26
+ # Maybe the plugin is not registered or the plugin threw an exception
27
+ Rails.logger.warn(e)
28
+ end
17
29
  end
30
+ pms
18
31
  end
19
32
 
20
33
  def self.find_non_external_by_account_id(account_id, with_plugin_info = false, options = {})
@@ -2,6 +2,14 @@ class Kaui::Transaction < KillBillClient::Model::Transaction
2
2
 
3
3
  attr_accessor :next_retry_date
4
4
 
5
+ def self.build_from_raw_transaction(raw_transaction)
6
+ result = Kaui::Transaction.new
7
+ KillBillClient::Model::PaymentTransactionAttributes.instance_variable_get('@json_attributes').each do |attr|
8
+ result.send("#{attr}=", raw_transaction.send(attr))
9
+ end
10
+ result
11
+ end
12
+
5
13
  def create(account_id = nil, payment_method_id = nil, user = nil, reason = nil, comment = nil, options = {})
6
14
  if transaction_type == 'AUTHORIZE'
7
15
  auth(account_id, payment_method_id, user, reason, comment, options)
@@ -35,6 +35,10 @@ module Kaui
35
35
  end
36
36
  end
37
37
 
38
+ def root?
39
+ Kaui.root_username == kb_username
40
+ end
41
+
38
42
  private
39
43
 
40
44
  def self.do_find_permissions(options = {})
@@ -10,6 +10,15 @@
10
10
  <% end %>
11
11
  </h1>
12
12
 
13
+ <% unless @tags.find { |tag| tag.tag_definition_name == '__PARK__' }.nil? %>
14
+ <div class="alert alert-danger">
15
+ This account is parked, meaning the system detected inconsistent state which could lead to mis-billing. For safety, no more invoices will be generated.
16
+ <% if can? :trigger, Kaui::Invoice %>
17
+ Once the data is fixed, you can unpark it by manually <%= link_to 'triggering an invoice run', kaui_engine.trigger_invoice_path(params), :method => :post %>.
18
+ <% end %>
19
+ </div>
20
+ <% end %>
21
+
13
22
  <div class="info-wrapper">
14
23
 
15
24
  <div class="tag-bar tag-bar-breathe">
@@ -49,8 +58,20 @@
49
58
  <td><%= @account.currency %></td>
50
59
  </tr>
51
60
  <tr>
52
- <th>Timezone</th>
53
- <td><%= @account.time_zone %></td>
61
+ <th>Timezone</th>
62
+ <td><%= @account.time_zone %></td>
63
+ </tr>
64
+ <tr>
65
+ <th>Locale</th>
66
+ <td><%= @account.locale %></td>
67
+ </tr>
68
+ <tr>
69
+ <th>Notes</th>
70
+ <% if @account.notes %>
71
+ <td><textarea rows="3" class="form-control" readonly="true"><%= @account.notes %></textarea></td>
72
+ <% else %>
73
+ <td>N/A</td>
74
+ <% end %>
54
75
  </tr>
55
76
  </table>
56
77
  </div>
@@ -95,6 +95,27 @@
95
95
  </td>
96
96
  </tr>
97
97
  </table>
98
+
99
+ <% if can? :trigger, Kaui::Invoice %>
100
+ <div style="padding-bottom: 50px;">
101
+ <%= form_tag kaui_engine.trigger_invoice_path(params), :method => :post do %>
102
+ <div class="form-group">
103
+ <%= label_tag :target_date, 'Trigger invoice generation', :class => 'col-sm-6 control-label', :style => 'padding-left: 0;' %>
104
+ <div class="col-sm-3">
105
+ <input class="form-control" name="target_date" type="text" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-today-highlight="true">
106
+ </div>
107
+ <div style="width: 14.5%; float: left; padding: 0;">
108
+ <%= label_tag :dry_run do %>
109
+ <%= check_box_tag :dry_run, '1', true %>&nbsp;<small>Dry-run</small>
110
+ <% end %>
111
+ </div>
112
+ <div class="col-xs-1" style="padding: 0;">
113
+ <%= button_tag '<i class="fa fa-magic"></i>'.html_safe, :class => 'btn btn-xs' %>
114
+ </div>
115
+ </div>
116
+ <% end %>
117
+ </div>
118
+ <% end %>
98
119
  </div>
99
120
  </div>
100
121
 
@@ -46,6 +46,12 @@
46
46
  </div>
47
47
  </div>
48
48
  <% end %>
49
+ <div class="form-group">
50
+ <%= f.label :locale, 'Locale', :class => 'col-sm-3 control-label' %>
51
+ <div class="col-sm-9">
52
+ <%= f.text_field :locale, :class => 'form-control' %>
53
+ </div>
54
+ </div>
49
55
  <div class="form-group">
50
56
  <%= f.label :address1, 'Address line 1', :class => 'col-sm-3 control-label' %>
51
57
  <div class="col-sm-9">
@@ -89,16 +95,16 @@
89
95
  </div>
90
96
  </div>
91
97
  <div class="form-group">
92
- <%= f.label :locale, 'Locale', :class => 'col-sm-3 control-label' %>
93
- <div class="col-sm-9">
94
- <%= f.text_field :locale, :class => 'form-control' %>
95
- </div>
98
+ <%= f.label :phone, 'Phone', :class => 'col-sm-3 control-label' %>
99
+ <div class="col-sm-9">
100
+ <%= f.text_field :phone, :class => 'form-control' %>
101
+ </div>
96
102
  </div>
97
103
  <div class="form-group">
98
- <%= f.label :phone, 'Phone', :class => 'col-sm-3 control-label' %>
99
- <div class="col-sm-9">
100
- <%= f.text_field :phone, :class => 'form-control' %>
101
- </div>
104
+ <%= f.label :notes, 'Notes', :class => 'col-sm-3 control-label' %>
105
+ <div class="col-sm-9">
106
+ <%= f.text_field :notes, :class => 'form-control' %>
107
+ </div>
102
108
  </div>
103
109
  <% unless @account.persisted? %>
104
110
  <div class="form-group">
@@ -35,8 +35,11 @@ $(document).ready(function() {
35
35
  $('#accounts-table').dataTable({
36
36
  "dom": "<'row'r>t<'row'<'col-md-6'i><'col-md-6'p>>",
37
37
  "pagingType": "full_numbers",
38
- "pageLength": 50,
39
- "order": [[ 0, "asc" ]],
38
+ "pageLength": <%= @limit %>,
39
+ "displayStart": <%= @offset %>,
40
+ <% unless @ordering.blank? %>
41
+ "order": [[ 0, "<%= @ordering %>" ]],
42
+ <% end %>
40
43
  "processing": true,
41
44
  "serverSide": true,
42
45
  "search": {"search": "<%= @search_query %>"},
@@ -0,0 +1,39 @@
1
+ <div class="column-block">
2
+
3
+ <h1>Clock</h1>
4
+
5
+ <ul>
6
+ <li>Current date/time: <span id="kb_clock"><%= @clock['currentUtcTime'] %></span></li>
7
+ </ul>
8
+
9
+ <div>
10
+ <%= form_tag kaui_engine.admin_set_clock_path(params), :method => :put, :class => 'form-horizontal' do %>
11
+ <div class="form-group">
12
+ <%= label_tag :new_date, 'Set new date', :class => 'col-sm-2 control-label' %>
13
+ <div class="col-sm-2">
14
+ <input class="form-control" name="new_date" type="text" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-today-highlight="true" value="<%= @clock['localDate'] %>">
15
+ </div>
16
+ </div>
17
+ <div class="form-group">
18
+ <div class="col-sm-offset-2 col-sm-10">
19
+ <%= submit_tag 'Submit', :class => 'btn btn-default' %>
20
+ <%= button_tag 'Reset', :class => 'btn btn-danger' %>
21
+ </div>
22
+ </div>
23
+ <% end %>
24
+ </div>
25
+ </div>
26
+
27
+ <%= javascript_tag do %>
28
+ $(document).ready(function() {
29
+ scheduleRefresh();
30
+ });
31
+
32
+ function scheduleRefresh() {
33
+ setTimeout(refreshClock, 1000);
34
+ }
35
+
36
+ function refreshClock() {
37
+ $.ajax({url: "<%= admin_path(:format => :js) %>"});
38
+ }
39
+ <% end %>
@@ -0,0 +1,3 @@
1
+ $('#kb_clock').html("<%= @clock['currentUtcTime'] %>");
2
+
3
+ scheduleRefresh();
@@ -22,14 +22,14 @@
22
22
  <% @tenants.each do |tenant| %>
23
23
  <div class="checkbox">
24
24
  <label>
25
- <%= check_box_tag "tenant_#{tenant.id}", tenant.id, @allowed_user.kaui_tenants.map(&:id).include?(tenant.id), :disabled => Kaui.root_username != current_user.kb_username %>
25
+ <%= check_box_tag "tenant_#{tenant.id}", tenant.id, @allowed_user.kaui_tenants.map(&:id).include?(tenant.id), :disabled => !current_user.root? %>
26
26
  <span><%= tenant.name || tenant.api_key %></span>
27
27
  </label>
28
28
  </div>
29
29
  <% end %>
30
30
  </div>
31
31
  </div>
32
- <% unless Kaui.root_username != current_user.kb_username %>
32
+ <% unless !current_user.root? %>
33
33
  <div class="form-group">
34
34
  <div class="col-sm-offset-2 col-sm-10">
35
35
  <%= f.submit 'Save', :class => 'btn btn-default' %>
@@ -0,0 +1,15 @@
1
+ <div class="col-md-6">
2
+
3
+ <div class="column-block">
4
+
5
+ <h1>Tenant details</h1>
6
+
7
+ <b>Name:</b> <%= @tenant.name %>
8
+ <br/>
9
+ <b>Tenant ID:</b> <%= @tenant.kb_tenant_id %>
10
+ <br/>
11
+ <b>API Key:</b> <%= @tenant.api_key %>
12
+
13
+ </div>
14
+
15
+ </div>