diff --git a/myproject/inventory/forms.py b/myproject/inventory/forms.py
index fd2a074..03b03db 100644
--- a/myproject/inventory/forms.py
+++ b/myproject/inventory/forms.py
@@ -68,41 +68,7 @@ class SaleForm(forms.ModelForm):
return sale_price
-class WriteOffForm(forms.ModelForm):
- class Meta:
- model = WriteOff
- fields = ['batch', 'quantity', 'reason', 'document_number', 'notes']
- widgets = {
- 'batch': forms.Select(attrs={'class': 'form-control'}),
- 'quantity': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.001'}),
- 'reason': forms.Select(attrs={'class': 'form-control'}),
- 'document_number': forms.TextInput(attrs={'class': 'form-control'}),
- 'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
- }
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- # Фильтруем партии - показываем только активные
- self.fields['batch'].queryset = StockBatch.objects.filter(
- is_active=True
- ).select_related('product', 'warehouse').order_by('-created_at')
-
- def clean(self):
- cleaned_data = super().clean()
- batch = cleaned_data.get('batch')
- quantity = cleaned_data.get('quantity')
-
- if batch and quantity:
- if quantity > batch.quantity:
- raise ValidationError(
- f'Невозможно списать {quantity} шт из партии, '
- f'где только {batch.quantity} шт. '
- f'Недостаток: {quantity - batch.quantity} шт.'
- )
- if quantity <= 0:
- raise ValidationError('Количество должно быть больше нуля')
-
- return cleaned_data
+# WriteOffForm удалён - используйте WriteOffDocumentForm для работы с документами списания
class InventoryForm(forms.ModelForm):
diff --git a/myproject/inventory/templates/inventory/base_inventory_minimal.html b/myproject/inventory/templates/inventory/base_inventory_minimal.html
index b3179c8..1aab649 100644
--- a/myproject/inventory/templates/inventory/base_inventory_minimal.html
+++ b/myproject/inventory/templates/inventory/base_inventory_minimal.html
@@ -26,7 +26,6 @@
diff --git a/myproject/inventory/templates/inventory/sale/sale_list.html b/myproject/inventory/templates/inventory/sale/sale_list.html
index b0e0a28..b843e93 100644
--- a/myproject/inventory/templates/inventory/sale/sale_list.html
+++ b/myproject/inventory/templates/inventory/sale/sale_list.html
@@ -20,6 +20,7 @@
| Склад |
Количество |
Цена продажи |
+
Сумма |
Заказ |
Статус |
Дата |
@@ -33,6 +34,7 @@
{{ sale.warehouse.name }} |
{{ sale.quantity|smart_quantity }} шт |
{{ sale.sale_price }} руб. |
+
{{ sale.quantity|multiply:sale.sale_price|format_decimal:2 }} руб. |
{% if sale.order %}
{{ sale.order.order_number }}
diff --git a/myproject/inventory/templates/inventory/writeoff/writeoff_confirm_delete.html b/myproject/inventory/templates/inventory/writeoff/writeoff_confirm_delete.html
deleted file mode 100644
index c6917d1..0000000
--- a/myproject/inventory/templates/inventory/writeoff/writeoff_confirm_delete.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends 'inventory/base_inventory_minimal.html' %}
-{% block inventory_title %}Отмена списания{% endblock %}
-{% block breadcrumb_current %}Списания{% endblock %}
-{% block inventory_content %}
-
-
-
- Внимание! Вы собираетесь отменить списание товара.
-
-
-
-{% endblock %}
diff --git a/myproject/inventory/templates/inventory/writeoff/writeoff_form.html b/myproject/inventory/templates/inventory/writeoff/writeoff_form.html
deleted file mode 100644
index 0fc340d..0000000
--- a/myproject/inventory/templates/inventory/writeoff/writeoff_form.html
+++ /dev/null
@@ -1,335 +0,0 @@
-{% extends 'inventory/base_inventory_minimal.html' %}
-{% load static inventory_filters %}
-{% block inventory_title %}{% if form.instance.pk %}Редактирование списания{% else %}Новое списание{% endif %}{% endblock %}
-{% block breadcrumb_current %}Списания{% endblock %}
-
-{% block inventory_content %}
-
-
-
-
-
-
- {% include 'products/components/product_search_picker.html' with container_id='writeoff-product-picker' title='Найти товар для списания' filter_in_stock_only=True categories=categories tags=tags add_button_text='Выбрать товар' multi_select=False content_height='350px' %}
-
-
-
-
-
-
-
-
- {% if form.non_field_errors %}
-
- Ошибка:
- {% for error in form.non_field_errors %}
- {{ error }}
- {% endfor %}
-
-
- {% endif %}
-
-
-
-
- ![]()
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-{% endblock %}
diff --git a/myproject/inventory/templates/inventory/writeoff/writeoff_list.html b/myproject/inventory/templates/inventory/writeoff/writeoff_list.html
deleted file mode 100644
index f15fbb1..0000000
--- a/myproject/inventory/templates/inventory/writeoff/writeoff_list.html
+++ /dev/null
@@ -1,51 +0,0 @@
-{% extends 'inventory/base_inventory_minimal.html' %}
-{% load inventory_filters %}
-{% block inventory_title %}История списаний{% endblock %}
-{% block breadcrumb_current %}Списания{% endblock %}
-{% block inventory_content %}
-
-
-
- {% if writeoffs %}
-
-
-
-
- | Товар |
- Количество |
- Причина |
- Дата |
- Действия |
-
-
-
- {% for writeoff in writeoffs %}
-
- | {{ writeoff.batch.product.name }} |
- {{ writeoff.quantity|smart_quantity }} шт |
- {{ writeoff.get_reason_display }} |
- {{ writeoff.date|date:"d.m.Y H:i" }} |
-
-
-
-
-
-
-
- |
-
- {% endfor %}
-
-
-
- {% else %}
- Списаний не найдено.
- {% endif %}
-
-
-{% endblock %}
diff --git a/myproject/inventory/templatetags/inventory_filters.py b/myproject/inventory/templatetags/inventory_filters.py
index e837b4e..d9974fe 100644
--- a/myproject/inventory/templatetags/inventory_filters.py
+++ b/myproject/inventory/templatetags/inventory_filters.py
@@ -87,12 +87,60 @@ def format_decimal(value, decimal_places=2):
quantize_value = Decimal(10) ** -decimal_places
rounded = num.quantize(quantize_value)
- # Убираем лишние нули
- normalized = rounded.normalize()
+ # Форматируем без научной нотации
+ result = format(rounded, 'f')
- # Форматируем с запятой
- result = str(normalized).replace('.', ',')
+ # Убираем лишние нули справа после десятичной точки
+ if '.' in result:
+ result = result.rstrip('0').rstrip('.')
+
+ # Форматируем с запятой вместо точки
+ result = result.replace('.', ',')
return result
except (ValueError, TypeError, ArithmeticError):
return str(value)
+
+
+@register.filter(name='multiply')
+def multiply(value, arg):
+ """
+ Умножает значение на аргумент.
+
+ Использование в шаблонах:
+ {{ quantity|multiply:price }}
+
+ Args:
+ value: первое число
+ arg: второе число (множитель)
+
+ Returns:
+ Decimal: результат умножения
+ """
+ try:
+ if value is None or arg is None:
+ return 0
+
+ # Преобразуем в Decimal для точности
+ if isinstance(value, str):
+ val = Decimal(value)
+ elif isinstance(value, (int, float)):
+ val = Decimal(str(value))
+ elif isinstance(value, Decimal):
+ val = value
+ else:
+ val = Decimal(str(value))
+
+ if isinstance(arg, str):
+ multiplier = Decimal(arg)
+ elif isinstance(arg, (int, float)):
+ multiplier = Decimal(str(arg))
+ elif isinstance(arg, Decimal):
+ multiplier = arg
+ else:
+ multiplier = Decimal(str(arg))
+
+ return val * multiplier
+
+ except (ValueError, TypeError, ArithmeticError):
+ return 0
diff --git a/myproject/inventory/urls.py b/myproject/inventory/urls.py
index 0428e57..14e27bb 100644
--- a/myproject/inventory/urls.py
+++ b/myproject/inventory/urls.py
@@ -9,8 +9,6 @@ from .views import (
InventoryListView, InventoryCreateView, InventoryDetailView, InventoryLineCreateBulkView,
InventoryLineAddView, InventoryLineUpdateView, InventoryLineDeleteView,
InventoryCompleteView, InventoryDeleteView,
- # WriteOff
- WriteOffListView, WriteOffCreateView, WriteOffUpdateView, WriteOffDeleteView,
# Transfer
TransferListView, TransferBulkCreateView, TransferDetailView, TransferDeleteView, GetProductStockView,
# Reservation
@@ -83,10 +81,9 @@ urlpatterns = [
path('inventory-ops//delete/', InventoryDeleteView.as_view(), name='inventory-delete'),
# ==================== WRITEOFF (одиночные записи) ====================
- path('writeoffs/', WriteOffListView.as_view(), name='writeoff-list'),
- path('writeoffs/create/', WriteOffCreateView.as_view(), name='writeoff-create'),
- path('writeoffs//edit/', WriteOffUpdateView.as_view(), name='writeoff-update'),
- path('writeoffs//delete/', WriteOffDeleteView.as_view(), name='writeoff-delete'),
+ # УДАЛЕНО: пользователи работают только через WriteOffDocument
+ # Модель WriteOff остаётся - она используется для хранения фактических списаний
+ # Записи WriteOff создаются автоматически при проведении документов списания
# ==================== WRITEOFF DOCUMENT (документы списания) ====================
path('writeoff-documents/', WriteOffDocumentListView.as_view(), name='writeoff-document-list'),
diff --git a/myproject/inventory/views/__init__.py b/myproject/inventory/views/__init__.py
index 992a626..6fa7abc 100644
--- a/myproject/inventory/views/__init__.py
+++ b/myproject/inventory/views/__init__.py
@@ -7,7 +7,7 @@ Inventory Views Package
- incoming.py: Управление приходами товара
- sale.py: Управление продажами
- inventory_ops.py: Инвентаризация и её строки
-- writeoff.py: Списания товара
+- writeoff_document.py: Документы списания товара
- transfer.py: Перемещения между складами
- reservation.py: Резервирования товара (view-only)
- stock.py: Справочник остатков (view-only)
@@ -25,7 +25,6 @@ from .inventory_ops import (
InventoryLineCreateBulkView, InventoryLineAddView, InventoryLineUpdateView,
InventoryLineDeleteView, InventoryCompleteView, InventoryDeleteView
)
-from .writeoff import WriteOffListView, WriteOffCreateView, WriteOffUpdateView, WriteOffDeleteView
from .writeoff_document import (
WriteOffDocumentListView, WriteOffDocumentCreateView, WriteOffDocumentDetailView,
WriteOffDocumentAddItemView, WriteOffDocumentUpdateItemView, WriteOffDocumentRemoveItemView,
@@ -61,8 +60,6 @@ __all__ = [
'InventoryListView', 'InventoryCreateView', 'InventoryDetailView', 'InventoryLineCreateBulkView',
'InventoryLineAddView', 'InventoryLineUpdateView', 'InventoryLineDeleteView',
'InventoryCompleteView', 'InventoryDeleteView',
- # WriteOff
- 'WriteOffListView', 'WriteOffCreateView', 'WriteOffUpdateView', 'WriteOffDeleteView',
# WriteOffDocument
'WriteOffDocumentListView', 'WriteOffDocumentCreateView', 'WriteOffDocumentDetailView',
'WriteOffDocumentAddItemView', 'WriteOffDocumentUpdateItemView', 'WriteOffDocumentRemoveItemView',
diff --git a/myproject/inventory/views/writeoff.py b/myproject/inventory/views/writeoff.py
deleted file mode 100644
index 0443889..0000000
--- a/myproject/inventory/views/writeoff.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-WriteOff (Списание товара) views
-GROUP 2: MEDIUM PRIORITY
-"""
-from django.views.generic import ListView, CreateView, UpdateView, DeleteView
-from django.urls import reverse_lazy
-from django.contrib.auth.mixins import LoginRequiredMixin
-from django.contrib import messages
-from ..models import WriteOff
-from ..forms import WriteOffForm
-from products.models import ProductCategory, ProductTag
-
-
-class WriteOffListView(LoginRequiredMixin, ListView):
- model = WriteOff
- template_name = 'inventory/writeoff/writeoff_list.html'
- context_object_name = 'writeoffs'
- paginate_by = 20
-
- def get_queryset(self):
- return WriteOff.objects.select_related('batch', 'batch__product').order_by('-date')
-
-
-class WriteOffCreateView(LoginRequiredMixin, CreateView):
- model = WriteOff
- form_class = WriteOffForm
- template_name = 'inventory/writeoff/writeoff_form.html'
- success_url = reverse_lazy('inventory:writeoff-list')
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['categories'] = ProductCategory.objects.filter(is_active=True).order_by('name')
- context['tags'] = ProductTag.objects.filter(is_active=True).order_by('name')
- return context
-
- def form_valid(self, form):
- messages.success(self.request, f'Списание товара успешно создано.')
- return super().form_valid(form)
-
-
-class WriteOffUpdateView(LoginRequiredMixin, UpdateView):
- model = WriteOff
- form_class = WriteOffForm
- template_name = 'inventory/writeoff/writeoff_form.html'
- success_url = reverse_lazy('inventory:writeoff-list')
-
- def form_valid(self, form):
- messages.success(self.request, f'Списание товара обновлено.')
- return super().form_valid(form)
-
-
-class WriteOffDeleteView(LoginRequiredMixin, DeleteView):
- model = WriteOff
- template_name = 'inventory/writeoff/writeoff_confirm_delete.html'
- success_url = reverse_lazy('inventory:writeoff-list')
-
- def form_valid(self, form):
- writeoff = self.get_object()
- messages.success(self.request, f'Списание товара отменено.')
- return super().form_valid(form)
|