Перенос встроенных стилей из шаблона detail.html в отдельный CSS-файл transformation_detail.css
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
/* Стили для компонентов поиска товаров в трансформациях */
|
||||||
|
|
||||||
|
#transformation-input-picker .card,
|
||||||
|
#transformation-output-picker .card {
|
||||||
|
height: 380px !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#transformation-input-picker .product-picker-content,
|
||||||
|
#transformation-output-picker .product-picker-content {
|
||||||
|
max-height: 200px !important;
|
||||||
|
min-height: 200px !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Скрываем внутренние кнопки компонента Picker */
|
||||||
|
#transformation-input-picker .product-picker-add-selected,
|
||||||
|
#transformation-output-picker .product-picker-add-selected {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- CSS для компонента поиска -->
|
<!-- CSS для компонента поиска -->
|
||||||
<link rel="stylesheet" href="{% static 'products/css/product-search-picker.css' %}">
|
<link rel="stylesheet" href="{% static 'products/css/product-search-picker.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'inventory/transformation_detail.css' %}">
|
||||||
|
|
||||||
<div class="container-fluid px-4 py-3">
|
<div class="container-fluid px-4 py-3">
|
||||||
<!-- Breadcrumbs -->
|
<!-- Breadcrumbs -->
|
||||||
@@ -98,34 +99,60 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ОДИН виджет поиска товаров -->
|
<!-- ДВА виджета поиска товаров -->
|
||||||
{% if transformation.status == 'draft' %}
|
{% if transformation.status == 'draft' %}
|
||||||
<div class="card border-0 shadow-sm mb-3">
|
<div class="row g-3 mb-3 align-items-stretch">
|
||||||
<div class="card-header bg-light py-3">
|
<!-- Карточка для входящего товара -->
|
||||||
<h6 class="mb-0"><i class="bi bi-search me-2"></i>Найти и добавить товар</h6>
|
<div class="col-md-6">
|
||||||
</div>
|
<div class="card border-0 shadow-sm h-100 d-flex flex-column transformation-picker">
|
||||||
<div class="card-body">
|
<div class="card-header bg-warning bg-opacity-10 py-3">
|
||||||
<!-- Компонент поиска товаров -->
|
<h6 class="mb-0"><i class="bi bi-box-arrow-in-down me-2"></i>Добавить входящий товар (для списания)</h6>
|
||||||
<div class="mb-3">
|
</div>
|
||||||
{% include 'products/components/product_search_picker.html' with container_id='transformation-product-picker' title='Найти товар' warehouse_id=transformation.warehouse.id categories=categories tags=tags add_button_text='Выбрать товар' content_height='250px' %}
|
<div class="card-body flex-grow-1 d-flex flex-column overflow-hidden">
|
||||||
|
<!-- Компонент поиска С фильтрацией по наличию -->
|
||||||
|
{% include 'products/components/product_search_picker.html' with container_id='transformation-input-picker' title='Найти товар в наличии' warehouse_id=transformation.warehouse.id categories=categories tags=tags add_button_text='Добавить во входящий' content_height='200px' %}
|
||||||
|
</div>
|
||||||
|
<!-- Поле количества и кнопка всегда видимы внизу карточки -->
|
||||||
|
<div class="card-footer bg-transparent border-top">
|
||||||
|
<div class="row g-3 align-items-end">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<label for="input-quantity" class="form-label">Количество</label>
|
||||||
|
<input type="number" class="form-control" id="input-quantity" value="1" min="0.001" step="0.001">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<button type="button" id="confirm-add-input-btn" class="btn btn-warning w-100">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i>Добавить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ДВЕ кнопки действий (показываются после выбора товара) -->
|
<!-- Карточка для исходящего товара -->
|
||||||
<div id="action-buttons" class="row g-3 align-items-end" style="display:none;">
|
<div class="col-md-6">
|
||||||
<div class="col-md-4">
|
<div class="card border-0 shadow-sm h-100 d-flex flex-column transformation-picker">
|
||||||
<label for="product-quantity" class="form-label">Количество</label>
|
<div class="card-header bg-success bg-opacity-10 py-3">
|
||||||
<input type="number" class="form-control" id="product-quantity" value="1" min="0.001" step="0.001" placeholder="Введите количество">
|
<h6 class="mb-0"><i class="bi bi-box-arrow-up me-2"></i>Добавить исходящий товар (для получения)</h6>
|
||||||
<small id="quantity-hint" class="form-text text-muted" style="display: none; font-size: 0.75rem;"></small>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="card-body flex-grow-1 d-flex flex-column overflow-hidden">
|
||||||
<button type="button" id="add-to-input-btn" class="btn btn-warning w-100">
|
<!-- Компонент поиска БЕЗ фильтрации по наличию (skip_stock_filter=True) -->
|
||||||
<i class="bi bi-box-arrow-in-down me-1"></i>Добавить во входящий
|
{% include 'products/components/product_search_picker.html' with container_id='transformation-output-picker' title='Найти товар (даже если нет на складе)' warehouse_id=transformation.warehouse.id skip_stock_filter=True categories=categories tags=tags add_button_text='Добавить в исходящий' content_height='200px' %}
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<!-- Поле количества и кнопка всегда видимы внизу карточки -->
|
||||||
<button type="button" id="add-to-output-btn" class="btn btn-success w-100">
|
<div class="card-footer bg-transparent border-top">
|
||||||
<i class="bi bi-box-arrow-up me-1"></i>Добавить в исходящий
|
<div class="row g-3 align-items-end">
|
||||||
</button>
|
<div class="col-md-8">
|
||||||
|
<label for="output-quantity" class="form-label">Количество</label>
|
||||||
|
<input type="number" class="form-control" id="output-quantity" value="1" min="0.001" step="0.001">
|
||||||
|
<small id="output-quantity-hint" class="form-text" style="display: none;"></small>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<button type="button" id="confirm-add-output-btn" class="btn btn-success w-100">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i>Добавить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -334,107 +361,122 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
var selectedProduct = null;
|
var selectedInputProduct = null;
|
||||||
var picker = null;
|
var selectedOutputProduct = null;
|
||||||
|
var inputPicker = null;
|
||||||
|
var outputPicker = null;
|
||||||
|
|
||||||
// Инициализация ОДНОГО компонента поиска
|
// ========== ВХОДЯЩИЙ ТОВАР (с фильтрацией по наличию) ==========
|
||||||
if (document.getElementById('transformation-product-picker')) {
|
if (document.getElementById('transformation-input-picker')) {
|
||||||
picker = ProductSearchPicker.init('#transformation-product-picker', {
|
inputPicker = ProductSearchPicker.init('#transformation-input-picker', {
|
||||||
onSelect: function(product, instance) {
|
onSelect: function(product, instance) {
|
||||||
// Товар выбран - показываем кнопки действий
|
selectedInputProduct = product;
|
||||||
selectedProduct = product;
|
// Focus on the quantity input when a product is selected
|
||||||
var actionButtons = document.getElementById('action-buttons');
|
|
||||||
actionButtons.style.display = 'block';
|
|
||||||
|
|
||||||
// Автофокус на поле количества и выделяем текст
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
var quantityInput = document.getElementById('product-quantity');
|
var qtyInput = document.getElementById('input-quantity');
|
||||||
if (quantityInput) {
|
if (qtyInput) {
|
||||||
quantityInput.focus();
|
qtyInput.focus();
|
||||||
quantityInput.select();
|
qtyInput.select();
|
||||||
|
qtyInput.onkeypress = function(e) {
|
||||||
// Обработчик Enter - добавляет в входящие по умолчанию
|
|
||||||
quantityInput.onkeypress = function(e) {
|
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
document.getElementById('add-to-input-btn').click();
|
document.getElementById('confirm-add-input-btn').click();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Обработчик изменения количества для подсказки при добавлении в выходные
|
|
||||||
quantityInput.addEventListener('input', function() {
|
|
||||||
updateQuantityHint();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
// Функция обновления подсказки количества
|
|
||||||
function updateQuantityHint() {
|
|
||||||
var quantityInput = document.getElementById('product-quantity');
|
|
||||||
var quantityHint = document.getElementById('quantity-hint');
|
|
||||||
var totalInputEl = document.getElementById('total-input-quantity');
|
|
||||||
var totalOutputEl = document.getElementById('total-output-quantity');
|
|
||||||
|
|
||||||
if (!quantityInput || !quantityHint || !totalInputEl || !totalOutputEl) return;
|
|
||||||
|
|
||||||
var quantity = parseFloat(quantityInput.value) || 0;
|
|
||||||
var totalInput = parseFloat(totalInputEl.textContent.replace(/\s/g, '').replace(',', '.'));
|
|
||||||
var totalOutput = parseFloat(totalOutputEl.textContent.replace(/\s/g, '').replace(',', '.'));
|
|
||||||
var newTotalOutput = totalOutput + quantity;
|
|
||||||
|
|
||||||
if (quantity > 0 && totalInput > 0) {
|
|
||||||
if (newTotalOutput > totalInput) {
|
|
||||||
var maxAllowed = totalInput - totalOutput;
|
|
||||||
quantityHint.textContent = 'Максимум: ' + maxAllowed.toFixed(3) + ' (превышение!)';
|
|
||||||
quantityHint.className = 'form-text text-danger';
|
|
||||||
quantityHint.style.display = 'block';
|
|
||||||
} else if (newTotalOutput === totalInput) {
|
|
||||||
quantityHint.textContent = 'Количества будут равны ✓';
|
|
||||||
quantityHint.className = 'form-text text-success';
|
|
||||||
quantityHint.style.display = 'block';
|
|
||||||
} else {
|
|
||||||
var remaining = totalInput - newTotalOutput;
|
|
||||||
quantityHint.textContent = 'Остаток после добавления: ' + remaining.toFixed(3);
|
|
||||||
quantityHint.className = 'form-text text-info';
|
|
||||||
quantityHint.style.display = 'block';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quantityHint.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onDeselect: function(instance) {
|
onDeselect: function(instance) {
|
||||||
// Товар отменен - скрываем кнопки
|
selectedInputProduct = null;
|
||||||
selectedProduct = null;
|
|
||||||
document.getElementById('action-buttons').style.display = 'none';
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Кнопка "Добавить во входящий товар"
|
// ========== ИСХОДЯЩИЙ ТОВАР (БЕЗ фильтрации по наличию) ==========
|
||||||
var addToInputBtn = document.getElementById('add-to-input-btn');
|
if (document.getElementById('transformation-output-picker')) {
|
||||||
if (addToInputBtn) {
|
outputPicker = ProductSearchPicker.init('#transformation-output-picker', {
|
||||||
addToInputBtn.addEventListener('click', function() {
|
onSelect: function(product, instance) {
|
||||||
if (!selectedProduct) return;
|
selectedOutputProduct = product;
|
||||||
|
updateOutputQuantityHint();
|
||||||
|
setTimeout(function() {
|
||||||
|
var qtyInput = document.getElementById('output-quantity');
|
||||||
|
if (qtyInput) {
|
||||||
|
qtyInput.focus();
|
||||||
|
qtyInput.select();
|
||||||
|
qtyInput.onkeypress = function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
document.getElementById('confirm-add-output-btn').click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
qtyInput.addEventListener('input', updateOutputQuantityHint);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
},
|
||||||
|
onDeselect: function(instance) {
|
||||||
|
selectedOutputProduct = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Получаем количество из поля ввода
|
// Функция обновления подсказки количества для исходящего товара
|
||||||
var quantityInput = document.getElementById('product-quantity');
|
function updateOutputQuantityHint() {
|
||||||
var quantity = parseFloat(quantityInput.value);
|
var qtyInput = document.getElementById('output-quantity');
|
||||||
|
var hint = document.getElementById('output-quantity-hint');
|
||||||
|
var totalInputEl = document.getElementById('total-input-quantity');
|
||||||
|
var totalOutputEl = document.getElementById('total-output-quantity');
|
||||||
|
|
||||||
|
if (!qtyInput || !hint || !totalInputEl || !totalOutputEl) return;
|
||||||
|
|
||||||
|
var qty = parseFloat(qtyInput.value) || 0;
|
||||||
|
var totalInput = parseFloat(totalInputEl.textContent.replace(/\s/g, '').replace(',', '.'));
|
||||||
|
var totalOutput = parseFloat(totalOutputEl.textContent.replace(/\s/g, '').replace(',', '.'));
|
||||||
|
var newTotalOutput = totalOutput + qty;
|
||||||
|
|
||||||
|
if (qty > 0 && totalInput > 0) {
|
||||||
|
if (newTotalOutput > totalInput) {
|
||||||
|
var maxAllowed = totalInput - totalOutput;
|
||||||
|
hint.textContent = 'Максимум: ' + maxAllowed.toFixed(3) + ' (превышение!)';
|
||||||
|
hint.className = 'form-text text-danger';
|
||||||
|
hint.style.display = 'block';
|
||||||
|
} else if (newTotalOutput === totalInput) {
|
||||||
|
hint.textContent = 'Количества будут равны ✓';
|
||||||
|
hint.className = 'form-text text-success';
|
||||||
|
hint.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
var remaining = totalInput - newTotalOutput;
|
||||||
|
hint.textContent = 'Остаток после добавления: ' + remaining.toFixed(3);
|
||||||
|
hint.className = 'form-text text-info';
|
||||||
|
hint.style.display = 'block';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hint.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== Кнопка "Добавить входящий товар" ==========
|
||||||
|
var confirmAddInputBtn = document.getElementById('confirm-add-input-btn');
|
||||||
|
if (confirmAddInputBtn) {
|
||||||
|
confirmAddInputBtn.addEventListener('click', function() {
|
||||||
|
if (!selectedInputProduct) {
|
||||||
|
alert('Выберите товар для добавления');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var qtyInput = document.getElementById('input-quantity');
|
||||||
|
var quantity = parseFloat(qtyInput.value);
|
||||||
|
|
||||||
// Валидация количества
|
|
||||||
if (!quantity || quantity <= 0) {
|
if (!quantity || quantity <= 0) {
|
||||||
alert('Введите корректное количество (больше 0)');
|
alert('Введите корректное количество (больше 0)');
|
||||||
quantityInput.focus();
|
qtyInput.focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Заполняем скрытую форму
|
// Заполняем скрытую форму
|
||||||
var productSelect = document.getElementById('hidden-input-product');
|
var productSelect = document.getElementById('hidden-input-product');
|
||||||
productSelect.innerHTML = '<option value="' + selectedProduct.id + '">' +
|
productSelect.innerHTML = '<option value="' + selectedInputProduct.id + '">' +
|
||||||
selectedProduct.text + '</option>';
|
selectedInputProduct.text + '</option>';
|
||||||
productSelect.value = selectedProduct.id;
|
productSelect.value = selectedInputProduct.id;
|
||||||
|
|
||||||
// Устанавливаем количество
|
|
||||||
document.getElementById('hidden-input-quantity').value = quantity;
|
document.getElementById('hidden-input-quantity').value = quantity;
|
||||||
|
|
||||||
// Отправляем форму через AJAX
|
// Отправляем форму через AJAX
|
||||||
@@ -452,7 +494,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
// Перезагружаем страницу чтобы обновить список
|
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert('Ошибка: ' + data.error);
|
alert('Ошибка: ' + data.error);
|
||||||
@@ -463,31 +504,28 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
alert('Ошибка при добавлении товара');
|
alert('Ошибка при добавлении товара');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Очищаем выбор в пикере
|
// Очищаем выбор
|
||||||
if (picker) {
|
if (inputPicker) inputPicker.clearSelection();
|
||||||
picker.clearSelection();
|
selectedInputProduct = null;
|
||||||
}
|
qtyInput.value = 1;
|
||||||
selectedProduct = null;
|
|
||||||
document.getElementById('action-buttons').style.display = 'none';
|
|
||||||
// Сбрасываем количество на 1
|
|
||||||
document.getElementById('product-quantity').value = 1;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Кнопка "Добавить в исходящий товар"
|
// ========== Кнопка "Добавить исходящий товар" ==========
|
||||||
var addToOutputBtn = document.getElementById('add-to-output-btn');
|
var confirmAddOutputBtn = document.getElementById('confirm-add-output-btn');
|
||||||
if (addToOutputBtn) {
|
if (confirmAddOutputBtn) {
|
||||||
addToOutputBtn.addEventListener('click', function() {
|
confirmAddOutputBtn.addEventListener('click', function() {
|
||||||
if (!selectedProduct) return;
|
if (!selectedOutputProduct) {
|
||||||
|
alert('Выберите товар для добавления');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Получаем количество из поля ввода
|
var qtyInput = document.getElementById('output-quantity');
|
||||||
var quantityInput = document.getElementById('product-quantity');
|
var quantity = parseFloat(qtyInput.value);
|
||||||
var quantity = parseFloat(quantityInput.value);
|
|
||||||
|
|
||||||
// Валидация количества
|
|
||||||
if (!quantity || quantity <= 0) {
|
if (!quantity || quantity <= 0) {
|
||||||
alert('Введите корректное количество (больше 0)');
|
alert('Введите корректное количество (больше 0)');
|
||||||
quantityInput.focus();
|
qtyInput.focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,19 +543,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
'Вход: ' + totalInput + '\n' +
|
'Вход: ' + totalInput + '\n' +
|
||||||
'Выход (текущий): ' + totalOutput + '\n' +
|
'Выход (текущий): ' + totalOutput + '\n' +
|
||||||
'Максимально можно добавить: ' + maxAllowed.toFixed(3));
|
'Максимально можно добавить: ' + maxAllowed.toFixed(3));
|
||||||
quantityInput.focus();
|
qtyInput.focus();
|
||||||
quantityInput.select();
|
qtyInput.select();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Заполняем скрытую форму
|
// Заполняем скрытую форму
|
||||||
var productSelect = document.getElementById('hidden-output-product');
|
var productSelect = document.getElementById('hidden-output-product');
|
||||||
productSelect.innerHTML = '<option value="' + selectedProduct.id + '">' +
|
productSelect.innerHTML = '<option value="' + selectedOutputProduct.id + '">' +
|
||||||
selectedProduct.text + '</option>';
|
selectedOutputProduct.text + '</option>';
|
||||||
productSelect.value = selectedProduct.id;
|
productSelect.value = selectedOutputProduct.id;
|
||||||
|
|
||||||
// Устанавливаем количество
|
|
||||||
document.getElementById('hidden-output-quantity').value = quantity;
|
document.getElementById('hidden-output-quantity').value = quantity;
|
||||||
|
|
||||||
// Отправляем форму через AJAX
|
// Отправляем форму через AJAX
|
||||||
@@ -535,7 +571,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
// Перезагружаем страницу чтобы обновить список
|
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert('Ошибка: ' + data.error);
|
alert('Ошибка: ' + data.error);
|
||||||
@@ -546,14 +581,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
alert('Ошибка при добавлении товара');
|
alert('Ошибка при добавлении товара');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Очищаем выбор в пикере
|
// Очищаем выбор
|
||||||
if (picker) {
|
if (outputPicker) outputPicker.clearSelection();
|
||||||
picker.clearSelection();
|
selectedOutputProduct = null;
|
||||||
}
|
qtyInput.value = 1;
|
||||||
selectedProduct = null;
|
|
||||||
document.getElementById('action-buttons').style.display = 'none';
|
|
||||||
// Сбрасываем количество на 1
|
|
||||||
document.getElementById('product-quantity').value = 1;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user