From 5b18a68e7d4e1ed0cbc08145b2cb36f4aea4b821 Mon Sep 17 00:00:00 2001 From: cursor-agent Date: Thu, 26 Feb 2026 16:58:27 +0000 Subject: [PATCH] =?UTF-8?q?Feature:=20=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=BF=D0=BE=20=D0=B2=D1=81=D0=B5?= =?UTF-8?q?=D0=BC=20=D0=BA=D0=BE=D0=BB=D0=BE=D0=BD=D0=BA=D0=B0=D0=BC=20?= =?UTF-8?q?=D0=B2=D0=BE=20=D0=B2=D1=81=D0=B5=D1=85=20=D1=81=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=D1=85=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- HISTORY.md | 10 ++ app/documents/views.py | 101 +++++++++++++++++- app/static/css/theme.css | 13 +++ .../documents/cash_expense_list.html | 10 +- app/templates/documents/cash_inflow_list.html | 10 +- .../documents/cash_transfer_list.html | 10 +- .../documents/customer_order_list.html | 14 +-- .../documents/supplier_order_list.html | 12 +-- 8 files changed, 147 insertions(+), 33 deletions(-) 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 @@ - - - - - + + + + + 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="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_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="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/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="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/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="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 %}
- - - - - - + + + + + +
ДатаНомерОрганизацияПоставщикСтоимость в валютеСтоимость заказа{% 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 %}