280 lines
9.5 KiB
Markdown
280 lines
9.5 KiB
Markdown
# Исправление: HTTP Status 201 ошибочно считается ошибкой
|
||
|
||
**Дата:** 2026-01-28
|
||
**Статус:** ✅ Исправлено во всех файлах
|
||
|
||
---
|
||
|
||
## Проблема
|
||
|
||
### Симптомы
|
||
- После успешной отправки документа в E-Taxes показывается ошибка
|
||
- Сообщение: "E-Taxes error (status 201)"
|
||
- Но на самом деле документ создан успешно
|
||
|
||
### Причина
|
||
HTTP статус **201 (Created)** - это успешный ответ, который означает что ресурс был создан.
|
||
|
||
Но код проверял только статус **200 (OK)**:
|
||
```python
|
||
if response.status_code != 200:
|
||
# Ошибка
|
||
```
|
||
|
||
### Почему это происходит?
|
||
E-Taxes API возвращает:
|
||
- **200 (OK)** - для операций чтения/обновления
|
||
- **201 (Created)** - для операций создания новых документов
|
||
|
||
Оба статуса означают успех, но старый код считал 201 ошибкой.
|
||
|
||
---
|
||
|
||
## Решение
|
||
|
||
Изменена проверка HTTP статуса во всех файлах:
|
||
|
||
**Было:**
|
||
```python
|
||
if response.status_code != 200:
|
||
return {"success": False, "message": f"E-Taxes error (status {response.status_code})"}
|
||
```
|
||
|
||
**Стало:**
|
||
```python
|
||
# Accept both 200 (OK) and 201 (Created) as success
|
||
if response.status_code not in [200, 201]:
|
||
return {"success": False, "message": f"E-Taxes error (status {response.status_code})"}
|
||
```
|
||
|
||
---
|
||
|
||
## Файлы исправлены
|
||
|
||
### 1. **send_purchase_api.py** (строка 586)
|
||
**Функция:** `send_agricultural_act_to_etaxes()`
|
||
**Когда вызывается:** Отправка Purchase Invoice (Agricultural Act) в E-Taxes
|
||
|
||
**Изменение:**
|
||
```python
|
||
# Line 586: Changed status check
|
||
if response.status_code not in [200, 201]:
|
||
```
|
||
|
||
### 2. **send_sales_api.py** (строка 588)
|
||
**Функция:** `send_sales_invoice_to_etaxes()`
|
||
**Когда вызывается:** Отправка Sales Invoice в E-Taxes
|
||
|
||
**Изменение:**
|
||
```python
|
||
# Line 588: Changed status check
|
||
if response.status_code not in [200, 201]:
|
||
```
|
||
|
||
### 3. **supplier_api.py** (строка 119)
|
||
**Функция:** `get_supplier_from_etaxes()`
|
||
**Когда вызывается:** Получение данных о поставщике из E-Taxes
|
||
|
||
**Изменение:**
|
||
```python
|
||
# Line 119: Changed status check
|
||
if response.status_code not in [200, 201]:
|
||
```
|
||
|
||
---
|
||
|
||
## HTTP Статусы E-Taxes API
|
||
|
||
| Код | Название | Значение | Обработка |
|
||
|-----|----------|----------|-----------|
|
||
| 200 | OK | Успешная операция | ✅ Успех |
|
||
| 201 | Created | Ресурс создан | ✅ Успех (после исправления) |
|
||
| 401 | Unauthorized | Токен истек | ❌ "Authentication expired" |
|
||
| 404 | Not Found | Не найдено | ❌ "Not found" |
|
||
| 500 | Internal Server Error | Ошибка сервера | ❌ "Service error" |
|
||
|
||
---
|
||
|
||
## Тестирование
|
||
|
||
### Purchase Invoice (Agricultural Act)
|
||
|
||
**Тест 1:** Отправить Purchase Invoice в E-Taxes
|
||
1. Создать Purchase Invoice с `agricultural_goods = True`
|
||
2. Добавить items с `tax_type = "Taxable"`
|
||
3. Submit и нажать "Send Agricultural Act to E-Taxes"
|
||
|
||
**Ожидание:**
|
||
- ✅ Акт создается успешно
|
||
- ✅ Статус меняется на "Created, not signed"
|
||
- ✅ Не показывается ошибка про status 201
|
||
|
||
### Sales Invoice
|
||
|
||
**Тест 2:** Отправить Sales Invoice в E-Taxes
|
||
1. Создать Sales Invoice
|
||
2. Submit и нажать "Send to E-Taxes"
|
||
|
||
**Ожидание:**
|
||
- ✅ Invoice создается успешно
|
||
- ✅ Статус меняется на "Created, not signed"
|
||
- ✅ Не показывается ошибка про status 201
|
||
|
||
### Supplier
|
||
|
||
**Тест 3:** Получить данные поставщика
|
||
1. Открыть Supplier формы
|
||
2. Нажать "Get from E-Taxes"
|
||
|
||
**Ожидание:**
|
||
- ✅ Данные загружаются успешно
|
||
- ✅ Не показывается ошибка про status 201
|
||
|
||
---
|
||
|
||
## Логика обработки ошибок
|
||
|
||
После исправления:
|
||
|
||
```python
|
||
def handle_etaxes_response(response):
|
||
# 1. Проверить 401 (Authentication expired)
|
||
if response.status_code == 401:
|
||
return {"success": False, "message": "Authentication expired"}
|
||
|
||
# 2. Проверить 500 (Server error)
|
||
if response.status_code == 500:
|
||
return {"success": False, "message": "Server error"}
|
||
|
||
# 3. Проверить 404 (Not found) - только для некоторых API
|
||
if response.status_code == 404:
|
||
return {"success": False, "message": "Not found"}
|
||
|
||
# 4. Принять 200 и 201 как успех
|
||
if response.status_code not in [200, 201]:
|
||
# Попытаться получить сообщение об ошибке
|
||
try:
|
||
error_data = response.json()
|
||
# ... parse error message
|
||
except:
|
||
pass
|
||
return {"success": False, "message": f"E-Taxes error (status {response.status_code})"}
|
||
|
||
# 5. Успех!
|
||
result = response.json()
|
||
return {"success": True, "data": result}
|
||
```
|
||
|
||
---
|
||
|
||
## Развертывание
|
||
|
||
```bash
|
||
# Очистить кэш Python
|
||
cd /home/frappe/frappe-bench
|
||
bench --site site1 clear-cache
|
||
|
||
# Перезапустить (если в production)
|
||
bench restart
|
||
```
|
||
|
||
**Примечание:** Не нужно пересобирать JavaScript, т.к. изменения только в Python.
|
||
|
||
---
|
||
|
||
## Дополнительная информация
|
||
|
||
### Почему E-Taxes возвращает 201?
|
||
|
||
**REST API стандарт:**
|
||
- **POST** запрос для создания нового ресурса должен возвращать **201 (Created)**
|
||
- **GET** запрос возвращает **200 (OK)**
|
||
- **PUT/PATCH** запросы возвращают **200 (OK)**
|
||
|
||
**E-Taxes API:**
|
||
- `POST /api/po/invoice/public/v1/act` → создает новый act → **201**
|
||
- `POST /api/po/invoice/public/v2/invoice` → создает новый invoice → **201**
|
||
- `POST /api/po/invoice/public/v1/invoice/sign/withAsanImza` → подписывает → **200**
|
||
|
||
### HTTP Status Code Categories
|
||
|
||
| Категория | Диапазон | Значение |
|
||
|-----------|----------|----------|
|
||
| 1xx | 100-199 | Информационные |
|
||
| 2xx | 200-299 | **Успешные** ✅ |
|
||
| 3xx | 300-399 | Перенаправления |
|
||
| 4xx | 400-499 | Ошибки клиента ❌ |
|
||
| 5xx | 500-599 | Ошибки сервера ❌ |
|
||
|
||
**Важно:** Все статусы 2xx (200-299) означают успех!
|
||
|
||
### Другие успешные статусы (на будущее)
|
||
|
||
Если E-Taxes начнет использовать другие 2xx статусы:
|
||
- **202 (Accepted)** - запрос принят, обработка в процессе
|
||
- **204 (No Content)** - успех, но нет данных для возврата
|
||
|
||
Можно расширить проверку:
|
||
```python
|
||
if 200 <= response.status_code < 300:
|
||
# Успех (все 2xx статусы)
|
||
```
|
||
|
||
---
|
||
|
||
## Проверка после исправления
|
||
|
||
### 1. Проверить логи
|
||
```bash
|
||
bench --site site1 logs
|
||
```
|
||
Не должно быть ошибок с "status 201"
|
||
|
||
### 2. Проверить в UI
|
||
- Создать и отправить Purchase Invoice → не должно быть ошибки
|
||
- Создать и отправить Sales Invoice → не должно быть ошибки
|
||
- Получить данные Supplier → не должно быть ошибки
|
||
|
||
### 3. Проверить E-Taxes Outbox
|
||
Документы должны создаваться со статусом "Created, not signed"
|
||
|
||
---
|
||
|
||
## История изменений
|
||
|
||
| Дата | Файл | Строка | Изменение |
|
||
|------|------|--------|-----------|
|
||
| 2026-01-28 | send_purchase_api.py | 586 | Добавлен 201 в успешные статусы |
|
||
| 2026-01-28 | send_sales_api.py | 588 | Добавлен 201 в успешные статусы |
|
||
| 2026-01-28 | supplier_api.py | 119 | Добавлен 201 в успешные статусы |
|
||
|
||
---
|
||
|
||
## Ссылки
|
||
|
||
- **RFC 7231 (HTTP Semantics):** https://tools.ietf.org/html/rfc7231#section-6.3.2
|
||
- **MDN: HTTP Status Codes:** https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
|
||
- **REST API Best Practices:** POST requests should return 201 Created
|
||
|
||
---
|
||
|
||
## FAQ
|
||
|
||
**Q: Почему раньше это не было проблемой?**
|
||
A: Возможно E-Taxes API раньше возвращал 200, или это новая функция.
|
||
|
||
**Q: Нужно ли проверять другие 2xx статусы?**
|
||
A: Пока нет, но можно расширить до `if 200 <= status < 300` на будущее.
|
||
|
||
**Q: Влияет ли это на старые документы?**
|
||
A: Нет, это только исправляет создание новых документов.
|
||
|
||
**Q: Нужно ли пересобирать frontend?**
|
||
A: Нет, изменения только в Python коде.
|
||
|
||
---
|
||
|
||
**Статус:** ✅ Все файлы исправлены и протестированы
|
||
**Приоритет:** Высокий (блокирует отправку документов)
|
||
**Результат:** Документы теперь успешно создаются без ложных ошибок
|