From 5d5de1fe31aacdd78f431fbded98b1a4909464b9 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Mon, 10 Nov 2025 23:44:05 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B8=20=D1=81?= =?UTF-8?q?=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=D1=80=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BA=D0=BE=D0=BC=D0=BF?= =?UTF-8?q?=D0=BB=D0=B5=D0=BA=D1=82=D0=BE=D0=B2=20=D0=B2=20=D1=81=D0=B5?= =?UTF-8?q?=D1=80=D0=B2=D0=B8=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Изменения: - Удалена функция create_temporary_kit из myproject/orders/views.py - Перенесена в новый сервис myproject/products/services/kit_service.py - Добавлен API endpoint products:api-temporary-kit-create для создания временных комплектов - Обновлены URL-ы соответственно Преимущества: - Логика временных комплектов теперь находится в соответствующем приложении (products) - Упрощена архитектура orders приложения - Сервис может быть переиспользован в других контекстах - Лучшее разделение ответственности между приложениями 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .claude/settings.local.json | 3 +- myproject/orders/urls.py | 3 - myproject/orders/views.py | 123 +------------- myproject/products/forms.py | 4 +- myproject/products/services/kit_service.py | 159 ++++++++++++++++++ .../products/includes/kititem_formset.html | 3 +- .../templates/products/productkit_create.html | 48 ++++-- myproject/products/urls.py | 1 + myproject/products/views/__init__.py | 3 +- myproject/products/views/api_views.py | 89 ++++++++++ myproject/products/views/productkit_views.py | 44 +++++ test_api.sh | 47 ++++++ test_api_simple.py | 52 ++++++ test_customer_api.sh | 26 +++ ГИД ПО ЗАПУСКУ | 16 +- 15 files changed, 471 insertions(+), 150 deletions(-) create mode 100644 myproject/products/services/kit_service.py create mode 100644 test_api.sh create mode 100644 test_api_simple.py create mode 100644 test_customer_api.sh diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 70568f3..be97d13 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -2,7 +2,8 @@ "permissions": { "allow": [ "Bash(dir /b /s settings.py)", - "Bash(git add:*)" + "Bash(git add:*)", + "Bash(..venvScriptspython.exe manage.py check)" ], "deny": [], "ask": [] diff --git a/myproject/orders/urls.py b/myproject/orders/urls.py index c10bef6..4c669e2 100644 --- a/myproject/orders/urls.py +++ b/myproject/orders/urls.py @@ -14,7 +14,4 @@ urlpatterns = [ # AJAX endpoints path('/autosave/', views.autosave_draft_order, name='order-autosave'), path('create-draft/', views.create_draft_from_form, name='order-create-draft'), - - # Временные комплекты - path('temporary-kits/create/', views.create_temporary_kit, name='temporary-kit-create'), ] diff --git a/myproject/orders/views.py b/myproject/orders/views.py index 9d5b14b..db4fe00 100644 --- a/myproject/orders/views.py +++ b/myproject/orders/views.py @@ -3,7 +3,6 @@ from django.shortcuts import render, redirect, get_object_or_404 from django.contrib import messages from django.core.paginator import Paginator from django.http import JsonResponse -from django.db import transaction from django.views.decorators.http import require_http_methods from django.contrib.auth.decorators import login_required from django.core.exceptions import ValidationError @@ -11,7 +10,6 @@ from .models import Order, OrderItem from .forms import OrderForm, OrderItemFormSet from .filters import OrderFilter from .services import DraftOrderService -from products.models import ProductKit, KitItem, Product import json @@ -383,122 +381,5 @@ def create_draft_from_form(request): # === ВРЕМЕННЫЕ КОМПЛЕКТЫ === - -@require_http_methods(["POST"]) -def create_temporary_kit(request): - """ - AJAX endpoint для создания временного комплекта. - Используется при оформлении заказа для создания букета "на лету". - - Принимает JSON: - { - "name": "Букет для Анны", - "description": "Красные розы и белые лилии", - "order_id": 123, // опционально, если заказ уже создан - "components": [ - {"product_id": 1, "quantity": "5"}, - {"product_id": 2, "quantity": "3"} - ] - } - - Возвращает JSON: - { - "success": true, - "kit_id": 456, - "kit_name": "Букет для Анны", - "kit_sku": "KIT-000456", - "kit_price": "1500.00", - "message": "Временный комплект создан успешно" - } - """ - import json - from decimal import Decimal - - try: - data = json.loads(request.body) - - name = data.get('name', '').strip() - description = data.get('description', '').strip() - order_id = data.get('order_id') - components = data.get('components', []) - - # Валидация - if not name: - return JsonResponse({ - 'success': False, - 'error': 'Необходимо указать название комплекта' - }, status=400) - - if not components or len(components) == 0: - return JsonResponse({ - 'success': False, - 'error': 'Комплект должен содержать хотя бы один компонент' - }, status=400) - - # Создаем временный комплект - with transaction.atomic(): - # Получаем заказ если указан - order = None - if order_id: - try: - order = Order.objects.get(pk=order_id) - except Order.DoesNotExist: - return JsonResponse({ - 'success': False, - 'error': f'Заказ #{order_id} не найден' - }, status=404) - - # Создаем комплект - kit = ProductKit.objects.create( - name=name, - description=description, - is_temporary=True, - is_active=True, - order=order, - price_adjustment_type='none' - ) - - # Добавляем компоненты - for component in components: - product_id = component.get('product_id') - quantity = component.get('quantity') - - if not product_id or not quantity: - continue - - try: - product = Product.objects.get(pk=product_id) - KitItem.objects.create( - kit=kit, - product=product, - quantity=Decimal(str(quantity)) - ) - except Product.DoesNotExist: - # Пропускаем несуществующие товары - continue - except (ValueError, TypeError): - # Пропускаем некорректные количества - continue - - # Пересчитываем цену комплекта - kit.recalculate_base_price() - - return JsonResponse({ - 'success': True, - 'kit_id': kit.id, - 'kit_name': kit.name, - 'kit_sku': kit.sku, - 'kit_price': str(kit.actual_price), - 'message': f'Временный комплект "{kit.name}" создан успешно' - }) - - except json.JSONDecodeError: - return JsonResponse({ - 'success': False, - 'error': 'Некорректный JSON' - }, status=400) - except Exception as e: - return JsonResponse({ - 'success': False, - 'error': f'Ошибка при создании комплекта: {str(e)}' - }, status=500) +# УДАЛЕНО: Логика создания временных комплектов перенесена в products.services.kit_service +# Используйте API endpoint: products:api-temporary-kit-create diff --git a/myproject/products/forms.py b/myproject/products/forms.py index a2b0aa3..3f965c3 100644 --- a/myproject/products/forms.py +++ b/myproject/products/forms.py @@ -252,7 +252,7 @@ KitItemFormSetCreate = inlineformset_factory( KitItem, form=KitItemForm, formset=BaseKitItemFormSet, - fields=['id', 'product', 'variant_group', 'quantity'], + fields=['product', 'variant_group', 'quantity'], extra=1, # Показать 1 пустую форму для первого компонента can_delete=True, # Разрешить удаление компонентов min_num=0, # Минимум 0 компонентов (можно создать пустой комплект) @@ -266,7 +266,7 @@ KitItemFormSetUpdate = inlineformset_factory( KitItem, form=KitItemForm, formset=BaseKitItemFormSet, - fields=['id', 'product', 'variant_group', 'quantity'], + fields=['product', 'variant_group', 'quantity'], extra=0, # НЕ показывать пустые формы при редактировании can_delete=True, # Разрешить удаление компонентов min_num=0, # Минимум 0 компонентов diff --git a/myproject/products/services/kit_service.py b/myproject/products/services/kit_service.py new file mode 100644 index 0000000..5446606 --- /dev/null +++ b/myproject/products/services/kit_service.py @@ -0,0 +1,159 @@ +""" +Сервис для работы с комплектами товаров. +Содержит бизнес-логику создания, обновления и управления комплектами. +""" +from decimal import Decimal +from django.db import transaction +from typing import List, Dict, Optional + +from ..models import ProductKit, Product, KitItem + + +def create_temporary_kit( + name: str, + components: List[Dict], + description: str = '', + order=None +) -> ProductKit: + """ + Создает временный комплект с компонентами. + + Временные комплекты используются для создания букетов "на лету" при оформлении заказа. + Они автоматически помечаются как временные (is_temporary=True) и связываются с заказом. + + Args: + name: Название комплекта + components: Список компонентов в формате [{"product_id": 1, "quantity": "5"}, ...] + description: Описание комплекта (опционально) + order: Заказ, к которому привязан временный комплект (опционально) + + Returns: + ProductKit: Созданный временный комплект + + Raises: + ValueError: Если данные невалидны + Product.DoesNotExist: Если товар не найден + + Example: + >>> kit = create_temporary_kit( + ... name="Букет для Анны", + ... description="Красные розы и белые лилии", + ... components=[ + ... {"product_id": 1, "quantity": "5"}, + ... {"product_id": 2, "quantity": "3"} + ... ] + ... ) + """ + # Валидация + if not name or not name.strip(): + raise ValueError('Необходимо указать название комплекта') + + if not components or len(components) == 0: + raise ValueError('Комплект должен содержать хотя бы один компонент') + + with transaction.atomic(): + # Создаем комплект + kit = ProductKit.objects.create( + name=name.strip(), + description=description.strip() if description else '', + is_temporary=True, + is_active=True, + order=order, + price_adjustment_type='none' + ) + + # Добавляем компоненты + added_count = 0 + for component in components: + product_id = component.get('product_id') + quantity = component.get('quantity') + + if not product_id or not quantity: + continue + + try: + product = Product.objects.get(pk=product_id, is_active=True) + KitItem.objects.create( + kit=kit, + product=product, + quantity=Decimal(str(quantity)) + ) + added_count += 1 + except Product.DoesNotExist: + # Пропускаем несуществующие товары + continue + except (ValueError, TypeError) as e: + # Пропускаем некорректные количества + continue + + if added_count == 0: + raise ValueError('Не удалось добавить ни одного компонента в комплект') + + # Пересчитываем цену комплекта на основе компонентов + kit.recalculate_base_price() + + return kit + + +def make_kit_permanent(kit: ProductKit) -> bool: + """ + Преобразует временный комплект в постоянный. + + Args: + kit: Комплект для преобразования + + Returns: + bool: True если комплект был преобразован, False если уже постоянный + """ + if not kit.is_temporary: + return False + + kit.is_temporary = False + kit.order = None # Отвязываем от заказа + kit.save() + return True + + +def duplicate_kit(kit: ProductKit, new_name: Optional[str] = None) -> ProductKit: + """ + Создает копию комплекта со всеми компонентами. + + Args: + kit: Комплект для дублирования + new_name: Новое название (если None, используется "Копия {original_name}") + + Returns: + ProductKit: Новый комплект-копия + """ + with transaction.atomic(): + # Копируем комплект + new_kit = ProductKit.objects.create( + name=new_name or f"Копия {kit.name}", + description=kit.description, + short_description=kit.short_description, + price_adjustment_type=kit.price_adjustment_type, + price_adjustment_value=kit.price_adjustment_value, + sale_price=kit.sale_price, + is_temporary=False, # Копия всегда постоянная + is_active=kit.is_active + ) + + # Копируем категории + new_kit.categories.set(kit.categories.all()) + + # Копируем теги + new_kit.tags.set(kit.tags.all()) + + # Копируем компоненты + for item in kit.kit_items.all(): + KitItem.objects.create( + kit=new_kit, + product=item.product, + variant_group=item.variant_group, + quantity=item.quantity + ) + + # Пересчитываем цену + new_kit.recalculate_base_price() + + return new_kit diff --git a/myproject/products/templates/products/includes/kititem_formset.html b/myproject/products/templates/products/includes/kititem_formset.html index 7ec0fb8..b053974 100644 --- a/myproject/products/templates/products/includes/kititem_formset.html +++ b/myproject/products/templates/products/includes/kititem_formset.html @@ -10,6 +10,7 @@ {% for kititem_form in kititem_formset %}
{{ kititem_form.id }}
@@ -61,7 +62,7 @@
{% if kititem_form.DELETE %} - {{ kititem_form.DELETE }} diff --git a/myproject/products/templates/products/productkit_create.html b/myproject/products/templates/products/productkit_create.html index cf5a7ee..6343dde 100644 --- a/myproject/products/templates/products/productkit_create.html +++ b/myproject/products/templates/products/productkit_create.html @@ -442,10 +442,23 @@ document.addEventListener('DOMContentLoaded', function() { return 0; } - const productId = parseInt(selectElement.value); + const rawValue = selectElement.value; + if (!rawValue) { + console.warn('getProductPrice: no value'); + return 0; + } - if (!selectElement.value || isNaN(productId) || productId <= 0) { - console.warn('getProductPrice: no valid product id', selectElement.value); + // Извлекаем числовой ID из значения (может быть "product_123" или "123") + let productId; + if (rawValue.includes('_')) { + const parts = rawValue.split('_'); + productId = parseInt(parts[1]); + } else { + productId = parseInt(rawValue); + } + + if (isNaN(productId) || productId <= 0) { + console.warn('getProductPrice: invalid product id', rawValue); return 0; } @@ -471,15 +484,19 @@ document.addEventListener('DOMContentLoaded', function() { } } - // Пытаемся получить из Select2 option data (сначала actual_price, потом price) - const selectedOption = $(selectElement).find('option:selected'); - let priceData = selectedOption.data('actual_price') || selectedOption.data('price'); - if (priceData) { - const price = parseFloat(priceData) || 0; - if (price > 0) { - priceCache[productId] = price; - console.log('getProductPrice: from select2 data', productId, price); - return price; + // Пытаемся получить из Select2 data (приоритет: actual_price > price) + const $select = $(selectElement); + const selectedData = $select.select2('data'); + if (selectedData && selectedData.length > 0) { + const itemData = selectedData[0]; + const priceData = itemData.actual_price || itemData.price; + if (priceData) { + const price = parseFloat(priceData) || 0; + if (price > 0) { + priceCache[productId] = price; + console.log('getProductPrice: from select2 data', productId, price); + return price; + } } } @@ -514,7 +531,12 @@ document.addEventListener('DOMContentLoaded', function() { $('[name$="-product"]').on('select2:select', async function() { const form = $(this).closest('.kititem-form'); if (this.value) { - form.attr('data-product-id', this.value); + // Извлекаем числовой ID из "product_123" + let numericId = this.value; + if (this.value.includes('_')) { + numericId = this.value.split('_')[1]; + } + form.attr('data-product-id', numericId); // Загружаем цену и пересчитываем await getProductPrice(this); calculateFinalPrice(); diff --git a/myproject/products/urls.py b/myproject/products/urls.py index 8b5b394..3feba6c 100644 --- a/myproject/products/urls.py +++ b/myproject/products/urls.py @@ -36,6 +36,7 @@ urlpatterns = [ # API endpoints path('api/search-products-variants/', views.search_products_and_variants, name='api-search-products-variants'), + path('api/kits/temporary/create/', views.create_temporary_kit_api, name='api-temporary-kit-create'), # CRUD URLs for ProductVariantGroup (Варианты товаров) path('variant-groups/', views.ProductVariantGroupListView.as_view(), name='variantgroup-list'), diff --git a/myproject/products/views/__init__.py b/myproject/products/views/__init__.py index 8c448cb..537d006 100644 --- a/myproject/products/views/__init__.py +++ b/myproject/products/views/__init__.py @@ -71,7 +71,7 @@ from .variant_group_views import ( ) # API представления -from .api_views import search_products_and_variants, validate_kit_cost +from .api_views import search_products_and_variants, validate_kit_cost, create_temporary_kit_api __all__ = [ @@ -132,4 +132,5 @@ __all__ = [ # API 'search_products_and_variants', 'validate_kit_cost', + 'create_temporary_kit_api', ] diff --git a/myproject/products/views/api_views.py b/myproject/products/views/api_views.py index 37d7a3b..dc41b25 100644 --- a/myproject/products/views/api_views.py +++ b/myproject/products/views/api_views.py @@ -531,3 +531,92 @@ def validate_kit_cost(request): return JsonResponse({ 'error': str(e) }, status=500) + + +def create_temporary_kit_api(request): + """ + AJAX endpoint для создания временного комплекта. + Используется при оформлении заказа для создания букета "на лету". + + Принимает JSON: + { + "name": "Букет для Анны", + "description": "Красные розы и белые лилии", + "order_id": 123, // опционально, если заказ уже создан + "components": [ + {"product_id": 1, "quantity": "5"}, + {"product_id": 2, "quantity": "3"} + ] + } + + Возвращает JSON: + { + "success": true, + "kit_id": 456, + "kit_name": "Букет для Анны", + "kit_sku": "KIT-000456", + "kit_price": "1500.00", + "message": "Временный комплект создан успешно" + } + """ + if request.method != 'POST': + return JsonResponse({ + 'success': False, + 'error': 'Метод не поддерживается' + }, status=405) + + import json + from ..services.kit_service import create_temporary_kit + from orders.models import Order + + try: + data = json.loads(request.body) + + name = data.get('name', '').strip() + description = data.get('description', '').strip() + order_id = data.get('order_id') + components = data.get('components', []) + + # Получаем заказ если указан + order = None + if order_id: + try: + order = Order.objects.get(pk=order_id) + except Order.DoesNotExist: + return JsonResponse({ + 'success': False, + 'error': f'Заказ #{order_id} не найден' + }, status=404) + + # Создаем временный комплект через сервис + kit = create_temporary_kit( + name=name, + description=description, + components=components, + order=order + ) + + return JsonResponse({ + 'success': True, + 'kit_id': kit.id, + 'kit_name': kit.name, + 'kit_sku': kit.sku, + 'kit_price': str(kit.actual_price), + 'message': f'Временный комплект "{kit.name}" создан успешно' + }) + + except ValueError as e: + return JsonResponse({ + 'success': False, + 'error': str(e) + }, status=400) + except json.JSONDecodeError: + return JsonResponse({ + 'success': False, + 'error': 'Некорректный JSON' + }, status=400) + except Exception as e: + return JsonResponse({ + 'success': False, + 'error': f'Ошибка при создании комплекта: {str(e)}' + }, status=500) diff --git a/myproject/products/views/productkit_views.py b/myproject/products/views/productkit_views.py index f6aa2e3..8c867c3 100644 --- a/myproject/products/views/productkit_views.py +++ b/myproject/products/views/productkit_views.py @@ -93,6 +93,28 @@ class ProductKitCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVi template_name = 'products/productkit_create.html' permission_required = 'products.add_productkit' + def post(self, request, *args, **kwargs): + """ + Обрабатываем POST данные и очищаем ID товаров/комплектов от префиксов. + API возвращает ID в формате "product_123" или "kit_456", но Django ожидает числа. + """ + # Создаем изменяемую копию POST данных + post_data = request.POST.copy() + + # Очищаем product ID от префиксов (product_123 -> 123) + for key in post_data.keys(): + if key.endswith('-product') and post_data[key]: + value = post_data[key] + if '_' in value: + # Извлекаем числовой ID из "product_123" + numeric_id = value.split('_')[1] + post_data[key] = numeric_id + + # Заменяем request.POST на очищенные данные + request.POST = post_data + + return super().post(request, *args, **kwargs) + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -199,6 +221,28 @@ class ProductKitUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateVi template_name = 'products/productkit_edit.html' permission_required = 'products.change_productkit' + def post(self, request, *args, **kwargs): + """ + Обрабатываем POST данные и очищаем ID товаров/комплектов от префиксов. + API возвращает ID в формате "product_123" или "kit_456", но Django ожидает числа. + """ + # Создаем изменяемую копию POST данных + post_data = request.POST.copy() + + # Очищаем product ID от префиксов (product_123 -> 123) + for key in post_data.keys(): + if key.endswith('-product') and post_data[key]: + value = post_data[key] + if '_' in value: + # Извлекаем числовой ID из "product_123" + numeric_id = value.split('_')[1] + post_data[key] = numeric_id + + # Заменяем request.POST на очищенные данные + request.POST = post_data + + return super().post(request, *args, **kwargs) + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) diff --git a/test_api.sh b/test_api.sh new file mode 100644 index 0000000..787e081 --- /dev/null +++ b/test_api.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Тест API endpoints для поиска и создания клиентов +# Использование: bash test_api.sh + +BASE_URL="http://grach.localhost:8000" + +echo "==================================================" +echo "ТЕСТ API ENDPOINTS ДЛЯ ПОИСКА КЛИЕНТОВ" +echo "==================================================" +echo "" + +# Test 1: Поиск по имени +echo "TEST 1: Поиск по имени (q=Иван)" +echo "URL: $BASE_URL/customers/api/search/?q=Иван" +echo "" +curl -s "$BASE_URL/customers/api/search/?q=Иван" | python -m json.tool 2>/dev/null || curl -s "$BASE_URL/customers/api/search/?q=Иван" +echo "" +echo "---" +echo "" + +# Test 2: Поиск по телефону +echo "TEST 2: Поиск по телефону (q=375)" +echo "URL: $BASE_URL/customers/api/search/?q=375" +echo "" +curl -s "$BASE_URL/customers/api/search/?q=375" | python -m json.tool 2>/dev/null || curl -s "$BASE_URL/customers/api/search/?q=375" +echo "" +echo "---" +echo "" + +# Test 3: Пустой поиск (должна вернуться пустая строка results) +echo "TEST 3: Пустой поиск (q=)" +echo "URL: $BASE_URL/customers/api/search/?q=" +echo "" +curl -s "$BASE_URL/customers/api/search/?q=" | python -m json.tool 2>/dev/null || curl -s "$BASE_URL/customers/api/search/?q=" +echo "" +echo "---" +echo "" + +# Test 4: Проверка что endpoint существует +echo "TEST 4: Проверка доступности endpoint'а" +echo "URL: $BASE_URL/customers/api/search/" +echo "" +curl -i "$BASE_URL/customers/api/search/?q=test" 2>&1 | head -15 +echo "" +echo "==================================================" +echo "" diff --git a/test_api_simple.py b/test_api_simple.py new file mode 100644 index 0000000..70ab4b3 --- /dev/null +++ b/test_api_simple.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +""" +Простой скрипт для проверки API endpoints через Django shell +""" +import os +import django + +# Настройка Django +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') +django.setup() + +from django.test import Client +import json + +client = Client() +BASE_URL = '/customers/api/search/' + +print("=" * 60) +print("ТЕСТ API ENDPOINTS") +print("=" * 60) + +# Test 1: Empty query +print("\nТЕСТ 1: Пустой запрос") +response = client.get(f'{BASE_URL}?q=') +print(f"Статус: {response.status_code}") +print(f"Ответ: {response.content.decode()}") + +# Test 2: Search by single letter +print("\n" + "=" * 60) +print("ТЕСТ 2: Поиск по букве 'И'") +response = client.get(f'{BASE_URL}?q=И') +print(f"Статус: {response.status_code}") +data = json.loads(response.content) +print(f"Результатов: {len(data.get('results', []))}") +if data.get('results'): + for item in data['results'][:3]: + print(f" - {item.get('text', 'No text')}") + +# Test 3: Search by number +print("\n" + "=" * 60) +print("ТЕСТ 3: Поиск по цифрам '29'") +response = client.get(f'{BASE_URL}?q=29') +print(f"Статус: {response.status_code}") +data = json.loads(response.content) +print(f"Результатов: {len(data.get('results', []))}") +if data.get('results'): + for item in data['results'][:3]: + print(f" - {item.get('text', 'No text')}") + +print("\n" + "=" * 60) +print("Готово!") +print("=" * 60) diff --git a/test_customer_api.sh b/test_customer_api.sh new file mode 100644 index 0000000..b7e5470 --- /dev/null +++ b/test_customer_api.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Тест API эндпоинтов для поиска и создания клиентов + +API_HOST="http://grach.localhost:8000" + +echo "=== Тест API поиска клиентов ===" +echo "" + +# Тест 1: Поиск по имени +echo "1. Поиск по имени 'Иван':" +curl -s -X GET "${API_HOST}/customers/api/search/?q=Иван" \ + -H "Accept: application/json" | python -m json.tool || echo "Ошибка в запросе" + +echo "" +echo "2. Поиск по частичному номеру телефона '2912':" +curl -s -X GET "${API_HOST}/customers/api/search/?q=2912" \ + -H "Accept: application/json" | python -m json.tool || echo "Ошибка в запросе" + +echo "" +echo "3. Поиск по email 'ivan':" +curl -s -X GET "${API_HOST}/customers/api/search/?q=ivan" \ + -H "Accept: application/json" | python -m json.tool || echo "Ошибка в запросе" + +echo "" +echo "=== Тесты завершены ===" diff --git a/ГИД ПО ЗАПУСКУ b/ГИД ПО ЗАПУСКУ index 05dd019..97884db 100644 --- a/ГИД ПО ЗАПУСКУ +++ b/ГИД ПО ЗАПУСКУ @@ -1,12 +1,12 @@ -docker run -d \ - --name postgres17 \ - -e POSTGRES_PASSWORD=postgres \ - -e POSTGRES_USER=postgres \ - -e POSTGRES_DB=inventory_db \ - -p 5432:5432 \ - -v postgres17-data:/var/lib/postgresql/data \ +docker run -d ` + --name postgres17 ` + -e POSTGRES_PASSWORD=postgres ` + -e POSTGRES_USER=postgres ` + -e POSTGRES_DB=inventory_db ` + -p 5432:5432 ` + -v postgres17-data:/var/lib/postgresql/data ` postgres:17 - + # 2. Создаем миграции с нуля python manage.py makemigrations