Docs: начальная структура проекта
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
0
app/documents/__init__.py
Normal file
0
app/documents/__init__.py
Normal file
47
app/documents/admin.py
Normal file
47
app/documents/admin.py
Normal 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
7
app/documents/apps.py
Normal 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
58
app/documents/forms.py
Normal 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")
|
||||
136
app/documents/migrations/0001_initial.py
Normal file
136
app/documents/migrations/0001_initial.py
Normal 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': 'Строки заказа поставщику',
|
||||
},
|
||||
),
|
||||
]
|
||||
0
app/documents/migrations/__init__.py
Normal file
0
app/documents/migrations/__init__.py
Normal file
105
app/documents/models.py
Normal file
105
app/documents/models.py
Normal 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
12
app/documents/services.py
Normal 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
27
app/documents/urls.py
Normal 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
348
app/documents/views.py
Normal 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"
|
||||
Reference in New Issue
Block a user