Feature: общая сумма заказа и автор только для отображения в формах заказов

Made-with: Cursor
This commit is contained in:
2026-02-26 16:17:24 +00:00
parent 824d5512c7
commit a183121721
6 changed files with 61 additions and 11 deletions

View File

@@ -1,5 +1,15 @@
# История изменений ERP WaterSurf
## 2025-02-26 16:15 UTC Форма заказа: общая сумма и автор только для отображения
**Задача**: В форме заказа показывать общую сумму заказа (сумма стоимостей строк табличной части) как надпись; поле «Автор» сделать надписью (не вводом), при создании документа подставлять текущего пользователя и сохранять, при открытии — только отображать.
**Решение**: В формах заказа клиента и заказа поставщику поле `author` убрано из полей формы; в представлениях при создании вызывается `set_author(form, request)` (подстановка сотрудника из профиля пользователя), при редактировании автор не меняется. В контекст передаётся `author_display` (для создания — сотрудник из профиля, для редактирования — автор документа). В шаблонах «Автор» выводится как надпись `{{ author_display|default:"—" }}`. Добавлена надпись «Общая сумма заказа» под таблицей товаров; значение обновляется в JS при изменении строк (сумма всех полей «Стоимость»).
**Изменения**: documents/forms.py (убрано author из CustomerOrderForm и SupplierOrderForm), documents/views.py (set_author только при создании, author_display в контексте), order_form.html и supplier_order_form.html (автор — надпись, блок общей суммы и JS), theme-compact.css (стили .ws-readonly, .ws-order-total-row).
---
## 2025-02-26 15:50 UTC Количество: только целое число 099
**Проблема**: Поле «Количество» допускало дробные значения и числа больше двухзначного; требовалось ограничить целыми числами от 0 до 99 везде.

View File

@@ -82,7 +82,7 @@ SupplierOrderItemFormSetUpdate = inlineformset_factory(
class CustomerOrderForm(forms.ModelForm):
class Meta:
model = CustomerOrder
fields = ("date", "number", "order_kind", "organization", "client", "author")
fields = ("date", "number", "order_kind", "organization", "client")
widgets = {
"date": forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d"),
"number": forms.TextInput(attrs={"size": 15, "maxlength": 15}),
@@ -92,7 +92,7 @@ class CustomerOrderForm(forms.ModelForm):
class SupplierOrderForm(forms.ModelForm):
class Meta:
model = SupplierOrder
fields = ("date", "number", "organization", "supplier", "customer_order", "currency", "rate", "author")
fields = ("date", "number", "organization", "supplier", "customer_order", "currency", "rate")
widgets = {
"date": forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d"),
"number": forms.TextInput(attrs={"size": 15, "maxlength": 15}),

View File

@@ -32,9 +32,11 @@ logger = logging.getLogger(__name__)
def set_author(form, request):
"""Подставить автора из профиля пользователя."""
"""Подставить автора из профиля пользователя при создании документа."""
if form.instance.pk:
return # при редактировании автора не меняем
author = get_author_employee(request.user)
if author and "author" in form.fields:
if author:
form.instance.author = author
@@ -54,6 +56,7 @@ class CustomerOrderCreate(LoginRequiredMixin, CreateView):
ctx = super().get_context_data(**kwargs)
ctx["formset"] = CustomerOrderItemFormSet(instance=self.object) if self.object and self.object.pk else CustomerOrderItemFormSet()
ctx["title"] = "Заказ клиента"
ctx["author_display"] = get_author_employee(self.request.user) if not self.object or not self.object.pk else self.object.author
return ctx
def form_valid(self, form):
@@ -85,6 +88,7 @@ class CustomerOrderUpdate(LoginRequiredMixin, UpdateView):
ctx = super().get_context_data(**kwargs)
ctx["formset"] = CustomerOrderItemFormSetUpdate(instance=self.object)
ctx["title"] = "Заказ клиента"
ctx["author_display"] = self.object.author
return ctx
def form_valid(self, form):
@@ -134,6 +138,7 @@ class SupplierOrderCreate(LoginRequiredMixin, CreateView):
ctx = super().get_context_data(**kwargs)
ctx["formset"] = SupplierOrderItemFormSet(instance=self.object) if self.object and self.object.pk else SupplierOrderItemFormSet()
ctx["title"] = "Заказ поставщику"
ctx["author_display"] = get_author_employee(self.request.user) if not self.object or not self.object.pk else self.object.author
return ctx
def form_valid(self, form):
@@ -165,6 +170,7 @@ class SupplierOrderUpdate(LoginRequiredMixin, UpdateView):
ctx = super().get_context_data(**kwargs)
ctx["formset"] = SupplierOrderItemFormSetUpdate(instance=self.object)
ctx["title"] = "Заказ поставщику"
ctx["author_display"] = self.object.author
return ctx
def form_valid(self, form):

View File

@@ -1,5 +1,17 @@
/* Компактные формы и таблицы документов — в стиле Material Design 3 */
/* Поле только для отображения (Автор и т.п.) */
.ws-form-compact .ws-readonly {
color: var(--ws-text-primary);
font-size: 14px;
}
.ws-form-compact .ws-order-total-row {
margin-top: 0.75rem;
margin-bottom: 0;
font-size: 14px;
}
/* Подпись слева от поля, в одну строку */
.ws-form-compact .ws-form-group {
display: flex;
@@ -38,7 +50,7 @@
.ws-form-compact .ws-form-row {
display: grid;
gap: 10px;
margin-bottom: 10px;
margin-bottom: 6px;
align-items: center;
}

View File

@@ -36,9 +36,8 @@
{% if form.client.errors %}<small class="ws-text-danger">{{ form.client.errors.0 }}</small>{% endif %}
</div>
<div class="ws-form-group">
<label for="{{ form.author.id_for_label }}">{{ form.author.label }}</label>
{{ form.author }}
{% if form.author.errors %}<small class="ws-text-danger">{{ form.author.errors.0 }}</small>{% endif %}
<label>Автор</label>
<span class="ws-readonly">{{ author_display|default:"—" }}</span>
</div>
</div>
<div class="ws-form-section">
@@ -78,6 +77,7 @@
</tbody>
</table>
</div>
<p class="ws-order-total-row"><strong>Общая сумма заказа:</strong> <span id="order-total-sum" class="ws-num"></span></p>
<div class="ws-btn-group" style="margin-top: 0.75rem;">
<button type="button" class="btn btn-ws-secondary" id="add-order-row">+ Добавить строку</button>
</div>
@@ -126,6 +126,17 @@
var el = row.querySelector('.row-amount');
if (el) el.textContent = formatNum(price * qty);
});
var totalEl = document.getElementById('order-total-sum');
if (totalEl) {
var sum = 0;
document.querySelectorAll('#order-items .item-row').forEach(function(row) {
var priceInput = row.querySelector('input[name$="-price"]');
var qty = parseQty(row.querySelector('input[name$="-quantity"]')?.value);
var price = parseNum(priceInput?.value);
sum += price * qty;
});
totalEl.textContent = formatNum(sum);
}
}
form.addEventListener('input', updateRowAmounts);

View File

@@ -50,9 +50,8 @@
</div>
<div class="ws-form-row ws-form-row-2">
<div class="ws-form-group">
<label for="{{ form.author.id_for_label }}">{{ form.author.label }}</label>
{{ form.author }}
{% if form.author.errors %}<small class="ws-text-danger">{{ form.author.errors.0 }}</small>{% endif %}
<label>Автор</label>
<span class="ws-readonly">{{ author_display|default:"—" }}</span>
</div>
</div>
<div class="ws-form-section">
@@ -92,6 +91,7 @@
</tbody>
</table>
</div>
<p class="ws-order-total-row"><strong>Общая сумма заказа:</strong> <span id="supplier-order-total-sum" class="ws-num"></span></p>
<div class="ws-btn-group" style="margin-top: 0.75rem;">
<button type="button" class="btn btn-ws-secondary" id="add-supplier-order-row">+ Добавить строку</button>
</div>
@@ -138,6 +138,17 @@
var el = row.querySelector('.row-amount');
if (el) el.textContent = formatNum(price * qty);
});
var totalEl = document.getElementById('supplier-order-total-sum');
if (totalEl) {
var sum = 0;
document.querySelectorAll('#supplier-order-items .item-row').forEach(function(row) {
var priceInput = row.querySelector('input[name$="-price"]');
var qty = parseQty(row.querySelector('input[name$="-quantity"]')?.value);
var price = parseNum(priceInput?.value);
sum += price * qty;
});
totalEl.textContent = formatNum(sum);
}
}
form.addEventListener('input', updateRowAmounts);