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

@@ -215,35 +215,6 @@ class WriteOff(models.Model):
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):
"""
@@ -731,38 +702,6 @@ class Stock(models.Model):
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):
"""
Счетчик номеров документов для различных операций.
@@ -811,7 +750,7 @@ class DocumentCounter(models.Model):
return obj.current_value
class TransferBatch(models.Model):
class TransferDocument(models.Model):
"""
Документ перемещения товара между складами.
Один номер документа = одна операция перемещения множественных товаров.
@@ -819,13 +758,13 @@ class TransferBatch(models.Model):
from_warehouse = models.ForeignKey(
Warehouse,
on_delete=models.CASCADE,
related_name='transfer_batches_from',
related_name='transfer_documents_from',
verbose_name="Склад-отгрузки"
)
to_warehouse = models.ForeignKey(
Warehouse,
on_delete=models.CASCADE,
related_name='transfer_batches_to',
related_name='transfer_documents_to',
verbose_name="Склад-приемки"
)
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})"
class TransferItem(models.Model):
class TransferDocumentItem(models.Model):
"""
Строка документа перемещения (товар в перемещении).
Связь между документом и товарами.
"""
transfer_batch = models.ForeignKey(
TransferBatch,
transfer_document = models.ForeignKey(
TransferDocument,
on_delete=models.CASCADE,
related_name='items',
verbose_name="Документ перемещения"
@@ -880,13 +819,13 @@ class TransferItem(models.Model):
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='transfer_items',
related_name='transfer_document_items',
verbose_name="Товар"
)
batch = models.ForeignKey(
StockBatch,
on_delete=models.CASCADE,
related_name='transfer_items',
related_name='transfer_document_items',
verbose_name="Исходная партия (FIFO)"
)
quantity = models.DecimalField(
@@ -899,17 +838,17 @@ class TransferItem(models.Model):
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='transfer_items_created',
related_name='transfer_document_items_created',
verbose_name="Созданная партия на целевом складе"
)
class Meta:
verbose_name = "Строка перемещения"
verbose_name_plural = "Строки перемещения"
unique_together = [['transfer_batch', 'batch']]
unique_together = [['transfer_document', 'batch']]
ordering = ['id']
indexes = [
models.Index(fields=['transfer_batch']),
models.Index(fields=['transfer_document']),
models.Index(fields=['product']),
]