diff --git a/HISTORY.md b/HISTORY.md index f07491e..edc1bf6 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,15 @@ # История изменений ERP WaterSurf +## 2025-02-26 17:00 UTC – Справочник «Статусы заказов» и поле «Статус заказа» в заказе клиента + +**Задача**: Справочник статусов заказов, поле в заказе клиента, отображение в списке с цветовой подложкой («Выполнено» — зелёная, «В работе» — песочная). + +**Решение**: В справочниках добавлена модель **OrderStatus** (название). Начальные данные: «В работе», «Выполнено». В модель **CustomerOrder** добавлено поле **status** (FK на OrderStatus, null/blank). В форме заказа клиента добавлено поле выбора статуса (по умолчанию при создании — «В работе»). В списке заказов клиентов добавлена колонка «Статус заказа» после «Номер»; значение выводится бейджем с классом `ws-status-done` (зелёная подложка) для «Выполнено» и `ws-status-inprogress` (песочная подложка) для «В работе». + +**Изменения**: references/models.py (OrderStatus), references/admin.py, references/views.py, references/urls.py, base.html (меню «Статусы заказов»), references/migrations 0002, 0003 (данные); documents/models.py (status у CustomerOrder), documents/migrations 0004; documents/forms.py, views.py (форма, get_initial); order_form.html (поле статус); customer_order_list.html (колонка и бейдж); theme.css (ws-status-badge, ws-status-done, ws-status-inprogress). + +--- + ## 2025-02-26 16:50 UTC – Общая сумма над блоком Товары; клик по строке списка открывает документ **Изменения**: Блок «Общая сумма заказа» перенесён над всей табличной частью «Товары» — сразу под полями Клиент/Автор. В списках заказов клиентов и заказов поставщику документ открывается при клике по строке (в обоих случаях — форма редактирования); клик по ссылкам «Изменить»/«Удалить» не переходит по строке. Добавлены класс строки `ws-row-clickable`, атрибут `data-href` и скрипт перехода. diff --git a/app/documents/forms.py b/app/documents/forms.py index 847e115..782c52d 100644 --- a/app/documents/forms.py +++ b/app/documents/forms.py @@ -82,7 +82,7 @@ SupplierOrderItemFormSetUpdate = inlineformset_factory( class CustomerOrderForm(forms.ModelForm): class Meta: model = CustomerOrder - fields = ("date", "number", "order_kind", "organization", "client") + fields = ("date", "number", "order_kind", "organization", "client", "status") widgets = { "date": forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d"), "number": forms.TextInput(attrs={"size": 15, "maxlength": 15}), diff --git a/app/documents/migrations/0004_customer_order_status.py b/app/documents/migrations/0004_customer_order_status.py new file mode 100644 index 0000000..b76a07c --- /dev/null +++ b/app/documents/migrations/0004_customer_order_status.py @@ -0,0 +1,20 @@ +# Generated by Django 5.2.11 on 2026-02-26 16:42 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('documents', '0003_quantity_integer_0_99'), + ('references', '0002_order_status'), + ] + + operations = [ + migrations.AddField( + model_name='customerorder', + name='status', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='customer_orders', to='references.orderstatus', verbose_name='Статус заказа'), + ), + ] diff --git a/app/documents/models.py b/app/documents/models.py index 93a98a7..3a22a08 100644 --- a/app/documents/models.py +++ b/app/documents/models.py @@ -4,7 +4,7 @@ from decimal import Decimal from django.db import models from django.db.models import Sum from django.core.validators import MinValueValidator, MaxValueValidator -from references.models import Currency, OrderKind, Client, Organization, Supplier, Employee, CashAccount, Product +from references.models import Currency, OrderKind, Client, Organization, Supplier, Employee, CashAccount, Product, OrderStatus class CustomerOrder(models.Model): date = models.DateField("Дата") @@ -12,6 +12,14 @@ class CustomerOrder(models.Model): order_kind = models.ForeignKey(OrderKind, on_delete=models.PROTECT, verbose_name="Вид заказа") organization = models.ForeignKey(Organization, on_delete=models.PROTECT, verbose_name="Организация") client = models.ForeignKey(Client, on_delete=models.PROTECT, verbose_name="Клиент") + status = models.ForeignKey( + OrderStatus, + on_delete=models.PROTECT, + verbose_name="Статус заказа", + null=True, + blank=True, + related_name="customer_orders", + ) total_amount = models.DecimalField("Стоимость заказа", max_digits=18, decimal_places=2, default=Decimal("0"), editable=False) author = models.ForeignKey(Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Автор", related_name="customer_orders") class Meta: diff --git a/app/documents/views.py b/app/documents/views.py index ae6f329..227ad45 100644 --- a/app/documents/views.py +++ b/app/documents/views.py @@ -8,7 +8,7 @@ from django.contrib import messages from django.http import HttpResponseRedirect from users.utils import get_author_employee -from references.models import Employee +from references.models import Employee, OrderStatus from .models import ( CustomerOrder, SupplierOrder, @@ -67,6 +67,14 @@ class CustomerOrderCreate(LoginRequiredMixin, CreateView): template_name = "documents/order_form.html" success_url = reverse_lazy("documents:customer_order_list") + def get_initial(self): + initial = super().get_initial() + if not initial.get("status"): + in_progress = OrderStatus.objects.filter(name="В работе").first() + if in_progress: + initial["status"] = in_progress + return initial + def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx["formset"] = CustomerOrderItemFormSet(instance=self.object) if self.object and self.object.pk else CustomerOrderItemFormSet() diff --git a/app/references/admin.py b/app/references/admin.py index 8e9ed2a..2419835 100644 --- a/app/references/admin.py +++ b/app/references/admin.py @@ -8,6 +8,7 @@ from .models import ( Employee, CashAccount, Product, + OrderStatus, ) admin.site.register(Currency) @@ -18,3 +19,4 @@ admin.site.register(Supplier) admin.site.register(Employee) admin.site.register(CashAccount) admin.site.register(Product) +admin.site.register(OrderStatus) diff --git a/app/references/migrations/0002_order_status.py b/app/references/migrations/0002_order_status.py new file mode 100644 index 0000000..abf28e3 --- /dev/null +++ b/app/references/migrations/0002_order_status.py @@ -0,0 +1,24 @@ +# Generated by Django 5.2.11 on 2026-02-26 16:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('references', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='OrderStatus', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Название')), + ], + options={ + 'verbose_name': 'Статус заказа', + 'verbose_name_plural': 'Статусы заказов', + }, + ), + ] diff --git a/app/references/migrations/0003_orderstatus_data.py b/app/references/migrations/0003_orderstatus_data.py new file mode 100644 index 0000000..5c24b35 --- /dev/null +++ b/app/references/migrations/0003_orderstatus_data.py @@ -0,0 +1,24 @@ +# Data migration: начальные статусы заказов + +from django.db import migrations + + +def create_default_statuses(apps, schema_editor): + OrderStatus = apps.get_model("references", "OrderStatus") + for name in ["В работе", "Выполнено"]: + OrderStatus.objects.get_or_create(name=name) + + +def noop(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ("references", "0002_order_status"), + ] + + operations = [ + migrations.RunPython(create_default_statuses, noop), + ] diff --git a/app/references/models.py b/app/references/models.py index c139790..79c0e30 100644 --- a/app/references/models.py +++ b/app/references/models.py @@ -99,3 +99,15 @@ class Product(models.Model): def __str__(self): return self.name + + +class OrderStatus(models.Model): + """Статусы заказов.""" + name = models.CharField("Название", max_length=100) + + class Meta: + verbose_name = "Статус заказа" + verbose_name_plural = "Статусы заказов" + + def __str__(self): + return self.name diff --git a/app/references/urls.py b/app/references/urls.py index 0f886fb..0b98c6b 100644 --- a/app/references/urls.py +++ b/app/references/urls.py @@ -36,4 +36,8 @@ urlpatterns = [ path("products/create/", views.ProductCreate.as_view(), name="product_create"), path("products//edit/", views.ProductUpdate.as_view(), name="product_update"), path("products//delete/", views.ProductDelete.as_view(), name="product_delete"), + path("order-statuses/", views.OrderStatusList.as_view(), name="orderstatus_list"), + path("order-statuses/create/", views.OrderStatusCreate.as_view(), name="orderstatus_create"), + path("order-statuses//edit/", views.OrderStatusUpdate.as_view(), name="orderstatus_update"), + path("order-statuses//delete/", views.OrderStatusDelete.as_view(), name="orderstatus_delete"), ] diff --git a/app/references/views.py b/app/references/views.py index 8f35a5f..938b3d0 100644 --- a/app/references/views.py +++ b/app/references/views.py @@ -17,6 +17,7 @@ from .models import ( Employee, CashAccount, Product, + OrderStatus, ) logger = logging.getLogger(__name__) @@ -129,3 +130,10 @@ ProductList, ProductCreate, ProductUpdate, ProductDelete = _ref_view( "references:product_update", "references:product_delete", "Товары", "Товар", ) + +# Статусы заказов +OrderStatusList, OrderStatusCreate, OrderStatusUpdate, OrderStatusDelete = _ref_view( + OrderStatus, "references:orderstatus_list", "references:orderstatus_create", + "references:orderstatus_update", "references:orderstatus_delete", + "Статусы заказов", "Статус заказа", +) diff --git a/app/static/css/theme.css b/app/static/css/theme.css index 8cd08a8..dfe34dc 100644 --- a/app/static/css/theme.css +++ b/app/static/css/theme.css @@ -260,6 +260,23 @@ body { color: var(--ws-danger-dark); } +/* Бейдж статуса заказа в списке */ +.ws-status-badge { + display: inline-block; + padding: 2px 8px; + border-radius: 6px; + font-size: 0.875em; + font-weight: 500; +} +.ws-status-done { + background: rgba(76, 175, 80, 0.35); + color: #a5d6a7; +} +.ws-status-inprogress { + background: rgba(255, 193, 7, 0.35); + color: #ffe082; +} + .ws-table .ws-num { text-align: right; font-variant-numeric: tabular-nums; diff --git a/app/templates/base.html b/app/templates/base.html index fe2b6b3..b7892e9 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -34,6 +34,7 @@
  • Сотрудники
  • Счета денежных средств
  • Товары
  • +
  • Статусы заказов