refactor: стандартизация моделей документов перемещения

Приведение к единому паттерну именования документов:
- TransferBatch → TransferDocument
- TransferItem → TransferDocumentItem
- Удалена устаревшая модель Transfer (одиночные перемещения)
- Удалена неиспользуемая модель StockMovement

Изменения:
- models.py: переименование классов, обновление related_names
- admin.py: удаление регистраций Transfer/StockMovement
- forms.py: обновление TransferHeaderForm
- views/transfer.py: обновление всех view классов
- templates: замена transfer_batch → transfer_document
- urls.py: удаление путей для movements
- views/__init__.py: удаление импорта StockMovementListView
- views/movements.py: удален файл

Миграция: 0005_refactor_transfer_models
- RenameModel операции для сохранения данных
- DeleteModel для Transfer и StockMovement

Единый паттерн: *Document + *DocumentItem
(WriteOffDocument, IncomingDocument, TransferDocument)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-26 20:29:11 +03:00
parent c534e27c41
commit 08bae834c8
9 changed files with 84 additions and 170 deletions

View File

@@ -5,11 +5,11 @@ from django.db.models import Sum
from decimal import Decimal from decimal import Decimal
from inventory.models import ( from inventory.models import (
Warehouse, StockBatch, Sale, WriteOff, Transfer, Warehouse, StockBatch, Sale, WriteOff,
Inventory, InventoryLine, Reservation, Stock, StockMovement, Inventory, InventoryLine, Reservation, Stock,
SaleBatchAllocation, Showcase, WriteOffDocument, WriteOffDocumentItem, SaleBatchAllocation, Showcase, WriteOffDocument, WriteOffDocumentItem,
IncomingDocument, IncomingDocumentItem, Transformation, TransformationInput, IncomingDocument, IncomingDocumentItem, Transformation, TransformationInput,
TransformationOutput TransformationOutput, TransferDocument, TransferDocumentItem
) )
@@ -166,28 +166,6 @@ class WriteOffAdmin(admin.ModelAdmin):
reason_display.short_description = 'Причина' reason_display.short_description = 'Причина'
# ===== TRANSFER =====
@admin.register(Transfer)
class TransferAdmin(admin.ModelAdmin):
list_display = ('batch', 'from_warehouse', 'to_warehouse', 'quantity', 'date')
list_filter = ('date', 'from_warehouse', 'to_warehouse')
search_fields = ('batch__product__name', 'document_number')
date_hierarchy = 'date'
fieldsets = (
('Перемещение', {
'fields': ('batch', 'from_warehouse', 'to_warehouse', 'quantity', 'new_batch')
}),
('Документ', {
'fields': ('document_number',)
}),
('Дата', {
'fields': ('date',),
'classes': ('collapse',)
}),
)
readonly_fields = ('date', 'new_batch')
# ===== INVENTORY LINE (INLINE) ===== # ===== INVENTORY LINE (INLINE) =====
class InventoryLineInline(admin.TabularInline): class InventoryLineInline(admin.TabularInline):
model = InventoryLine model = InventoryLine
@@ -317,16 +295,6 @@ class StockAdmin(admin.ModelAdmin):
readonly_fields = ('quantity_available', 'quantity_reserved', 'updated_at') readonly_fields = ('quantity_available', 'quantity_reserved', 'updated_at')
# ===== STOCK MOVEMENT (для аудита) =====
@admin.register(StockMovement)
class StockMovementAdmin(admin.ModelAdmin):
list_display = ('product', 'change', 'reason', 'order', 'created_at')
list_filter = ('reason', 'created_at')
search_fields = ('product__name', 'order__order_number')
date_hierarchy = 'created_at'
readonly_fields = ('created_at',)
# ===== WRITEOFF DOCUMENT (документы списания) ===== # ===== WRITEOFF DOCUMENT (документы списания) =====
class WriteOffDocumentItemInline(admin.TabularInline): class WriteOffDocumentItemInline(admin.TabularInline):
model = WriteOffDocumentItem model = WriteOffDocumentItem

View File

@@ -4,8 +4,8 @@ from django.core.exceptions import ValidationError
from decimal import Decimal from decimal import Decimal
from .models import ( from .models import (
Warehouse, Sale, WriteOff, Transfer, Reservation, Inventory, InventoryLine, StockBatch, Warehouse, Sale, WriteOff, Reservation, Inventory, InventoryLine, StockBatch,
TransferBatch, TransferItem, Showcase, WriteOffDocument, WriteOffDocumentItem, Stock, TransferDocument, TransferDocumentItem, Showcase, WriteOffDocument, WriteOffDocumentItem, Stock,
IncomingDocument, IncomingDocumentItem, Transformation, TransformationInput, TransformationOutput IncomingDocument, IncomingDocumentItem, Transformation, TransformationInput, TransformationOutput
) )
from products.models import Product from products.models import Product
@@ -157,7 +157,7 @@ class TransferHeaderForm(forms.ModelForm):
Содержит информацию о складах-источнике и складе-назначении, примечания. Содержит информацию о складах-источнике и складе-назначении, примечания.
""" """
class Meta: class Meta:
model = TransferBatch model = TransferDocument
fields = ['from_warehouse', 'to_warehouse', 'notes'] fields = ['from_warehouse', 'to_warehouse', 'notes']
widgets = { widgets = {
'from_warehouse': forms.Select(attrs={'class': 'form-control'}), 'from_warehouse': forms.Select(attrs={'class': 'form-control'}),

View File

@@ -0,0 +1,38 @@
# Generated migration for Transfer models refactoring
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0004_remove_incoming_batch_and_incoming'),
('products', '0001_initial'),
]
operations = [
# 1. Удаление устаревших моделей ПЕРЕД переименованием
migrations.DeleteModel(
name='Transfer',
),
migrations.DeleteModel(
name='StockMovement',
),
# 2. Переименование моделей
migrations.RenameModel(
old_name='TransferBatch',
new_name='TransferDocument',
),
migrations.RenameModel(
old_name='TransferItem',
new_name='TransferDocumentItem',
),
# 3. Переименование поля transfer_batch → transfer_document в TransferDocumentItem
migrations.RenameField(
model_name='transferdocumentitem',
old_name='transfer_batch',
new_name='transfer_document',
),
]

View File

@@ -215,35 +215,6 @@ class WriteOff(models.Model):
super().save(*args, **kwargs) super().save(*args, **kwargs)
class Transfer(models.Model):
"""
Перемещение товара между складами. Сохраняет партийность.
"""
batch = models.ForeignKey(StockBatch, on_delete=models.CASCADE,
related_name='transfers', verbose_name="Партия")
from_warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
related_name='transfers_from', verbose_name="Из склада")
to_warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
related_name='transfers_to', verbose_name="На склад")
quantity = models.DecimalField(max_digits=10, decimal_places=3, verbose_name="Количество")
document_number = models.CharField(max_length=100, blank=True, null=True,
verbose_name="Номер документа")
date = models.DateTimeField(auto_now_add=True, verbose_name="Дата операции")
new_batch = models.ForeignKey(StockBatch, on_delete=models.SET_NULL, null=True, blank=True,
related_name='transfer_sources', verbose_name="Новая партия")
class Meta:
verbose_name = "Перемещение"
verbose_name_plural = "Перемещения"
ordering = ['-date']
indexes = [
models.Index(fields=['from_warehouse', 'to_warehouse']),
models.Index(fields=['date']),
]
def __str__(self):
return f"Перемещение {self.batch.product.name} ({self.quantity} шт): {self.from_warehouse}{self.to_warehouse}"
class Inventory(models.Model): class Inventory(models.Model):
""" """
@@ -731,38 +702,6 @@ class Stock(models.Model):
self.save() self.save()
class StockMovement(models.Model):
"""
Журнал всех складских операций (приход, списание, коррекция).
Используется для аудита.
"""
REASON_CHOICES = [
('purchase', 'Закупка'),
('sale', 'Продажа'),
('write_off', 'Списание'),
('adjustment', 'Корректировка'),
]
product = models.ForeignKey(Product, on_delete=models.CASCADE,
related_name='movements', verbose_name="Товар")
change = models.DecimalField(max_digits=10, decimal_places=3, verbose_name="Изменение")
reason = models.CharField(max_length=20, choices=REASON_CHOICES, verbose_name="Причина")
order = models.ForeignKey('orders.Order', on_delete=models.SET_NULL, null=True, blank=True,
related_name='stock_movements', verbose_name="Заказ")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
class Meta:
verbose_name = "Движение товара"
verbose_name_plural = "Движения товаров"
indexes = [
models.Index(fields=['product']),
models.Index(fields=['created_at']),
]
def __str__(self):
return f"{self.product.name}: {self.change} ({self.reason})"
class DocumentCounter(models.Model): class DocumentCounter(models.Model):
""" """
Счетчик номеров документов для различных операций. Счетчик номеров документов для различных операций.
@@ -811,7 +750,7 @@ class DocumentCounter(models.Model):
return obj.current_value return obj.current_value
class TransferBatch(models.Model): class TransferDocument(models.Model):
""" """
Документ перемещения товара между складами. Документ перемещения товара между складами.
Один номер документа = одна операция перемещения множественных товаров. Один номер документа = одна операция перемещения множественных товаров.
@@ -819,13 +758,13 @@ class TransferBatch(models.Model):
from_warehouse = models.ForeignKey( from_warehouse = models.ForeignKey(
Warehouse, Warehouse,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='transfer_batches_from', related_name='transfer_documents_from',
verbose_name="Склад-отгрузки" verbose_name="Склад-отгрузки"
) )
to_warehouse = models.ForeignKey( to_warehouse = models.ForeignKey(
Warehouse, Warehouse,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='transfer_batches_to', related_name='transfer_documents_to',
verbose_name="Склад-приемки" verbose_name="Склад-приемки"
) )
document_number = models.CharField( document_number = models.CharField(
@@ -866,13 +805,13 @@ class TransferBatch(models.Model):
return f"Перемещение {self.document_number}: {total_items} товаров, {total_qty} шт ({self.from_warehouse}{self.to_warehouse})" return f"Перемещение {self.document_number}: {total_items} товаров, {total_qty} шт ({self.from_warehouse}{self.to_warehouse})"
class TransferItem(models.Model): class TransferDocumentItem(models.Model):
""" """
Строка документа перемещения (товар в перемещении). Строка документа перемещения (товар в перемещении).
Связь между документом и товарами. Связь между документом и товарами.
""" """
transfer_batch = models.ForeignKey( transfer_document = models.ForeignKey(
TransferBatch, TransferDocument,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='items', related_name='items',
verbose_name="Документ перемещения" verbose_name="Документ перемещения"
@@ -880,13 +819,13 @@ class TransferItem(models.Model):
product = models.ForeignKey( product = models.ForeignKey(
Product, Product,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='transfer_items', related_name='transfer_document_items',
verbose_name="Товар" verbose_name="Товар"
) )
batch = models.ForeignKey( batch = models.ForeignKey(
StockBatch, StockBatch,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='transfer_items', related_name='transfer_document_items',
verbose_name="Исходная партия (FIFO)" verbose_name="Исходная партия (FIFO)"
) )
quantity = models.DecimalField( quantity = models.DecimalField(
@@ -899,17 +838,17 @@ class TransferItem(models.Model):
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
blank=True, blank=True,
related_name='transfer_items_created', related_name='transfer_document_items_created',
verbose_name="Созданная партия на целевом складе" verbose_name="Созданная партия на целевом складе"
) )
class Meta: class Meta:
verbose_name = "Строка перемещения" verbose_name = "Строка перемещения"
verbose_name_plural = "Строки перемещения" verbose_name_plural = "Строки перемещения"
unique_together = [['transfer_batch', 'batch']] unique_together = [['transfer_document', 'batch']]
ordering = ['id'] ordering = ['id']
indexes = [ indexes = [
models.Index(fields=['transfer_batch']), models.Index(fields=['transfer_document']),
models.Index(fields=['product']), models.Index(fields=['product']),
] ]

View File

@@ -1,6 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}Документ перемещения {{ transfer_batch.document_number }}{% endblock %} {% block title %}Документ перемещения {{ transfer_document.document_number }}{% endblock %}
{% block content %} {% block content %}
<div class="container-fluid px-4 py-3"> <div class="container-fluid px-4 py-3">
@@ -8,7 +8,7 @@
<nav aria-label="breadcrumb" class="mb-2"> <nav aria-label="breadcrumb" class="mb-2">
<ol class="breadcrumb breadcrumb-sm mb-0"> <ol class="breadcrumb breadcrumb-sm mb-0">
<li class="breadcrumb-item"><a href="{% url 'inventory:transfer-list' %}">Перемещения</a></li> <li class="breadcrumb-item"><a href="{% url 'inventory:transfer-list' %}">Перемещения</a></li>
<li class="breadcrumb-item active">{{ transfer_batch.document_number }}</li> <li class="breadcrumb-item active">{{ transfer_document.document_number }}</li>
</ol> </ol>
</nav> </nav>
@@ -18,36 +18,36 @@
<div class="card border-0 shadow-sm mb-3"> <div class="card border-0 shadow-sm mb-3">
<div class="card-header bg-light py-3"> <div class="card-header bg-light py-3">
<h5 class="mb-0"> <h5 class="mb-0">
<i class="bi bi-arrow-left-right me-2"></i>{{ transfer_batch.document_number }} <i class="bi bi-arrow-left-right me-2"></i>{{ transfer_document.document_number }}
</h5> </h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row mb-3"> <div class="row mb-3">
<div class="col-md-6"> <div class="col-md-6">
<p class="text-muted small mb-1">Склад-отгрузки</p> <p class="text-muted small mb-1">Склад-отгрузки</p>
<p class="fw-semibold">{{ transfer_batch.from_warehouse.name }}</p> <p class="fw-semibold">{{ transfer_document.from_warehouse.name }}</p>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<p class="text-muted small mb-1">Склад-приемки</p> <p class="text-muted small mb-1">Склад-приемки</p>
<p class="fw-semibold">{{ transfer_batch.to_warehouse.name }}</p> <p class="fw-semibold">{{ transfer_document.to_warehouse.name }}</p>
</div> </div>
</div> </div>
{% if transfer_batch.notes %} {% if transfer_document.notes %}
<div class="mb-3"> <div class="mb-3">
<p class="text-muted small mb-1">Примечания</p> <p class="text-muted small mb-1">Примечания</p>
<p>{{ transfer_batch.notes }}</p> <p>{{ transfer_document.notes }}</p>
</div> </div>
{% endif %} {% endif %}
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<p class="text-muted small mb-1">Дата создания</p> <p class="text-muted small mb-1">Дата создания</p>
<p class="fw-semibold">{{ transfer_batch.created_at|date:"d.m.Y H:i" }}</p> <p class="fw-semibold">{{ transfer_document.created_at|date:"d.m.Y H:i" }}</p>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<p class="text-muted small mb-1">Последнее обновление</p> <p class="text-muted small mb-1">Последнее обновление</p>
<p class="fw-semibold">{{ transfer_batch.updated_at|date:"d.m.Y H:i" }}</p> <p class="fw-semibold">{{ transfer_document.updated_at|date:"d.m.Y H:i" }}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -132,7 +132,7 @@
<a href="{% url 'inventory:transfer-list' %}" class="btn btn-outline-secondary btn-sm"> <a href="{% url 'inventory:transfer-list' %}" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-left me-1"></i>Вернуться к списку <i class="bi bi-arrow-left me-1"></i>Вернуться к списку
</a> </a>
<a href="{% url 'inventory:transfer-delete' transfer_batch.id %}" class="btn btn-outline-danger btn-sm"> <a href="{% url 'inventory:transfer-delete' transfer_document.id %}" class="btn btn-outline-danger btn-sm">
<i class="bi bi-trash me-1"></i>Удалить <i class="bi bi-trash me-1"></i>Удалить
</a> </a>
</div> </div>

View File

@@ -21,8 +21,6 @@ from .views import (
StockBatchListView, StockBatchDetailView, StockBatchListView, StockBatchDetailView,
# SaleBatchAllocation # SaleBatchAllocation
SaleBatchAllocationListView, SaleBatchAllocationListView,
# StockMovement
StockMovementListView,
) )
# Showcase views # Showcase views
from .views.showcase import ShowcaseListView, ShowcaseCreateView, ShowcaseUpdateView, ShowcaseDeleteView, SetDefaultShowcaseView from .views.showcase import ShowcaseListView, ShowcaseCreateView, ShowcaseUpdateView, ShowcaseDeleteView, SetDefaultShowcaseView
@@ -131,9 +129,6 @@ urlpatterns = [
# ==================== ALLOCATION (READ ONLY) ==================== # ==================== ALLOCATION (READ ONLY) ====================
path('allocations/', SaleBatchAllocationListView.as_view(), name='allocation-list'), path('allocations/', SaleBatchAllocationListView.as_view(), name='allocation-list'),
# ==================== MOVEMENT (READ ONLY) ====================
path('movements/', StockMovementListView.as_view(), name='movement-list'),
# ==================== SHOWCASE ==================== # ==================== SHOWCASE ====================
path('showcases/', ShowcaseListView.as_view(), name='showcase-list'), path('showcases/', ShowcaseListView.as_view(), name='showcase-list'),
path('showcases/create/', ShowcaseCreateView.as_view(), name='showcase-create'), path('showcases/create/', ShowcaseCreateView.as_view(), name='showcase-create'),

View File

@@ -13,7 +13,6 @@ Inventory Views Package
- stock.py: Справочник остатков (view-only) - stock.py: Справочник остатков (view-only)
- batch.py: Справочник партий товара (view-only) - batch.py: Справочник партий товара (view-only)
- allocation.py: Распределение продаж по партиям (view-only) - allocation.py: Распределение продаж по партиям (view-only)
- movements.py: Журнал складских операций (view-only)
""" """
from django.shortcuts import render from django.shortcuts import render
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
@@ -41,7 +40,6 @@ from .transfer import TransferListView, TransferBulkCreateView, TransferDetailVi
from .reservation import ReservationListView from .reservation import ReservationListView
from .stock import StockListView, StockDetailView from .stock import StockListView, StockDetailView
from .allocation import SaleBatchAllocationListView from .allocation import SaleBatchAllocationListView
from .movements import StockMovementListView
@login_required @login_required

View File

@@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
"""
StockMovement (Журнал всех складских операций) views - READ ONLY
GROUP 3: LOW PRIORITY - Аудит логирование
"""
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from ..models import StockMovement
class StockMovementListView(LoginRequiredMixin, ListView):
"""
Полный журнал всех складских операций (приход, продажа, списание, корректировка).
Используется для аудита и контроля.
"""
model = StockMovement
template_name = 'inventory/movements/movement_list.html'
context_object_name = 'movements'
paginate_by = 50
def get_queryset(self):
return StockMovement.objects.select_related(
'product', 'order'
).order_by('-created_at')

View File

@@ -13,7 +13,7 @@ from django.contrib import messages
from django.http import JsonResponse from django.http import JsonResponse
from django.db import transaction from django.db import transaction
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from ..models import TransferBatch, TransferItem, Stock from ..models import TransferDocument, TransferDocumentItem, Stock
from ..forms import TransferBulkForm from ..forms import TransferBulkForm
from inventory.utils.document_generator import generate_transfer_document_number from inventory.utils.document_generator import generate_transfer_document_number
from inventory.services.batch_manager import StockBatchManager from inventory.services.batch_manager import StockBatchManager
@@ -24,19 +24,19 @@ class TransferListView(LoginRequiredMixin, ListView):
""" """
View для просмотра списка документов перемещений товаров. View для просмотра списка документов перемещений товаров.
""" """
model = TransferBatch model = TransferDocument
template_name = 'inventory/transfer/transfer_list.html' template_name = 'inventory/transfer/transfer_list.html'
context_object_name = 'transfers' context_object_name = 'transfers'
paginate_by = 20 paginate_by = 20
def get_queryset(self): def get_queryset(self):
return TransferBatch.objects.select_related( return TransferDocument.objects.select_related(
'from_warehouse', 'to_warehouse' 'from_warehouse', 'to_warehouse'
).order_by('-created_at') ).order_by('-created_at')
# ============================================================================ # ============================================================================
# VIEWS ДЛЯ ПЕРЕМЕЩЕНИЯ ТОВАРОВ (TransferBatch + TransferItem) # VIEWS ДЛЯ ПЕРЕМЕЩЕНИЯ ТОВАРОВ (TransferDocument + TransferDocumentItem)
# ============================================================================ # ============================================================================
class TransferBulkCreateView(LoginRequiredMixin, View): class TransferBulkCreateView(LoginRequiredMixin, View):
@@ -100,8 +100,8 @@ class TransferBulkCreateView(LoginRequiredMixin, View):
# Начинаем транзакцию # Начинаем транзакцию
try: try:
with transaction.atomic(): with transaction.atomic():
# 1. Создаем документ TransferBatch # 1. Создаем документ TransferDocument
transfer_batch = TransferBatch.objects.create( transfer_document = TransferDocument.objects.create(
from_warehouse=from_warehouse, from_warehouse=from_warehouse,
to_warehouse=to_warehouse, to_warehouse=to_warehouse,
document_number=generate_transfer_document_number(), document_number=generate_transfer_document_number(),
@@ -119,10 +119,10 @@ class TransferBulkCreateView(LoginRequiredMixin, View):
quantity=quantity quantity=quantity
) )
# Создаем TransferItem для каждого использованного batch # Создаем TransferDocumentItem для каждого использованного batch
for source_batch, qty_transferred, new_batch in transfers: for source_batch, qty_transferred, new_batch in transfers:
TransferItem.objects.create( TransferDocumentItem.objects.create(
transfer_batch=transfer_batch, transfer_document=transfer_document,
product=product, product=product,
batch=source_batch, batch=source_batch,
quantity=qty_transferred, quantity=qty_transferred,
@@ -136,11 +136,11 @@ class TransferBulkCreateView(LoginRequiredMixin, View):
# 3. Успешно создали документ # 3. Успешно создали документ
messages.success( messages.success(
request, request,
f'Документ перемещения {transfer_batch.document_number} успешно создан. ' f'Документ перемещения {transfer_document.document_number} успешно создан. '
f'Перемещено {len(products)} видов товаров.' f'Перемещено {len(products)} видов товаров.'
) )
return redirect('inventory:transfer-detail', pk=transfer_batch.id) return redirect('inventory:transfer-detail', pk=transfer_document.id)
except Exception as e: except Exception as e:
messages.error(request, f'Ошибка при создании документа перемещения: {str(e)}') messages.error(request, f'Ошибка при создании документа перемещения: {str(e)}')
@@ -151,12 +151,12 @@ class TransferDetailView(LoginRequiredMixin, DetailView):
""" """
View для просмотра деталей документа перемещения. View для просмотра деталей документа перемещения.
""" """
model = TransferBatch model = TransferDocument
template_name = 'inventory/transfer/transfer_detail.html' template_name = 'inventory/transfer/transfer_detail.html'
context_object_name = 'transfer_batch' context_object_name = 'transfer_document'
def get_queryset(self): def get_queryset(self):
return TransferBatch.objects.select_related( return TransferDocument.objects.select_related(
'from_warehouse', 'to_warehouse' 'from_warehouse', 'to_warehouse'
).prefetch_related( ).prefetch_related(
'items__product', 'items__batch', 'items__new_batch' 'items__product', 'items__batch', 'items__new_batch'
@@ -164,10 +164,10 @@ class TransferDetailView(LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
transfer_batch = self.object transfer_document = self.object
# Собираем статистику по документу # Собираем статистику по документу
items = transfer_batch.items.all() items = transfer_document.items.all()
total_items = items.count() total_items = items.count()
total_qty = sum(Decimal(str(item.quantity)) for item in items) total_qty = sum(Decimal(str(item.quantity)) for item in items)
@@ -182,13 +182,13 @@ class TransferDeleteView(LoginRequiredMixin, DeleteView):
""" """
View для удаления документа перемещения. View для удаления документа перемещения.
""" """
model = TransferBatch model = TransferDocument
template_name = 'inventory/transfer/transfer_confirm_delete.html' template_name = 'inventory/transfer/transfer_confirm_delete.html'
success_url = reverse_lazy('inventory:transfer-list') success_url = reverse_lazy('inventory:transfer-list')
def form_valid(self, form): def form_valid(self, form):
transfer_batch = self.get_object() transfer_document = self.get_object()
messages.success(self.request, f'Документ перемещения {transfer_batch.document_number} удалён.') messages.success(self.request, f'Документ перемещения {transfer_document.document_number} удалён.')
return super().form_valid(form) return super().form_valid(form)