final look
This commit is contained in:
parent
39213fec27
commit
f7e1657f1a
|
|
@ -48,6 +48,7 @@ def get_items_for_selection(filters=None, start=0, page_length=50, custom_filter
|
|||
company = filters.get('company')
|
||||
transaction_date = filters.get('transaction_date')
|
||||
only_in_stock = filters.get('only_in_stock', False)
|
||||
maintain_stock = filters.get('maintain_stock', False)
|
||||
sort_by = filters.get('sort_by', 'item_name') # item_name, available_qty, rate
|
||||
sort_order = filters.get('sort_order', 'asc') # asc, desc
|
||||
|
||||
|
|
@ -70,6 +71,9 @@ def get_items_for_selection(filters=None, start=0, page_length=50, custom_filter
|
|||
.where(Item.is_sales_item == 1)
|
||||
)
|
||||
|
||||
if maintain_stock:
|
||||
query = query.where(Item.is_stock_item == 1)
|
||||
|
||||
# Фильтр по поиску
|
||||
if search_term:
|
||||
search_condition = (
|
||||
|
|
|
|||
|
|
@ -296,6 +296,25 @@ body.selection-resizing * {
|
|||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* Иконка открытия товара в новом окне */
|
||||
.item-open-link {
|
||||
margin-left: 6px;
|
||||
color: var(--primary-color);
|
||||
opacity: 0.25;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.selection-table tbody tr:hover .item-open-link {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.item-open-link:hover {
|
||||
opacity: 1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Изображения товаров */
|
||||
.item-image-cell {
|
||||
width: 50px;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,14 @@ class SelectionDialog {
|
|||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<div class="selection-stock-toggle">
|
||||
<label class="stock-toggle-label">
|
||||
<input type="checkbox" id="selection-maintain-stock">
|
||||
<span>${__('Maintain stock only')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label>${__('Item Group')}</label>
|
||||
<div id="selection-item-group-tree" class="item-group-tree">
|
||||
|
|
@ -218,6 +226,13 @@ class SelectionDialog {
|
|||
|
||||
// Поиск — обработчик вешается в setup_search_field()
|
||||
|
||||
// Фильтр "Только складские товары"
|
||||
this.dialog.$wrapper.find('#selection-maintain-stock').on('change', function() {
|
||||
me.filters.maintain_stock = $(this).prop('checked');
|
||||
me.current_page = 0;
|
||||
me.load_items();
|
||||
});
|
||||
|
||||
// Фильтр "Только в наличии"
|
||||
this.dialog.$wrapper.find('#selection-only-in-stock').on('change', function() {
|
||||
if (!me.filters.warehouse) {
|
||||
|
|
@ -239,6 +254,7 @@ class SelectionDialog {
|
|||
if (me.warehouse_field) me.warehouse_field.set_value('');
|
||||
if (me.pricelist_field) me.pricelist_field.set_value('');
|
||||
me.dialog.$wrapper.find('#selection-only-in-stock').prop('checked', false);
|
||||
me.dialog.$wrapper.find('#selection-maintain-stock').prop('checked', false);
|
||||
me.dialog.$wrapper.find('.item-group-tree .active').removeClass('active');
|
||||
me._update_stock_toggle_state();
|
||||
// Очищаем произвольные фильтры
|
||||
|
|
@ -353,29 +369,33 @@ class SelectionDialog {
|
|||
const $rows = this.dialog.$wrapper.find('#selection-items-grid tbody tr');
|
||||
const row_count = $rows.length;
|
||||
|
||||
if (row_count === 0) return;
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
if (row_count === 0) return;
|
||||
e.preventDefault();
|
||||
this.focused_row_index = Math.min(this.focused_row_index + 1, row_count - 1);
|
||||
this._update_focused_row($rows);
|
||||
$rows.eq(this.focused_row_index).find('.row-qty-input').focus().select();
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
if (row_count === 0) return;
|
||||
e.preventDefault();
|
||||
this.focused_row_index = Math.max(this.focused_row_index - 1, 0);
|
||||
this._update_focused_row($rows);
|
||||
$rows.eq(this.focused_row_index).find('.row-qty-input').focus().select();
|
||||
} else if (e.key === 'Enter' && e.ctrlKey) {
|
||||
// Добавить всё в документ
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.finalize_selection();
|
||||
} else if (e.key === 'Enter' && !e.ctrlKey) {
|
||||
// Добавить выделенный товар в корзину
|
||||
// Всегда блокируем — иначе Frappe перехватит и вызовет primary action
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (this.focused_row_index >= 0 && this.items_data[this.focused_row_index]) {
|
||||
e.preventDefault();
|
||||
const $row = $rows.eq(this.focused_row_index);
|
||||
const qty_val = parseFloat(($row.find('.row-qty-input').val() || '').replace(',', '.')) || 1;
|
||||
const item = this.items_data[this.focused_row_index];
|
||||
this.add_to_cart(item, qty_val);
|
||||
}
|
||||
} else if (e.key === 'Enter' && e.ctrlKey) {
|
||||
// Добавить всё в документ
|
||||
e.preventDefault();
|
||||
this.finalize_selection();
|
||||
} else if (e.key === 'Escape') {
|
||||
this.dialog.hide();
|
||||
}
|
||||
|
|
@ -555,6 +575,7 @@ class SelectionDialog {
|
|||
company: this.frm.doc.company || '',
|
||||
transaction_date: this.frm.doc.posting_date || frappe.datetime.get_today(),
|
||||
only_in_stock: this.filters.only_in_stock || false,
|
||||
maintain_stock: this.filters.maintain_stock || false,
|
||||
sort_by: this.sort_by,
|
||||
sort_order: this.sort_order
|
||||
};
|
||||
|
|
@ -645,6 +666,13 @@ class SelectionDialog {
|
|||
<td class="item-image-cell">${img_html}</td>
|
||||
<td>
|
||||
<strong>${item.item_name}</strong>
|
||||
<a href="/app/item/${encodeURIComponent(item.item_code)}"
|
||||
target="_blank"
|
||||
class="item-open-link"
|
||||
title="${__('Open Item')}"
|
||||
onclick="event.stopPropagation()">
|
||||
<i class="fa fa-external-link"></i>
|
||||
</a>
|
||||
<br><small class="text-muted">${item.item_code}</small>
|
||||
${item.item_group ? `<br><small class="text-muted">${item.item_group}</small>` : ''}
|
||||
</td>
|
||||
|
|
@ -713,10 +741,12 @@ class SelectionDialog {
|
|||
|
||||
// Клик по строке для навигации и инфо-панели
|
||||
$grid.find('tbody tr').on('click', function(e) {
|
||||
if (!$(e.target).closest('.btn-add-to-cart, .row-qty-input, .stock-indicator').length) {
|
||||
if (!$(e.target).closest('.btn-add-to-cart, .row-qty-input, .stock-indicator, .item-open-link').length) {
|
||||
me.focused_row_index = parseInt($(this).data('item-index'));
|
||||
me._update_focused_row($grid.find('tbody tr'));
|
||||
me.show_item_info(me.focused_row_index);
|
||||
// Фокус на qty чтобы сразу можно было вводить количество
|
||||
$(this).find('.row-qty-input').focus().select();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -725,7 +755,21 @@ class SelectionDialog {
|
|||
const item_index = parseInt($(this).data('item-index'));
|
||||
const is_whole = me.items_data[item_index] && me.items_data[item_index].must_be_whole_number;
|
||||
$(this).find('.row-qty-input')
|
||||
.on('keydown', function(e) { me._handle_qty_keydown(e, is_whole); });
|
||||
.on('keydown', function(e) {
|
||||
// Enter в qty — добавить в корзину, не пропускать дальше
|
||||
if (e.key === 'Enter' && !e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const qty_val = parseFloat(($(this).val() || '').replace(',', '.')) || 1;
|
||||
if (me.items_data[item_index]) {
|
||||
me.add_to_cart(me.items_data[item_index], qty_val);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Ctrl+Enter пропускаем вверх — _handle_keydown вызовет finalize_selection
|
||||
if (e.key === 'Enter' && e.ctrlKey) return;
|
||||
me._handle_qty_keydown(e, is_whole);
|
||||
});
|
||||
});
|
||||
|
||||
// Tooltip остатков по складам
|
||||
|
|
@ -1315,27 +1359,15 @@ class SelectionDialog {
|
|||
}
|
||||
|
||||
_handle_qty_keydown(e, is_whole) {
|
||||
// Останавливаем всплытие — иначе dialog._handle_keydown перехватит стрелки и Enter
|
||||
// ArrowUp/Down пропускаем наверх — _handle_keydown обработает навигацию по строкам
|
||||
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') return;
|
||||
|
||||
// Останавливаем всплытие для всех остальных клавиш
|
||||
e.stopPropagation();
|
||||
|
||||
// Разрешаем управляющие комбинации (Ctrl+A, Ctrl+C, Ctrl+V и т.п.)
|
||||
if (e.ctrlKey || e.metaKey) return;
|
||||
|
||||
// Стрелки вверх/вниз — шаг +1/-1
|
||||
if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
const current = parseFloat(e.target.value.replace(',', '.')) || 0;
|
||||
e.target.value = current + 1;
|
||||
return;
|
||||
}
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
const current = parseFloat(e.target.value.replace(',', '.')) || 0;
|
||||
const min_val = is_whole ? 1 : 0.001;
|
||||
e.target.value = Math.max(min_val, current - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Всегда разрешаем служебные клавиши
|
||||
const control_keys = [
|
||||
'Backspace', 'Delete', 'Tab', 'Escape',
|
||||
|
|
|
|||
Loading…
Reference in New Issue