diff --git a/HISTORY.md b/HISTORY.md
index 4f1ad0e..30e4bc4 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,5 +1,15 @@
# История изменений ERP WaterSurf
+## 2025-02-26 17:25 UTC – Сортировка по всем колонкам в списках документов
+
+**Задача**: Во всех списках документов сделать сортировку по каждой колонке таблицы.
+
+**Решение**: Добавлен миксин **SortableListMixin**: читает GET-параметры `sort` и `order` (asc/desc), применяет order_by к queryset. Заголовки колонок — ссылки, активная колонка отмечена стрелкой ↑/↓. По умолчанию сортировка по дате по убыванию. Списки: заказы клиентов, заказы поставщику, поступления, перемещения, расходы денежных средств.
+
+**Изменения**: documents/views.py (SortableListMixin, sort_fields в ListView), шаблоны списков (ссылки в th), theme.css (ws-th-sort, ws-sort-arrow).
+
+---
+
## 2025-02-26 17:15 UTC – Иконки «Карандаш» и «Крестик» вместо надписей «Изменить» и «Удалить» в списках
**Задача**: В таблицах списков документов заменить текстовые ссылки «Изменить» и «Удалить» на иконки: карандаш (редактирование) и красный крестик (удаление).
diff --git a/app/documents/views.py b/app/documents/views.py
index 227ad45..3863826 100644
--- a/app/documents/views.py
+++ b/app/documents/views.py
@@ -32,6 +32,49 @@ from .services import next_number
logger = logging.getLogger(__name__)
+class SortableListMixin:
+ """Миксин для сортировки списка по GET-параметрам sort и order."""
+ sort_param = "sort"
+ order_param = "order"
+ sort_fields = {} # ключ (из GET) -> поле order_by, например "date", "-date" не нужен, order добавляется в get_queryset
+ default_sort = None # например "date"
+ default_order = "desc" # asc или desc
+
+ def get_sort_fields(self):
+ return self.sort_fields
+
+ def get_queryset(self):
+ qs = super().get_queryset()
+ fields = self.get_sort_fields()
+ if not fields:
+ return qs
+ sort_key = self.request.GET.get(self.sort_param)
+ order = (self.request.GET.get(self.order_param) or "").lower()
+ if order not in ("asc", "desc"):
+ order = self.default_order or "asc"
+ if sort_key and sort_key in fields:
+ order_by_field = fields[sort_key]
+ if order == "desc":
+ order_by_field = "-" + order_by_field
+ return qs.order_by(order_by_field)
+ if self.default_sort and self.default_sort in fields:
+ order_by_field = fields[self.default_sort]
+ if (self.default_order or "asc") == "desc":
+ order_by_field = "-" + order_by_field
+ return qs.order_by(order_by_field)
+ return qs
+
+ def get_context_data(self, **kwargs):
+ ctx = super().get_context_data(**kwargs)
+ ctx["sort_key"] = self.request.GET.get(self.sort_param) or self.default_sort
+ ctx["sort_order"] = (self.request.GET.get(self.order_param) or self.default_order or "asc").lower()
+ if ctx["sort_order"] not in ("asc", "desc"):
+ ctx["sort_order"] = "asc"
+ ctx["sort_param"] = self.sort_param
+ ctx["order_param"] = self.order_param
+ return ctx
+
+
def set_author(form, request):
"""Подставить автора при создании документа: из профиля пользователя (UserProfile.employee) или сотрудник по имени пользователя."""
if form.instance.pk:
@@ -56,10 +99,21 @@ def _get_author_for_request(request):
# --- Заказы клиентов ---
-class CustomerOrderList(LoginRequiredMixin, ListView):
+class CustomerOrderList(LoginRequiredMixin, SortableListMixin, ListView):
model = CustomerOrder
template_name = "documents/customer_order_list.html"
context_object_name = "object_list"
+ default_sort = "date"
+ default_order = "desc"
+ sort_fields = {
+ "date": "date",
+ "number": "number",
+ "status": "status__name",
+ "order_kind": "order_kind__name",
+ "organization": "organization__name",
+ "client": "client__name",
+ "total_amount": "total_amount",
+ }
class CustomerOrderCreate(LoginRequiredMixin, CreateView):
model = CustomerOrder
@@ -146,10 +200,20 @@ class CustomerOrderDetail(LoginRequiredMixin, DetailView):
# --- Заказы поставщику ---
-class SupplierOrderList(LoginRequiredMixin, ListView):
+class SupplierOrderList(LoginRequiredMixin, SortableListMixin, ListView):
model = SupplierOrder
template_name = "documents/supplier_order_list.html"
context_object_name = "object_list"
+ default_sort = "date"
+ default_order = "desc"
+ sort_fields = {
+ "date": "date",
+ "number": "number",
+ "organization": "organization__name",
+ "supplier": "supplier__name",
+ "total_in_currency": "total_in_currency",
+ "total_amount": "total_amount",
+ }
class SupplierOrderCreate(LoginRequiredMixin, CreateView):
model = SupplierOrder
@@ -217,10 +281,19 @@ class SupplierOrderDelete(LoginRequiredMixin, DeleteView):
# --- Поступление денежных средств ---
-class CashInflowList(LoginRequiredMixin, ListView):
+class CashInflowList(LoginRequiredMixin, SortableListMixin, ListView):
model = CashInflow
template_name = "documents/cash_inflow_list.html"
context_object_name = "object_list"
+ default_sort = "date"
+ default_order = "desc"
+ sort_fields = {
+ "date": "date",
+ "number": "number",
+ "recipient": "recipient__name",
+ "amount": "amount",
+ "customer_order": "customer_order__number",
+ }
class CashInflowCreate(LoginRequiredMixin, CreateView):
model = CashInflow
@@ -275,10 +348,19 @@ class CashInflowDelete(LoginRequiredMixin, DeleteView):
# --- Перемещение денежных средств ---
-class CashTransferList(LoginRequiredMixin, ListView):
+class CashTransferList(LoginRequiredMixin, SortableListMixin, ListView):
model = CashTransfer
template_name = "documents/cash_transfer_list.html"
context_object_name = "object_list"
+ default_sort = "date"
+ default_order = "desc"
+ sort_fields = {
+ "date": "date",
+ "number": "number",
+ "sender": "sender__name",
+ "recipient": "recipient__name",
+ "amount": "amount",
+ }
class CashTransferCreate(LoginRequiredMixin, CreateView):
model = CashTransfer
@@ -333,10 +415,19 @@ class CashTransferDelete(LoginRequiredMixin, DeleteView):
# --- Расход денежных средств ---
-class CashExpenseList(LoginRequiredMixin, ListView):
+class CashExpenseList(LoginRequiredMixin, SortableListMixin, ListView):
model = CashExpense
template_name = "documents/cash_expense_list.html"
context_object_name = "object_list"
+ default_sort = "date"
+ default_order = "desc"
+ sort_fields = {
+ "date": "date",
+ "number": "number",
+ "sender": "sender__name",
+ "amount": "amount",
+ "supplier_order": "supplier_order__number",
+ }
class CashExpenseCreate(LoginRequiredMixin, CreateView):
model = CashExpense
diff --git a/app/static/css/theme.css b/app/static/css/theme.css
index 5f90585..eef1660 100644
--- a/app/static/css/theme.css
+++ b/app/static/css/theme.css
@@ -231,6 +231,19 @@ body {
color: var(--ws-text-secondary);
}
+.ws-table th .ws-th-sort {
+ color: inherit;
+ text-decoration: none;
+}
+.ws-table th .ws-th-sort:hover {
+ color: var(--ws-accent);
+}
+.ws-table th .ws-sort-arrow {
+ margin-left: 2px;
+ font-weight: 700;
+ color: var(--ws-accent);
+}
+
.ws-table tbody tr:hover {
background: var(--ws-bg-hover);
}
diff --git a/app/templates/documents/cash_expense_list.html b/app/templates/documents/cash_expense_list.html
index 3100b65..e501298 100644
--- a/app/templates/documents/cash_expense_list.html
+++ b/app/templates/documents/cash_expense_list.html
@@ -11,11 +11,11 @@
- | Дата |
- Номер |
- Отправитель |
- Сумма |
- Заказ поставщику |
+ {% with key="date" %}Дата{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="number" %}Номер{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="sender" %}Отправитель{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="amount" %}Сумма{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="supplier_order" %}Заказ поставщику{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
|
diff --git a/app/templates/documents/cash_inflow_list.html b/app/templates/documents/cash_inflow_list.html
index 41bcd42..de9ed1e 100644
--- a/app/templates/documents/cash_inflow_list.html
+++ b/app/templates/documents/cash_inflow_list.html
@@ -11,11 +11,11 @@
- | Дата |
- Номер |
- Получатель |
- Сумма |
- Заказ клиента |
+ {% with key="date" %}Дата{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="number" %}Номер{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="recipient" %}Получатель{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="amount" %}Сумма{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="customer_order" %}Заказ клиента{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
|
diff --git a/app/templates/documents/cash_transfer_list.html b/app/templates/documents/cash_transfer_list.html
index fc2ca67..1097c7f 100644
--- a/app/templates/documents/cash_transfer_list.html
+++ b/app/templates/documents/cash_transfer_list.html
@@ -11,11 +11,11 @@
- | Дата |
- Номер |
- Отправитель |
- Получатель |
- Сумма |
+ {% with key="date" %}Дата{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="number" %}Номер{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="sender" %}Отправитель{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="recipient" %}Получатель{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="amount" %}Сумма{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
|
diff --git a/app/templates/documents/customer_order_list.html b/app/templates/documents/customer_order_list.html
index ba74c98..ab59aea 100644
--- a/app/templates/documents/customer_order_list.html
+++ b/app/templates/documents/customer_order_list.html
@@ -11,13 +11,13 @@
- | Дата |
- Номер |
- Статус заказа |
- Вид заказа |
- Организация |
- Клиент |
- Стоимость заказа |
+ {% with key="date" %}Дата{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="number" %}Номер{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="status" %}Статус заказа{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="order_kind" %}Вид заказа{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="organization" %}Организация{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="client" %}Клиент{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="total_amount" %}Стоимость заказа{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
|
diff --git a/app/templates/documents/supplier_order_list.html b/app/templates/documents/supplier_order_list.html
index b5bd5db..7d5e38b 100644
--- a/app/templates/documents/supplier_order_list.html
+++ b/app/templates/documents/supplier_order_list.html
@@ -11,12 +11,12 @@
- | Дата |
- Номер |
- Организация |
- Поставщик |
- Стоимость в валюте |
- Стоимость заказа |
+ {% with key="date" %}Дата{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="number" %}Номер{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="organization" %}Организация{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="supplier" %}Поставщик{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="total_in_currency" %}Стоимость в валюте{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
+ {% with key="total_amount" %}Стоимость заказа{% if sort_key == key %} {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}{% endif %}{% endwith %} |
|