Fix media file storage path and permissions

- Fix MEDIA_ROOT path to match Docker volume mount (/app/myproject/media)
- Update docker-compose.yml volume mounts to match MEDIA_ROOT
- Add setup_directories() function in entrypoint.sh to create media directories with proper permissions
- Add logging to TenantAwareFileSystemStorage for debugging
- Fix is_returned flag logic improvements (from previous work)
This commit is contained in:
2025-12-21 16:54:44 +03:00
parent a55be3095b
commit 812ecb53e6
7 changed files with 241 additions and 20 deletions

View File

@@ -20,6 +20,7 @@ from django.test import TestCase
from django.db import connection
from django.contrib.auth import get_user_model
from django_tenants.utils import schema_context
from django.core.exceptions import ValidationError
from decimal import Decimal
from tenants.models import Client, Domain
@@ -661,3 +662,142 @@ class OrderStatusTransitionCriticalTest(TestCase):
Decimal('100.00'),
"[DRAFT ROLLBACK] StockBatch должен восстановиться до исходного значения"
)
# ==================== ТЕСТ 6: Валидация резервов (is_returned + только released) ====================
def test_06_validation_released_reservations(self):
"""
ТЕСТ #6: Валидация резервов при is_returned=True и только released резервах
Сценарий:
1. completed (Sale созданы)
2. → cancelled (резервы released, is_returned=True)
3. Попытка перейти в положительный статус должна быть запрещена
Проверяем:
- Заказ с is_returned=True и только released резервами не может перейти в положительный статус
- Валидация корректно исключает released резервы
"""
with schema_context('test_order_status'):
# ШАГ 1: Создаём заказ и переводим в completed
order = self._create_order(self.status_draft, quantity=Decimal('10.00'))
order.status = self.status_completed
order.save()
# Проверяем, что Sale созданы
self._assert_sale_exists(order, should_exist=True)
self.assertFalse(order.is_returned, "[COMPLETED] is_returned должен быть False")
# ШАГ 2: Переводим в cancelled (резервы станут released)
order.status = self.status_cancelled
order.save()
# Проверяем, что Sale удалены и резервы released
self._assert_sale_exists(order, should_exist=False)
self._assert_reservation_status(order, 'released', "[CANCELLED] ")
order.refresh_from_db()
self.assertTrue(order.is_returned, "[CANCELLED] is_returned должен быть True")
# ШАГ 3: Попытка перейти в положительный статус должна быть запрещена
order.status = self.status_completed
with self.assertRaises(ValidationError) as context:
order.save()
error_message = str(context.exception)
self.assertIn('был отменён', error_message)
self.assertIn('товары проданы', error_message)
# ==================== ТЕСТ 7: Ручное удаление Sale ====================
def test_07_manual_sale_deletion_updates_flag(self):
"""
ТЕСТ #7: Обновление is_returned при ручном удалении Sale
Сценарий:
1. completed (Sale созданы, is_returned=False)
2. Ручное удаление Sale
3. is_returned должен стать True
Проверяем:
- При удалении Sale через delete() флаг is_returned обновляется
- Сигнал pre_delete корректно обрабатывает удаление
"""
with schema_context('test_order_status'):
# ШАГ 1: Создаём заказ и переводим в completed
order = self._create_order(self.status_draft, quantity=Decimal('10.00'))
order.status = self.status_completed
order.save()
# Проверяем начальное состояние
sale = self._assert_sale_exists(order, should_exist=True)
order.refresh_from_db()
self.assertFalse(order.is_returned, "[INITIAL] is_returned должен быть False")
# ШАГ 2: Ручное удаление Sale
sale.delete()
# В тестах on_commit может не сработать сразу, поэтому принудительно коммитим транзакцию
from django.db import transaction
transaction.get_connection().commit()
# Проверяем, что флаг обновился
order.refresh_from_db()
self.assertTrue(order.is_returned, "[AFTER DELETE] is_returned должен быть True после удаления Sale")
self._assert_sale_exists(order, should_exist=False)
# ==================== ТЕСТ 8: Edge case - completed без резервов ====================
def test_08_completed_without_reservations(self):
"""
ТЕСТ #8: Поведение при переходе в completed без резервов
Сценарий:
1. Создаём заказ без резервов (или удаляем резервы)
2. Переход в completed
3. Проверяем корректную обработку edge case
Проверяем:
- Заказ может перейти в completed без резервов (если они уже converted_to_sale)
- Корректное логирование ситуации
- is_returned обновляется корректно
"""
with schema_context('test_order_status'):
# ШАГ 1: Создаём заказ и переводим в completed (резервы создаются автоматически)
order = self._create_order(self.status_draft, quantity=Decimal('10.00'))
# Проверяем, что резервы созданы
reservation = Reservation.objects.filter(order_item__order=order).first()
self.assertIsNotNone(reservation, "[DRAFT] Резерв должен быть создан")
self.assertEqual(reservation.status, 'reserved')
# ШАГ 2: Переводим в completed (резервы станут converted_to_sale)
order.status = self.status_completed
order.save()
# Проверяем, что резервы converted_to_sale
reservation.refresh_from_db()
self.assertEqual(reservation.status, 'converted_to_sale')
self._assert_sale_exists(order, should_exist=True)
# ШАГ 3: Откатываем в draft (резервы вернутся в reserved)
order.status = self.status_draft
order.save()
reservation.refresh_from_db()
self.assertEqual(reservation.status, 'reserved')
self._assert_sale_exists(order, should_exist=False)
# ШАГ 4: Снова переводим в completed
# Теперь резервы уже есть в статусе reserved, но проверим edge case
# когда все резервы уже converted_to_sale (не должно быть, но проверим)
order.status = self.status_completed
order.save()
# Проверяем, что Sale созданы снова
self._assert_sale_exists(order, should_exist=True)
reservation.refresh_from_db()
self.assertEqual(reservation.status, 'converted_to_sale')
# Проверяем, что is_returned False (есть Sale)
order.refresh_from_db()
self.assertFalse(order.is_returned, "[COMPLETED AGAIN] is_returned должен быть False при наличии Sale")