diff --git a/HISTORY.md b/HISTORY.md
index 445fc0c..b81f528 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -2,6 +2,16 @@
# История изменений ERP WaterSurf
+## 2025-02-25 22:35 UTC – Табличная часть заказа: без скролла, цена/стоимость с разделителем
+
+**Проблема**: В таблице товаров появлялась горизонтальная полоса прокрутки; поле «Товар» слишком широкое; в полях «Цена» и «Стоимость» не было разделителя разрядов.
+
+**Решение**: Включён table-layout: fixed и заданы доли колонок в % (Товар 28%, Цена 14%, Валюта 12%, Количество 10%, Стоимость 14%, Удалить 5%), чтобы таблица помещалась без скролла. Поле «Товар» ограничено по ширине, select с overflow: hidden. Поля «Цена» и «Стоимость»: выравнивание по правому краю, узкое поле под 8-значную сумму. В JS добавлены formatNum/parseNum: разделитель тысяч (неразрывный пробел) и запятая как десятичный разделитель; при blur цена форматируется, при focus и при submit — пробелы снимаются перед отправкой. Стоимость в ячейке выводится с разделителем. Аналогичные правки в форме заказа поставщику.
+
+**Изменения**: order_form.html, supplier_order_form.html (colgroup, классы ячеек, JS форматирование), theme-compact.css (ширины колонок).
+
+---
+
## 2025-02-25 22:25 UTC – Временное отключение авторизации для отладки
**Проблема**: Для быстрой разработки и отладки нужно работать без входа.
diff --git a/app/static/css/theme-compact.css b/app/static/css/theme-compact.css
index 42c1eb7..b1782bd 100644
--- a/app/static/css/theme-compact.css
+++ b/app/static/css/theme-compact.css
@@ -84,14 +84,54 @@
max-width: 100%;
}
-/* Таблица товаров: колонка Товар шире, Количество уже */
+/* Таблица товаров: без горизонтального скролла, компактные колонки */
+.ws-table-wrap {
+ overflow-x: auto;
+ max-width: 100%;
+}
+
+.ws-table-items {
+ table-layout: fixed;
+ width: 100%;
+ min-width: 0;
+}
+
+/* Товар: ограниченная ширина, длинное название обрезается */
.ws-table-items .ws-col-product {
- width: 35%;
- min-width: 14rem;
+ width: 28%;
+ overflow: hidden;
+}
+
+.ws-table-items .ws-col-product select {
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+/* Цена: поле под 8 знаков, значение справа */
+.ws-table-items .ws-col-price {
+ width: 14%;
+}
+
+.ws-table-items .ws-col-price input {
+ width: 100% !important;
+ max-width: 100%;
+ text-align: right;
+ font-variant-numeric: tabular-nums;
+}
+
+/* Валюта */
+.ws-table-items .ws-col-currency {
+ width: 12%;
+}
+
+.ws-table-items .ws-col-currency select {
+ max-width: 100%;
}
.ws-table-items .ws-col-qty {
- width: 5rem;
+ width: 10%;
+ min-width: 4rem;
}
.ws-table-items td.ws-col-qty input,
@@ -101,6 +141,14 @@
box-sizing: border-box;
}
-.ws-table-items .ws-col-del {
- width: 4rem;
+/* Стоимость: справа, разделитель в JS */
+.ws-table-items .ws-col-cost {
+ width: 14%;
+ text-align: right;
+ font-variant-numeric: tabular-nums;
+}
+
+.ws-table-items .ws-col-del {
+ width: 5%;
+ min-width: 3rem;
}
diff --git a/app/templates/documents/order_form.html b/app/templates/documents/order_form.html
index 1eddbfd..7debfdb 100644
--- a/app/templates/documents/order_form.html
+++ b/app/templates/documents/order_form.html
@@ -48,10 +48,10 @@
-
-
+
+
-
+
@@ -67,11 +67,11 @@
{% for f in formset %}
- | {{ f.id }}{{ f.product }} |
- {{ f.price }} |
- {{ f.currency }} |
+ {{ f.id }}{{ f.product }} |
+ {{ f.price }} |
+ {{ f.currency }} |
{{ f.quantity }} |
- — |
+ — |
{% if f.DELETE %}{{ f.DELETE }}{% endif %} |
{% endfor %}
@@ -101,17 +101,52 @@
return name.replace(/-TOTAL_FORMS$/, '');
}
+ function formatNum(x) {
+ var n = parseFloat(x);
+ if (isNaN(n)) return '—';
+ var parts = n.toFixed(2).split('.');
+ parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '\u202f');
+ return parts.join(',');
+ }
+ function parseNum(s) {
+ return parseFloat(String(s).replace(/\s/g, '').replace(',', '.')) || 0;
+ }
+
function updateRowAmounts() {
document.querySelectorAll('#order-items .item-row').forEach(function(row) {
- var price = parseFloat(row.querySelector('input[name$="-price"]')?.value) || 0;
- var qty = parseFloat(row.querySelector('input[name$="-quantity"]')?.value) || 0;
+ var priceInput = row.querySelector('input[name$="-price"]');
+ var qty = parseNum(row.querySelector('input[name$="-quantity"]')?.value);
+ var price = parseNum(priceInput?.value);
var el = row.querySelector('.row-amount');
- if (el) el.textContent = (price * qty).toFixed(2);
+ if (el) el.textContent = formatNum(price * qty);
});
}
form.addEventListener('input', updateRowAmounts);
+ form.querySelectorAll('#order-items input[name$="-price"]').forEach(function(input) {
+ input.addEventListener('blur', function() {
+ var v = parseNum(this.value);
+ if (!isNaN(v) && this.value.trim() !== '') this.value = formatNum(v).replace(',', '.');
+ });
+ input.addEventListener('focus', function() {
+ this.value = String(this.value).replace(/\s/g, '').replace(',', '.');
+ });
+ });
+ form.addEventListener('submit', function() {
+ form.querySelectorAll('input[name$="-price"]').forEach(function(input) {
+ input.value = String(input.value).replace(/\s/g, '').replace(',', '.');
+ });
+ });
+
+ document.querySelectorAll('#order-items input[name$="-price"]').forEach(function(input) {
+ if (input.value && input.value.trim() !== '') {
+ var v = parseNum(input.value);
+ input.value = formatNum(v).replace(',', '.');
+ }
+ });
+ updateRowAmounts();
+
document.getElementById('add-order-row').addEventListener('click', function() {
var rows = tbody.querySelectorAll('.item-row');
var lastRow = rows[rows.length - 1];
@@ -135,6 +170,13 @@
});
var amountCell = clone.querySelector('.row-amount');
if (amountCell) amountCell.textContent = '—';
+ clone.querySelector('input[name$="-price"]')?.addEventListener('blur', function() {
+ var v = parseNum(this.value);
+ if (!isNaN(v) && this.value.trim() !== '') this.value = formatNum(v).replace(',', '.');
+ });
+ clone.querySelector('input[name$="-price"]')?.addEventListener('focus', function() {
+ this.value = String(this.value).replace(/\s/g, '').replace(',', '.');
+ });
tbody.appendChild(clone);
totalInput.value = nextIndex + 1;
});
diff --git a/app/templates/documents/supplier_order_form.html b/app/templates/documents/supplier_order_form.html
index 5d0e6a8..0980a4a 100644
--- a/app/templates/documents/supplier_order_form.html
+++ b/app/templates/documents/supplier_order_form.html
@@ -55,10 +55,10 @@
-
-
+
+
-
+
@@ -74,11 +74,11 @@
{% for f in formset %}
- | {{ f.id }}{{ f.product }} |
- {{ f.price }} |
- {{ f.currency }} |
+ {{ f.id }}{{ f.product }} |
+ {{ f.price }} |
+ {{ f.currency }} |
{{ f.quantity }} |
- — |
+ — |
{% if f.DELETE %}{{ f.DELETE }}{% endif %} |
{% endfor %}
@@ -107,17 +107,52 @@
return name.replace(/-TOTAL_FORMS$/, '');
}
+ function formatNum(x) {
+ var n = parseFloat(x);
+ if (isNaN(n)) return '—';
+ var parts = n.toFixed(2).split('.');
+ parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '\u202f');
+ return parts.join(',');
+ }
+ function parseNum(s) {
+ return parseFloat(String(s).replace(/\s/g, '').replace(',', '.')) || 0;
+ }
+
function updateRowAmounts() {
document.querySelectorAll('#supplier-order-items .item-row').forEach(function(row) {
- var price = parseFloat(row.querySelector('input[name$="-price"]')?.value) || 0;
- var qty = parseFloat(row.querySelector('input[name$="-quantity"]')?.value) || 0;
+ var priceInput = row.querySelector('input[name$="-price"]');
+ var qty = parseNum(row.querySelector('input[name$="-quantity"]')?.value);
+ var price = parseNum(priceInput?.value);
var el = row.querySelector('.row-amount');
- if (el) el.textContent = (price * qty).toFixed(2);
+ if (el) el.textContent = formatNum(price * qty);
});
}
form.addEventListener('input', updateRowAmounts);
+ form.querySelectorAll('#supplier-order-items input[name$="-price"]').forEach(function(input) {
+ input.addEventListener('blur', function() {
+ var v = parseNum(this.value);
+ if (!isNaN(v) && this.value.trim() !== '') this.value = formatNum(v).replace(',', '.');
+ });
+ input.addEventListener('focus', function() {
+ this.value = String(this.value).replace(/\s/g, '').replace(',', '.');
+ });
+ });
+ form.addEventListener('submit', function() {
+ form.querySelectorAll('input[name$="-price"]').forEach(function(input) {
+ input.value = String(input.value).replace(/\s/g, '').replace(',', '.');
+ });
+ });
+
+ document.querySelectorAll('#supplier-order-items input[name$="-price"]').forEach(function(input) {
+ if (input.value && input.value.trim() !== '') {
+ var v = parseNum(input.value);
+ input.value = formatNum(v).replace(',', '.');
+ }
+ });
+ updateRowAmounts();
+
document.getElementById('add-supplier-order-row').addEventListener('click', function() {
var rows = tbody.querySelectorAll('.item-row');
var lastRow = rows[rows.length - 1];
@@ -141,6 +176,13 @@
});
var amountCell = clone.querySelector('.row-amount');
if (amountCell) amountCell.textContent = '—';
+ clone.querySelector('input[name$="-price"]')?.addEventListener('blur', function() {
+ var v = parseNum(this.value);
+ if (!isNaN(v) && this.value.trim() !== '') this.value = formatNum(v).replace(',', '.');
+ });
+ clone.querySelector('input[name$="-price"]')?.addEventListener('focus', function() {
+ this.value = String(this.value).replace(/\s/g, '').replace(',', '.');
+ });
tbody.appendChild(clone);
totalInput.value = nextIndex + 1;
});