diff options
| author | spl3g <spleefer6@gmail.com> | 2026-06-01 21:23:12 +0300 |
|---|---|---|
| committer | spl3g <spleefer6@gmail.com> | 2026-06-01 21:23:12 +0300 |
| commit | c0fe3e1db70bdba85cf382624817d8d8ea9d6f85 (patch) | |
| tree | 74065ec21768d63f6cc537bdce72b63ddf513b32 /core | |
| parent | 386e9defbea931ebc5139a5db5c74e6f44bbbe02 (diff) | |
add orders list, update, create
Diffstat (limited to 'core')
| -rw-r--r-- | core/forms.py | 103 | ||||
| -rw-r--r-- | core/management/commands/import_data.py | 14 | ||||
| -rw-r--r-- | core/models.py | 27 | ||||
| -rw-r--r-- | core/templates/core/base.html | 2 | ||||
| -rw-r--r-- | core/templates/core/order_form.html | 20 | ||||
| -rw-r--r-- | core/templates/core/order_list.html | 32 | ||||
| -rw-r--r-- | core/templates/core/product_list.html | 5 | ||||
| -rw-r--r-- | core/views.py | 104 |
8 files changed, 293 insertions, 14 deletions
diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..1ade0fe --- /dev/null +++ b/core/forms.py @@ -0,0 +1,103 @@ +from django import forms +from django.forms import inlineformset_factory + +from .models import Product, Supplier, Order, OrderItem +from extra_views import InlineFormSetFactory + +class ProductForm(forms.ModelForm): + supplier_name = forms.CharField(label="Поставщик", required=True) + + class Meta: + model = Product + fields = [ + "article", + "name", + "unit", + "price", + "manufacturer", + "category", + "discount", + "quantity", + "description", + "photo", + ] + labels = { + "article": "Артикуль", + "name": "Название", + "unit": "Единица измерения", + "price": "Цена", + "manufacturer": "Производитель", + "category": "Категория", + "discount": "Скидка", + "quantity": "Колличество", + "description": "Описание", + "photo": "Фото", + } + widgets = { + "description": forms.Textarea(attrs={"rows": 3}) + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.instance.pk: + if self.instance.supplier: + self.fields["supplier_name"].initial = self.instance.supplier.name + + def save(self, commit=True): + supplier, _ = Supplier.objects.get_or_create( + name=self.cleaned_data["supplier_name"].strip() + ) + + instance = super().save(commit=False) + instance.supplier = supplier + + if commit: + instance.save() + + return instance + + + def clean_price(self): + price = self.cleaned_data.get("price") + if price < 0: + raise forms.ValidationError("Цена не может быть отрицательной") + return price + + def clean_quantity(self): + quantity = self.cleaned_data.get("quantity") + if quantity < 0: + raise forms.ValidationError("Колличество не может быть отрицательной") + return quantity + + +class OrderForm(forms.ModelForm): + class Meta: + model = Order + fields = [ + "delivery_date", + "pickup_point", + "pickup_code", + "status", + ] + + labels = { + "order_date": "Дата заказа", + "delivery_date": "Дата доставки", + "pickup_point": "Пункт выдачи", + "client_name": "Имя клиента", + "pickup_code": "Код выдачи", + "status": "Статус", + "items": "Продукты", + } + widgets = { + "delivery_date": forms.SelectDateWidget + } + +class OrderItemFormSet(InlineFormSetFactory): + model = OrderItem + fields = ["product", "count"] + factory_kwargs = { + "extra": 1, + "can_delete": True, + "labels": {"product": "Продукт", "count": "Количество"} + } diff --git a/core/management/commands/import_data.py b/core/management/commands/import_data.py index f78f27e..cf1d4b6 100644 --- a/core/management/commands/import_data.py +++ b/core/management/commands/import_data.py @@ -79,6 +79,8 @@ class Command(BaseCommand): else PickupPoint.objects.first() ) + status = "completed" if row.pop("status", None) == "Завершен" else "new" + user_obj = User.objects.get(full_name=row["client_name"]) order, created = Order.objects.get_or_create( id=row["id"], @@ -86,16 +88,14 @@ class Command(BaseCommand): user=user_obj, delivery_date=delivery_date, order_date=order_date, + status=status, defaults=row, ) if created and items_string: items = items_string.split(", ") for i in range(0, len(items), 2): art = items[i].strip() - try: - prod = Products.get(article=art) - OrderItem.objects.create( - order=order, product=prod, count=int(items[i+1]) - ) - except Exception: - pass + prod = Product.objects.get(article=art) + OrderItem.objects.create( + order=order, product=prod, count=int(items[i+1]) + ) diff --git a/core/models.py b/core/models.py index db8b4c1..ee673ff 100644 --- a/core/models.py +++ b/core/models.py @@ -1,5 +1,10 @@ from django.contrib.auth.models import AbstractUser from django.db import models +from PIL import Image +from io import BytesIO +from django.core.files.base import ContentFile +import os + class Role(models.Model): name = models.CharField(max_length=50, unique=True, verbose_name="Name of the role") @@ -43,8 +48,23 @@ class Product(models.Model): this = Product.objects.get(id=self.id) if this.photo and self.photo and this.photo != self.photo: this.photo.delete(save=False) + except Exception: pass + + if self.photo: + img = Image.open(self.photo) + img.thumbnail((300, 200)) + + output = BytesIO() + img.save(output, img.format) + output.seek(0) + self.photo.save( + os.path.basename(self.photo.name), + output, + save=False + ) + super().save(*args, **kwargs) @property @@ -56,12 +76,17 @@ class Product(models.Model): return self.name class Order(models.Model): + STATUS_CHOICES = ( + ("new", "Новый"), + ("completed", "Завершен"), + ) + order_date = models.DateTimeField(auto_now_add=True) delivery_date = models.DateTimeField() pickup_point = models.ForeignKey(PickupPoint, on_delete=models.CASCADE) client_name = models.CharField(max_length=255, null=True, blank=True) pickup_code = models.IntegerField() - status = models.CharField(max_length=50, default="Новый") + status = models.CharField(max_length=50, choices=STATUS_CHOICES, default="Новый") user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) class OrderItem(models.Model): diff --git a/core/templates/core/base.html b/core/templates/core/base.html index 56d9e17..74aba44 100644 --- a/core/templates/core/base.html +++ b/core/templates/core/base.html @@ -14,7 +14,7 @@ </div> <div class="header-right"> {% if user.is_authenticated %} - <span>{{ user.role.name }}</span> + <span>{{ user.role.name }}</span> | <strong>{{ user.full_name }}</strong> <form action="{% url 'logout' %}" method="post" style="display: inline;"> {% csrf_token %} diff --git a/core/templates/core/order_form.html b/core/templates/core/order_form.html new file mode 100644 index 0000000..b46b593 --- /dev/null +++ b/core/templates/core/order_form.html @@ -0,0 +1,20 @@ +{% extends 'core/base.html' %} + +{% block title %}{% if is_edit %}Редактирование{% else %}Добавление{% endif %} заказа - ОООБувь{% endblock %} + +{% block h1 %}{% if is_edit %}Редактирование{% else %}Добавление{% endif %} заказа{% endblock %} + +{% block content %} +<div> + <form method="post" enctype="multipart/form-data"> + {% csrf_token %} + {{ form }} + + <h3>Товары в заказе</h3> + {% for formset in inlines %} + {{ formset }} + {% endfor %} + <button type="submit" class="btn">Сохранить</button> + </form> +</div> +{% endblock %} diff --git a/core/templates/core/order_list.html b/core/templates/core/order_list.html new file mode 100644 index 0000000..8fe80bd --- /dev/null +++ b/core/templates/core/order_list.html @@ -0,0 +1,32 @@ +{% extends 'core/base.html' %} + +{% block title %}Заказы - ООООБУВЬ {% endblock %} + +{% block h1 %}Заказы{% endblock %} + +{% block content %} +{% if user.role.name == "Администратор" or user.role.name == "Менеджер" %} +<div> + <a href="{% url 'order_create' %}" class="btn">Добавить заказ</a> +</div> +{% endif %} +<div> + {% for order in orders %} + <div class="product-card"> + <div class="details"> + <strong> + {% for orderitem in order.items.all %} + {{orderitem.product.article}} + {% endfor %} + </strong><br/> + Пункт выдачи: {{ order.pickup_point.address }}<br/> + Статус: {{ order.status }}<br/> + {% if user.role.name == "Администратор" or user.role.name == "Менеджер" %} + <a href="{% url 'order_update' order.id %}">Редактировать заказ</a> + {% endif %} + </div> + <div class="delivery">{{ order.delivery_date }}</div> + </div> + {% endfor %} +</div> +{% endblock %} diff --git a/core/templates/core/product_list.html b/core/templates/core/product_list.html index ad7a5bf..b2aa6e0 100644 --- a/core/templates/core/product_list.html +++ b/core/templates/core/product_list.html @@ -33,11 +33,9 @@ </select> </div> - {% comment %} <div style="margin-left: 10px;"> <a href="{% url 'order_list' %}" class="btn" style="background-color: #5bc0de;">Заказы</a> </div> - {% endcomment %} {% if user.role.name == 'Администратор' %} <div style="margin-left: auto;"> @@ -75,6 +73,9 @@ Единица измерения: {{ product.unit }} <br/> Колличество на складе: {{ product.discount }} + {% if user.role.name == 'Администратор' %} + <br/><a href="{% url 'product_edit' product.id %}">Редактировать товар</a> + {% endif%} </div> <div class="sale">{{ product.discount }}</div> </div> diff --git a/core/views.py b/core/views.py index f090d81..0e62eaf 100644 --- a/core/views.py +++ b/core/views.py @@ -7,9 +7,12 @@ from django.contrib.auth.views import LoginView from django.contrib import messages from django.db.models import Q from django.contrib.auth.mixins import UserPassesTestMixin +from django.http import HttpResponseRedirect +from extra_views import CreateWithInlinesView, UpdateWithInlinesView +from django import forms -from .models import Product, Supplier -from .forms import ProductForm +from .models import Product, Supplier, Order, OrderItem +from .forms import ProductForm, OrderForm, OrderItemFormSet class UserLoginView(LoginView): template_name = "core/login.html" @@ -54,7 +57,7 @@ class AdminRequiredMixin(UserPassesTestMixin): return ( self.request.user.is_authenticated and self.request.user.role - and self.request.user.role == "Администратор" + and self.request.user.role.name == "Администратор" ) class ProductCreateUpdateMixin: @@ -72,3 +75,98 @@ class ProductCreateView(AdminRequiredMixin, ProductCreateUpdateMixin, CreateView def form_valid(self, form): messages.success(self.request, "Товар успешно добавлен") return super().form_valid(form) + +class ProductUpdateView(AdminRequiredMixin, ProductCreateUpdateMixin, UpdateView): + model = Product + form_class = ProductForm + template_name = "core/product_form.html" + success_url = reverse_lazy("product_list") + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["is_edit"] = True + return context + + +class AdminManagerRequiredMixin(UserPassesTestMixin): + def test_func(self): + return ( + self.request.user.is_authenticated + and self.request.user.role + and (self.request.user.role.name == "Администратор" or self.request.user.role.name == "Менеджер") + ) + +class OrderListView(AdminManagerRequiredMixin, ListView): + model = Order + template_name = "core/order_list.html" + context_object_name = "orders" + + +class OrderCreateView(CreateWithInlinesView): + model = Order + form_class = OrderForm + inlines = [OrderItemFormSet] + success_url = reverse_lazy("order_list") + +class OrderUpdateView(UpdateWithInlinesView): + model = Order + form_class = OrderForm + inlines = [OrderItemFormSet] + success_url = reverse_lazy("order_list") + +# formset variant +# class OrderCreateView(CreateView): +# model = Order +# form_class = OrderForm +# template_name = "core/order_form.html" +# success_url = reverse_lazy("order_list") + +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) +# if self.request.POST: +# # re-use the bound formset if post() already stashed an invalid one +# context["formset"] = getattr(self, "formset", OrderItemFormSet(self.request.POST)) +# else: +# context["formset"] = OrderItemFormSet() +# return context + +# def post(self, request, *args, **kwargs): +# self.object = None +# form = self.get_form() +# formset = OrderItemFormSet(request.POST) +# if form.is_valid() and formset.is_valid(): +# self.object = form.save() +# formset.instance = self.object +# formset.save() +# return HttpResponseRedirect(self.get_success_url()) +# # stash the invalid formset so get_context_data renders it with errors +# self.formset = formset +# return self.form_invalid(form) + + +# class OrderUpdateView(UpdateView): +# model = Order +# form_class = OrderForm +# template_name = "core/order_form.html" +# success_url = reverse_lazy("order_list") + +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) +# if self.request.POST: +# context["formset"] = getattr( +# self, "formset", OrderItemFormSet(self.request.POST, instance=self.object) +# ) +# else: +# context["formset"] = OrderItemFormSet(instance=self.object) +# return context + +# def post(self, request, *args, **kwargs): +# self.object = self.get_object() +# form = self.get_form() +# formset = OrderItemFormSet(request.POST, instance=self.object) +# if form.is_valid() and formset.is_valid(): +# form.save() +# formset.save() +# return HttpResponseRedirect(self.get_success_url()) +# self.formset = formset +# return self.form_invalid(form) |
