diff --git a/myproject/pos/static/pos/js/terminal.js b/myproject/pos/static/pos/js/terminal.js index bda4b10..5013860 100644 --- a/myproject/pos/static/pos/js/terminal.js +++ b/myproject/pos/static/pos/js/terminal.js @@ -98,7 +98,8 @@ function updateCustomerDisplay() { } // Обновляем видимость кнопок сброса (в корзине и в модалке продажи) - const isSystemCustomer = selectedCustomer.id === SYSTEM_CUSTOMER.id; + // Приводим к числу для надёжного сравнения (JSON может вернуть разные типы) + const isSystemCustomer = Number(selectedCustomer.id) === Number(SYSTEM_CUSTOMER.id); [document.getElementById('resetCustomerBtn'), document.getElementById('checkoutResetCustomerBtn')].forEach(resetBtn => { diff --git a/myproject/pos/templates/pos/terminal.html b/myproject/pos/templates/pos/terminal.html index 7557d45..931d8fc 100644 --- a/myproject/pos/templates/pos/terminal.html +++ b/myproject/pos/templates/pos/terminal.html @@ -37,10 +37,10 @@
- - {% if current_warehouse %} +
+ {% if current_warehouse %}
Склад: {{ current_warehouse.name }} @@ -48,9 +48,14 @@ + {% else %} +
+ Склад: + Не выбран +
+ {% endif %}
- {% endif %}
diff --git a/myproject/pos/views.py b/myproject/pos/views.py index 774dae4..3b868cb 100644 --- a/myproject/pos/views.py +++ b/myproject/pos/views.py @@ -172,15 +172,24 @@ def pos_terminal(request): current_warehouse = get_pos_warehouse(request) if not current_warehouse: - # Нет активных складов - показываем ошибку - from django.contrib import messages - messages.error(request, 'Нет активных складов. Обратитесь к администратору.') + # Нет активных складов - информация отображается в блоке склада в шаблоне + # Получаем системного клиента для корректного рендеринга JSON в шаблоне + system_customer, _ = Customer.get_or_create_system_customer() context = { 'categories_json': json.dumps([]), 'items_json': json.dumps([]), 'showcase_kits_json': json.dumps([]), 'current_warehouse': None, 'warehouses': [], + 'system_customer': { + 'id': system_customer.id, + 'name': system_customer.name + }, + 'selected_customer': { + 'id': system_customer.id, + 'name': system_customer.name + }, + 'cart_data': json.dumps({}), 'title': 'POS Terminal', } return render(request, 'pos/terminal.html', context) diff --git a/myproject/products/utils/storage.py b/myproject/products/utils/storage.py index 02cb2c2..145e7d4 100644 --- a/myproject/products/utils/storage.py +++ b/myproject/products/utils/storage.py @@ -144,24 +144,30 @@ class TenantAwareFileSystemStorage(FileSystemStorage): Удалить файл, убедившись что он принадлежит текущему тенанту. Args: - name (str): Путь к файлу + name (str): Путь к файлу (может быть БЕЗ tenant_id) """ # Получаем tenant_id для проверки tenant_id = self._get_tenant_id() - # Проверяем что файл принадлежит текущему тенанту - if not name.startswith(f"tenants/{tenant_id}/"): - logger.warning( - f"[Storage] Security: Attempted to delete file from different tenant! " - f"Current tenant: {tenant_id}, file: {name}" - ) - raise RuntimeError( - f"Cannot delete file - it belongs to a different tenant. " - f"Current tenant: {tenant_id}" - ) + # Если путь уже содержит tenants/, проверяем принадлежность тенанту + if name.startswith("tenants/"): + if not name.startswith(f"tenants/{tenant_id}/"): + logger.warning( + f"[Storage] Security: Attempted to delete file from different tenant! " + f"Current tenant: {tenant_id}, file: {name}" + ) + raise RuntimeError( + f"Cannot delete file - it belongs to a different tenant. " + f"Current tenant: {tenant_id}" + ) + # Если путь уже содержит tenants/, удаляем его как есть + logger.debug(f"[Storage] delete: {name} (already has tenant prefix)") + return super().delete(name) - logger.debug(f"[Storage] delete: {name}") - return super().delete(name) + # Иначе добавляем tenant_id перед удалением + tenant_aware_name = self._get_tenant_path(name) + logger.debug(f"[Storage] delete: {name} → {tenant_aware_name}") + return super().delete(tenant_aware_name) def exists(self, name): """ @@ -220,3 +226,76 @@ class TenantAwareFileSystemStorage(FileSystemStorage): # Иначе добавляем tenant_id tenant_aware_name = self._get_tenant_path(name) return super().url(tenant_aware_name) + + def _open(self, name, mode='rb'): + """ + Открыть файл, добавив tenant_id в путь если необходимо. + Это критически важно для Celery задач, которые читают файлы из БД. + + Когда ImageProcessor вызывает Image.open(photo_obj.image), Django вызывает + это метод. БД содержит путь БЕЗ tenant_id (например, 'products/temp/image.jpg'), + но файл находится на диске с tenant_id (tenants/{tenant_id}/products/temp/image.jpg). + + Args: + name (str): Путь к файлу (может быть БЕЗ tenant_id) + mode (str): Режим открытия файла + + Returns: + File-like object + """ + # Получаем tenant_id + tenant_id = self._get_tenant_id() + + # Если путь уже содержит tenants/, проверяем принадлежность тенанту + if name.startswith("tenants/"): + if not name.startswith(f"tenants/{tenant_id}/"): + logger.warning( + f"[Storage] Security: Attempted to open file from different tenant! " + f"Current tenant: {tenant_id}, file: {name}" + ) + raise RuntimeError( + f"Cannot open file - it belongs to a different tenant. " + f"Current tenant: {tenant_id}" + ) + # Если путь уже содержит tenants/, используем его как есть + logger.debug(f"[Storage] _open: {name} (already has tenant prefix)") + return super()._open(name, mode) + + # Иначе добавляем tenant_id перед открытием + tenant_aware_name = self._get_tenant_path(name) + logger.debug(f"[Storage] _open: {name} → {tenant_aware_name}") + return super()._open(tenant_aware_name, mode) + + def path(self, name): + """ + Получить полный системный путь к файлу, добавив tenant_id если необходимо. + Используется для конвертации 'products/temp/image.jpg' в '/media/tenants/papa/products/temp/image.jpg' + + Args: + name (str): Путь к файлу (может быть БЕЗ tenant_id) + + Returns: + str: Полный системный путь к файлу + """ + # Получаем tenant_id + tenant_id = self._get_tenant_id() + + # Если путь уже содержит tenants/, проверяем принадлежность тенанту + if name.startswith("tenants/"): + if not name.startswith(f"tenants/{tenant_id}/"): + logger.warning( + f"[Storage] Security: Attempted to get path for file from different tenant! " + f"Current tenant: {tenant_id}, file: {name}" + ) + raise RuntimeError( + f"Cannot get path for file - it belongs to a different tenant. " + f"Current tenant: {tenant_id}" + ) + # Если путь уже содержит tenants/, используем его как есть + logger.debug(f"[Storage] path: {name} (already has tenant prefix)") + return super().path(name) + + # Иначе добавляем tenant_id перед получением пути + tenant_aware_name = self._get_tenant_path(name) + logger.debug(f"[Storage] path: {name} → {tenant_aware_name}") + return super().path(tenant_aware_name)