kaui 4.0.12 → 4.0.14
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 +4 -4
- data/app/assets/images/kaui/save.svg +5 -0
- data/app/assets/javascripts/kaui/kaui_override.js +21 -0
- data/app/assets/javascripts/kaui/multi_functions_bar_utils.js +183 -7
- data/app/assets/stylesheets/kaui/common.css +4 -1
- data/app/assets/stylesheets/kaui/subscription.css +75 -0
- data/app/assets/stylesheets/kaui/tags.css +6 -3
- data/app/controllers/kaui/accounts_controller.rb +1 -1
- data/app/controllers/kaui/admin_tenants_controller.rb +22 -8
- data/app/controllers/kaui/bundles_controller.rb +53 -5
- data/app/controllers/kaui/engine_controller_util.rb +2 -2
- data/app/controllers/kaui/invoices_controller.rb +1 -1
- data/app/helpers/kaui/plugin_helper.rb +3 -5
- data/app/helpers/kaui/subscription_helper.rb +18 -0
- data/app/models/kaui/bundle.rb +6 -0
- data/app/models/kaui/overdue.rb +5 -2
- data/app/views/kaui/accounts/_account_details.html.erb +1 -1
- data/app/views/kaui/accounts/_account_filterbar.html.erb +34 -1
- data/app/views/kaui/accounts/_form_account.html.erb +1 -1
- data/app/views/kaui/accounts/_functions_bar.html.erb +3 -1
- data/app/views/kaui/accounts/edit.html.erb +9 -0
- data/app/views/kaui/accounts/index.html.erb +20 -0
- data/app/views/kaui/accounts/new.html.erb +14 -0
- data/app/views/kaui/admin_tenants/_show_overdue.erb +1 -1
- data/app/views/kaui/admin_tenants/new_overdue_config.html.erb +25 -1
- data/app/views/kaui/bundles/_bundle_filterbar.html.erb +133 -0
- data/app/views/kaui/bundles/index.html.erb +35 -3
- data/app/views/kaui/components/menu_dropdown/_menu_dropdown.html.erb +2 -2
- data/app/views/kaui/invoices/_invoice_filterbar.html.erb +42 -4
- data/app/views/kaui/invoices/_multi_functions_bar.html.erb +9 -10
- data/app/views/kaui/invoices/index.html.erb +27 -11
- data/app/views/kaui/layouts/kaui_application.html.erb +10 -10
- data/app/views/kaui/layouts/kaui_flash.html.erb +5 -26
- data/app/views/kaui/payments/_multi_functions_bar.html.erb +9 -9
- data/app/views/kaui/payments/_payment_filterbar.html.erb +42 -4
- data/app/views/kaui/payments/index.html.erb +12 -0
- data/app/views/kaui/refunds/_form.html.erb +3 -11
- data/app/views/kaui/subscriptions/_subscriptions_table.html.erb +1 -1
- data/config/locales/en.yml +1 -0
- data/lib/kaui/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e5d83c894b4705f72b548117b270bd7ea3c0d1fb962be8a49198dd81771c8c79
|
|
4
|
+
data.tar.gz: f95c3ff23e3b8729036252c2f45b2b2294bdb14dc474bf8666c188714bb29eff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f0ea477030153cfad26f64b1e8840a72613362a6a0655cf34e5c91c53439d1b67953b2b2c76b4cc1622c171389bb1676b7aa742fc3b90ec3e21b4d897c343a66
|
|
7
|
+
data.tar.gz: 5f0a1131795c3072024624fc7c55aace8111e8ec65cbf2dc4878b200bf4e6019ef6bf826e4d54364145b2a904fec983fa5f77fdaa809f2dbef43e2cd73bf6895
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#A4A7AE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
|
|
3
|
+
<polyline points="17 21 17 13 7 13 7 21"/>
|
|
4
|
+
<polyline points="7 3 7 8 15 8"/>
|
|
5
|
+
</svg>
|
|
@@ -302,6 +302,27 @@ jQuery(document).ready(function ($) {
|
|
|
302
302
|
|
|
303
303
|
setObjectIdPopover();
|
|
304
304
|
setObjectIdTooltip();
|
|
305
|
+
|
|
306
|
+
/*
|
|
307
|
+
* Tag dropdown overflow prevention
|
|
308
|
+
* Flips each .tag-select-box to open leftward when it would overflow the right edge of the viewport.
|
|
309
|
+
*/
|
|
310
|
+
function repositionTagDropdowns() {
|
|
311
|
+
$('.tag-select').each(function() {
|
|
312
|
+
var $box = $(this).find('.tag-select-box');
|
|
313
|
+
var triggerRight = $(this).offset().left + $(this).outerWidth();
|
|
314
|
+
var boxWidth = Math.max($box[0].scrollWidth, 240);
|
|
315
|
+
var viewportWidth = $(window).width();
|
|
316
|
+
if (triggerRight + boxWidth > viewportWidth) {
|
|
317
|
+
$box.css({ left: 'auto', right: '0' });
|
|
318
|
+
} else {
|
|
319
|
+
$box.css({ left: '0', right: 'auto' });
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
repositionTagDropdowns();
|
|
325
|
+
$(window).on('resize', repositionTagDropdowns);
|
|
305
326
|
});
|
|
306
327
|
|
|
307
328
|
|
|
@@ -84,11 +84,6 @@ function populateSearchLabelsFromUrl() {
|
|
|
84
84
|
|
|
85
85
|
function searchQuery(account_id){
|
|
86
86
|
var searchFields = $('.search-field');
|
|
87
|
-
var searchLabelsContainer = $('#search-labels-container');
|
|
88
|
-
|
|
89
|
-
if (searchFields.length > 0) {
|
|
90
|
-
searchLabelsContainer.empty();
|
|
91
|
-
}
|
|
92
87
|
var searchLabels = '';
|
|
93
88
|
if (searchFields.length > 0) {
|
|
94
89
|
searchLabels = searchFields.map(function() {
|
|
@@ -135,17 +130,25 @@ function searchQuery(account_id){
|
|
|
135
130
|
};
|
|
136
131
|
|
|
137
132
|
function clearAdvanceSearch() {
|
|
133
|
+
var hasActiveSearch = $('#search-labels-container .label').length > 0 ||
|
|
134
|
+
window.location.search.includes('_q=1');
|
|
135
|
+
|
|
138
136
|
// Clear all search fields
|
|
139
137
|
$('#search-fields-container').empty();
|
|
140
138
|
|
|
141
139
|
// Remove all search labels
|
|
142
140
|
$('#search-labels-container').empty();
|
|
143
141
|
|
|
144
|
-
//
|
|
145
|
-
|
|
142
|
+
// Clear Persisted Search for this page
|
|
143
|
+
localStorage.removeItem('kaui_adv_search_' + window.location.pathname);
|
|
146
144
|
|
|
147
145
|
// Hide the modal
|
|
148
146
|
$('#advanceSearchModal').modal('hide');
|
|
147
|
+
|
|
148
|
+
// Only reload if there was an active search to clear
|
|
149
|
+
if (hasActiveSearch) {
|
|
150
|
+
window.location.href = window.location.pathname;
|
|
151
|
+
}
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
function showAdvanceSearchModal() {
|
|
@@ -230,4 +233,177 @@ $(document).on('click', '.filter-close-icon', function() {
|
|
|
230
233
|
var newUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
|
|
231
234
|
window.history.pushState({ path: newUrl }, '', newUrl);
|
|
232
235
|
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// --- Saved Searches Logic ---
|
|
239
|
+
function getSavedSearches() {
|
|
240
|
+
var key = 'kaui_saved_searches_' + window.location.pathname;
|
|
241
|
+
var data = localStorage.getItem(key);
|
|
242
|
+
if (!data) return {};
|
|
243
|
+
try {
|
|
244
|
+
var parsed = JSON.parse(data);
|
|
245
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
246
|
+
return parsed;
|
|
247
|
+
}
|
|
248
|
+
} catch (e) {
|
|
249
|
+
// Legacy string format or invalid JSON
|
|
250
|
+
var legacy = {};
|
|
251
|
+
legacy["Default"] = data;
|
|
252
|
+
localStorage.setItem(key, JSON.stringify(legacy));
|
|
253
|
+
return legacy;
|
|
254
|
+
}
|
|
255
|
+
return {};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function saveSavedSearches(searches) {
|
|
259
|
+
var key = 'kaui_saved_searches_' + window.location.pathname;
|
|
260
|
+
localStorage.setItem(key, JSON.stringify(searches));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function populateSavedSearchesDropdown() {
|
|
264
|
+
var menu = $('#savedSearchesDropdown_menu');
|
|
265
|
+
if (!menu.length) return;
|
|
266
|
+
|
|
267
|
+
menu.empty();
|
|
268
|
+
var searches = getSavedSearches();
|
|
269
|
+
var names = Object.keys(searches);
|
|
270
|
+
|
|
271
|
+
var dropdownButton = $('#savedSearchesDropdown_button');
|
|
272
|
+
if (names.length === 0) {
|
|
273
|
+
menu.append('<li><span class="dropdown-item text-muted">No saved searches</span></li>');
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
names.forEach(function(name) {
|
|
278
|
+
var li = $('<li>');
|
|
279
|
+
var a = $('<a>', {
|
|
280
|
+
class: 'dropdown-item d-flex align-items-center justify-content-between apply-saved-search',
|
|
281
|
+
href: 'javascript:void(0);',
|
|
282
|
+
'data-name': name,
|
|
283
|
+
style: 'cursor: pointer;'
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
var nameSpan = $('<span>', {
|
|
287
|
+
class: 'text-black-700 fs-6 text-normal',
|
|
288
|
+
style: 'font-weight: 500; font-size: 0.875rem !important; line-height: 1.25rem;',
|
|
289
|
+
text: name
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
var deleteIcon = $('<span>', {
|
|
293
|
+
class: 'delete-saved-search text-danger',
|
|
294
|
+
'data-name': name,
|
|
295
|
+
style: 'cursor: pointer; padding: 0 5px; font-weight: bold;',
|
|
296
|
+
html: '×',
|
|
297
|
+
title: 'Delete saved search'
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
a.append(nameSpan).append(deleteIcon);
|
|
301
|
+
li.append(a);
|
|
302
|
+
menu.append(li);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
$(document).ready(function() {
|
|
307
|
+
populateSavedSearchesDropdown();
|
|
308
|
+
|
|
309
|
+
$(document).on('submit', '#advanceSearchForm', function(e) {
|
|
310
|
+
e.preventDefault();
|
|
311
|
+
return false;
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
$(document).on('input', '#savedSearchName', function() {
|
|
315
|
+
var val = $(this).val().trim();
|
|
316
|
+
if (val === '') {
|
|
317
|
+
$('#saveAdvanceSearch').text('Save Search As...');
|
|
318
|
+
} else {
|
|
319
|
+
$('#saveAdvanceSearch').text('Save');
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
$(document).on('click', '#saveAdvanceSearch', function(e) {
|
|
324
|
+
e.preventDefault();
|
|
325
|
+
var container = $('#save-search-container');
|
|
326
|
+
var input = $('#savedSearchName');
|
|
327
|
+
|
|
328
|
+
if (!container.is(':visible')) {
|
|
329
|
+
container.show();
|
|
330
|
+
input.focus();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
var name = input.val().trim();
|
|
335
|
+
if (name === '') {
|
|
336
|
+
container.hide();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
var searchParams = '';
|
|
341
|
+
|
|
342
|
+
// For bundles, it's search_by and q. For everything else, it's searchQuery()
|
|
343
|
+
if (window.location.pathname.includes('/bundles')) {
|
|
344
|
+
var searchBy = $('#searchFieldSelect').val();
|
|
345
|
+
var q = $('#bundleSearchValue').val();
|
|
346
|
+
if (q) q = q.trim();
|
|
347
|
+
if (!q) {
|
|
348
|
+
alert("Please enter a value to search.");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
searchParams = 'search_by=' + encodeURIComponent(searchBy) + '&q=' + encodeURIComponent(q);
|
|
352
|
+
} else {
|
|
353
|
+
// Check if there are any search fields before calling searchQuery(),
|
|
354
|
+
// because searchQuery() calls clearAdvanceSearch() (which reloads the page)
|
|
355
|
+
// when no fields exist.
|
|
356
|
+
if ($('.search-field').length === 0 && $('#search-labels-container .label').length === 0) {
|
|
357
|
+
alert("Please enter at least one search field.");
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
searchParams = searchQuery();
|
|
361
|
+
if (!searchParams) {
|
|
362
|
+
alert("Please enter at least one search field.");
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
searchParams = searchParams.replace(/account_id/g, 'ac_id');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
var searches = getSavedSearches();
|
|
369
|
+
searches[name] = searchParams;
|
|
370
|
+
saveSavedSearches(searches);
|
|
371
|
+
|
|
372
|
+
input.val('');
|
|
373
|
+
$('#saveAdvanceSearch').text('Save Search As...');
|
|
374
|
+
container.hide();
|
|
375
|
+
|
|
376
|
+
populateSavedSearchesDropdown();
|
|
377
|
+
$('#advanceSearchModal').modal('hide');
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
$(document).on('hidden.bs.modal', '#advanceSearchModal', function () {
|
|
381
|
+
$('#save-search-container').hide();
|
|
382
|
+
$('#savedSearchName').val('');
|
|
383
|
+
$('#saveAdvanceSearch').text('Save Search As...');
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
$(document).on('click', '.apply-saved-search', function(e) {
|
|
387
|
+
// If they clicked the delete icon, don't apply the search
|
|
388
|
+
if ($(e.target).hasClass('delete-saved-search')) return;
|
|
389
|
+
|
|
390
|
+
var name = $(this).data('name');
|
|
391
|
+
var searches = getSavedSearches();
|
|
392
|
+
var params = searches[name];
|
|
393
|
+
if (params) {
|
|
394
|
+
var newUrl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + params;
|
|
395
|
+
window.location.href = newUrl;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
$(document).on('click', '.delete-saved-search', function(e) {
|
|
400
|
+
e.stopPropagation(); // prevent dropdown from closing and prevent applying search
|
|
401
|
+
var name = $(this).data('name');
|
|
402
|
+
if (confirm("Are you sure you want to delete the saved search '" + name + "'?")) {
|
|
403
|
+
var searches = getSavedSearches();
|
|
404
|
+
delete searches[name];
|
|
405
|
+
saveSavedSearches(searches);
|
|
406
|
+
populateSavedSearchesDropdown();
|
|
407
|
+
}
|
|
408
|
+
});
|
|
233
409
|
});
|
|
@@ -1293,7 +1293,6 @@ table thead th:last-child {
|
|
|
1293
1293
|
align-items: center;
|
|
1294
1294
|
gap: 0.625rem;
|
|
1295
1295
|
padding: 0.875rem 1.125rem;
|
|
1296
|
-
margin: 0.625rem 0;
|
|
1297
1296
|
border-radius: 0.375rem;
|
|
1298
1297
|
font-size: 0.9375rem;
|
|
1299
1298
|
font-weight: 500;
|
|
@@ -1467,6 +1466,10 @@ table thead th:last-child {
|
|
|
1467
1466
|
width: 100%;
|
|
1468
1467
|
}
|
|
1469
1468
|
|
|
1469
|
+
.field_with_errors {
|
|
1470
|
+
display: contents;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1470
1473
|
/* Navbar color changes - Higher specificity to override existing rules */
|
|
1471
1474
|
header .header-icon {
|
|
1472
1475
|
color: #374151 !important;
|
|
@@ -689,4 +689,79 @@ table tr.expired td {
|
|
|
689
689
|
line-height: 1.25rem;
|
|
690
690
|
color: #414651;
|
|
691
691
|
margin-bottom: 0.25rem;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/* Advanced Search modal – Subscription Bundles page */
|
|
695
|
+
.subscription-bundl-index .close-button {
|
|
696
|
+
background: transparent;
|
|
697
|
+
padding: 0;
|
|
698
|
+
margin-top: -1.25rem;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
.subscription-bundl-index .close-button:hover {
|
|
702
|
+
background: transparent;
|
|
703
|
+
padding: 0;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
.subscription-bundl-index .border-button {
|
|
707
|
+
background: transparent;
|
|
708
|
+
display: inline-flex;
|
|
709
|
+
justify-content: center;
|
|
710
|
+
align-items: center;
|
|
711
|
+
border: 0.0625rem solid #D5D7DA;
|
|
712
|
+
border-radius: 0.375rem;
|
|
713
|
+
width: 2.5rem;
|
|
714
|
+
height: 2.5rem;
|
|
715
|
+
padding: 0.625rem;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.subscription-bundl-index .border-button:hover {
|
|
719
|
+
background: transparent;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.subscription-bundl-index .button {
|
|
723
|
+
background: transparent;
|
|
724
|
+
display: inline-flex;
|
|
725
|
+
justify-content: center;
|
|
726
|
+
align-items: center;
|
|
727
|
+
width: 2.5rem;
|
|
728
|
+
height: 2.5rem;
|
|
729
|
+
padding: 0.625rem;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.subscription-bundl-index .button:hover {
|
|
733
|
+
background: transparent;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.subscription-bundl-index .field-label {
|
|
737
|
+
font-weight: 500;
|
|
738
|
+
font-size: 0.875rem;
|
|
739
|
+
line-height: 1.25rem;
|
|
740
|
+
color: #414651;
|
|
741
|
+
margin-top: -1.25rem;
|
|
742
|
+
min-width: 9.59375rem;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.subscription-bundl-index .search-field-label {
|
|
746
|
+
margin-top: -1.25rem;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.subscription-bundl-index .form-group.row.align-items-center {
|
|
750
|
+
display: flex;
|
|
751
|
+
align-items: center;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.subscription-bundl-index .form-group.row.align-items-center label {
|
|
755
|
+
margin-bottom: 0;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.subscription-bundl-index .form-group.row.align-items-center .form-control {
|
|
759
|
+
margin-right: 0.25rem;
|
|
760
|
+
height: 2.5rem;
|
|
761
|
+
border-radius: 0.375rem;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
.subscription-bundl-index .form-group.row.align-items-center.search-field div {
|
|
765
|
+
padding: 0;
|
|
766
|
+
margin-right: 0.25rem;
|
|
692
767
|
}
|
|
@@ -58,15 +58,18 @@
|
|
|
58
58
|
.tag-bar .tag-select .tag-select-box {
|
|
59
59
|
position: absolute;
|
|
60
60
|
top: 100%;
|
|
61
|
-
left:
|
|
61
|
+
left: 0;
|
|
62
|
+
right: auto;
|
|
62
63
|
max-height: 0;
|
|
63
64
|
background-color: #fff;
|
|
64
65
|
z-index: 100;
|
|
65
66
|
padding: 0 0.9375rem;
|
|
66
67
|
font-size: 0.75rem;
|
|
67
68
|
color: #999999;
|
|
68
|
-
overflow:
|
|
69
|
-
width:
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
min-width: 15rem;
|
|
71
|
+
width: max-content;
|
|
72
|
+
max-width: min(25rem, 95vw);
|
|
70
73
|
border-bottom: 0;
|
|
71
74
|
display: inline-block;
|
|
72
75
|
}
|
|
@@ -5,7 +5,7 @@ module Kaui
|
|
|
5
5
|
class AccountsController < Kaui::EngineController
|
|
6
6
|
def index
|
|
7
7
|
@search_query = params[:q]
|
|
8
|
-
@advance_search_query = @search_query ||
|
|
8
|
+
@advance_search_query = @search_query.presence || params[:advance_search_query].presence
|
|
9
9
|
if @search_query.present?
|
|
10
10
|
account = Kaui::Account.list_or_search(@search_query, -1, 1, options_for_klient).first
|
|
11
11
|
if account.nil?
|
|
@@ -61,8 +61,8 @@ module Kaui
|
|
|
61
61
|
nil
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
@overdue = wait(fetch_overdue)
|
|
65
|
-
@overdue_xml = wait(fetch_overdue_xml)
|
|
64
|
+
@overdue = flash[:overdue_deleted] ? nil : wait(fetch_overdue)
|
|
65
|
+
@overdue_xml = flash[:overdue_deleted] ? nil : wait(fetch_overdue_xml)
|
|
66
66
|
@tenant_plugin_config = begin
|
|
67
67
|
wait(fetch_tenant_plugin_config)
|
|
68
68
|
rescue StandardError
|
|
@@ -288,7 +288,15 @@ module Kaui
|
|
|
288
288
|
options = tenant_options_for_client
|
|
289
289
|
options[:api_key] = @tenant.api_key
|
|
290
290
|
options[:api_secret] = @tenant.api_secret
|
|
291
|
-
|
|
291
|
+
begin
|
|
292
|
+
@overdue = Kaui::Overdue.get_overdue_json(options)
|
|
293
|
+
@overdue_corrupted = false
|
|
294
|
+
rescue StandardError => e
|
|
295
|
+
Rails.logger.warn("Failed to load overdue configuration for tenant #{@tenant.id}: #{e.class}: #{e.message}")
|
|
296
|
+
@overdue = KillBillClient::Model::Overdue.new.tap { |o| o.overdue_states = [] }
|
|
297
|
+
@overdue_corrupted = true
|
|
298
|
+
flash.now[:warning] = 'The existing overdue configuration is corrupted and cannot be loaded. Use the XML upload below to replace it with a valid configuration.'
|
|
299
|
+
end
|
|
292
300
|
end
|
|
293
301
|
|
|
294
302
|
def modify_overdue_config
|
|
@@ -298,12 +306,18 @@ module Kaui
|
|
|
298
306
|
options[:api_key] = current_tenant.api_key
|
|
299
307
|
options[:api_secret] = current_tenant.api_secret
|
|
300
308
|
|
|
301
|
-
|
|
309
|
+
overdue_params = params[:kill_bill_client_model_overdue]&.permit!
|
|
310
|
+
view_form_model = overdue_params&.to_h&.compact_blank || {}
|
|
302
311
|
view_form_model['states'] = view_form_model['states'].values if view_form_model['states'].present?
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
312
|
+
if view_form_model['states'].blank?
|
|
313
|
+
Kaui::AdminTenant.delete_tenant_user_key_value('OVERDUE_CONFIG', options[:username], nil, comment, options)
|
|
314
|
+
flash[:overdue_deleted] = true
|
|
315
|
+
redirect_to admin_tenant_path(current_tenant.id, active_tab: 'OverdueShow'), notice: I18n.t('flashes.notices.overdue_updated_successfully')
|
|
316
|
+
else
|
|
317
|
+
overdue = Kaui::Overdue.from_overdue_form_model(view_form_model)
|
|
318
|
+
Kaui::Overdue.upload_tenant_overdue_config_json(overdue.to_json, options[:username], nil, comment, options)
|
|
319
|
+
redirect_to admin_tenant_path(current_tenant.id, active_tab: 'OverdueShow'), notice: I18n.t('flashes.notices.overdue_added_successfully')
|
|
320
|
+
end
|
|
307
321
|
end
|
|
308
322
|
|
|
309
323
|
def upload_overdue_config
|
|
@@ -5,10 +5,11 @@ module Kaui
|
|
|
5
5
|
# rubocop:disable Lint/HashCompareByIdentity
|
|
6
6
|
def index
|
|
7
7
|
cached_options_for_klient = options_for_klient
|
|
8
|
+
@search_query = params[:q].presence
|
|
9
|
+
@search_by = params[:search_by] || 'bundle_id'
|
|
8
10
|
@per_page = (params[:per_page] || 10).to_i
|
|
9
11
|
@page = (params[:page] || 1).to_i
|
|
10
12
|
|
|
11
|
-
fetch_bundles = promise { Kaui::Account.paginated_bundles(@account.account_id, (@page - 1) * @per_page, @per_page, 'NONE', cached_options_for_klient) }
|
|
12
13
|
fetch_bundle_tags = promise do
|
|
13
14
|
all_bundle_tags = @account.all_tags(:BUNDLE, false, 'NONE', cached_options_for_klient)
|
|
14
15
|
all_bundle_tags.each_with_object({}) do |entry, hsh|
|
|
@@ -36,8 +37,15 @@ module Kaui
|
|
|
36
37
|
fetch_available_tags = promise { Kaui::TagDefinition.all_for_bundle(cached_options_for_klient) }
|
|
37
38
|
fetch_available_subscription_tags = promise { Kaui::TagDefinition.all_for_subscription(cached_options_for_klient) }
|
|
38
39
|
|
|
39
|
-
@
|
|
40
|
-
|
|
40
|
+
if @search_query.present?
|
|
41
|
+
@bundles = search_bundles(@search_query, @search_by, cached_options_for_klient)
|
|
42
|
+
@total_pages = 1
|
|
43
|
+
@page = 1
|
|
44
|
+
else
|
|
45
|
+
fetched = Kaui::Account.paginated_bundles(@account.account_id, (@page - 1) * @per_page, @per_page, 'NONE', cached_options_for_klient)
|
|
46
|
+
@bundles = fetched
|
|
47
|
+
@total_pages = (fetched.pagination_max_nb_records.to_f / @per_page).ceil
|
|
48
|
+
end
|
|
41
49
|
|
|
42
50
|
@tags_per_bundle = wait(fetch_bundle_tags)
|
|
43
51
|
@tags_per_subscription = wait(fetch_subscription_tags)
|
|
@@ -46,8 +54,15 @@ module Kaui
|
|
|
46
54
|
@available_tags = wait(fetch_available_tags)
|
|
47
55
|
@available_subscription_tags = wait(fetch_available_subscription_tags)
|
|
48
56
|
|
|
49
|
-
#
|
|
50
|
-
|
|
57
|
+
# Collect the distinct start dates from subscriptions on this page, then fetch
|
|
58
|
+
# only the catalog versions needed — one per unique date — to avoid loading all
|
|
59
|
+
# historical versions into memory.
|
|
60
|
+
start_dates = @bundles.flat_map(&:subscriptions).filter_map(&:start_date).uniq
|
|
61
|
+
@catalogs = start_dates.filter_map do |date|
|
|
62
|
+
Kaui::Catalog.get_account_catalog_json(@account.account_id, date, cached_options_for_klient)&.last
|
|
63
|
+
rescue StandardError
|
|
64
|
+
nil
|
|
65
|
+
end.uniq(&:effective_date)
|
|
51
66
|
|
|
52
67
|
@subscription = {}
|
|
53
68
|
@bundles.each do |bundle|
|
|
@@ -112,5 +127,38 @@ module Kaui
|
|
|
112
127
|
end
|
|
113
128
|
redirect_to kaui_engine.account_bundles_path(@account.account_id), notice: msg
|
|
114
129
|
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def search_bundles(query, search_by, options)
|
|
134
|
+
case search_by
|
|
135
|
+
when 'bundle_id'
|
|
136
|
+
bundle = Kaui::Bundle.find_by_id(query, options)
|
|
137
|
+
bundle ? [bundle] : []
|
|
138
|
+
when 'bundle_external_key'
|
|
139
|
+
bundle = Kaui::Bundle.find_by_external_key(query, false, options)
|
|
140
|
+
bundle ? [bundle] : []
|
|
141
|
+
when 'subscription_id'
|
|
142
|
+
subscription = KillBillClient::Model::Subscription.find_by_id(query, 'NONE', options)
|
|
143
|
+
if subscription
|
|
144
|
+
bundle = Kaui::Bundle.find_by_id(subscription.bundle_id, options)
|
|
145
|
+
bundle ? [bundle] : []
|
|
146
|
+
else
|
|
147
|
+
[]
|
|
148
|
+
end
|
|
149
|
+
when 'subscription_external_key'
|
|
150
|
+
subscription = KillBillClient::Model::Subscription.find_by_external_key(query, 'NONE', options)
|
|
151
|
+
if subscription
|
|
152
|
+
bundle = Kaui::Bundle.find_by_id(subscription.bundle_id, options)
|
|
153
|
+
bundle ? [bundle] : []
|
|
154
|
+
else
|
|
155
|
+
[]
|
|
156
|
+
end
|
|
157
|
+
else
|
|
158
|
+
[]
|
|
159
|
+
end
|
|
160
|
+
rescue KillBillClient::API::NotFound
|
|
161
|
+
[]
|
|
162
|
+
end
|
|
115
163
|
end
|
|
116
164
|
end
|
|
@@ -127,7 +127,7 @@ module Kaui
|
|
|
127
127
|
as_string(exception.cause)
|
|
128
128
|
else
|
|
129
129
|
log_rescue_error(exception)
|
|
130
|
-
exception.message
|
|
130
|
+
exception.message.to_s[0..200]
|
|
131
131
|
end
|
|
132
132
|
end
|
|
133
133
|
|
|
@@ -150,7 +150,7 @@ module Kaui
|
|
|
150
150
|
error_message += " (code=#{error_message['code']})" if error_message['code'].present?
|
|
151
151
|
end
|
|
152
152
|
# Limit the error size to avoid ActionDispatch::Cookies::CookieOverflow
|
|
153
|
-
error_message[0..
|
|
153
|
+
error_message.to_s[0..200]
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
def nested_hash_value(obj, key)
|
|
@@ -6,7 +6,7 @@ module Kaui
|
|
|
6
6
|
def index
|
|
7
7
|
@search_query = params[:account_id]
|
|
8
8
|
@advance_search_query = params[:q] || request.query_string
|
|
9
|
-
@ordering = params[:ordering] ||
|
|
9
|
+
@ordering = params[:ordering] || 'desc'
|
|
10
10
|
@offset = params[:offset] || 0
|
|
11
11
|
@limit = params[:limit] || 50
|
|
12
12
|
@search_fields = Kaui::Invoice::ADVANCED_SEARCH_COLUMNS.map { |attr| [attr, attr.split('_').join(' ').capitalize] }
|
|
@@ -11,10 +11,8 @@ module Kaui
|
|
|
11
11
|
def installed_plugin_names
|
|
12
12
|
plugins = []
|
|
13
13
|
nodes_info = KillBillClient::Model::NodesInfo.nodes_info(Kaui.current_tenant_user_options(current_user, session)) || []
|
|
14
|
-
plugins_info = nodes_info.
|
|
15
|
-
plugins_info.each do |plugin|
|
|
16
|
-
next unless plugin.state == 'RUNNING'
|
|
17
|
-
|
|
14
|
+
plugins_info = nodes_info.flat_map { |node| node.plugins_info || [] }
|
|
15
|
+
plugins_info.select { |p| p.state == 'RUNNING' }.uniq(&:plugin_name).each do |plugin|
|
|
18
16
|
plugin_name = plugin.plugin_name
|
|
19
17
|
plugin_key = plugin_name.gsub('-plugin', '')
|
|
20
18
|
|
|
@@ -40,7 +38,7 @@ module Kaui
|
|
|
40
38
|
def installed_plugins
|
|
41
39
|
installed_plugins = []
|
|
42
40
|
nodes_info = KillBillClient::Model::NodesInfo.nodes_info(Kaui.current_tenant_user_options(current_user, session)) || []
|
|
43
|
-
plugins_info = nodes_info.
|
|
41
|
+
plugins_info = nodes_info.flat_map { |node| node.plugins_info || [] }
|
|
44
42
|
|
|
45
43
|
plugins_info.each do |plugin|
|
|
46
44
|
next if plugin.version.nil?
|
|
@@ -107,6 +107,24 @@ module Kaui
|
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
+
def catalog_for_subscription(sub, catalogs)
|
|
111
|
+
return nil if catalogs.blank? || sub&.start_date.blank?
|
|
112
|
+
|
|
113
|
+
start_date = Date.parse(sub.start_date)
|
|
114
|
+
|
|
115
|
+
# Find the latest catalog version whose effective_date <= subscription start_date
|
|
116
|
+
best = catalogs.select do |c|
|
|
117
|
+
Date.parse(c.effective_date) <= start_date
|
|
118
|
+
rescue StandardError
|
|
119
|
+
false
|
|
120
|
+
end.max_by(&:effective_date)
|
|
121
|
+
|
|
122
|
+
# Fall back to the oldest version if none precedes the start_date
|
|
123
|
+
best || catalogs.first
|
|
124
|
+
rescue StandardError
|
|
125
|
+
catalogs.last
|
|
126
|
+
end
|
|
127
|
+
|
|
110
128
|
def humanized_subscription_phase_type(sub)
|
|
111
129
|
sub.phase_type
|
|
112
130
|
end
|
data/app/models/kaui/bundle.rb
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Kaui
|
|
4
4
|
class Bundle < KillBillClient::Model::Bundle
|
|
5
|
+
SEARCH_FIELDS = [
|
|
6
|
+
['bundle_id', 'Bundle ID'],
|
|
7
|
+
['bundle_external_key', 'Bundle External Key'],
|
|
8
|
+
['subscription_id', 'Subscription ID'],
|
|
9
|
+
['subscription_external_key', 'Subscription External Key']
|
|
10
|
+
].freeze
|
|
5
11
|
def self.find_by_id_or_key(bundle_id_or_key, options = {})
|
|
6
12
|
if /[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}/.match?(bundle_id_or_key)
|
|
7
13
|
bundle = begin
|
data/app/models/kaui/overdue.rb
CHANGED
|
@@ -37,8 +37,11 @@ module Kaui
|
|
|
37
37
|
|
|
38
38
|
result.overdue_states << state
|
|
39
39
|
end
|
|
40
|
-
#
|
|
41
|
-
|
|
40
|
+
# Sort by days descending (most severe first) so Kill Bill receives them in the correct order
|
|
41
|
+
# regardless of the order the user added rows in the form.
|
|
42
|
+
result.overdue_states.sort_by! do |s|
|
|
43
|
+
-s.condition&.time_since_earliest_unpaid_invoice_equals_or_exceeds&.number.to_i
|
|
44
|
+
end
|
|
42
45
|
|
|
43
46
|
result
|
|
44
47
|
end
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
<% end %>
|
|
46
46
|
<span class="tag-bar tag-bar-breathe">
|
|
47
47
|
<% unless @available_tags.blank? %>
|
|
48
|
-
<div class="tag-select" onclick="void(0)">
|
|
48
|
+
<div class="tag-select position-relative" onclick="void(0)">
|
|
49
49
|
<%= render "kaui/components/button/button", {
|
|
50
50
|
label: "Tag As",
|
|
51
51
|
trailing_icon: "kaui/account/down-arrow.svg",
|