docs: Добавить документацию по исправлению race condition при загрузке корректировки цены

Добавлены три документа:
1. FINAL_FIX_SUMMARY.md - Финальное резюме исправления
2. ADJUSTMENT_VALUE_FIX_TESTING.md - План тестирования и проверки
3. TECHNICAL_RACE_CONDITION_FIX.md - Глубокий технический анализ проблемы и решения

Документы включают:
- Описание проблемы и решения
- Пошаговый план тестирования (4 сценария)
- Анализ race condition на примере JS event loop
- Трёхуровневая защита от race condition
- Логирование для отладки
- Сравнение подходов синхронизации

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-02 20:29:06 +03:00
parent c7bf23c79c
commit 8bec5823f3
3 changed files with 875 additions and 0 deletions

View File

@@ -0,0 +1,265 @@
# Тестирование исправления загрузки сохранённых значений корректировки цены
## Дата исправления: 2025-11-02
## Коммит: c7bf23c
---
## Описание проблемы (которая была исправлена)
**Проблема:** Сохранённые значения корректировки цены не отображались на странице редактирования комплекта.
- Отображались только в 1 из 10 случаев
- Большую часть времени поля были пустыми
- Когда отображались, то сразу затирались какой-то переинициализацией
**URL для воспроизведения:** `http://grach.localhost:8000/products/kits/4/update/`
**Корневая причина:**
1. При загрузке значений в input-поля срабатывают события `input` и `change`
2. Эти события вызывают `calculateFinalPrice()` и `validateSingleAdjustment()`
3. Функция `calculateFinalPrice()` перезаписывает скрытые поля (`id_price_adjustment_type`, `id_price_adjustment_value`) со значениями по умолчанию
4. Получается race condition: значения загружаются → события срабатывают → значения стираются
---
## Что было исправлено
### Решение: Два уровня защиты от перезаписи
**Уровень 1: Флаг `isLoadingAdjustmentValues`**
- Подавляет события `input` и `change` во время загрузки значений
- Код видит эти события, но пропускает обработку
- Логирует в консоль: "Skipping event during adjustment value loading"
**Уровень 2: Флаг `isInitializing`**
- Даже если событие обработается, `calculateFinalPrice()` не перезапишет скрытые поля
- Проверка: `if (!isInitializing) { adjustmentTypeInput.value = ...; }`
**Уровень 3: `requestAnimationFrame`**
- Гарантирует что `isInitializing = false` устанавливается в конце frame
- Синхронизация с браузерным rendering cycle
### Файлы изменены
**`productkit_edit.html`** (строки 435, 683-696, 912-935)
```javascript
// Строка 435: Добавлен новый флаг
let isLoadingAdjustmentValues = false;
// Строки 683-696: Добавлена проверка в event listeners
input.addEventListener('input', () => {
if (isLoadingAdjustmentValues) {
console.log('Skipping event during adjustment value loading');
return;
}
validateSingleAdjustment();
calculateFinalPrice();
});
// Строки 912-935: Используется флаг во время загрузки значений
isLoadingAdjustmentValues = true;
console.log('isLoadingAdjustmentValues = true, suppressing input/change events');
// Загрузка значений
// ...
isLoadingAdjustmentValues = false;
console.log('isLoadingAdjustmentValues = false, events are enabled again');
```
---
## Как тестировать исправление
### Тестовые данные
Используются комплекты в тенанте "grach":
- **Kit #4:** "Комплект Роза" с корректировкой `increase_percent: 10.00`
- **Kit #2:** "Комплект белые розы" с корректировкой `increase_amount: 5.00`
### Сценарий 1: Проверка отображения на странице редактирования (10 раз)
**Цель:** Убедиться что значение отображается ВСЕГДА, а не 1 раз из 10
**Шаги:**
1. Открыть http://grach.localhost:8000/products/kits/4/update/
2. Нажать Ctrl+F5 (очистить кэш и перезагрузить)
3. Найти блок "НА СКОЛЬКО БЫЛА ИЗМЕНЕНА ЦЕНА"
4. Должно отображаться: поле "Увеличить на %" с значением **10**
5. Повторить шаги 2-4 ещё 9 раз (всего 10 раз)
**Ожидаемый результат:** 10/10 раз значение 10 отображается в поле
**Признаки успеха:**
- ✅ Поле не пустое
- ✅ Значение = 10
- ✅ Остальные 3 поля (Увеличить на сумму, Уменьшить на %, Уменьшить на сумму) - отключены (disabled)
- ✅ Они помечены серым цветом (не активны)
### Сценарий 2: Проверка логирования в консоли браузера
**Цель:** Убедиться что логирование показывает правильный порядок выполнения
**Шаги:**
1. Открыть http://grach.localhost:8000/products/kits/4/update/
2. Нажать F12 (открыть DevTools)
3. Перейти на вкладку **Console**
4. Нажать Ctrl+F5
5. В консоли должны появиться логи (отсортировать по времени вверх):
**Ожидаемые логи (в таком порядке):**
```
Loading saved adjustment values: {type: 'increase_percent', value: 10}
isLoadingAdjustmentValues = true, suppressing input/change events
Loaded increase_percent: 10
isLoadingAdjustmentValues = false, events are enabled again
calculateFinalPrice: calculating...
[несколько логов о расчётах цен]
Initialization complete, isInitializing = false
```
**Признаки успеха:**
-`isLoadingAdjustmentValues = true` появляется ДО загрузки значений
-`Loaded increase_percent: 10` показывает что значение загружено
-`isLoadingAdjustmentValues = false` появляется ПОСЛЕ загрузки
-`Initialization complete` появляется в конце
- ✅ Нет ошибок в консоли (красных сообщений)
### Сценарий 3: Проверка редактирования корректировки
**Цель:** Убедиться что можно изменить значение и оно сохраняется
**Шаги:**
1. Открыть http://grach.localhost:8000/products/kits/4/update/
2. В поле "Увеличить на %" изменить значение с 10 на 15
3. Нажать кнопку "Сохранить"
4. Открыть страницу редактирования снова (F5)
5. Проверить что значение = 15
**Ожидаемый результат:**
- ✅ Значение измененo на 15
- ✅ Сохранилось в БД
- ✅ При перезагрузке отображается 15
### Сценарий 4: Проверка другого комплекта (с decrease_percent)
**Цель:** Убедиться что исправление работает для всех 4 типов корректировки
**Шаги:**
1. Создать новый комплект
2. Добавить товар
3. В блоке "НА СКОЛЬКО БЫЛА ИЗМЕНЕНА ЦЕНА" выбрать "Уменьшить на %" и ввести 20
4. Сохранить
5. Открыть для редактирования
6. Проверить что "Уменьшить на %" = 20
7. Повторить 5 раз
**Ожидаемый результат:** 5/5 раз значение отображается правильно
---
## Что смотреть в консоли браузера (для отладки)
**F12 → Console → Filter (Фильтр)**
Полезные логи:
```javascript
// Загрузка сохранённых значений
"Loading saved adjustment values:"
"isLoadingAdjustmentValues = true"
"Loaded increase_percent: 10"
"isLoadingAdjustmentValues = false"
// События которые подавляются
"Skipping event during adjustment value loading"
// Инициализация завершена
"Initialization complete, isInitializing = false"
```
**Если видите эти логи в консоли - значит исправление работает правильно.**
---
## Возможные проблемы и решения
### Проблема: Значение всё ещё не отображается
**Решение:**
1. Откройте консоль (F12)
2. Проверьте логи - есть ли ошибки?
3. Проверьте что комплект в БД имеет значение `price_adjustment_value` > 0
4. Очистите браузерный кэш (Ctrl+Shift+Delete)
5. Нажмите Ctrl+F5 на странице редактирования
### Проблема: Логи не появляются
**Решение:**
1. Проверьте что консоль не отфильтрована (нет активного фильтра)
2. Нажмите Ctrl+F5 (hard refresh)
3. Проверьте что в productkit_edit.html есть код с логами (смотрите коммит c7bf23c)
### Проблема: Значение загружается но потом исчезает
**Решение:**
1. Это была исходная проблема
2. Если она всё ещё есть - значит исправление не развернулось
3. Проверьте git статус: `git log -1`
4. Должен быть коммит "c7bf23c fix: Улучшить загрузку сохранённых значений"
5. Если коммита нет - обновите файл productkit_edit.html вручную
---
## Результаты тестирования
Заполните после выполнения тестов:
| Сценарий | Попыток | Успешных | Результат |
|----------|---------|----------|-----------|
| 1. Отображение (10 раз) | 10 | __/10 | ✅ / ❌ |
| 2. Логирование | 1 | __/1 | ✅ / ❌ |
| 3. Редактирование | 1 | __/1 | ✅ / ❌ |
| 4. Другой тип коррекции | 5 | __/5 | ✅ / ❌ |
**Итоговый результат:** ✅ ПРОЙДЕНО / ❌ НЕ ПРОЙДЕНО
---
## Архитектура исправления
```
Загрузка страницы редактирования
1. DOMContentLoaded срабатывает
2. Инициализация переменных
- isInitializing = true
- isLoadingAdjustmentValues = false
- priceCache = {}
3. Регистрация event listeners (с проверкой isLoadingAdjustmentValues)
4. setTimeout 500ms → Загрузка сохранённых значений
5a. Устанавливаем isLoadingAdjustmentValues = true
5b. Заполняем поля (input события ПОДАВЛЯЮТСЯ благодаря флагу)
5c. Вызываем validateSingleAdjustment()
5d. Устанавливаем isLoadingAdjustmentValues = false
6. calculateFinalPrice() с isInitializing = true
(не перезапишет скрытые поля даже если они обновятся)
7. requestAnimationFrame × 2 → isInitializing = false
(в конце frame cycle, после всех events)
8. ГОТОВО: значения загружены, события обрабатываются, скрытые поля защищены
```
---
## Заключение
Исправление использует трёхуровневую защиту:
1. **Подавление событий** (isLoadingAdjustmentValues) во время загрузки
2. **Защита скрытых полей** (isInitializing) от перезаписи
3. **Синхронизация с браузером** (requestAnimationFrame) для надёжности
Это должно полностью исправить проблему с надёжностью загрузки сохранённых значений корректировки цены.
🎉 **Готово к тестированию!**