Feature: компактные формы заказов, колонки Товар/Количество

Made-with: Cursor
This commit is contained in:
2026-02-26 05:17:21 +00:00
parent 90ae4bdc1b
commit 8af8e0b1f4
7 changed files with 221 additions and 26 deletions

View File

@@ -1,5 +1,32 @@
# История изменений ERP WaterSurf
## 2025-02-25 21:30 UTC Компактные формы документов (заказы клиента и поставщику)
**Проблема**: Формы создания/редактирования заказов занимали много места: каждое поле с новой строки на всю ширину.
**Решение**:
- Дата и Номер в одной строке: поле «Дата» — 10 символов (size=10), «Номер» — 15 символов (size=15, maxlength=15).
- Вид заказа и Организация в одной строке; Клиент и Автор в одной строке (заказ клиента). Аналогично заказ поставщику: Организация и Поставщик, Валюта и Курс, затем Автор.
- Таблица товаров: колонка «Товар» шире (35%, min 14rem), колонка «Количество» уже (поле ввода 4ch, до двухзначного числа).
- Добавлены виджеты для даты/номера в формах и для количества в formset (size=3, width: 4ch). Новый файл `static/css/theme-compact.css` с раскладкой строк формы (grid) и ширинами колонок таблицы.
**Изменения**: documents/forms.py (CustomerOrderForm, SupplierOrderForm, CustomerOrderItemForm, SupplierOrderItemForm, виджеты), documents/order_form.html и supplier_order_form.html (компактная разметка по полям), base.html (подключение theme-compact.css), новый theme-compact.css.
---
## 2025-02-25 21:00 UTC Принудительное применение темы (кэш, шрифт, кнопки)
**Проблема**: На скриншоте стили не применялись: кнопка бирюзовая вместо синей (#34AFE3), шрифт Geologica не отображался.
**Решение**:
- К ссылке theme.css добавлен параметр ?v=4 для сброса кэша браузера и прокси.
- Для кнопки .btn-ws-primary заданы явные цвета #34AFE3 и #00868F и усилена специфичность селекторов (body .btn.btn-ws-primary).
- На html и body задан font-family Geologica; для потомков — font-family: inherit, чтобы шрифт применялся ко всей странице.
**Изменения**: base.html (theme.css?v=4), theme.css (коммент v4, кнопки по HEX, наследование шрифта).
---
## 2025-02-25 20:45 UTC Логотип убран; дизайн строго по брендбуку
**Проблема**: Логотип в шапке был вставлен некорректно; визуально тема не отличалась от дефолтной — цвета и шрифты не применялись.

View File

@@ -11,9 +11,29 @@ from .models import (
)
from django.forms import inlineformset_factory
class CustomerOrderItemForm(forms.ModelForm):
class Meta:
model = CustomerOrderItem
fields = ("product", "price", "currency", "quantity")
widgets = {
"quantity": forms.NumberInput(attrs={"size": 3, "min": 0, "max": 99, "style": "width: 4ch"}),
}
class SupplierOrderItemForm(forms.ModelForm):
class Meta:
model = SupplierOrderItem
fields = ("product", "price", "currency", "quantity")
widgets = {
"quantity": forms.NumberInput(attrs={"size": 3, "min": 0, "max": 99, "style": "width: 4ch"}),
}
CustomerOrderItemFormSet = inlineformset_factory(
CustomerOrder,
CustomerOrderItem,
form=CustomerOrderItemForm,
fields=("product", "price", "currency", "quantity"),
extra=1,
can_delete=True,
@@ -22,6 +42,7 @@ CustomerOrderItemFormSet = inlineformset_factory(
SupplierOrderItemFormSet = inlineformset_factory(
SupplierOrder,
SupplierOrderItem,
form=SupplierOrderItemForm,
fields=("product", "price", "currency", "quantity"),
extra=1,
can_delete=True,
@@ -32,12 +53,20 @@ class CustomerOrderForm(forms.ModelForm):
class Meta:
model = CustomerOrder
fields = ("date", "number", "order_kind", "organization", "client", "author")
widgets = {
"date": forms.DateInput(attrs={"size": 10}),
"number": forms.TextInput(attrs={"size": 15, "maxlength": 15}),
}
class SupplierOrderForm(forms.ModelForm):
class Meta:
model = SupplierOrder
fields = ("date", "number", "organization", "supplier", "currency", "rate", "author")
widgets = {
"date": forms.DateInput(attrs={"size": 10}),
"number": forms.TextInput(attrs={"size": 15, "maxlength": 15}),
}
class CashInflowForm(forms.ModelForm):

View File

@@ -0,0 +1,47 @@
/* Компактная раскладка форм документов */
.ws-form-compact .ws-form-row {
display: grid;
gap: 1rem;
margin-bottom: 1rem;
align-items: start;
}
.ws-form-compact .ws-form-row-date-number {
grid-template-columns: 10ch 15ch;
}
.ws-form-compact .ws-form-row-date-number .ws-form-group input {
max-width: 100%;
width: 100%;
}
.ws-form-compact .ws-form-row-2 {
grid-template-columns: 1fr 1fr;
max-width: 520px;
}
.ws-form-compact .ws-form-row-2 .ws-form-group input,
.ws-form-compact .ws-form-row-2 .ws-form-group select {
max-width: 100%;
}
/* Таблица товаров: колонка Товар шире, Количество уже */
.ws-table-items .ws-col-product {
width: 35%;
min-width: 14rem;
}
.ws-table-items .ws-col-qty {
width: 5rem;
}
.ws-table-items td.ws-col-qty input,
.ws-table-items .ws-col-qty input {
width: 4ch !important;
max-width: 5rem;
box-sizing: border-box;
}
.ws-table-items .ws-col-del {
width: 4rem;
}

View File

@@ -1,4 +1,4 @@
/* ERP WaterSurf — только цвета и шрифты из брендбука */
/* ERP WaterSurf — тема v4 (только цвета и шрифты из брендбука) */
/* Основные цвета бренда (базовые): White, Gray 0104, Black */
:root {
@@ -37,6 +37,12 @@
html {
scroll-behavior: smooth;
background: var(--ws-bg-page) !important;
font-family: var(--ws-font) !important;
}
body,
body * {
font-family: inherit;
}
body {
@@ -230,10 +236,12 @@ body {
color: var(--ws-danger-dark) !important;
}
/* Кнопки — акцент только из дополнительных (Blue 02) */
.btn-ws-primary {
background: var(--ws-accent) !important;
color: var(--ws-black) !important;
/* Кнопки — акцент только из дополнительных (Blue 02), высокая специфичность */
.btn-ws-primary,
body .btn.btn-ws-primary,
a.btn.btn-ws-primary {
background: #34AFE3 !important;
color: #0A121D !important;
border: none !important;
border-radius: var(--ws-radius-sm);
padding: 0.5rem 1rem;
@@ -243,8 +251,10 @@ body {
letter-spacing: -0.01em;
}
.btn-ws-primary:hover {
background: var(--ws-accent-hover) !important;
.btn-ws-primary:hover,
body .btn.btn-ws-primary:hover,
a.btn.btn-ws-primary:hover {
background: #00868F !important;
color: var(--ws-white) !important;
}

View File

@@ -9,7 +9,8 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Geologica:wght@300;500;700&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="{% static 'css/theme.css' %}" rel="stylesheet">
<link href="{% static 'css/theme.css' %}?v=4" rel="stylesheet">
<link href="{% static 'css/theme-compact.css' %}?v=1" rel="stylesheet">
{% block extra_css %}{% endblock %}
</head>
<body>

View File

@@ -3,20 +3,57 @@
{% block content %}
<div class="ws-card">
<h2 class="ws-page-title">{% if object %}Редактировать{% else %}Создать{% endif %} {{ title }}</h2>
<form method="post" class="ws-form-card">
<form method="post" class="ws-form-card ws-form-compact">
{% csrf_token %}
{% for field in form %}
<div class="ws-form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% if field.errors %}<small class="ws-text-danger">{{ field.errors.0 }}</small>{% endif %}
<div class="ws-form-row ws-form-row-date-number">
<div class="ws-form-group ws-field-date">
<label for="{{ form.date.id_for_label }}">{{ form.date.label }}</label>
{{ form.date }}
{% if form.date.errors %}<small class="ws-text-danger">{{ form.date.errors.0 }}</small>{% endif %}
</div>
<div class="ws-form-group ws-field-number">
<label for="{{ form.number.id_for_label }}">{{ form.number.label }}</label>
{{ form.number }}
{% if form.number.errors %}<small class="ws-text-danger">{{ form.number.errors.0 }}</small>{% endif %}
</div>
</div>
<div class="ws-form-row ws-form-row-2">
<div class="ws-form-group">
<label for="{{ form.order_kind.id_for_label }}">{{ form.order_kind.label }}</label>
{{ form.order_kind }}
{% if form.order_kind.errors %}<small class="ws-text-danger">{{ form.order_kind.errors.0 }}</small>{% endif %}
</div>
<div class="ws-form-group">
<label for="{{ form.organization.id_for_label }}">{{ form.organization.label }}</label>
{{ form.organization }}
{% if form.organization.errors %}<small class="ws-text-danger">{{ form.organization.errors.0 }}</small>{% endif %}
</div>
</div>
<div class="ws-form-row ws-form-row-2">
<div class="ws-form-group">
<label for="{{ form.client.id_for_label }}">{{ form.client.label }}</label>
{{ form.client }}
{% 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 %}
</div>
</div>
{% endfor %}
<div class="ws-form-section">
<h3 class="ws-form-section-title">Товары</h3>
{{ formset.management_form }}
<div class="ws-table-wrap">
<table class="ws-table" id="order-items">
<table class="ws-table ws-table-items" id="order-items">
<colgroup>
<col class="ws-col-product">
<col>
<col>
<col class="ws-col-qty">
<col>
<col class="ws-col-del">
</colgroup>
<thead>
<tr>
<th>Товар</th>
@@ -33,7 +70,7 @@
<td>{{ f.id }}{{ f.product }}</td>
<td>{{ f.price }}</td>
<td>{{ f.currency }}</td>
<td>{{ f.quantity }}</td>
<td class="ws-col-qty">{{ f.quantity }}</td>
<td class="row-amount ws-num"></td>
<td>{% if f.DELETE %}{{ f.DELETE }}{% endif %}</td>
</tr>

View File

@@ -3,20 +3,64 @@
{% block content %}
<div class="ws-card">
<h2 class="ws-page-title">{% if object %}Редактировать{% else %}Создать{% endif %} {{ title }}</h2>
<form method="post" class="ws-form-card">
<form method="post" class="ws-form-card ws-form-compact">
{% csrf_token %}
{% for field in form %}
<div class="ws-form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% if field.errors %}<small class="ws-text-danger">{{ field.errors.0 }}</small>{% endif %}
<div class="ws-form-row ws-form-row-date-number">
<div class="ws-form-group ws-field-date">
<label for="{{ form.date.id_for_label }}">{{ form.date.label }}</label>
{{ form.date }}
{% if form.date.errors %}<small class="ws-text-danger">{{ form.date.errors.0 }}</small>{% endif %}
</div>
<div class="ws-form-group ws-field-number">
<label for="{{ form.number.id_for_label }}">{{ form.number.label }}</label>
{{ form.number }}
{% if form.number.errors %}<small class="ws-text-danger">{{ form.number.errors.0 }}</small>{% endif %}
</div>
</div>
<div class="ws-form-row ws-form-row-2">
<div class="ws-form-group">
<label for="{{ form.organization.id_for_label }}">{{ form.organization.label }}</label>
{{ form.organization }}
{% if form.organization.errors %}<small class="ws-text-danger">{{ form.organization.errors.0 }}</small>{% endif %}
</div>
<div class="ws-form-group">
<label for="{{ form.supplier.id_for_label }}">{{ form.supplier.label }}</label>
{{ form.supplier }}
{% if form.supplier.errors %}<small class="ws-text-danger">{{ form.supplier.errors.0 }}</small>{% endif %}
</div>
</div>
<div class="ws-form-row ws-form-row-2">
<div class="ws-form-group">
<label for="{{ form.currency.id_for_label }}">{{ form.currency.label }}</label>
{{ form.currency }}
{% if form.currency.errors %}<small class="ws-text-danger">{{ form.currency.errors.0 }}</small>{% endif %}
</div>
<div class="ws-form-group">
<label for="{{ form.rate.id_for_label }}">{{ form.rate.label }}</label>
{{ form.rate }}
{% if form.rate.errors %}<small class="ws-text-danger">{{ form.rate.errors.0 }}</small>{% endif %}
</div>
</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 %}
</div>
</div>
{% endfor %}
<div class="ws-form-section">
<h3 class="ws-form-section-title">Товары</h3>
{{ formset.management_form }}
<div class="ws-table-wrap">
<table class="ws-table" id="supplier-order-items">
<table class="ws-table ws-table-items" id="supplier-order-items">
<colgroup>
<col class="ws-col-product">
<col>
<col>
<col class="ws-col-qty">
<col>
<col class="ws-col-del">
</colgroup>
<thead>
<tr>
<th>Товар</th>
@@ -33,7 +77,7 @@
<td>{{ f.id }}{{ f.product }}</td>
<td>{{ f.price }}</td>
<td>{{ f.currency }}</td>
<td>{{ f.quantity }}</td>
<td class="ws-col-qty">{{ f.quantity }}</td>
<td class="row-amount ws-num"></td>
<td>{% if f.DELETE %}{{ f.DELETE }}{% endif %}</td>
</tr>