Improved incoming form validation: require cost price, better error highlighting

This commit is contained in:
2025-12-01 11:41:44 +03:00
parent 11c76ece53
commit cdaf43afbd

View File

@@ -158,6 +158,11 @@
font-size: 0.875rem;
margin-top: 0.25rem;
}
.is-invalid {
border-color: #dc3545 !important;
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important;
}
</style>
<script>
@@ -277,9 +282,18 @@ document.addEventListener('DOMContentLoaded', function() {
const priceInput = row.querySelector('.price-input');
const removeBtn = row.querySelector('.btn-remove-row');
$(productSelect).on('change', updateTotals);
quantityInput.addEventListener('input', updateTotals);
priceInput.addEventListener('input', updateTotals);
$(productSelect).on('change', function() {
productSelect.classList.remove('is-invalid');
updateTotals();
});
quantityInput.addEventListener('input', function() {
quantityInput.classList.remove('is-invalid');
updateTotals();
});
priceInput.addEventListener('input', function() {
priceInput.classList.remove('is-invalid');
updateTotals();
});
removeBtn.addEventListener('click', function(e) {
e.preventDefault();
$(productSelect).select2('destroy');
@@ -304,14 +318,15 @@ document.addEventListener('DOMContentLoaded', function() {
const productId = productSelect.value;
const quantity = parseFloat(quantityInput.value) || 0;
const price = parseFloat(priceInput.value) || 0;
const priceValue = priceInput.value.trim();
const price = parseFloat(priceValue) || 0;
const sum = quantity * price;
// Обновляем дисплей суммы
sumDisplay.value = sum.toFixed(2);
// Только считаем если данные заполнены
if (productId && quantity > 0 && price >= 0) {
// Только считаем если данные ПОЛНОСТЬЮ заполнены (включая цену!)
if (productId && quantity > 0 && priceValue !== '' && price >= 0) {
totalItems++;
totalQuantity += quantity;
totalSum += sum;
@@ -462,16 +477,10 @@ document.addEventListener('DOMContentLoaded', function() {
}
}
const productsData = JSON.parse(productsJsonInput.value);
if (productsData.length === 0) {
e.preventDefault();
alert('Пожалуйста, добавьте хотя бы один товар.');
return false;
}
// Проверяем что все товары корректно заполнены
// СНАЧАЛА проверяем корректность заполнения товаров
let hasErrors = false;
let hasAnyProduct = false;
productsBody.querySelectorAll('tr').forEach(row => {
const productSelect = row.querySelector('.product-select');
const quantityInput = row.querySelector('.quantity-input');
@@ -483,33 +492,51 @@ document.addEventListener('DOMContentLoaded', function() {
let rowHasError = false;
// Проверка товара
if (!productSelect.value) {
productError.textContent = 'Выберите товар';
productError.style.display = 'block';
productSelect.classList.add('is-invalid');
hasErrors = true;
rowHasError = true;
} else {
productError.style.display = 'none';
productSelect.classList.remove('is-invalid');
hasAnyProduct = true;
}
// Проверка количества
const quantity = parseFloat(quantityInput.value) || 0;
if (quantity <= 0) {
quantityError.textContent = 'Количество должно быть > 0';
quantityError.style.display = 'block';
quantityInput.classList.add('is-invalid');
hasErrors = true;
rowHasError = true;
} else {
quantityError.style.display = 'none';
quantityInput.classList.remove('is-invalid');
}
const price = parseFloat(priceInput.value) || 0;
if (price < 0) {
// Проверка цены - ОБЯЗАТЕЛЬНОЕ ПОЛЕ!
const priceValue = priceInput.value.trim();
const price = parseFloat(priceValue);
if (priceValue === '' || isNaN(price)) {
priceError.textContent = 'Укажите цену закупки';
priceError.style.display = 'block';
priceInput.classList.add('is-invalid');
hasErrors = true;
rowHasError = true;
} else if (price < 0) {
priceError.textContent = 'Цена не может быть отрицательной';
priceError.style.display = 'block';
priceInput.classList.add('is-invalid');
hasErrors = true;
rowHasError = true;
} else {
priceError.style.display = 'none';
priceInput.classList.remove('is-invalid');
}
if (rowHasError) {
@@ -519,9 +546,32 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
// Проверка что есть хотя бы один товар
if (!hasAnyProduct) {
e.preventDefault();
alert('Пожалуйста, добавьте хотя бы один товар.');
return false;
}
// Если есть ошибки валидации - показываем
if (hasErrors) {
e.preventDefault();
alert('Пожалуйста, исправьте ошибки в форме.');
alert('⚠️ Пожалуйста, исправьте ошибки в форме (см. подсвеченные поля).');
// Прокручиваем к первой ошибке
const firstError = productsBody.querySelector('.is-invalid');
if (firstError) {
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
firstError.focus();
}
return false;
}
// Проверяем финальные данные
const productsData = JSON.parse(productsJsonInput.value);
if (productsData.length === 0) {
e.preventDefault();
alert('⚠️ Необходимо корректно заполнить все поля товаров.');
return false;
}
});