Files
watersurf-erp/app/documents/views.py

482 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Представления документов: списки и формы создания/редактирования."""
import logging
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, DetailView
from config.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.shortcuts import redirect, get_object_or_404
from django.contrib import messages
from django.http import HttpResponseRedirect
from users.utils import get_author_employee
from references.models import Employee, OrderStatus
from .models import (
CustomerOrder,
SupplierOrder,
CashInflow,
CashTransfer,
CashExpense,
)
from .forms import (
CustomerOrderForm,
CustomerOrderItemFormSet,
CustomerOrderItemFormSetUpdate,
SupplierOrderForm,
SupplierOrderItemFormSet,
SupplierOrderItemFormSetUpdate,
CashInflowForm,
CashTransferForm,
CashExpenseForm,
)
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:
return # при редактировании автора не меняем
author = _get_author_for_request(request)
if author:
form.instance.author = author
def _get_author_for_request(request):
"""Сотрудник-автор: из профиля пользователя или по ФИО/логину."""
if not request.user.is_authenticated:
return None
author = get_author_employee(request.user)
if author:
return author
name = (request.user.get_full_name() or "").strip() or request.user.username
author = Employee.objects.filter(name=name).first()
if not author:
author = Employee.objects.create(name=name)
return author
# --- Заказы клиентов ---
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
form_class = CustomerOrderForm
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()
ctx["title"] = "Заказ клиента"
ctx["author_display"] = _get_author_for_request(self.request) if not self.object or not self.object.pk else self.object.author
return ctx
def form_valid(self, form):
set_author(form, self.request)
self.object = form.save()
formset = CustomerOrderItemFormSet(self.request.POST, instance=self.object)
if formset.is_valid():
formset.save()
self.object.recalc_total()
self.object.save()
logger.info("Создан заказ клиента %s", self.object)
messages.success(self.request, "Заказ создан.")
return redirect(self.success_url)
return self.render_to_response(self.get_context_data(form=form, formset=formset))
def get(self, request, *args, **kwargs):
self.object = None
return super().get(request, *args, **kwargs)
class CustomerOrderUpdate(LoginRequiredMixin, UpdateView):
model = CustomerOrder
form_class = CustomerOrderForm
template_name = "documents/order_form.html"
context_object_name = "object"
success_url = reverse_lazy("documents:customer_order_list")
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["formset"] = CustomerOrderItemFormSetUpdate(instance=self.object)
ctx["title"] = "Заказ клиента"
ctx["author_display"] = self.object.author
return ctx
def form_valid(self, form):
formset = CustomerOrderItemFormSetUpdate(self.request.POST, instance=self.object)
if formset.is_valid():
form.save()
formset.save()
self.object.recalc_total()
self.object.save()
logger.info("Обновлён заказ клиента %s", self.object)
messages.success(self.request, "Заказ сохранён.")
return redirect(self.success_url)
return self.render_to_response(self.get_context_data(form=form, formset=formset))
class CustomerOrderDelete(LoginRequiredMixin, DeleteView):
model = CustomerOrder
template_name = "documents/confirm_delete.html"
success_url = reverse_lazy("documents:customer_order_list")
context_object_name = "object"
class CustomerOrderDetail(LoginRequiredMixin, DetailView):
model = CustomerOrder
template_name = "documents/customer_order_detail.html"
context_object_name = "object"
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["economics"] = self.object.get_economics()
return ctx
# --- Заказы поставщику ---
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
form_class = SupplierOrderForm
template_name = "documents/supplier_order_form.html"
success_url = reverse_lazy("documents:supplier_order_list")
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["formset"] = SupplierOrderItemFormSet(instance=self.object) if self.object and self.object.pk else SupplierOrderItemFormSet()
ctx["title"] = "Заказ поставщику"
ctx["author_display"] = _get_author_for_request(self.request) if not self.object or not self.object.pk else self.object.author
return ctx
def form_valid(self, form):
set_author(form, self.request)
self.object = form.save()
formset = SupplierOrderItemFormSet(self.request.POST, instance=self.object)
if formset.is_valid():
formset.save()
self.object.recalc_totals()
self.object.save()
logger.info("Создан заказ поставщику %s", self.object)
messages.success(self.request, "Заказ создан.")
return redirect(self.success_url)
return self.render_to_response(self.get_context_data(form=form, formset=formset))
def get(self, request, *args, **kwargs):
self.object = None
return super().get(request, *args, **kwargs)
class SupplierOrderUpdate(LoginRequiredMixin, UpdateView):
model = SupplierOrder
form_class = SupplierOrderForm
template_name = "documents/supplier_order_form.html"
context_object_name = "object"
success_url = reverse_lazy("documents:supplier_order_list")
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["formset"] = SupplierOrderItemFormSetUpdate(instance=self.object)
ctx["title"] = "Заказ поставщику"
ctx["author_display"] = self.object.author
return ctx
def form_valid(self, form):
formset = SupplierOrderItemFormSetUpdate(self.request.POST, instance=self.object)
if formset.is_valid():
form.save()
formset.save()
self.object.recalc_totals()
self.object.save()
logger.info("Обновлён заказ поставщику %s", self.object)
messages.success(self.request, "Заказ сохранён.")
return redirect(self.success_url)
return self.render_to_response(self.get_context_data(form=form, formset=formset))
class SupplierOrderDelete(LoginRequiredMixin, DeleteView):
model = SupplierOrder
template_name = "documents/confirm_delete.html"
success_url = reverse_lazy("documents:supplier_order_list")
context_object_name = "object"
# --- Поступление денежных средств ---
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
form_class = CashInflowForm
template_name = "documents/cash_doc_form.html"
success_url = reverse_lazy("documents:cash_inflow_list")
def get_initial(self):
initial = super().get_initial()
initial["number"] = next_number(CashInflow)
return initial
def form_valid(self, form):
set_author(form, self.request)
form.save()
logger.info("Создано поступление %s", form.instance)
messages.success(self.request, "Поступление создано.")
return redirect(self.success_url)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["title"] = "Поступление денежных средств"
ctx["cancel_url"] = reverse_lazy("documents:cash_inflow_list")
return ctx
class CashInflowUpdate(LoginRequiredMixin, UpdateView):
model = CashInflow
form_class = CashInflowForm
template_name = "documents/cash_doc_form.html"
context_object_name = "object"
success_url = reverse_lazy("documents:cash_inflow_list")
def form_valid(self, form):
form.save()
logger.info("Обновлено поступление %s", self.object)
messages.success(self.request, "Поступление сохранено.")
return redirect(self.success_url)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["title"] = "Поступление денежных средств"
ctx["cancel_url"] = reverse_lazy("documents:cash_inflow_list")
return ctx
class CashInflowDelete(LoginRequiredMixin, DeleteView):
model = CashInflow
template_name = "documents/confirm_delete.html"
success_url = reverse_lazy("documents:cash_inflow_list")
context_object_name = "object"
# --- Перемещение денежных средств ---
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
form_class = CashTransferForm
template_name = "documents/cash_doc_form.html"
success_url = reverse_lazy("documents:cash_transfer_list")
def get_initial(self):
initial = super().get_initial()
initial["number"] = next_number(CashTransfer)
return initial
def form_valid(self, form):
set_author(form, self.request)
form.save()
logger.info("Создано перемещение %s", form.instance)
messages.success(self.request, "Перемещение создано.")
return redirect(self.success_url)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["title"] = "Перемещение денежных средств"
ctx["cancel_url"] = reverse_lazy("documents:cash_transfer_list")
return ctx
class CashTransferUpdate(LoginRequiredMixin, UpdateView):
model = CashTransfer
form_class = CashTransferForm
template_name = "documents/cash_doc_form.html"
context_object_name = "object"
success_url = reverse_lazy("documents:cash_transfer_list")
def form_valid(self, form):
form.save()
logger.info("Обновлено перемещение %s", self.object)
messages.success(self.request, "Перемещение сохранено.")
return redirect(self.success_url)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["title"] = "Перемещение денежных средств"
ctx["cancel_url"] = reverse_lazy("documents:cash_transfer_list")
return ctx
class CashTransferDelete(LoginRequiredMixin, DeleteView):
model = CashTransfer
template_name = "documents/confirm_delete.html"
success_url = reverse_lazy("documents:cash_transfer_list")
context_object_name = "object"
# --- Расход денежных средств ---
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
form_class = CashExpenseForm
template_name = "documents/cash_doc_form.html"
success_url = reverse_lazy("documents:cash_expense_list")
def get_initial(self):
initial = super().get_initial()
initial["number"] = next_number(CashExpense)
return initial
def form_valid(self, form):
set_author(form, self.request)
form.save()
logger.info("Создан расход %s", form.instance)
messages.success(self.request, "Расход создан.")
return redirect(self.success_url)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["title"] = "Расход денежных средств"
ctx["cancel_url"] = reverse_lazy("documents:cash_expense_list")
return ctx
class CashExpenseUpdate(LoginRequiredMixin, UpdateView):
model = CashExpense
form_class = CashExpenseForm
template_name = "documents/cash_doc_form.html"
context_object_name = "object"
success_url = reverse_lazy("documents:cash_expense_list")
def form_valid(self, form):
form.save()
logger.info("Обновлён расход %s", self.object)
messages.success(self.request, "Расход сохранён.")
return redirect(self.success_url)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["title"] = "Расход денежных средств"
ctx["cancel_url"] = reverse_lazy("documents:cash_expense_list")
return ctx
class CashExpenseDelete(LoginRequiredMixin, DeleteView):
model = CashExpense
template_name = "documents/confirm_delete.html"
success_url = reverse_lazy("documents:cash_expense_list")
context_object_name = "object"