Docs: начальная структура проекта

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-25 14:59:46 +00:00
commit 1669c12182
56 changed files with 2085 additions and 0 deletions

View File

47
app/documents/admin.py Normal file
View File

@@ -0,0 +1,47 @@
from django.contrib import admin
from .models import (
CustomerOrder,
CustomerOrderItem,
SupplierOrder,
SupplierOrderItem,
CashInflow,
CashTransfer,
CashExpense,
)
class CustomerOrderItemInline(admin.TabularInline):
model = CustomerOrderItem
extra = 0
@admin.register(CustomerOrder)
class CustomerOrderAdmin(admin.ModelAdmin):
list_display = ("number", "date", "organization", "client", "total_amount")
inlines = [CustomerOrderItemInline]
class SupplierOrderItemInline(admin.TabularInline):
model = SupplierOrderItem
extra = 0
@admin.register(SupplierOrder)
class SupplierOrderAdmin(admin.ModelAdmin):
list_display = ("number", "date", "organization", "supplier", "total_in_currency", "total_amount")
inlines = [SupplierOrderItemInline]
@admin.register(CashInflow)
class CashInflowAdmin(admin.ModelAdmin):
list_display = ("number", "date", "recipient", "amount", "customer_order")
@admin.register(CashTransfer)
class CashTransferAdmin(admin.ModelAdmin):
list_display = ("number", "date", "sender", "recipient", "amount")
@admin.register(CashExpense)
class CashExpenseAdmin(admin.ModelAdmin):
list_display = ("number", "date", "sender", "amount", "supplier_order")

7
app/documents/apps.py Normal file
View File

@@ -0,0 +1,7 @@
from django.apps import AppConfig
class DocumentsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "documents"
verbose_name = "Документы"

58
app/documents/forms.py Normal file
View File

@@ -0,0 +1,58 @@
"""Формы документов."""
from django import forms
from .models import (
CustomerOrder,
CustomerOrderItem,
SupplierOrder,
SupplierOrderItem,
CashInflow,
CashTransfer,
CashExpense,
)
from django.forms import inlineformset_factory
CustomerOrderItemFormSet = inlineformset_factory(
CustomerOrder,
CustomerOrderItem,
fields=("product", "price", "currency", "quantity"),
extra=1,
can_delete=True,
)
SupplierOrderItemFormSet = inlineformset_factory(
SupplierOrder,
SupplierOrderItem,
fields=("product", "price", "currency", "quantity"),
extra=1,
can_delete=True,
)
class CustomerOrderForm(forms.ModelForm):
class Meta:
model = CustomerOrder
fields = ("date", "number", "order_kind", "organization", "client", "author")
class SupplierOrderForm(forms.ModelForm):
class Meta:
model = SupplierOrder
fields = ("date", "number", "organization", "supplier", "currency", "rate", "author")
class CashInflowForm(forms.ModelForm):
class Meta:
model = CashInflow
fields = ("date", "number", "recipient", "amount", "customer_order", "comment", "author")
class CashTransferForm(forms.ModelForm):
class Meta:
model = CashTransfer
fields = ("date", "number", "sender", "recipient", "amount", "comment", "author")
class CashExpenseForm(forms.ModelForm):
class Meta:
model = CashExpense
fields = ("date", "number", "sender", "amount", "supplier_order", "comment", "author")

View File

@@ -0,0 +1,136 @@
# Generated by Django 5.2.11 on 2026-02-25 14:58
import django.db.models.deletion
from decimal import Decimal
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('references', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='CashTransfer',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(verbose_name='Дата')),
('number', models.CharField(max_length=50, verbose_name='Номер')),
('amount', models.DecimalField(decimal_places=2, default=Decimal('0'), max_digits=18, verbose_name='Сумма')),
('comment', models.CharField(blank=True, max_length=500, verbose_name='Комментарий')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cash_transfers', to='references.employee', verbose_name='Автор')),
('recipient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_received', to='references.cashaccount', verbose_name='Получатель')),
('sender', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_sent', to='references.cashaccount', verbose_name='Отправитель')),
],
options={
'verbose_name': 'Перемещение денежных средств',
'verbose_name_plural': 'Перемещения денежных средств',
},
),
migrations.CreateModel(
name='CustomerOrder',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(verbose_name='Дата')),
('number', models.CharField(max_length=50, verbose_name='Номер')),
('total_amount', models.DecimalField(decimal_places=2, default=Decimal('0'), editable=False, max_digits=18, verbose_name='Стоимость заказа')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='customer_orders', to='references.employee', verbose_name='Автор')),
('client', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='references.client', verbose_name='Клиент')),
('order_kind', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='references.orderkind', verbose_name='Вид заказа')),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='references.organization', verbose_name='Организация')),
],
options={
'verbose_name': 'Заказ клиента',
'verbose_name_plural': 'Заказы клиентов',
},
),
migrations.CreateModel(
name='CashInflow',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(verbose_name='Дата')),
('number', models.CharField(max_length=50, verbose_name='Номер')),
('amount', models.DecimalField(decimal_places=2, default=Decimal('0'), max_digits=18, verbose_name='Сумма')),
('comment', models.CharField(blank=True, max_length=500, verbose_name='Комментарий')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cash_inflows', to='references.employee', verbose_name='Автор')),
('recipient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='inflows', to='references.cashaccount', verbose_name='Получатель')),
('customer_order', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cash_inflows', to='documents.customerorder', verbose_name='Заказ клиента')),
],
options={
'verbose_name': 'Поступление денежных средств',
'verbose_name_plural': 'Поступления денежных средств',
},
),
migrations.CreateModel(
name='CustomerOrderItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('price', models.DecimalField(decimal_places=2, default=Decimal('0'), max_digits=18, verbose_name='Цена')),
('quantity', models.DecimalField(decimal_places=4, default=Decimal('1'), max_digits=18, verbose_name='Количество')),
('amount', models.DecimalField(decimal_places=2, default=Decimal('0'), editable=False, max_digits=18, verbose_name='Стоимость')),
('currency', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='references.currency', verbose_name='Валюта')),
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='documents.customerorder', verbose_name='Заказ')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='references.product', verbose_name='Товар')),
],
options={
'verbose_name': 'Строка заказа клиента',
'verbose_name_plural': 'Строки заказа клиента',
},
),
migrations.CreateModel(
name='SupplierOrder',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(verbose_name='Дата')),
('number', models.CharField(max_length=50, verbose_name='Номер')),
('rate', models.DecimalField(decimal_places=6, default=Decimal('1'), max_digits=18, verbose_name='Курс валюты')),
('total_in_currency', models.DecimalField(decimal_places=2, default=Decimal('0'), editable=False, max_digits=18, verbose_name='Стоимость в валюте')),
('total_amount', models.DecimalField(decimal_places=2, default=Decimal('0'), editable=False, max_digits=18, verbose_name='Стоимость заказа')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='supplier_orders', to='references.employee', verbose_name='Автор')),
('currency', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='references.currency', verbose_name='Валюта')),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='references.organization', verbose_name='Организация')),
('supplier', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='references.supplier', verbose_name='Поставщик')),
],
options={
'verbose_name': 'Заказ поставщику',
'verbose_name_plural': 'Заказы поставщику',
},
),
migrations.CreateModel(
name='CashExpense',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(verbose_name='Дата')),
('number', models.CharField(max_length=50, verbose_name='Номер')),
('amount', models.DecimalField(decimal_places=2, default=Decimal('0'), max_digits=18, verbose_name='Сумма')),
('comment', models.CharField(blank=True, max_length=500, verbose_name='Комментарий')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cash_expenses', to='references.employee', verbose_name='Автор')),
('sender', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='references.cashaccount', verbose_name='Отправитель')),
('supplier_order', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cash_expenses', to='documents.supplierorder', verbose_name='Заказ поставщику')),
],
options={
'verbose_name': 'Расход денежных средств',
'verbose_name_plural': 'Расходы денежных средств',
},
),
migrations.CreateModel(
name='SupplierOrderItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('price', models.DecimalField(decimal_places=2, default=Decimal('0'), max_digits=18, verbose_name='Цена')),
('quantity', models.DecimalField(decimal_places=4, default=Decimal('1'), max_digits=18, verbose_name='Количество')),
('amount', models.DecimalField(decimal_places=2, default=Decimal('0'), editable=False, max_digits=18, verbose_name='Стоимость')),
('currency', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='references.currency', verbose_name='Валюта')),
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='documents.supplierorder', verbose_name='Заказ поставщику')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='references.product', verbose_name='Товар')),
],
options={
'verbose_name': 'Строка заказа поставщику',
'verbose_name_plural': 'Строки заказа поставщику',
},
),
]

View File

105
app/documents/models.py Normal file
View File

@@ -0,0 +1,105 @@
"""Документы ERP WaterSurf."""
from decimal import Decimal
from django.db import models
from references.models import Currency, OrderKind, Client, Organization, Supplier, Employee, CashAccount, Product
class CustomerOrder(models.Model):
date = models.DateField("Дата")
number = models.CharField("Номер", max_length=50)
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="Клиент")
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:
verbose_name = "Заказ клиента"
verbose_name_plural = "Заказы клиентов"
def __str__(self): return f"{self.number} от {self.date}"
def recalc_total(self): self.total_amount = sum((i.price * i.quantity for i in self.items.all()), Decimal("0"))
class CustomerOrderItem(models.Model):
document = models.ForeignKey(CustomerOrder, on_delete=models.CASCADE, related_name="items", verbose_name="Заказ")
product = models.ForeignKey(Product, on_delete=models.PROTECT, verbose_name="Товар")
price = models.DecimalField("Цена", max_digits=18, decimal_places=2, default=Decimal("0"))
currency = models.ForeignKey(Currency, on_delete=models.PROTECT, verbose_name="Валюта", null=True, blank=True)
quantity = models.DecimalField("Количество", max_digits=18, decimal_places=4, default=Decimal("1"))
amount = models.DecimalField("Стоимость", max_digits=18, decimal_places=2, default=Decimal("0"), editable=False)
class Meta:
verbose_name = "Строка заказа клиента"
verbose_name_plural = "Строки заказа клиента"
def save(self, *args, **kwargs):
self.amount = self.price * self.quantity
super().save(*args, **kwargs)
class SupplierOrder(models.Model):
date = models.DateField("Дата")
number = models.CharField("Номер", max_length=50)
organization = models.ForeignKey(Organization, on_delete=models.PROTECT, verbose_name="Организация")
supplier = models.ForeignKey(Supplier, on_delete=models.PROTECT, verbose_name="Поставщик")
currency = models.ForeignKey(Currency, on_delete=models.PROTECT, verbose_name="Валюта")
rate = models.DecimalField("Курс валюты", max_digits=18, decimal_places=6, default=Decimal("1"))
total_in_currency = models.DecimalField("Стоимость в валюте", max_digits=18, decimal_places=2, default=Decimal("0"), editable=False)
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="supplier_orders")
class Meta:
verbose_name = "Заказ поставщику"
verbose_name_plural = "Заказы поставщику"
def __str__(self): return f"{self.number} от {self.date}"
def recalc_totals(self):
self.total_in_currency = sum((i.price * i.quantity for i in self.items.all()), Decimal("0"))
self.total_amount = self.total_in_currency * self.rate
class SupplierOrderItem(models.Model):
document = models.ForeignKey(SupplierOrder, on_delete=models.CASCADE, related_name="items", verbose_name="Заказ поставщику")
product = models.ForeignKey(Product, on_delete=models.PROTECT, verbose_name="Товар")
price = models.DecimalField("Цена", max_digits=18, decimal_places=2, default=Decimal("0"))
currency = models.ForeignKey(Currency, on_delete=models.PROTECT, verbose_name="Валюта", null=True, blank=True)
quantity = models.DecimalField("Количество", max_digits=18, decimal_places=4, default=Decimal("1"))
amount = models.DecimalField("Стоимость", max_digits=18, decimal_places=2, default=Decimal("0"), editable=False)
class Meta:
verbose_name = "Строка заказа поставщику"
verbose_name_plural = "Строки заказа поставщику"
def save(self, *args, **kwargs):
self.amount = self.price * self.quantity
super().save(*args, **kwargs)
class CashInflow(models.Model):
date = models.DateField("Дата")
number = models.CharField("Номер", max_length=50)
recipient = models.ForeignKey(CashAccount, on_delete=models.PROTECT, verbose_name="Получатель", related_name="inflows")
amount = models.DecimalField("Сумма", max_digits=18, decimal_places=2, default=Decimal("0"))
customer_order = models.ForeignKey(CustomerOrder, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Заказ клиента", related_name="cash_inflows")
comment = models.CharField("Комментарий", max_length=500, blank=True)
author = models.ForeignKey(Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Автор", related_name="cash_inflows")
class Meta:
verbose_name = "Поступление денежных средств"
verbose_name_plural = "Поступления денежных средств"
def __str__(self): return f"{self.number} от {self.date}"
class CashTransfer(models.Model):
date = models.DateField("Дата")
number = models.CharField("Номер", max_length=50)
sender = models.ForeignKey(CashAccount, on_delete=models.PROTECT, verbose_name="Отправитель", related_name="transfers_sent")
recipient = models.ForeignKey(CashAccount, on_delete=models.PROTECT, verbose_name="Получатель", related_name="transfers_received")
amount = models.DecimalField("Сумма", max_digits=18, decimal_places=2, default=Decimal("0"))
comment = models.CharField("Комментарий", max_length=500, blank=True)
author = models.ForeignKey(Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Автор", related_name="cash_transfers")
class Meta:
verbose_name = "Перемещение денежных средств"
verbose_name_plural = "Перемещения денежных средств"
def __str__(self): return f"{self.number} от {self.date}"
class CashExpense(models.Model):
date = models.DateField("Дата")
number = models.CharField("Номер", max_length=50)
sender = models.ForeignKey(CashAccount, on_delete=models.PROTECT, verbose_name="Отправитель", related_name="expenses")
amount = models.DecimalField("Сумма", max_digits=18, decimal_places=2, default=Decimal("0"))
supplier_order = models.ForeignKey(SupplierOrder, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Заказ поставщику", related_name="cash_expenses")
comment = models.CharField("Комментарий", max_length=500, blank=True)
author = models.ForeignKey(Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Автор", related_name="cash_expenses")
class Meta:
verbose_name = "Расход денежных средств"
verbose_name_plural = "Расходы денежных средств"
def __str__(self): return f"{self.number} от {self.date}"

12
app/documents/services.py Normal file
View File

@@ -0,0 +1,12 @@
from django.db.models import Max
def next_number(model_class, field_name="number"):
"""Следующий номер по порядку (максимум + 1). Для числоподобных строк."""
agg = model_class.objects.aggregate(m=Max(field_name))
current = agg["m"]
if current is None:
return "1"
try:
return str(int(current) + 1)
except (ValueError, TypeError):
return "1"

27
app/documents/urls.py Normal file
View File

@@ -0,0 +1,27 @@
from django.urls import path
from . import views
app_name = "documents"
urlpatterns = [
path("customer-orders/", views.CustomerOrderList.as_view(), name="customer_order_list"),
path("customer-orders/create/", views.CustomerOrderCreate.as_view(), name="customer_order_create"),
path("customer-orders/<int:pk>/edit/", views.CustomerOrderUpdate.as_view(), name="customer_order_edit"),
path("customer-orders/<int:pk>/delete/", views.CustomerOrderDelete.as_view(), name="customer_order_delete"),
path("supplier-orders/", views.SupplierOrderList.as_view(), name="supplier_order_list"),
path("supplier-orders/create/", views.SupplierOrderCreate.as_view(), name="supplier_order_create"),
path("supplier-orders/<int:pk>/edit/", views.SupplierOrderUpdate.as_view(), name="supplier_order_edit"),
path("supplier-orders/<int:pk>/delete/", views.SupplierOrderDelete.as_view(), name="supplier_order_delete"),
path("cash-inflows/", views.CashInflowList.as_view(), name="cash_inflow_list"),
path("cash-inflows/create/", views.CashInflowCreate.as_view(), name="cash_inflow_create"),
path("cash-inflows/<int:pk>/edit/", views.CashInflowUpdate.as_view(), name="cash_inflow_edit"),
path("cash-inflows/<int:pk>/delete/", views.CashInflowDelete.as_view(), name="cash_inflow_delete"),
path("cash-transfers/", views.CashTransferList.as_view(), name="cash_transfer_list"),
path("cash-transfers/create/", views.CashTransferCreate.as_view(), name="cash_transfer_create"),
path("cash-transfers/<int:pk>/edit/", views.CashTransferUpdate.as_view(), name="cash_transfer_edit"),
path("cash-transfers/<int:pk>/delete/", views.CashTransferDelete.as_view(), name="cash_transfer_delete"),
path("cash-expenses/", views.CashExpenseList.as_view(), name="cash_expense_list"),
path("cash-expenses/create/", views.CashExpenseCreate.as_view(), name="cash_expense_create"),
path("cash-expenses/<int:pk>/edit/", views.CashExpenseUpdate.as_view(), name="cash_expense_edit"),
path("cash-expenses/<int:pk>/delete/", views.CashExpenseDelete.as_view(), name="cash_expense_delete"),
]

348
app/documents/views.py Normal file
View File

@@ -0,0 +1,348 @@
"""Представления документов: списки и формы создания/редактирования."""
import logging
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, DetailView
from django.contrib.auth.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 .models import (
CustomerOrder,
SupplierOrder,
CashInflow,
CashTransfer,
CashExpense,
)
from .forms import (
CustomerOrderForm,
CustomerOrderItemFormSet,
SupplierOrderForm,
SupplierOrderItemFormSet,
CashInflowForm,
CashTransferForm,
CashExpenseForm,
)
from .services import next_number
logger = logging.getLogger(__name__)
def set_author(form, request):
"""Подставить автора из профиля пользователя."""
author = get_author_employee(request.user)
if author and "author" in form.fields:
form.instance.author = author
# --- Заказы клиентов ---
class CustomerOrderList(LoginRequiredMixin, ListView):
model = CustomerOrder
template_name = "documents/customer_order_list.html"
context_object_name = "object_list"
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_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["formset"] = CustomerOrderItemFormSet(instance=self.object) if self.object.pk else CustomerOrderItemFormSet()
ctx["title"] = "Заказ клиента"
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))
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"] = CustomerOrderItemFormSet(instance=self.object)
ctx["title"] = "Заказ клиента"
return ctx
def form_valid(self, form):
formset = CustomerOrderItemFormSet(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))
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 SupplierOrderList(LoginRequiredMixin, ListView):
model = SupplierOrder
template_name = "documents/supplier_order_list.html"
context_object_name = "object_list"
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"] = "Заказ поставщику"
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))
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"] = SupplierOrderItemFormSet(instance=self.object)
ctx["title"] = "Заказ поставщику"
return ctx
def form_valid(self, form):
formset = SupplierOrderItemFormSet(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))
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, ListView):
model = CashInflow
template_name = "documents/cash_inflow_list.html"
context_object_name = "object_list"
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, ListView):
model = CashTransfer
template_name = "documents/cash_transfer_list.html"
context_object_name = "object_list"
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, ListView):
model = CashExpense
template_name = "documents/cash_expense_list.html"
context_object_name = "object_list"
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"