Files
octopus/myproject/inventory/views/sale.py
Andrey Smakotin 6735be9b08 feat: Реализовать систему поступления товаров с партиями (IncomingBatch)
Основные изменения:
- Создана модель IncomingBatch для группировки товаров по документам
- Каждое поступление (Incoming) связано с одной батчем поступления
- Автоматическое создание StockBatch для каждого товара в приходе
- Реализована система нумерации партий (IN-XXXX-XXXX) с поиском максимума в БД
- Обновлены все представления (views) для работы с новой архитектурой
- Добавлены детальные страницы просмотра партий поступлений
- Обновлены шаблоны для отображения информации о партиях и их товарах
- Исправлена логика сигналов для создания StockBatch при приходе товара
- Обновлены формы для работы с новой структурой IncomingBatch

Архитектура FIFO:
- IncomingBatch: одна партия поступления (номер IN-XXXX-XXXX)
- Incoming: товар в партии поступления
- StockBatch: одна партия товара на складе (создается для каждого товара)

Это позволяет системе правильно применять FIFO при продаже товаров.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 03:26:06 +03:00

104 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
from django.shortcuts import render
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, DetailView
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib import messages
from ..models import Sale, SaleBatchAllocation
from ..forms import SaleForm
class SaleListView(LoginRequiredMixin, ListView):
"""
Список всех продаж товара (истории реализации)
"""
model = Sale
template_name = 'inventory/sale/sale_list.html'
context_object_name = 'sales'
paginate_by = 20
def get_queryset(self):
queryset = Sale.objects.select_related('product', 'warehouse', 'order').order_by('-date')
# Фильтры (если переданы)
product_id = self.request.GET.get('product')
warehouse_id = self.request.GET.get('warehouse')
processed = self.request.GET.get('processed')
if product_id:
queryset = queryset.filter(product_id=product_id)
if warehouse_id:
queryset = queryset.filter(warehouse_id=warehouse_id)
if processed:
queryset = queryset.filter(processed=processed == 'true')
return queryset
class SaleCreateView(LoginRequiredMixin, CreateView):
"""
Регистрация новой продажи товара.
После сохранения автоматически применяется FIFO (через сигнал).
"""
model = Sale
form_class = SaleForm
template_name = 'inventory/sale/sale_form.html'
success_url = reverse_lazy('inventory:sale-list')
def form_valid(self, form):
messages.success(
self.request,
f'Продажа товара "{form.instance.product.name}" ({form.instance.quantity} шт) успешно зарегистрирована.'
)
return super().form_valid(form)
class SaleDetailView(LoginRequiredMixin, DetailView):
"""
Просмотр деталей продажи с распределением по партиям.
Показывает SaleBatchAllocation для данной продажи.
"""
model = Sale
template_name = 'inventory/sale/sale_detail.html'
context_object_name = 'sale'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Получаем все распределения этой продажи по партиям
context['allocations'] = SaleBatchAllocation.objects.filter(
sale=self.object
).select_related('batch', 'batch__product')
return context
class SaleUpdateView(LoginRequiredMixin, UpdateView):
"""
Редактирование продажи (только если ещё не обработана).
Обработанные продажи редактировать нельзя.
"""
model = Sale
form_class = SaleForm
template_name = 'inventory/sale/sale_form.html'
success_url = reverse_lazy('inventory:sale-list')
def form_valid(self, form):
messages.success(self.request, f'Продажа товара обновлена.')
return super().form_valid(form)
class SaleDeleteView(LoginRequiredMixin, DeleteView):
"""
Отмена/удаление продажи товара.
"""
model = Sale
template_name = 'inventory/sale/sale_confirm_delete.html'
success_url = reverse_lazy('inventory:sale-list')
def form_valid(self, form):
sale = self.get_object()
messages.success(
self.request,
f'Продажа товара "{sale.product.name}" отменена.'
)
return super().form_valid(form)