kaui 4.0.25 → 4.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 908a750ff22253e88a65a7330cd770b76335a4a334435c119730259ec0d34e92
4
- data.tar.gz: b74d7a880dcf04a21a890b617c5584115eb03376d38659cb261125b80af51f29
3
+ metadata.gz: 939bd1335abb8e3253fba3edb39871baab6a2bca5f7e10f26747b8bf22c3bfa9
4
+ data.tar.gz: eb850fd782524ffae4f8a7a652eece483244ddeb798d5221485b083e8f1d0039
5
5
  SHA512:
6
- metadata.gz: bda938be812b9b0902d96ca7524e35283b00cb50654bfaffeeea41bf8dc6a1eaa021ee01c81fd0e7fb4b76adbc9652e52bee2ae07422f9763fe955198238bd02
7
- data.tar.gz: 3e004500ec5e7c98f216c9642612537daa77373599a1b0c987d64cb4d33867a19e5cb0eda628ecec26e5eb58c8da2170ca152cbf5b7473469ccc17be6f98e6b3
6
+ metadata.gz: e07257d417598b8ef1514b22bf2551673b07c64aa66164f0e791751f2e93e362853a2d60c5d376bf3254b78498b208fb76c39abb7e342866288896b91acdeebe
7
+ data.tar.gz: a61104d90eddee06456c260d26c1adca1a6ad0fb0790587063592178ba40722be8b7efcbdaaf6de6575712f660d396b48613004dafadd43a12b7477c43495b79
data/README.md CHANGED
@@ -19,7 +19,7 @@ Kill Bill compatibility
19
19
  Dependencies
20
20
  ------------
21
21
 
22
- Ruby 3.2.2+ or JRuby 9.4.2.0+ required.
22
+ Ruby 3.1.0+ or JRuby 10.0.6.0+ required.
23
23
 
24
24
  Running Kaui locally
25
25
  ---------------------
@@ -0,0 +1,3 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M10 1.66675L17.0833 4.16675V9.44119C17.0833 13.2489 14.5321 16.5934 10.8563 17.5793L10 17.8085L9.14367 17.5793C5.46791 16.5934 2.91667 13.2489 2.91667 9.44119V4.16675L10 1.66675Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M10 18.3337C14.6024 18.3337 18.3334 14.6027 18.3334 10.0003C18.3334 5.39795 14.6024 1.66699 10 1.66699C5.39765 1.66699 1.66669 5.39795 1.66669 10.0003C1.66669 14.6027 5.39765 18.3337 10 18.3337Z" stroke="#414651" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M4.39587 4.39795L15.6042 15.6062" stroke="#414651" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -437,6 +437,44 @@ table tr.expired td {
437
437
  right: 0.5rem;
438
438
  }
439
439
 
440
+ /* app/views/kaui/components/blocking_state_form/_about_panel.html.erb */
441
+
442
+ .blocking-state-about-panel {
443
+ flex: 0 0 20rem;
444
+ max-width: 20rem;
445
+ height: fit-content;
446
+ padding: 1.25rem;
447
+ background-color: #EFF6FF;
448
+ border: 0.0625rem solid #DBEAFE;
449
+ border-radius: 0.5rem;
450
+ }
451
+
452
+ .blocking-state-about-panel .icon-container {
453
+ display: inline-flex;
454
+ justify-content: center;
455
+ align-items: center;
456
+ width: 2.5rem;
457
+ height: 2.5rem;
458
+ border-radius: 50%;
459
+ background-color: #2563EB;
460
+ flex-shrink: 0;
461
+ }
462
+
463
+ .blocking-state-about-panel h6 {
464
+ font-weight: 600;
465
+ font-size: 1rem;
466
+ line-height: 1.5rem;
467
+ color: #1B1C1E;
468
+ }
469
+
470
+ .blocking-state-about-panel p {
471
+ font-size: 0.875rem;
472
+ font-weight: 400;
473
+ line-height: 1.25rem;
474
+ color: #414651;
475
+ margin-bottom: 0;
476
+ }
477
+
440
478
  .kaui-subscription-new .form-group.d-flex.pb-3 .form-control {
441
479
  height: 2.5rem;
442
480
  border-radius: 0.375rem;
@@ -11,6 +11,7 @@ module Kaui
11
11
  @invoices = timeline.invoices
12
12
  @payments = timeline.payments
13
13
  extract_invoices_by_id(@invoices)
14
+ @account_blocking_states = fetch_account_blocking_states(@account)
14
15
 
15
16
  # Lookup all bundle names
16
17
  @bundle_names = {}
@@ -51,6 +52,7 @@ module Kaui
51
52
  @invoices = timeline.invoices
52
53
  @payments = timeline.payments
53
54
  extract_invoices_by_id(@invoices)
55
+ @account_blocking_states = fetch_account_blocking_states(@account)
54
56
 
55
57
  # Lookup all bundle names
56
58
  @bundle_names = {}
@@ -117,20 +119,30 @@ module Kaui
117
119
  @bundles.each do |bundle|
118
120
  bundle.subscriptions.each do |sub|
119
121
  sub.events.each do |event|
120
- # Skip SERVICE_STATE_CHANGE events
121
- next if event.event_type == 'SERVICE_STATE_CHANGE'
122
-
123
122
  effective_date = event.effective_date.present? ? Date.parse(event.effective_date).to_s : '[unknown]'
124
123
  bundle_keys = @bundle_names[bundle.external_key]
125
- event_type = event.event_type
126
- phase = event.phase
127
124
  audit_logs = event.audit_logs.present? ? event.audit_logs.map { |entry| Kaui::AuditLog.description(entry) }.join(', ') : ''
125
+ details = if event.event_type == 'SERVICE_STATE_CHANGE' || event.is_blocked_billing || event.is_blocked_entitlement
126
+ "#{event.service_name}/#{event.service_state_name} (billing=#{event.is_blocked_billing}, entitlement=#{event.is_blocked_entitlement})"
127
+ else
128
+ event.phase
129
+ end
128
130
 
129
- csv << [effective_date, bundle_keys, event_type, phase, audit_logs] if filter_date?(effective_date, start_date, end_date)
131
+ csv << [effective_date, bundle_keys, event.event_type, details, audit_logs] if filter_date?(effective_date, start_date, end_date)
130
132
  end
131
133
  end
132
134
  end
133
135
  end
136
+
137
+ if %w[ENTITLEMENT ALL].include?(event_type)
138
+ @account_blocking_states.each do |blocking_state|
139
+ effective_date = blocking_state.effective_date.present? ? Date.parse(blocking_state.effective_date).to_s : '[unknown]'
140
+ audit_logs = blocking_state.audit_logs.present? ? blocking_state.audit_logs.map { |entry| Kaui::AuditLog.description(entry) }.join(', ') : ''
141
+ details = "#{blocking_state.service}/#{blocking_state.state_name} (billing=#{blocking_state.is_block_billing}, entitlement=#{blocking_state.is_block_entitlement}, change=#{blocking_state.is_block_change})"
142
+
143
+ csv << [effective_date, '', 'SERVICE_STATE_CHANGE', details, audit_logs] if filter_date?(effective_date, start_date, end_date)
144
+ end
145
+ end
134
146
  end
135
147
 
136
148
  send_data csv_string, filename: "account-timelines-#{Time.zone.today}.csv", type: 'text/csv'
@@ -161,5 +173,11 @@ module Kaui
161
173
  [invoice.invoice_id, Kaui::Invoice.build_from_raw_invoice(invoice)]
162
174
  end
163
175
  end
176
+
177
+ def fetch_account_blocking_states(account)
178
+ account.blocking_states(nil, nil, 'NONE', options_for_klient)
179
+ rescue StandardError
180
+ []
181
+ end
164
182
  end
165
183
  end
@@ -391,5 +391,20 @@ module Kaui
391
391
  data = KillBillClient::Model::Export.find_by_account_id(params[:account_id], current_user.kb_username, options_for_klient)
392
392
  send_data data, filename: "account#{params[:account_id]}.txt", type: :txt
393
393
  end
394
+
395
+ def block
396
+ @account = Kaui::Account.find_by_id(params.require(:account_id), false, false, options_for_klient)
397
+ end
398
+
399
+ def do_block
400
+ account_id = params.require(:account_id)
401
+ account = Kaui::Account.new(account_id:)
402
+ account.set_blocking_state(params.require(:state_name), params.require(:service),
403
+ params[:is_block_change] == '1', params[:is_block_entitlement] == '1',
404
+ params[:is_block_billing] == '1', params[:requested_date].presence,
405
+ current_user.kb_username, params[:reason], params[:comment], options_for_klient)
406
+
407
+ redirect_to account_path(account_id), notice: 'Blocking state was successfully created'
408
+ end
394
409
  end
395
410
  end
@@ -128,6 +128,20 @@ module Kaui
128
128
  redirect_to kaui_engine.account_bundles_path(@account.account_id), notice: msg
129
129
  end
130
130
 
131
+ def block
132
+ @bundle = Kaui::Bundle.find_by_id_or_key(params.require(:id), options_for_klient)
133
+ end
134
+
135
+ def do_block
136
+ bundle = Kaui::Bundle.new(bundle_id: params.require(:id))
137
+ bundle.set_blocking_state(params.require(:state_name), params.require(:service),
138
+ params[:is_block_change] == '1', params[:is_block_entitlement] == '1',
139
+ params[:is_block_billing] == '1', params[:requested_date].presence,
140
+ current_user.kb_username, params[:reason], params[:comment], options_for_klient)
141
+
142
+ redirect_to kaui_engine.account_bundles_path(params.require(:account_id)), notice: 'Blocking state was successfully created'
143
+ end
144
+
131
145
  private
132
146
 
133
147
  def search_bundles(query, search_by, options)
@@ -150,6 +150,20 @@ module Kaui
150
150
  redirect_to kaui_engine.account_bundles_path(input_subscription['account_id']), notice: 'Subscription BCD was successfully changed'
151
151
  end
152
152
 
153
+ def block
154
+ @subscription = Kaui::Subscription.find_by_id(params.require(:id), 'NONE', options_for_klient)
155
+ end
156
+
157
+ def do_block
158
+ subscription = Kaui::Subscription.new(subscription_id: params.require(:id))
159
+ subscription.set_blocking_state(params.require(:state_name), params.require(:service),
160
+ params[:is_block_change] == '1', params[:is_block_entitlement] == '1',
161
+ params[:is_block_billing] == '1', params[:requested_date].presence,
162
+ current_user.kb_username, params[:reason], params[:comment], options_for_klient)
163
+
164
+ redirect_to kaui_engine.account_bundles_path(params.require(:account_id)), notice: 'Blocking state was successfully created'
165
+ end
166
+
153
167
  def edit_quantity
154
168
  @subscription = Kaui::Subscription.find_by_id(params.require(:id), 'NONE', options_for_klient)
155
169
  end
@@ -21,7 +21,7 @@ module Kaui
21
21
  I18n.t('errors.messages.error_communicating_killbill')
22
22
  else
23
23
  # Show detailed error in development/test, or when KAUI_SHOW_ERROR_DETAILS is set (for Docker)
24
- show_details = Rails.env.development? || Rails.env.test? || ENV['KAUI_SHOW_ERROR_DETAILS'].present?
24
+ show_details = Rails.env.local? || ENV['KAUI_SHOW_ERROR_DETAILS'].present?
25
25
  show_details ? exception.message : nil
26
26
  end
27
27
  end
@@ -29,19 +29,25 @@
29
29
  <div class="form-group p-0 w-100 max-width-148">
30
30
  <label for="payment_filter" class="control-label">Payment</label>
31
31
  <div class="d-flex">
32
- <%= select_tag 'payment_filter', options_for_select('<option value="all">ALL</option><option value="authorize">AUTHORIZE</option><option value="purchase">PURCHASE</option><option value="capture">CAPTURE</option><option value="credit">CREDIT</option><option value="refund">REFUND</option><option value="void">VOID</option><option value="chargeback">CHARGEBACK</option>'.html_safe), :class => 'form-control' %>
32
+ <%= select_tag 'payment_filter', options_for_select('<option value="all">ALL</option><option value="none">NONE</option><option value="authorize">AUTHORIZE</option><option value="purchase">PURCHASE</option><option value="capture">CAPTURE</option><option value="credit">CREDIT</option><option value="refund">REFUND</option><option value="void">VOID</option><option value="chargeback">CHARGEBACK</option>'.html_safe), :class => 'form-control' %>
33
33
  </div>
34
34
  </div>
35
35
  <div class="form-group p-0 w-100 max-width-148">
36
36
  <label for="subscription_filter" class="control-label">Subscription</label>
37
37
  <div class="d-flex">
38
- <%= select_tag 'subscription_filter', options_for_select('<option value="all">ALL</option><option value="start_entitlement">START_ENTITLEMENT</option><option value="pause_entitlement">PAUSE_ENTITLEMENT</option><option value="resume_entitlement">RESUME_ENTITLEMENT</option><option value="stop_entitlement">STOP_ENTITLEMENT</option><option value="phase">PHASE</option><option value="change">CHANGE</option>'.html_safe), :class => 'form-control' %>
38
+ <%= select_tag 'subscription_filter', options_for_select('<option value="all">ALL</option><option value="none">NONE</option><option value="start_entitlement">START_ENTITLEMENT</option><option value="pause_entitlement">PAUSE_ENTITLEMENT</option><option value="resume_entitlement">RESUME_ENTITLEMENT</option><option value="stop_entitlement">STOP_ENTITLEMENT</option><option value="phase">PHASE</option><option value="change">CHANGE</option>'.html_safe), :class => 'form-control' %>
39
39
  </div>
40
40
  </div>
41
41
  <div class="form-group p-0 w-100 max-width-148">
42
42
  <label for="billing_filter" class="control-label">Billing</label>
43
43
  <div class="d-flex">
44
- <%= select_tag 'billing_filter', options_for_select('<option value="all">ALL</option><option value="invoice">INVOICE</option><option value="start_billing">START_BILLING</option><option value="pause_billing">PAUSE_BILLING</option><option value="resume_billing">RESUME_BILLING</option><option value="stop_billing">STOP_BILLING</option>'.html_safe), :class => 'form-control' %>
44
+ <%= select_tag 'billing_filter', options_for_select('<option value="all">ALL</option><option value="none">NONE</option><option value="invoice">INVOICE</option><option value="start_billing">START_BILLING</option><option value="pause_billing">PAUSE_BILLING</option><option value="resume_billing">RESUME_BILLING</option><option value="stop_billing">STOP_BILLING</option>'.html_safe), :class => 'form-control' %>
45
+ </div>
46
+ </div>
47
+ <div class="form-group p-0 w-100 max-width-148">
48
+ <label for="blocking_state_filter" class="control-label">Blocking State</label>
49
+ <div class="d-flex">
50
+ <%= select_tag 'blocking_state_filter', options_for_select('<option value="all">ALL</option><option value="start_entitlement">START_ENTITLEMENT</option><option value="start_billing">START_BILLING</option><option value="service_state_change">SERVICE_STATE_CHANGE</option><option value="none">NONE</option>'.html_safe), :class => 'form-control' %>
45
51
  </div>
46
52
  </div>
47
53
  <div class="form-group p-0 d-flex align-self-end justify-content-end w-100">
@@ -49,13 +55,6 @@
49
55
  </div>
50
56
  </form>
51
57
 
52
- <div class="form-horizontal d-flex gap-2 mb-4 mx-0">
53
- <div class="form-group p-0 d-flex align-items-center gap-2">
54
- <label for="group-by" class="control-label mb-0 text-nowrap">Group by</label>
55
- <%= select_tag 'group-by', options_for_select([['Object Type', 'type'], ['Date', 'date']], 'type'), :class => 'form-control' %>
56
- </div>
57
- </div>
58
-
59
58
  <div id="timeline-entries">
60
59
  <% if @account.present? %>
61
60
  <% @invoices.each do |invoice_stub| %>
@@ -67,7 +66,7 @@
67
66
  <% else %>
68
67
  <% invoice = invoice_stub %>
69
68
  <% end %>
70
- <div title="<%= bundles %>" data-event-type="INVOICE" data-timestamp="<%= timeline_sort_timestamp(invoice.target_date) %>">
69
+ <div title="<%= bundles %>" data-event-type="INVOICE" data-event-category="invoice" data-timestamp="<%= timeline_sort_timestamp(invoice.target_date) %>">
71
70
  <div class="datetime-container">
72
71
  <% if invoice.target_date.present? %>
73
72
  <%= invoice.target_date %>
@@ -155,7 +154,7 @@
155
154
  <% invoice = nil %>
156
155
  <% end %>
157
156
  <% payment.transactions.each do |transaction| %>
158
- <div title="<%= bundles %>" data-event-type="<%= transaction.transaction_type %>" data-timestamp="<%= timeline_sort_timestamp(transaction.effective_date) %>">
157
+ <div title="<%= bundles %>" data-event-type="<%= transaction.transaction_type %>" data-event-category="payment" data-timestamp="<%= timeline_sort_timestamp(transaction.effective_date) %>">
159
158
  <div class="datetime-container">
160
159
  <% if transaction.effective_date.present? %>
161
160
  <%= format_date(transaction.effective_date, @account.time_zone).html_safe %>
@@ -235,61 +234,179 @@
235
234
  <% @bundles.each do |bundle| %>
236
235
  <% bundle.subscriptions.each do |sub| %>
237
236
  <% sub.events.each do |event| %>
238
- <% next if event.event_type == 'SERVICE_STATE_CHANGE' %>
239
- <div title="<%= @bundle_names[bundle.external_key] %>" data-event-type="<%= event.event_type %>" data-timestamp="<%= timeline_sort_timestamp(event.effective_date) %>">
240
- <div class="datetime-container">
241
- <% if event.effective_date.present? %>
242
- <%= format_date(event.effective_date, @account.time_zone).html_safe %>
243
- <% else %>
244
- [unknown]
245
- <% end %>
246
- </div>
247
- <div class="account-timeline-card d-flex gap-3">
248
- <span class="icon-container">
249
- <%= image_tag("kaui/timeline/invoice.svg", width: 20, height: 20) %>
250
- </span>
251
-
252
- <div class="d-flex justify-content-start flex-column gap-3 w-100 row">
253
- <h3><%= event.event_type %></h3>
254
- <div class="w-100">
255
- <div class="row account-timeline-card-detail">
256
- <div class="col-sm-4">
257
- <div>
258
- <b>Details</b>
259
- <p>
260
- <%= event.phase %><span class="hide" title="<%= sub.product_category %>"></span>
261
- </p>
237
+ <% if event.event_type == 'SERVICE_STATE_CHANGE' %>
238
+ <div title="<%= @bundle_names[bundle.external_key] %>" data-event-type="SERVICE_STATE_CHANGE" data-event-category="blocking_state" data-timestamp="<%= timeline_sort_timestamp(event.effective_date) %>">
239
+ <div class="datetime-container">
240
+ <% if event.effective_date.present? %>
241
+ <%= format_date(event.effective_date, @account.time_zone).html_safe %>
242
+ <% else %>
243
+ [unknown]
244
+ <% end %>
245
+ </div>
246
+ <div class="account-timeline-card d-flex gap-3">
247
+ <span class="icon-container">
248
+ <%= image_tag("kaui/timeline/block.svg", width: 20, height: 20) %>
249
+ </span>
250
+
251
+ <div class="d-flex justify-content-start flex-column gap-3 w-100 row">
252
+ <h3>BLOCKING STATE</h3>
253
+ <div class="w-100">
254
+ <div class="row account-timeline-card-detail">
255
+ <div class="col-sm-4">
256
+ <div>
257
+ <b>Service / State</b>
258
+ <p>
259
+ <%= event.service_name %> / <%= event.service_state_name %>
260
+ </p>
261
+ </div>
262
262
  </div>
263
-
264
- </div>
265
- <div class="col-sm-4">
266
- <div>
267
- <b>Bundle</b>
268
- <p>
269
- <%= truncate_uuid(@bundle_names[bundle.external_key]) %>
270
- </p>
263
+ <div class="col-sm-4">
264
+ <div>
265
+ <b>Blocked</b>
266
+ <p>
267
+ <%= 'Billing' if event.is_blocked_billing %>
268
+ <%= 'Entitlement' if event.is_blocked_entitlement %>
269
+ </p>
270
+ </div>
271
+ <div>
272
+ <b>Bundle</b>
273
+ <p>
274
+ <%= truncate_uuid(@bundle_names[bundle.external_key]) %>
275
+ </p>
276
+ </div>
277
+ </div>
278
+ <div class="col-sm-4">
279
+ <div>
280
+ <b>Reason Code / Comments</b>
281
+ <p>
282
+ <% if event.audit_logs.present? %>
283
+ <% event.audit_logs.each do |entry| %>
284
+ <%= Kaui::AuditLog.description(entry) %>
285
+ <% end %>
286
+ <% end %>
287
+ </p>
288
+ </div>
271
289
  </div>
272
290
  </div>
273
- <div class="col-sm-4">
274
- <div>
275
- <b>Reason Code / Comments</b>
276
- <p>
277
- <% if event.audit_logs.present? %>
278
- <% event.audit_logs.each do |entry| %>
279
- <%= Kaui::AuditLog.description(entry) %>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ <% else %>
296
+ <% event_category = %w[START_BILLING PAUSE_BILLING RESUME_BILLING STOP_BILLING].include?(event.event_type) ? 'billing' : 'subscription' %>
297
+ <div title="<%= @bundle_names[bundle.external_key] %>" data-event-type="<%= event.event_type %>" data-event-category="<%= event_category %>" data-timestamp="<%= timeline_sort_timestamp(event.effective_date) %>">
298
+ <div class="datetime-container">
299
+ <% if event.effective_date.present? %>
300
+ <%= format_date(event.effective_date, @account.time_zone).html_safe %>
301
+ <% else %>
302
+ [unknown]
303
+ <% end %>
304
+ </div>
305
+ <div class="account-timeline-card d-flex gap-3">
306
+ <span class="icon-container">
307
+ <%= image_tag("kaui/timeline/invoice.svg", width: 20, height: 20) %>
308
+ </span>
309
+
310
+ <div class="d-flex justify-content-start flex-column gap-3 w-100 row">
311
+ <h3><%= event.event_type %></h3>
312
+ <div class="w-100">
313
+ <div class="row account-timeline-card-detail">
314
+ <div class="col-sm-4">
315
+ <div>
316
+ <b>Details</b>
317
+ <p>
318
+ <% if event.is_blocked_billing || event.is_blocked_entitlement %>
319
+ <%= event.service_name %> / <%= event.service_state_name %><br/>
280
320
  <% end %>
281
- <% end %>
282
- </p>
321
+ <%= event.phase %><span class="hide" title="<%= sub.product_category %>"></span>
322
+ </p>
323
+ </div>
324
+
325
+ </div>
326
+ <div class="col-sm-4">
327
+ <div>
328
+ <b>Bundle</b>
329
+ <p>
330
+ <%= truncate_uuid(@bundle_names[bundle.external_key]) %>
331
+ </p>
332
+ </div>
333
+ </div>
334
+ <div class="col-sm-4">
335
+ <div>
336
+ <b>Reason Code / Comments</b>
337
+ <p>
338
+ <% if event.audit_logs.present? %>
339
+ <% event.audit_logs.each do |entry| %>
340
+ <%= Kaui::AuditLog.description(entry) %>
341
+ <% end %>
342
+ <% end %>
343
+ </p>
344
+ </div>
283
345
  </div>
284
346
  </div>
285
347
  </div>
286
348
  </div>
287
349
  </div>
288
350
  </div>
289
- </div>
351
+ <% end %>
290
352
  <% end %>
291
353
  <% end %>
292
354
  <% end %>
355
+
356
+ <% (@account_blocking_states || []).each do |blocking_state| %>
357
+ <div data-event-type="SERVICE_STATE_CHANGE" data-event-category="blocking_state" data-timestamp="<%= timeline_sort_timestamp(blocking_state.effective_date) %>">
358
+ <div class="datetime-container">
359
+ <% if blocking_state.effective_date.present? %>
360
+ <%= format_date(blocking_state.effective_date, @account.time_zone).html_safe %>
361
+ <% else %>
362
+ [unknown]
363
+ <% end %>
364
+ </div>
365
+ <div class="account-timeline-card d-flex gap-3">
366
+ <span class="icon-container">
367
+ <%= image_tag("kaui/timeline/block.svg", width: 20, height: 20) %>
368
+ </span>
369
+
370
+ <div class="d-flex justify-content-start flex-column gap-3 w-100 row">
371
+ <h3>BLOCKING STATE</h3>
372
+ <div class="w-100">
373
+ <div class="row account-timeline-card-detail">
374
+ <div class="col-sm-4">
375
+ <div>
376
+ <b>Service / State</b>
377
+ <p>
378
+ <%= blocking_state.service %> / <%= blocking_state.state_name %>
379
+ </p>
380
+ </div>
381
+ </div>
382
+ <div class="col-sm-4">
383
+ <div>
384
+ <b>Blocked</b>
385
+ <p>
386
+ <%= 'Change' if blocking_state.is_block_change %>
387
+ <%= 'Billing' if blocking_state.is_block_billing %>
388
+ <%= 'Entitlement' if blocking_state.is_block_entitlement %>
389
+ </p>
390
+ </div>
391
+ </div>
392
+ <div class="col-sm-4">
393
+ <div>
394
+ <b>Reason Code / Comments</b>
395
+ <p>
396
+ <% if blocking_state.audit_logs.present? %>
397
+ <% blocking_state.audit_logs.each do |entry| %>
398
+ <%= Kaui::AuditLog.description(entry) %>
399
+ <% end %>
400
+ <% end %>
401
+ </p>
402
+ </div>
403
+ </div>
404
+ </div>
405
+ </div>
406
+ </div>
407
+ </div>
408
+ </div>
409
+ <% end %>
293
410
  <% end %>
294
411
  </div>
295
412
  </div>
@@ -305,7 +422,7 @@
305
422
  }
306
423
 
307
424
  var eventsOrder = ["START_ENTITLEMENT", "START_BILLING", "PAUSE_ENTITLEMENT", "PAUSE_BILLING", "RESUME_ENTITLEMENT",
308
- "RESUME_BILLING", "PHASE", "CHANGE", "STOP_ENTITLEMENT", "STOP_BILLING", "INVOICE", "AUTHORIZE", "PURCHASE", "CAPTURE",
425
+ "RESUME_BILLING", "PHASE", "CHANGE", "STOP_ENTITLEMENT", "STOP_BILLING", "SERVICE_STATE_CHANGE", "INVOICE", "AUTHORIZE", "PURCHASE", "CAPTURE",
309
426
  "CREDIT", "REFUND", "VOID", "CHARGEBACK"];
310
427
 
311
428
  jQuery.fn.dataTableExt.oSort['timeline-event-asc'] = function(e1,e2) {
@@ -359,92 +476,95 @@
359
476
  });
360
477
  }
361
478
 
362
- var originalTimelineEntriesOrder = null;
363
-
364
- function sortTimelineEntries(sortByDate) {
479
+ function sortTimelineEntries() {
365
480
  var $container = $('#timeline-entries');
366
481
  var $entries = $container.children('[data-event-type]');
367
482
 
368
- if (originalTimelineEntriesOrder === null) {
369
- // Cache the initial (grouped-by-object-type) DOM order the first time we need it
370
- originalTimelineEntriesOrder = $entries.get();
371
- }
372
-
373
- var orderedEntries;
374
- if (sortByDate) {
375
- orderedEntries = $entries.get().sort(function (a, b) {
376
- var x = parseInt($(a).attr('data-timestamp'), 10);
377
- var y = parseInt($(b).attr('data-timestamp'), 10);
378
- x = isNaN(x) ? -Infinity : x;
379
- y = isNaN(y) ? -Infinity : y;
380
- // Most recent first; entries without a usable timestamp sort last
483
+ var orderedEntries = $entries.get().sort(function (a, b) {
484
+ var x = parseInt($(a).attr('data-timestamp'), 10);
485
+ var y = parseInt($(b).attr('data-timestamp'), 10);
486
+ x = isNaN(x) ? -Infinity : x;
487
+ y = isNaN(y) ? -Infinity : y;
488
+ if (y !== x) {
381
489
  return y - x;
382
- });
383
- } else {
384
- orderedEntries = originalTimelineEntriesOrder;
385
- }
490
+ }
491
+ var typeX = eventsOrder.indexOf($(a).attr('data-event-type'));
492
+ var typeY = eventsOrder.indexOf($(b).attr('data-event-type'));
493
+ typeX = typeX === -1 ? Infinity : typeX;
494
+ typeY = typeY === -1 ? Infinity : typeY;
495
+ return typeX - typeY;
496
+ });
386
497
 
387
498
  $.each(orderedEntries, function (_, entry) {
388
499
  $container.append(entry);
389
500
  });
390
501
 
391
- // Re-apply any active filters, since the DOM order just changed
392
502
  filterTimeline();
393
503
  }
394
504
 
395
505
  function filterTimeline() {
396
506
  var selectedBundle = $("#bundles").val();
397
- var selectedPayment = $('#payment_filter').val().toUpperCase();
398
- var selectedSubscription = $('#subscription_filter').val().toUpperCase();
399
- var selectedBilling = $('#billing_filter').val().toUpperCase();
400
507
  var searchQuery = $('#search').val().toLowerCase();
401
-
402
- // Show all cards initially
403
- $('.account-timeline-card').parent().show();
404
-
405
- // Apply all filters to each card
508
+ var blockingStateFilter = $('#blocking_state_filter').val().toUpperCase();
509
+
510
+ var categoryFilters = {
511
+ payment: { value: $('#payment_filter').val().toUpperCase(), categories: ['payment'] },
512
+ subscription: { value: $('#subscription_filter').val().toUpperCase(), categories: ['subscription'] },
513
+ billing: { value: $('#billing_filter').val().toUpperCase(), categories: ['billing', 'invoice'] }
514
+ };
515
+
406
516
  $('.account-timeline-card').parent().each(function() {
407
517
  var $card = $(this);
408
- var eventType = $card.data('event-type');
518
+ var eventType = ($card.attr('data-event-type') || '').toUpperCase();
519
+ var eventCategory = $card.attr('data-event-category') || '';
409
520
  var bundleTitle = $card.attr('title');
410
521
  var cardText = $card.text().toLowerCase();
411
-
522
+
412
523
  var shouldShow = true;
413
-
524
+
414
525
  if (selectedBundle && selectedBundle !== '' && selectedBundle !== 'All') {
415
526
  if (!bundleTitle || bundleTitle.indexOf(selectedBundle) === -1) {
416
527
  shouldShow = false;
417
528
  }
418
529
  }
419
-
420
- // Filter by payment events if not "ALL"
421
- if (selectedPayment !== "ALL" && shouldShow) {
422
- if (!eventType || eventType !== selectedPayment) {
423
- shouldShow = false;
424
- }
425
- }
426
-
427
- // Filter by subscription events if not "ALL"
428
- if (selectedSubscription !== "ALL" && shouldShow) {
429
- if (!eventType || eventType !== selectedSubscription) {
430
- shouldShow = false;
431
- }
432
- }
433
-
434
- // Filter by billing events if not "ALL"
435
- if (selectedBilling !== "ALL" && shouldShow) {
436
- if (!eventType || eventType !== selectedBilling) {
437
- shouldShow = false;
530
+
531
+ if (shouldShow) {
532
+ if (blockingStateFilter === 'NONE') {
533
+ if (eventCategory === 'blocking_state') {
534
+ shouldShow = false;
535
+ }
536
+ } else if (blockingStateFilter !== 'ALL') {
537
+ // Specific type selected: show only events matching that exact type
538
+ if (eventType !== blockingStateFilter) {
539
+ shouldShow = false;
540
+ }
541
+ } else {
542
+ // Blocking state filter is ALL — apply per-category filters
543
+ var governingFilter = null;
544
+ $.each(categoryFilters, function(_, f) {
545
+ if (f.categories.indexOf(eventCategory) !== -1) {
546
+ governingFilter = f;
547
+ }
548
+ });
549
+ if (governingFilter) {
550
+ if (governingFilter.value === 'NONE') {
551
+ shouldShow = false;
552
+ } else if (governingFilter.value !== 'ALL') {
553
+ if (eventType !== governingFilter.value) {
554
+ shouldShow = false;
555
+ }
556
+ }
557
+ }
438
558
  }
439
559
  }
440
-
560
+
441
561
  // Apply search filter if query exists
442
562
  if (searchQuery && shouldShow) {
443
563
  if (cardText.indexOf(searchQuery) === -1) {
444
564
  shouldShow = false;
445
565
  }
446
566
  }
447
-
567
+
448
568
  // Show or hide the card based on all filters
449
569
  if (shouldShow) {
450
570
  $card.show();
@@ -456,22 +576,15 @@
456
576
 
457
577
  $(document).ready(function() {
458
578
  disableLinks();
459
-
579
+
460
580
  // Initialize filters with debounce for search
461
581
  var searchTimeout;
462
- $("#bundles, #payment_filter, #subscription_filter, #billing_filter").on('change', filterTimeline);
582
+ $("#bundles, #payment_filter, #subscription_filter, #billing_filter, #blocking_state_filter").on('change', filterTimeline);
463
583
  $('#search').on('keyup', function() {
464
584
  clearTimeout(searchTimeout);
465
585
  searchTimeout = setTimeout(filterTimeline, 300);
466
586
  });
467
587
 
468
- $('#group-by').on('change', function() {
469
- sortTimelineEntries($(this).val() === 'date');
470
- });
471
-
472
-
473
-
474
-
475
588
  // Clear all filters button
476
589
  $('#clear-all-filters').click(function() {
477
590
  $('#search').val('');
@@ -479,10 +592,11 @@
479
592
  $('#payment_filter').val('all');
480
593
  $('#subscription_filter').val('all');
481
594
  $('#billing_filter').val('all');
595
+ $('#blocking_state_filter').val('all');
482
596
  filterTimeline();
483
597
  });
484
-
485
- // Apply initial filters if any are selected
486
- filterTimeline();
598
+
599
+ // Sort by date first, event type second, then apply filters
600
+ sortTimelineEntries();
487
601
  });
488
602
  <% end %>
@@ -3,7 +3,7 @@
3
3
  <div class="billing-details-header">
4
4
  <h2>Billing Information</h2>
5
5
  <span>
6
- <% if can?(:trigger, Kaui::Payment) || can?(:credit, Kaui::Account) || can?(:charge, Kaui::Account) %>
6
+ <% if can?(:trigger, Kaui::Payment) || can?(:credit, Kaui::Account) || can?(:charge, Kaui::Account) || can?(:update, Kaui::Account) %>
7
7
  <div class="tag-bar tag-bar-breathe">
8
8
  <div class="toggler">
9
9
  <div class="toggle">
@@ -59,6 +59,18 @@
59
59
  <% end %>
60
60
  <% end %>
61
61
 
62
+ <% if can? :update, Kaui::Account %>
63
+ <%= link_to kaui_engine.block_account_path(@account.account_id) do %>
64
+ <%= render "kaui/components/button/button", {
65
+ label: "Create Blocking State",
66
+ icon: "kaui/plus.svg",
67
+ variant: "outline-secondary d-inline-flex align-items-center gap-1",
68
+ type: "button",
69
+ html_class: "kaui-dropdown custom-hover",
70
+ } %>
71
+ <% end %>
72
+ <% end %>
73
+
62
74
  </div>
63
75
  </div>
64
76
  </div>
@@ -6,7 +6,7 @@
6
6
 
7
7
  <div class="info-wrapper">
8
8
 
9
- <% if can?(:trigger, Kaui::Payment) || can?(:credit, Kaui::Account) || can?(:charge, Kaui::Account) %>
9
+ <% if can?(:trigger, Kaui::Payment) || can?(:credit, Kaui::Account) || can?(:charge, Kaui::Account) || can?(:update, Kaui::Account) %>
10
10
  <div class="tag-bar tag-bar-breathe">
11
11
  <div class="toggler">
12
12
  <div class="toggle">
@@ -29,6 +29,10 @@
29
29
  <%= link_to '<i class="fa fa-plus-square"></i>&nbsp;Create deposit'.html_safe,
30
30
  deposit_engine.collection_index_path(:account_id => @account.account_id, :currency => @account.currency) %>
31
31
  <% end %>
32
+ <% if can? :update, Kaui::Account %>
33
+ <%= link_to '<i class="fa fa-plus-square"></i>&nbsp;Create blocking state'.html_safe,
34
+ kaui_engine.block_account_path(@account.account_id) %>
35
+ <% end %>
32
36
  </div>
33
37
  </div>
34
38
  </div>
@@ -0,0 +1,16 @@
1
+ <div class="register kaui-subscription-new">
2
+ <div class="d-flex justify-content-center flex-wrap" style="gap: 2rem; max-width: 60.5rem; margin: 0 auto;">
3
+ <h5 class="add-account-title border-bottom pb-3 mb-3" style="flex: 0 0 100%;">
4
+ <span class="icon-container">
5
+ <%= image_tag "kaui/timeline/block.svg", width: 20, height: 20 %>
6
+ </span>
7
+ Create blocking state
8
+ </h5>
9
+ <div style="max-width: 37.5rem; width: 100%;">
10
+ <%= form_tag do_block_account_path(:account_id => @account.account_id), :class => 'form-horizontal', :method => :put do %>
11
+ <%= render "kaui/components/blocking_state_form/fields", :applies_to_type => 'Account', :applies_to_id => @account.account_id %>
12
+ <% end %>
13
+ </div>
14
+ <%= render "kaui/components/blocking_state_form/about_panel" %>
15
+ </div>
16
+ </div>
@@ -1,7 +1,11 @@
1
1
  <%= form_for @allowed_user, :url => @allowed_user.persisted? ? admin_allowed_user_path(@allowed_user.id) : admin_allowed_users_path, :method => @allowed_user.persisted? ? :put : :post, :html => {:class => 'form-horizontal'} do |f| %>
2
2
  <input type="hidden" id="killbillManaged" value="<%= @is_killbill_managed %>" />
3
3
  <div class='form-group d-flex pb-3'>
4
- <%= f.label ' ', :class => 'col-sm-3 control-label' %>
4
+ <%# Not associated with a real attribute - use label_tag instead of f.label, which under
5
+ Rails 7.2 raises NoMethodError (undefined method `humanize' for nil) for a blank name,
6
+ since ActiveModel::Translation#human_attribute_name only handles a blank *namespaced*
7
+ attribute (e.g. "foo."), not a plain blank string. %>
8
+ <%= label_tag nil, ' ', :class => 'col-sm-3 control-label' %>
5
9
  <div class="col-sm-9">
6
10
  <div class="checkbox">
7
11
  <label for="external" class="d-flex align-items-center gap-2 control-label">
@@ -0,0 +1,16 @@
1
+ <div class="register kaui-subscription-new">
2
+ <div class="d-flex justify-content-center flex-wrap" style="gap: 2rem; max-width: 60.5rem; margin: 0 auto;">
3
+ <h5 class="add-account-title border-bottom pb-3 mb-3" style="flex: 0 0 100%;">
4
+ <span class="icon-container">
5
+ <%= image_tag "kaui/timeline/block.svg", width: 20, height: 20 %>
6
+ </span>
7
+ Create blocking state
8
+ </h5>
9
+ <div style="max-width: 37.5rem; width: 100%;">
10
+ <%= form_tag do_block_bundle_path(:id => @bundle.bundle_id, :account_id => @bundle.account_id), :class => 'form-horizontal', :method => :put do %>
11
+ <%= render "kaui/components/blocking_state_form/fields", :applies_to_type => 'Bundle', :applies_to_id => @bundle.bundle_id %>
12
+ <% end %>
13
+ </div>
14
+ <%= render "kaui/components/blocking_state_form/about_panel" %>
15
+ </div>
16
+ </div>
@@ -0,0 +1,9 @@
1
+ <div class="blocking-state-about-panel">
2
+ <div class="d-flex align-items-center gap-2 mb-2">
3
+ <div class="icon-container">
4
+ <%= image_tag "kaui/blocking-states/shield-white.svg", width: 20, height: 20 %>
5
+ </div>
6
+ <h6 class="mb-0"><%= t('blocking_state_about_panel.title') %></h6>
7
+ </div>
8
+ <p><%= t('blocking_state_about_panel.description') %></p>
9
+ </div>
@@ -0,0 +1,79 @@
1
+ <div class="form-group d-flex pb-3">
2
+ <label class="col-sm-3 control-label">Applies to</label>
3
+ <div class="col-sm-9">
4
+ <p class="form-control-plaintext mb-0"><%= applies_to_type %> &mdash; <%= applies_to_id %></p>
5
+ </div>
6
+ </div>
7
+ <div class="form-group d-flex pb-3">
8
+ <%= label_tag :state_name, 'State', :class => 'col-sm-3 control-label' %>
9
+ <div class="col-sm-9">
10
+ <%= text_field_tag :state_name, nil, :class => 'form-control', :placeholder => 'e.g., OVERDUE_ENFORCEMENT' %>
11
+ <small class="form-text text-muted">The name of the state being applied, e.g. a step in an overdue enforcement process.</small>
12
+ </div>
13
+ </div>
14
+ <div class="form-group d-flex pb-3">
15
+ <%= label_tag :service, 'Service', :class => 'col-sm-3 control-label' %>
16
+ <div class="col-sm-9">
17
+ <%= text_field_tag :service, nil, :class => 'form-control' %>
18
+ <small class="form-text text-muted">The service responsible for setting and enforcing this blocking state.</small>
19
+ </div>
20
+ </div>
21
+ <div class="form-group d-flex pb-3">
22
+ <label class="col-sm-3 control-label">Block</label>
23
+ <div class="col-sm-9">
24
+ <div class="d-flex gap-3">
25
+ <div class="form-check">
26
+ <%= check_box_tag :is_block_change, '1', false, :class => 'form-check-input' %>
27
+ <%= label_tag :is_block_change, 'Change', :class => 'form-check-label' %>
28
+ </div>
29
+ <div class="form-check">
30
+ <%= check_box_tag :is_block_entitlement, '1', false, :class => 'form-check-input' %>
31
+ <%= label_tag :is_block_entitlement, 'Entitlement', :class => 'form-check-label' %>
32
+ </div>
33
+ <div class="form-check">
34
+ <%= check_box_tag :is_block_billing, '1', false, :class => 'form-check-input' %>
35
+ <%= label_tag :is_block_billing, 'Billing', :class => 'form-check-label' %>
36
+ </div>
37
+ </div>
38
+ <small class="form-text text-muted">Change blocks plan/subscription changes, Entitlement blocks usage of the service, and Billing blocks invoicing.</small>
39
+ </div>
40
+ </div>
41
+ <div class="form-group d-flex pb-3" id="form_effective_from_date">
42
+ <%= label_tag :requested_date, 'Effective date', :class => 'col-sm-3 control-label' %>
43
+ <div class="col-sm-9">
44
+ <div class="custom-date-input-wrapper kaui-button custom-hover" onclick="$(this).find('input').focus();">
45
+ <svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
46
+ <path d="M14.9167 12.5V18.3333M17.8333 15.4167H12" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
47
+ <path d="M14.0833 1.66675V5.00008M6.58325 1.66675V5.00008" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
48
+ <path d="M17.8333 10.8333V4.99992C17.8333 4.07944 17.0871 3.33325 16.1666 3.33325H4.49992C3.57944 3.33325 2.83325 4.07944 2.83325 4.99992V16.6666C2.83325 17.5871 3.57944 18.3333 4.49992 18.3333H10.3333" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
49
+ <path d="M2.83325 8.33325H17.8333" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
50
+ </svg>
51
+ <input class="form-control" name="requested_date" type="text" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-date-today-highlight="true">
52
+ </div>
53
+ <small class="form-text text-muted">Defaults to now if left blank.</small>
54
+ </div>
55
+ </div>
56
+ <div class="form-group d-flex pb-3 border-bottom mb-3">
57
+ <%= label_tag :comment, 'Comment', :class => 'col-sm-3 control-label' %>
58
+ <div class="col-sm-9">
59
+ <%= text_area_tag :comment, nil, :rows => 3, :class => 'form-control' %>
60
+ </div>
61
+ </div>
62
+ <div class="form-group d-flex justify-content-end pb-3">
63
+ <%= render "kaui/components/button/button", {
64
+ label: 'Close',
65
+ variant: "outline-secondary d-inline-flex align-items-center gap-1",
66
+ type: "button",
67
+ html_class: "kaui-button custom-hover mx-2",
68
+ html_options: {
69
+ id: "closeButton",
70
+ onclick: "window.history.back();"
71
+ }
72
+ } %>
73
+ <%= render "kaui/components/button/button", {
74
+ label: 'Submit',
75
+ variant: "outline-secondary d-inline-flex align-items-center gap-1",
76
+ type: "submit",
77
+ html_class: "kaui-dropdown custom-hover"
78
+ } %>
79
+ </div>
@@ -243,6 +243,22 @@
243
243
  } %>
244
244
  <% end %>
245
245
 
246
+ <% if can?(:pause_resume, Kaui::Subscription) && sub.product_category != 'ADD_ON' %>
247
+ <% menu_items << {
248
+ label: "Create blocking state (bundle)",
249
+ path: kaui_engine.block_bundle_path(account_id: bundle.account_id, id: bundle.bundle_id),
250
+ icon: "kaui/subscription/pause.svg"
251
+ } %>
252
+ <% end %>
253
+
254
+ <% if can?(:pause_resume, Kaui::Subscription) %>
255
+ <% menu_items << {
256
+ label: "Create blocking state (subscription)",
257
+ path: kaui_engine.block_subscription_path(account_id: bundle.account_id, id: sub.subscription_id),
258
+ icon: "kaui/subscription/pause.svg"
259
+ } %>
260
+ <% end %>
261
+
246
262
  <% if menu_items.any? %>
247
263
  <%= render partial: "kaui/components/menu_dropdown/menu_dropdown", locals: {
248
264
  variant: "btn-light",
@@ -0,0 +1,16 @@
1
+ <div class="register kaui-subscription-new">
2
+ <div class="d-flex justify-content-center flex-wrap" style="gap: 2rem; max-width: 60.5rem; margin: 0 auto;">
3
+ <h5 class="add-account-title border-bottom pb-3 mb-3" style="flex: 0 0 100%;">
4
+ <span class="icon-container">
5
+ <%= image_tag "kaui/timeline/block.svg", width: 20, height: 20 %>
6
+ </span>
7
+ Create blocking state
8
+ </h5>
9
+ <div style="max-width: 37.5rem; width: 100%;">
10
+ <%= form_tag do_block_subscription_path(:id => @subscription.subscription_id, :account_id => @subscription.account_id), :class => 'form-horizontal', :method => :put do %>
11
+ <%= render "kaui/components/blocking_state_form/fields", :applies_to_type => 'Subscription', :applies_to_id => @subscription.subscription_id %>
12
+ <% end %>
13
+ </div>
14
+ <%= render "kaui/components/blocking_state_form/about_panel" %>
15
+ </div>
16
+ </div>
@@ -44,6 +44,10 @@ en:
44
44
  loading_data_from_server: "Loading data from server"
45
45
  requested_amount_colon: "Requested amount: "
46
46
 
47
+ blocking_state_about_panel:
48
+ title: "About Blocking States"
49
+ description: "Blocking states restrict actions on accounts, bundles, or subscriptions. They are enforced by specific services such as entitlement, billing, or custom services."
50
+
47
51
  advanced_search:
48
52
  title: "Advanced Search"
49
53
  search_field: "Search field"
data/config/routes.rb CHANGED
@@ -40,6 +40,8 @@ Kaui::Engine.routes.draw do
40
40
  post '/trigger_invoice' => 'accounts#trigger_invoice', :as => 'trigger_invoice'
41
41
  put '/link_to_parent' => 'accounts#link_to_parent', :as => 'link_to_parent'
42
42
  delete '/unlink_to_parent' => 'accounts#unlink_to_parent', :as => 'unlink_to_parent'
43
+ get '/block' => 'accounts#block', :as => 'block_account'
44
+ put '/do_block' => 'accounts#do_block', :as => 'do_block_account'
43
45
 
44
46
  scope '/account_tags' do
45
47
  get '/' => 'account_tags#index', :as => 'account_tags'
@@ -134,6 +136,8 @@ Kaui::Engine.routes.draw do
134
136
  get '/:id/pause_resume', to: 'bundles#pause_resume', as: 'pause_resume_bundle'
135
137
  put '/:id/do_transfer', to: 'bundles#do_transfer', as: 'do_transfer_bundle'
136
138
  get '/:id/transfer', to: 'bundles#transfer', as: 'transfer_bundle'
139
+ put '/:id/do_block', to: 'bundles#do_block', as: 'do_block_bundle'
140
+ get '/:id/block', to: 'bundles#block', as: 'block_bundle'
137
141
  get '/:id' => 'bundles#restful_show', :as => 'bundle'
138
142
  end
139
143
 
@@ -141,6 +145,8 @@ Kaui::Engine.routes.draw do
141
145
  post '/:id/tags' => 'subscriptions#update_tags', :as => 'update_subscriptions_tags'
142
146
  get '/:id/edit_bcd' => 'subscriptions#edit_bcd', :as => 'edit_bcd'
143
147
  put '/:id/update_bcd' => 'subscriptions#update_bcd', :as => 'update_bcd'
148
+ get '/:id/block' => 'subscriptions#block', :as => 'block_subscription'
149
+ put '/:id/do_block' => 'subscriptions#do_block', :as => 'do_block_subscription'
144
150
  get '/:id/edit_quantity' => 'subscriptions#edit_quantity', :as => 'edit_quantity'
145
151
  put '/:id/update_quantity' => 'subscriptions#update_quantity', :as => 'update_quantity'
146
152
  get '/:id/record_usage' => 'subscriptions#record_usage', :as => 'record_usage'
data/lib/kaui/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kaui
4
- VERSION = '4.0.25'
4
+ VERSION = '4.0.26'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kaui
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.25
4
+ version: 4.0.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kill Bill core team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-19 00:00:00.000000000 Z
11
+ date: 2026-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: csv
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: devise
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -226,14 +240,14 @@ dependencies:
226
240
  requirements:
227
241
  - - "~>"
228
242
  - !ruby/object:Gem::Version
229
- version: '7.0'
243
+ version: '7.2'
230
244
  type: :runtime
231
245
  prerelease: false
232
246
  version_requirements: !ruby/object:Gem::Requirement
233
247
  requirements:
234
248
  - - "~>"
235
249
  - !ruby/object:Gem::Version
236
- version: '7.0'
250
+ version: '7.2'
237
251
  - !ruby/object:Gem::Dependency
238
252
  name: spinjs-rails
239
253
  requirement: !ruby/object:Gem::Requirement
@@ -309,6 +323,7 @@ files:
309
323
  - app/assets/images/kaui/account/purchase.svg
310
324
  - app/assets/images/kaui/account/refresh.svg
311
325
  - app/assets/images/kaui/arrow.svg
326
+ - app/assets/images/kaui/blocking-states/shield-white.svg
312
327
  - app/assets/images/kaui/card.svg
313
328
  - app/assets/images/kaui/copy.svg
314
329
  - app/assets/images/kaui/dashboard/aviate.svg
@@ -376,6 +391,7 @@ files:
376
391
  - app/assets/images/kaui/subscription/pause.svg
377
392
  - app/assets/images/kaui/subscription/transfer.svg
378
393
  - app/assets/images/kaui/timeline/authorize.svg
394
+ - app/assets/images/kaui/timeline/block.svg
379
395
  - app/assets/images/kaui/timeline/capture.svg
380
396
  - app/assets/images/kaui/timeline/chargeback.svg
381
397
  - app/assets/images/kaui/timeline/credit.svg
@@ -541,6 +557,7 @@ files:
541
557
  - app/views/kaui/accounts/_payment_methods.html.erb
542
558
  - app/views/kaui/accounts/_personal_details.html.erb
543
559
  - app/views/kaui/accounts/_personal_info.html.erb
560
+ - app/views/kaui/accounts/block.html.erb
544
561
  - app/views/kaui/accounts/edit.html.erb
545
562
  - app/views/kaui/accounts/index.html.erb
546
563
  - app/views/kaui/accounts/new.html.erb
@@ -580,6 +597,7 @@ files:
580
597
  - app/views/kaui/bundle_tags/edit.html.erb
581
598
  - app/views/kaui/bundles/_bundle_details.html.erb
582
599
  - app/views/kaui/bundles/_bundle_filterbar.html.erb
600
+ - app/views/kaui/bundles/block.html.erb
583
601
  - app/views/kaui/bundles/index.html.erb
584
602
  - app/views/kaui/bundles/pause_resume.html.erb
585
603
  - app/views/kaui/bundles/transfer.html.erb
@@ -587,6 +605,8 @@ files:
587
605
  - app/views/kaui/chargebacks/new.html.erb
588
606
  - app/views/kaui/charges/_form.html.erb
589
607
  - app/views/kaui/charges/new.html.erb
608
+ - app/views/kaui/components/blocking_state_form/_about_panel.html.erb
609
+ - app/views/kaui/components/blocking_state_form/_fields.html.erb
590
610
  - app/views/kaui/components/breadcrumb/_breadcrumb.html.erb
591
611
  - app/views/kaui/components/button/_button.html.erb
592
612
  - app/views/kaui/components/dashboard/_card.html.erb
@@ -652,6 +672,7 @@ files:
652
672
  - app/views/kaui/subscriptions/_form.html.erb
653
673
  - app/views/kaui/subscriptions/_subscriptions_table.html.erb
654
674
  - app/views/kaui/subscriptions/_view_json_modal.html.erb
675
+ - app/views/kaui/subscriptions/block.html.erb
655
676
  - app/views/kaui/subscriptions/edit.html.erb
656
677
  - app/views/kaui/subscriptions/edit_bcd.erb
657
678
  - app/views/kaui/subscriptions/edit_quantity.erb
@@ -707,7 +728,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
707
728
  requirements:
708
729
  - - ">="
709
730
  - !ruby/object:Gem::Version
710
- version: 2.7.0
731
+ version: 3.1.0
711
732
  required_rubygems_version: !ruby/object:Gem::Requirement
712
733
  requirements:
713
734
  - - ">="