diff --git a/myproject/inventory/services/showcase_manager.py b/myproject/inventory/services/showcase_manager.py index 201c916..6e09493 100644 --- a/myproject/inventory/services/showcase_manager.py +++ b/myproject/inventory/services/showcase_manager.py @@ -666,6 +666,113 @@ class ShowcaseManager: 'message': f'Ошибка разбора: {str(e)}' } + @staticmethod + def write_off_from_showcase(showcase_item, reason='spoilage', notes=None, created_by=None): + """ + Списывает экземпляр витринного комплекта: + 1. Создаёт документ списания с компонентами комплекта + 2. Преобразует резервы комплекта в позиции документа списания + 3. Помечает экземпляр как разобранный + + Args: + showcase_item: ShowcaseItem - экземпляр для списания + reason: str - причина списания (spoilage по умолчанию) + notes: str - примечания + created_by: User - пользователь + + Returns: + dict: { + 'success': bool, + 'document_id': int, + 'document_number': str, + 'items_count': int, + 'message': str, + 'error': str (при ошибке) + } + """ + from inventory.services.writeoff_document_service import WriteOffDocumentService + + # Проверка статуса + if showcase_item.status == 'sold': + return { + 'success': False, + 'document_id': None, + 'message': 'Нельзя списать проданный экземпляр' + } + + if showcase_item.status == 'dismantled': + return { + 'success': False, + 'document_id': None, + 'message': 'Экземпляр уже разобран' + } + + try: + with transaction.atomic(): + warehouse = showcase_item.showcase.warehouse + product_kit = showcase_item.product_kit + + # Создаём документ списания (черновик) + document = WriteOffDocumentService.create_document( + warehouse=warehouse, + date=timezone.now().date(), + notes=f'Списание витринного комплекта: {product_kit.name}', + created_by=created_by + ) + + # Получаем резервы этого экземпляра + reservations = Reservation.objects.filter( + showcase_item=showcase_item, + status='reserved' + ).select_related('product') + + items_count = 0 + + for reservation in reservations: + # Добавляем позицию в документ списания + # Используем add_item без создания резерва (меняем статус существующего) + from inventory.models import WriteOffDocumentItem + + item = WriteOffDocumentItem.objects.create( + document=document, + product=reservation.product, + quantity=reservation.quantity, + reason=reason, + notes=notes + ) + + # Привязываем существующий резерв к позиции документа + reservation.writeoff_document_item = item + reservation.status = 'converted_to_writeoff' + reservation.converted_at = timezone.now() + reservation.save(update_fields=['writeoff_document_item', 'status', 'converted_at']) + + items_count += 1 + + # Помечаем экземпляр как разобранный + showcase_item.status = 'dismantled' + showcase_item.save(update_fields=['status']) + + # Помечаем шаблон комплекта как снятый + if product_kit.status != 'discontinued': + product_kit.status = 'discontinued' + product_kit.save(update_fields=['status']) + + return { + 'success': True, + 'document_id': document.id, + 'document_number': document.document_number, + 'items_count': items_count, + 'message': f'Создан документ {document.document_number} с {items_count} позициями' + } + + except Exception as e: + return { + 'success': False, + 'document_id': None, + 'message': f'Ошибка списания: {str(e)}' + } + @staticmethod def get_showcase_items_for_pos(showcase=None): """ diff --git a/myproject/pos/static/pos/js/terminal.js b/myproject/pos/static/pos/js/terminal.js index 2512642..0665b26 100644 --- a/myproject/pos/static/pos/js/terminal.js +++ b/myproject/pos/static/pos/js/terminal.js @@ -1923,6 +1923,7 @@ async function openEditKitModal(kitId) { // По��азываем кнопку "Разобрать" и блок добавления товаров document.getElementById('disassembleKitBtn').style.display = 'block'; + document.getElementById('writeOffKitBtn').style.display = 'block'; document.getElementById('showcaseKitQuantityBlock').style.display = 'none'; document.getElementById('addProductBlock').style.display = 'block'; @@ -2554,6 +2555,53 @@ document.getElementById('disassembleKitBtn').addEventListener('click', async () } }); +// Обработчик кнопки "Списать букет" +document.getElementById('writeOffKitBtn').addEventListener('click', async () => { + if (!isEditMode || !editingKitId) { + alert('Ошибка: режим редактирования не активен'); + return; + } + + // Запрос подтверждения + const confirmed = confirm( + 'Вы уверены?\n\n' + + 'Букет будет списан:\n' + + '• Будет создан документ списания с компонентами букета\n' + + '• Комплект будет помечен как "Снят"\n' + + '• Будет открыта страница документа для редактирования\n\n' + + 'Продолжить?' + ); + + if (!confirmed) { + return; + } + + try { + const response = await fetch(`/pos/api/product-kits/${editingKitId}/write-off/`, { + method: 'POST', + headers: { + 'X-CSRFToken': getCsrfToken() + } + }); + + const data = await response.json(); + + if (data.success) { + // Закрываем модальное окно + const modal = bootstrap.Modal.getInstance(document.getElementById('createTempKitModal')); + modal.hide(); + + // Перенаправляем на страницу документа + window.location.href = data.redirect_url; + } else { + alert(`❌ Ошибка: ${data.error}`); + } + } catch (error) { + console.error('Error writing off kit:', error); + alert('Произошла ошибка при списании букета'); + } +}); + // Вспомогательная функция для определения мобильного устройства function isMobileDevice() { // Проверяем по юзер-агенту и размеру экрана @@ -2616,8 +2664,9 @@ document.getElementById('createTempKitModal').addEventListener('hidden.bs.modal' document.getElementById('createTempKitModalLabel').textContent = 'Создать витринный букет из корзины'; document.getElementById('confirmCreateTempKit').innerHTML = ' Создать и зарезервировать'; - // Скрываем кнопку "Разобрать" и блок добавления товаров + // Скрываем кнопки "Разобрать" и "Списать" и блок добавления товаров document.getElementById('disassembleKitBtn').style.display = 'none'; + document.getElementById('writeOffKitBtn').style.display = 'none'; document.getElementById('showcaseKitQuantityBlock').style.display = 'block'; document.getElementById('addProductBlock').style.display = 'none'; } diff --git a/myproject/pos/templates/pos/terminal.html b/myproject/pos/templates/pos/terminal.html index 05682d4..a1ba5f9 100644 --- a/myproject/pos/templates/pos/terminal.html +++ b/myproject/pos/templates/pos/terminal.html @@ -330,6 +330,11 @@ Разобрать букет + + +