# Статус исправления багов VAT Operations Import **Дата:** 2026-01-13 **Статус лимита токенов:** ~73K остаток --- ## ✅ ИСПРАВЛЕНО И РАБОТАЕТ ### БАГ 1: Фильтрация операций (income без expense) **Статус:** ✅ РАБОТАЕТ **Исправление:** Добавлена фильтрация в `vat_api.py` в функцию `get_vat_operations()` (строки 124-143) **Проверено:** Пользователь подтвердил - фильтр работает --- ## ❌ НЕ РАБОТАЕТ / ЕСТЬ ПРОБЛЕМЫ ### БАГ 2: Визуальные баги (прогресс-бар, алерты) **Статус:** ⚠️ ЧАСТИЧНО (требует проверки после bench build) **Что исправлено в коде:** - `journal_entry.js` строка 1198: `currentIndex - 1` → `currentIndex` - `journal_entry.js` строки 1237-1239: убраны индивидуальные `frappe.show_alert()` во время импорта **Почему может не работать:** - Нужно выполнить `bench build --app invoice_az` чтобы скомпилировать JavaScript - Нужно обновить страницу в браузере (Ctrl+F5) **Проблема:** Пользователь не проверял после исправлений --- ### БАГ 3: Детальные ошибки не отображаются **Статус:** ❌ НЕ РАБОТАЕТ **Проблема:** При "Import Completed with Errors" нет информации о том, что именно пошло не так **Что исправлено в коде:** 1. **vat_api.py** (строки 489-513): Добавлены поля `tin`, `customer_name`, `income`, `operation_date`, `operation_type`, `account_number`, `missing_tin` в errors 2. **journal_entry.js** (строка 1242): Изменено с `VATETaxes.errors.add(operation, error.error_type, error.message)` на `VATETaxes.errors.add(operation, error.error_type, error.message, error)` - передается полный объект 3. **journal_entry.js** (строки 628-654): Добавлено отображение Amount, Customer Name, Missing Account, Missing TIN **Почему не работает:** - JavaScript не пересобран: нужен `bench build --app invoice_az` - Кэш браузера: нужен Ctrl+F5 **Как проверить что работает:** 1. Импортировать операцию с несуществующим TIN 2. Кликнуть "View Error Details" 3. Должны видеть: - **Amount:** сумма в AZN - **Customer Name:** название клиента из операции - **Missing TIN:** TIN который не найден - **Missing Account:** номер счета (226 или 211) который отсутствует --- ### БАГ 4: ПАРАДОКС УДАЛЕНИЯ (КРИТИЧЕСКАЯ ПРОБЛЕМА!) **Статус:** 🔴 КРИТИЧЕСКИЙ БАГ - СОЗДАН DEADLOCK **Проблема:** 1. Нельзя удалить **E-Taxes VAT Operations** → выходит ошибка "Cannot delete... because it is linked with Journal Entry" 2. Нельзя удалить **Journal Entry** → при попытке удалить JE, система пытается удалить связанный E-Taxes VAT Operations 3. E-Taxes VAT Operations выбрасывает ошибку из `on_trash()` → удаление JE прерывается 4. **РЕЗУЛЬТАТ: Невозможно удалить ни JE, ни VAT Operations!** **Текущий код:** **e_taxes_vat_operations.py** (строки 10-19): ```python def on_trash(self): """Защита от удаления если связано с Journal Entry""" if self.journal_entry: # Проверяем, существует ли Journal Entry if frappe.db.exists("Journal Entry", self.journal_entry): frappe.throw( _("Cannot delete or cancel E-Taxes VAT Operations {0} because it is linked with Journal Entry {1}").format( self.name, self.journal_entry ) ) ``` **vat_operations.py** (строки 28-34): ```python # Удаляем с force=True чтобы обойти before_delete() frappe.delete_doc( 'E-Taxes VAT Operations', record.name, ignore_permissions=True, force=True # ⚠️ НЕ ОБХОДИТ on_trash!!! ) ``` **ПРОБЛЕМА:** `force=True` игнорирует только permissions, но **НЕ** игнорирует `on_trash()`! --- ## 🔧 РЕШЕНИЯ ДЛЯ БАГА 4 (ПАРАДОКС УДАЛЕНИЯ) ### Вариант 1: Использовать frappe.db.delete() (РЕКОМЕНДУЕТСЯ) **Изменить `vat_operations.py`:** ```python # ВМЕСТО: frappe.delete_doc('E-Taxes VAT Operations', record.name, ignore_permissions=True, force=True) # ИСПОЛЬЗОВАТЬ: frappe.db.delete('E-Taxes VAT Operations', {'name': record.name}) frappe.db.commit() ``` **Плюсы:** - Прямое удаление из БД, обходит все хуки - Простое и надежное **Минусы:** - Не запускает cascade delete для связанных документов (но у VAT Operations нет связанных) --- ### Вариант 2: Проверка контекста в on_trash() **Изменить `e_taxes_vat_operations.py`:** ```python def on_trash(self): """Защита от удаления если связано с Journal Entry""" # Проверяем флаг принудительного удаления if getattr(self, '_skip_trash_validation', False): return if self.journal_entry: if frappe.db.exists("Journal Entry", self.journal_entry): frappe.throw( _("Cannot delete or cancel E-Taxes VAT Operations {0} because it is linked with Journal Entry {1}").format( self.name, self.journal_entry ) ) ``` **Изменить `vat_operations.py`:** ```python for record in vat_records: # Получаем документ doc = frappe.get_doc('E-Taxes VAT Operations', record.name) # Устанавливаем флаг для обхода валидации doc._skip_trash_validation = True # Удаляем doc.delete(ignore_permissions=True, force=True) ``` **Плюсы:** - Сохраняет логику валидации для ручного удаления - Разрешает каскадное удаление из кода **Минусы:** - Более сложная логика - Использует "внутренний" флаг --- ### Вариант 3: Изменить логику защиты (САМЫЙ ПРАВИЛЬНЫЙ) **Идея:** Разрешить удаление VAT Operations всегда, но проверять только если пытаемся удалить ВРУЧНУЮ (не программно) **Изменить `e_taxes_vat_operations.py`:** ```python def on_trash(self): """Защита от удаления если связано с Journal Entry""" # Пропускаем проверку если удаление идет из кода (не от пользователя) if frappe.flags.in_test or frappe.flags.in_migrate: return # Проверяем только при ручном удалении if self.journal_entry: if frappe.db.exists("Journal Entry", self.journal_entry): frappe.throw( _("Cannot delete E-Taxes VAT Operations {0} because it is linked with Journal Entry {1}. " "Please delete the Journal Entry first.").format( self.name, self.journal_entry ) ) ``` **НО:** Это не различает программное удаление от ручного! Все равно заблокирует. --- ### Вариант 4: Вообще убрать защиту от удаления **Просто удалить метод `on_trash()` из `e_taxes_vat_operations.py`** **Плюсы:** - Просто - Нет парадокса **Минусы:** - Пользователь может случайно удалить VAT Operations вручную - Нарушится связь с Journal Entry --- ## 📋 РЕКОМЕНДУЕМОЕ РЕШЕНИЕ ### Использовать **Вариант 1** - frappe.db.delete() **Файл:** `/home/frappe/frappe-bench/apps/invoice_az/invoice_az/vat_operations.py` **Заменить строки 28-34:** ```python # СТАРЫЙ КОД (не работает): frappe.delete_doc( 'E-Taxes VAT Operations', record.name, ignore_permissions=True, force=True ) # НОВЫЙ КОД: frappe.db.delete('E-Taxes VAT Operations', {'name': record.name}) ``` **Полная функция:** ```python @frappe.whitelist() def on_delete_journal_entry(doc, method): """Удаляет связанные E-Taxes VAT Operations при удалении Journal Entry""" try: vat_records = frappe.get_all( "E-Taxes VAT Operations", filters={"journal_entry": doc.name}, fields=["name"] ) if vat_records: for record in vat_records: frappe.logger().info( f"Deleting E-Taxes VAT Operations {record.name} " f"due to Journal Entry {doc.name} deletion" ) # Прямое удаление из БД (обходит on_trash) frappe.db.delete('E-Taxes VAT Operations', {'name': record.name}) frappe.db.commit() frappe.logger().info(f"Successfully deleted {len(vat_records)} VAT Operations records") except Exception as e: frappe.log_error( f"Error deleting E-Taxes VAT Operations for Journal Entry {doc.name}: {str(e)}\n{frappe.get_traceback()}", "Journal Entry Delete Error" ) ``` **После исправления:** ```bash bench restart ``` **Тест:** 1. Импортировать операцию → создается JE + VAT Operations 2. Попытаться удалить VAT Operations вручную → ошибка (защита работает) 3. Удалить Journal Entry → VAT Operations автоматически удаляется (каскад работает) --- ## 📝 КОМАНДЫ ДЛЯ ПРИМЕНЕНИЯ ВСЕХ ИСПРАВЛЕНИЙ ```bash # 1. Очистить кэш bench clear-cache # 2. Собрать JavaScript (для БАГ 2 и БАГ 3) bench build --app invoice_az # 3. Перезапустить сервер (для всех Python изменений) bench restart # 4. В браузере: Ctrl+F5 (полная перезагрузка страницы) ``` --- ## 🧪 ТЕСТИРОВАНИЕ ПОСЛЕ ИСПРАВЛЕНИЙ ### Тест 1: Фильтрация ✅ **Работает** - пользователь подтвердил ### Тест 2: Визуальные баги **Проверить:** 1. Импортировать 5-10 операций 2. Прогресс-бар должен идти от 0% до 100% плавно 3. Не должно быть зеленых алертов во время импорта 4. Только один диалог в конце ### Тест 3: Детальные ошибки **Проверить:** 1. Импортировать операцию с несуществующим TIN 2. Кликнуть "View Error Details" 3. Должны увидеть: Amount, Customer Name, Missing TIN 4. Экспорт в CSV должен содержать эти поля ### Тест 4: Удаление (после исправления) **Проверить:** 1. Импортировать операцию → создается JE + VAT Operations 2. Открыть VAT Operations напрямую → попытаться удалить → должна быть ошибка 3. Открыть Journal Entry → удалить → JE удален + VAT Operations автоматически удален 4. Проверить что VAT Operations больше нет в списке --- ## 📊 ИТОГОВАЯ СТАТИСТИКА | Баг | Статус | Что нужно | |-----|--------|-----------| | **1. Фильтрация** | ✅ Работает | - | | **2. Визуальные баги** | ⚠️ Требует bench build | `bench build --app invoice_az` | | **3. Детальные ошибки** | ⚠️ Требует bench build | `bench build --app invoice_az` | | **4. Парадокс удаления** | 🔴 Критический | Заменить `frappe.delete_doc` на `frappe.db.delete` | --- ## 📁 ИЗМЕНЕННЫЕ ФАЙЛЫ 1. ✅ `/home/frappe/frappe-bench/apps/invoice_az/invoice_az/vat_api.py` - Строки 124-143: фильтрация в `get_vat_operations()` - Строки 489-513: детальные ошибки в `import_vat_operations()` 2. ✅ `/home/frappe/frappe-bench/apps/invoice_az/invoice_az/client/journal_entry.js` - Строка 1198: исправлен прогресс-бар - Строки 1237-1239: убраны алерты - Строка 1242: передача полного объекта ошибки - Строки 628-654: отображение детальной информации 3. ✅ `/home/frappe/frappe-bench/apps/invoice_az/invoice_az/invoice_az/doctype/e_taxes_vat_operations/e_taxes_vat_operations.py` - Строки 10-19: защита от удаления через `on_trash()` 4. ✅ `/home/frappe/frappe-bench/apps/invoice_az/invoice_az/vat_operations.py` - Создан новый файл с каскадным удалением - ⚠️ **ТРЕБУЕТ ИСПРАВЛЕНИЯ** - заменить `frappe.delete_doc` на `frappe.db.delete` 5. ✅ `/home/frappe/frappe-bench/apps/invoice_az/invoice_az/hooks.py` - Добавлен хук для Journal Entry `on_trash` и `on_cancel` --- ## 🎯 ПРИОРИТЕТ ИСПРАВЛЕНИЙ 1. **ВЫСОКИЙ:** БАГ 4 - Парадокс удаления (блокирует удаление документов) 2. **СРЕДНИЙ:** БАГ 3 - Детальные ошибки (нужно для отладки) 3. **НИЗКИЙ:** БАГ 2 - Визуальные баги (косметические) --- **Конец документа**