diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ebd7ddf --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pyc +__pycache__/* +inventory/migrations/__pycache__/0001_initial.cpython-310.pyc diff --git a/inventory/__pycache__/__init__.cpython-310.pyc b/inventory/__pycache__/__init__.cpython-310.pyc index c625c7f..cbdfa45 100644 Binary files a/inventory/__pycache__/__init__.cpython-310.pyc and b/inventory/__pycache__/__init__.cpython-310.pyc differ diff --git a/inventory/__pycache__/apps.cpython-310.pyc b/inventory/__pycache__/apps.cpython-310.pyc index 0af6901..978f291 100644 Binary files a/inventory/__pycache__/apps.cpython-310.pyc and b/inventory/__pycache__/apps.cpython-310.pyc differ diff --git a/inventory/__pycache__/models.cpython-310.pyc b/inventory/__pycache__/models.cpython-310.pyc index 8242b78..a87835c 100644 Binary files a/inventory/__pycache__/models.cpython-310.pyc and b/inventory/__pycache__/models.cpython-310.pyc differ diff --git a/inventory/migrations/__pycache__/0001_initial.cpython-310.pyc b/inventory/migrations/__pycache__/0001_initial.cpython-310.pyc index 1735705..c4ea00a 100644 Binary files a/inventory/migrations/__pycache__/0001_initial.cpython-310.pyc and b/inventory/migrations/__pycache__/0001_initial.cpython-310.pyc differ diff --git a/inventory/migrations/__pycache__/__init__.cpython-310.pyc b/inventory/migrations/__pycache__/__init__.cpython-310.pyc index 1a2af7b..0a89eba 100644 Binary files a/inventory/migrations/__pycache__/__init__.cpython-310.pyc and b/inventory/migrations/__pycache__/__init__.cpython-310.pyc differ diff --git a/inventory/serializers.py b/inventory/serializers.py index d8f7513..2e8c110 100644 --- a/inventory/serializers.py +++ b/inventory/serializers.py @@ -5,39 +5,45 @@ class ProductSerializer(serializers.ModelSerializer): class Meta: model = Product fields = '__all__' - + ref_name = 'InventoryProductSerializer' # Unique ref_name for inventory class PositionSerializer(serializers.ModelSerializer): class Meta: model = Position fields = '__all__' + ref_name = 'InventoryPositionSerializer' # Unique ref_name for inventory class EmployeeSerializer(serializers.ModelSerializer): class Meta: model = Employee fields = '__all__' + ref_name = 'InventoryEmployeeSerializer' # Unique ref_name for inventory class StorageLocationSerializer(serializers.ModelSerializer): class Meta: model = StorageLocation fields = '__all__' + ref_name = 'InventoryStorageLocationSerializer' # Unique ref_name for inventory class ContractorSerializer(serializers.ModelSerializer): class Meta: model = Contractor fields = '__all__' + ref_name = 'InventoryContractorSerializer' # Unique ref_name for inventory class SupplyContractSerializer(serializers.ModelSerializer): class Meta: model = SupplyContract fields = '__all__' + ref_name = 'InventorySupplyContractSerializer' # Unique ref_name for inventory class TruckSerializer(serializers.ModelSerializer): class Meta: model = Truck - fields = '__all__' \ No newline at end of file + fields = '__all__' + ref_name = 'InventoryTruckSerializer' # Unique ref_name for inventory diff --git a/inventory/urls.py b/inventory/urls.py index 20311a5..ed9c461 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -1,27 +1,29 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter +from drf_yasg.views import get_schema_view +from drf_yasg import openapi +from rest_framework.permissions import AllowAny from .views import ( ProductViewSet, EmployeeViewSet, PositionViewSet, StorageLocationViewSet, ContractorViewSet, SupplyContractViewSet, TruckViewSet ) -from drf_yasg.views import get_schema_view -from drf_yasg import openapi -from rest_framework.permissions import AllowAny -router = DefaultRouter() -router.register(r'products', ProductViewSet) -router.register(r'employees', EmployeeViewSet) -router.register(r'positions', PositionViewSet) -router.register(r'storage-locations', StorageLocationViewSet) -router.register(r'contractors', ContractorViewSet) -router.register(r'supply-contracts', SupplyContractViewSet) -router.register(r'trucks', TruckViewSet) +# Create the router for inventory-related views +router_inventory = DefaultRouter() +router_inventory.register(r'products', ProductViewSet) +router_inventory.register(r'employees', EmployeeViewSet) +router_inventory.register(r'positions', PositionViewSet) +router_inventory.register(r'storage-locations', StorageLocationViewSet) +router_inventory.register(r'contractors', ContractorViewSet) +router_inventory.register(r'supply-contracts', SupplyContractViewSet) +router_inventory.register(r'trucks', TruckViewSet) -schema_view = get_schema_view( +# Swagger schema view for inventory +schema_view_inventory = get_schema_view( openapi.Info( - title="Store Management API", + title="Store Management API - Inventory", default_version='v1', - description="API for managing the franchise store", + description="API for managing inventory in the franchise store", terms_of_service="https://www.example.com/terms/", contact=openapi.Contact(email="support@example.com"), license=openapi.License(name="BSD License"), @@ -31,7 +33,7 @@ schema_view = get_schema_view( ) urlpatterns = [ - path('api/', include(router.urls)), - path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), - path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), -] \ No newline at end of file + path('', include(router_inventory.urls)), + path('swagger/', schema_view_inventory.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui-inventory'), + path('redoc/', schema_view_inventory.with_ui('redoc', cache_timeout=0), name='schema-redoc-inventory'), +] diff --git a/pricing/__init__.py b/pricing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pricing/admin.py b/pricing/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/pricing/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/pricing/apps.py b/pricing/apps.py new file mode 100644 index 0000000..237e8ef --- /dev/null +++ b/pricing/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PricingConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'pricing' diff --git a/pricing/migrations/0001_initial.py b/pricing/migrations/0001_initial.py new file mode 100644 index 0000000..d9a75c6 --- /dev/null +++ b/pricing/migrations/0001_initial.py @@ -0,0 +1,113 @@ +# Generated by Django 5.1.4 on 2025-01-08 14:02 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Discount', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('discount_percentage', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Процент скидки')), + ('start_date', models.DateField(verbose_name='Дата начала')), + ('end_date', models.DateField(verbose_name='Дата окончания')), + ('description', models.TextField(blank=True, null=True, verbose_name='Описание акции')), + ], + ), + migrations.CreateModel( + name='PriceList', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('entry_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Входная цена')), + ('final_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Итоговая цена')), + ('date_effective', models.DateField(verbose_name='Дата вступления в силу')), + ('constraint_percent_limit', models.DecimalField(decimal_places=2, default=1000, max_digits=5, verbose_name='Лимит на наценку (%)')), + ('constraint_price_change', models.DecimalField(decimal_places=2, default=90, max_digits=5, verbose_name='Лимит на изменение цены (%)')), + ], + ), + migrations.CreateModel( + name='PriceType', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(choices=[('regular', 'Регулярная'), ('discount', 'Скидочная'), ('promotional', 'Акционная')], max_length=50, verbose_name='Тип цены')), + ], + ), + migrations.CreateModel( + name='Product', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='Наименование товара')), + ('manufacturer_name', models.CharField(max_length=255, verbose_name='Производитель')), + ('manufacturer_country', models.CharField(max_length=255, verbose_name='Страна производителя')), + ('manufacturer_code', models.CharField(blank=True, max_length=50, null=True, verbose_name='Код производителя')), + ('dimensions', models.CharField(blank=True, max_length=255, null=True, verbose_name='Размеры')), + ('unit_of_measure', models.CharField(max_length=50, verbose_name='Единица измерения')), + ('shelf_life_days', models.IntegerField(verbose_name='Срок годности (дни)')), + ('barcode', models.CharField(max_length=50, unique=True, verbose_name='Штрихкод')), + ], + ), + migrations.CreateModel( + name='DiscountHistory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('old_discount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Старая скидка')), + ('new_discount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Новая скидка')), + ('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Дата изменения скидки')), + ('reason', models.TextField(blank=True, null=True, verbose_name='Причина изменения скидки')), + ('discount', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pricing.discount', verbose_name='Акция/Скидка')), + ], + ), + migrations.CreateModel( + name='PriceListWithDiscount', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('final_price_after_discount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Итоговая цена после скидки')), + ('discount', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pricing.discount', verbose_name='Скидка')), + ('price_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pricing.pricelist', verbose_name='Прайс-лист')), + ], + ), + migrations.AddField( + model_name='pricelist', + name='price_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pricing.pricetype', verbose_name='Тип цены'), + ), + migrations.CreateModel( + name='PriceTag', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('tag_image', models.ImageField(blank=True, null=True, upload_to='price_tags/', verbose_name='Изображение ценника')), + ('price_effective_date', models.DateField(verbose_name='Дата вступления в силу ценника')), + ('price_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pricing.pricelist', verbose_name='Прайс-лист')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pricing.product', verbose_name='Товар')), + ], + ), + migrations.AddField( + model_name='pricelist', + name='product', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pricing.product', verbose_name='Товар'), + ), + migrations.CreateModel( + name='PriceChangeHistory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('old_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Старая цена')), + ('new_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Новая цена')), + ('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Дата изменения цены')), + ('reason', models.TextField(blank=True, null=True, verbose_name='Причина изменения цены')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pricing.product', verbose_name='Товар')), + ], + ), + migrations.AddField( + model_name='discount', + name='product', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pricing.product', verbose_name='Товар'), + ), + ] diff --git a/pricing/migrations/__init__.py b/pricing/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pricing/models.py b/pricing/models.py new file mode 100644 index 0000000..bca45d2 --- /dev/null +++ b/pricing/models.py @@ -0,0 +1,108 @@ +from django.db import models + +# Справочник товаров +class Product(models.Model): + name = models.CharField(max_length=255, verbose_name="Наименование товара") + manufacturer_name = models.CharField(max_length=255, verbose_name="Производитель") + manufacturer_country = models.CharField(max_length=255, verbose_name="Страна производителя") + manufacturer_code = models.CharField(max_length=50, verbose_name="Код производителя", blank=True, null=True) + dimensions = models.CharField(max_length=255, verbose_name="Размеры", blank=True, null=True) + unit_of_measure = models.CharField(max_length=50, verbose_name="Единица измерения") + shelf_life_days = models.IntegerField(verbose_name="Срок годности (дни)") + barcode = models.CharField(max_length=50, unique=True, verbose_name="Штрихкод") + + def __str__(self): + return self.name + + +# Типы цен (например, регулярная, скидочная, акционная) +class PriceType(models.Model): + name = models.CharField(max_length=50, verbose_name="Тип цены", choices=[ + ('regular', 'Регулярная'), + ('discount', 'Скидочная'), + ('promotional', 'Акционная'), + ]) + + def __str__(self): + return self.name + + +# Прайс-листы (со всеми ценами) +class PriceList(models.Model): + product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name="Товар") + price_type = models.ForeignKey(PriceType, on_delete=models.CASCADE, verbose_name="Тип цены") + entry_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Входная цена") + final_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Итоговая цена") + date_effective = models.DateField(verbose_name="Дата вступления в силу") + constraint_percent_limit = models.DecimalField(max_digits=5, decimal_places=2, default=1000, verbose_name="Лимит на наценку (%)") + constraint_price_change = models.DecimalField(max_digits=5, decimal_places=2, default=90, verbose_name="Лимит на изменение цены (%)") + + def save(self, *args, **kwargs): + # Логика проверки на изменение цены + if self.pk: + old_price = PriceList.objects.get(pk=self.pk).final_price + price_change_percent = abs((self.final_price - old_price) / old_price) * 100 + if price_change_percent > self.constraint_price_change: + raise ValueError(f"Цена не может измениться более чем на {self.constraint_price_change}%") + + super().save(*args, **kwargs) + + def __str__(self): + return f"{self.product.name} - {self.price_type.name} - {self.final_price} руб." + + +# Скидки и акции (могут применяться к товарам) +class Discount(models.Model): + product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name="Товар") + discount_percentage = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Процент скидки") + start_date = models.DateField(verbose_name="Дата начала") + end_date = models.DateField(verbose_name="Дата окончания") + description = models.TextField(verbose_name="Описание акции", blank=True, null=True) + + def __str__(self): + return f"Скидка {self.discount_percentage}% на {self.product.name} с {self.start_date} по {self.end_date}" + + +# История изменений цен +class PriceChangeHistory(models.Model): + product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name="Товар") + old_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Старая цена") + new_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Новая цена") + change_date = models.DateTimeField(auto_now_add=True, verbose_name="Дата изменения цены") + reason = models.TextField(verbose_name="Причина изменения цены", blank=True, null=True) + + def __str__(self): + return f"{self.product.name} - изменение цены с {self.old_price} на {self.new_price} - {self.change_date}" + + +# Пример использования ценников для печати +class PriceTag(models.Model): + product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name="Товар") + price_list = models.ForeignKey(PriceList, on_delete=models.CASCADE, verbose_name="Прайс-лист") + tag_image = models.ImageField(upload_to='price_tags/', verbose_name="Изображение ценника", blank=True, null=True) + price_effective_date = models.DateField(verbose_name="Дата вступления в силу ценника") + + def __str__(self): + return f"Ценник для {self.product.name} - {self.price_effective_date}" + + +# Связь с историей акций +class DiscountHistory(models.Model): + discount = models.ForeignKey(Discount, on_delete=models.CASCADE, verbose_name="Акция/Скидка") + old_discount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Старая скидка") + new_discount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Новая скидка") + change_date = models.DateTimeField(auto_now_add=True, verbose_name="Дата изменения скидки") + reason = models.TextField(verbose_name="Причина изменения скидки", blank=True, null=True) + + def __str__(self): + return f"Изменение скидки с {self.old_discount}% на {self.new_discount}% для {self.discount.product.name}" + + +# Пример применения скидки к прайс-листу +class PriceListWithDiscount(models.Model): + price_list = models.ForeignKey(PriceList, on_delete=models.CASCADE, verbose_name="Прайс-лист") + discount = models.ForeignKey(Discount, on_delete=models.CASCADE, verbose_name="Скидка") + final_price_after_discount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Итоговая цена после скидки") + + def __str__(self): + return f"{self.price_list.product.name} - {self.final_price_after_discount} руб. (с учетом скидки)" diff --git a/pricing/serializers.py b/pricing/serializers.py new file mode 100644 index 0000000..1336eab --- /dev/null +++ b/pricing/serializers.py @@ -0,0 +1,80 @@ +from rest_framework import serializers +from .models import Product, PriceType, PriceList, Discount, PriceChangeHistory, PriceTag, DiscountHistory, PriceListWithDiscount + +# Сериализатор для модели Product +class ProductSerializer(serializers.ModelSerializer): + class Meta: + model = Product + fields = ['id', 'name', 'manufacturer_name', 'manufacturer_country', 'manufacturer_code', 'dimensions', 'unit_of_measure', 'shelf_life_days', 'barcode'] + ref_name = 'PricingProductSerializer' # Unique ref_name for pricing + + +# Сериализатор для модели PriceType +class PriceTypeSerializer(serializers.ModelSerializer): + class Meta: + model = PriceType + fields = ['id', 'name'] + ref_name = 'PricingPriceTypeSerializer' # Unique ref_name for pricing + + +# Сериализатор для модели PriceList +class PriceListSerializer(serializers.ModelSerializer): + product = ProductSerializer() # Вложенный сериализатор для связи с продуктом + price_type = PriceTypeSerializer() # Вложенный сериализатор для связи с типом цены + + class Meta: + model = PriceList + fields = ['id', 'product', 'price_type', 'entry_price', 'final_price', 'date_effective', 'constraint_percent_limit', 'constraint_price_change'] + ref_name = 'PricingPriceListSerializer' # Unique ref_name for pricing + + +# Сериализатор для модели Discount +class DiscountSerializer(serializers.ModelSerializer): + product = ProductSerializer() # Вложенный сериализатор для связи с продуктом + + class Meta: + model = Discount + fields = ['id', 'product', 'discount_percentage', 'start_date', 'end_date', 'description'] + ref_name = 'PricingDiscountSerializer' # Unique ref_name for pricing + + +# Сериализатор для модели PriceChangeHistory +class PriceChangeHistorySerializer(serializers.ModelSerializer): + product = ProductSerializer() # Вложенный сериализатор для связи с продуктом + + class Meta: + model = PriceChangeHistory + fields = ['id', 'product', 'old_price', 'new_price', 'change_date', 'reason'] + ref_name = 'PricingPriceChangeHistorySerializer' # Unique ref_name for pricing + + +# Сериализатор для модели PriceTag +class PriceTagSerializer(serializers.ModelSerializer): + product = ProductSerializer() # Вложенный сериализатор для связи с продуктом + price_list = PriceListSerializer() # Вложенный сериализатор для связи с прайс-листом + + class Meta: + model = PriceTag + fields = ['id', 'product', 'price_list', 'tag_image', 'price_effective_date'] + ref_name = 'PricingPriceTagSerializer' # Unique ref_name for pricing + + +# Сериализатор для модели DiscountHistory +class DiscountHistorySerializer(serializers.ModelSerializer): + discount = DiscountSerializer() # Вложенный сериализатор для связи с акцией + + class Meta: + model = DiscountHistory + fields = ['id', 'discount', 'old_discount', 'new_discount', 'change_date', 'reason'] + ref_name = 'PricingDiscountHistorySerializer' # Unique ref_name for pricing + + +# Сериализатор для модели PriceListWithDiscount +class PriceListWithDiscountSerializer(serializers.ModelSerializer): + price_list = PriceListSerializer() # Вложенный сериализатор для связи с прайс-листом + discount = DiscountSerializer() # Вложенный сериализатор для связи с скидкой + + class Meta: + model = PriceListWithDiscount + fields = ['id', 'price_list', 'discount', 'final_price_after_discount'] + ref_name = 'PricingPriceListWithDiscountSerializer' # Unique ref_name for pricing diff --git a/pricing/tests.py b/pricing/tests.py new file mode 100644 index 0000000..a8087bb --- /dev/null +++ b/pricing/tests.py @@ -0,0 +1,46 @@ +from rest_framework.test import APITestCase +from rest_framework import status +from .models import Product +from django.urls import reverse + +class ProductAPITest(APITestCase): + + def setUp(self): + # Создание тестового продукта + self.product_data = { + 'name': 'Test Product', + 'manufacturer_name': 'Test Manufacturer', + 'manufacturer_country': 'Test Country', + 'unit_of_measure': 'pcs', + 'shelf_life_days': 365, + 'barcode': '123456789012', + } + self.product = Product.objects.create(**self.product_data) + self.url = reverse('product-list') # Замени на свой URL + + def test_create_product(self): + """Test creating a new product""" + response = self.client.post(self.url, self.product_data, format='json') + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(Product.objects.count(), 2) + self.assertEqual(Product.objects.latest('id').name, self.product_data['name']) + + def test_get_product(self): + """Test retrieving a product""" + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) # Только один продукт в базе + + def test_update_product(self): + """Test updating a product""" + updated_data = {'name': 'Updated Product'} + response = self.client.put(reverse('product-detail', args=[self.product.id]), updated_data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.product.refresh_from_db() + self.assertEqual(self.product.name, 'Updated Product') + + def test_delete_product(self): + """Test deleting a product""" + response = self.client.delete(reverse('product-detail', args=[self.product.id])) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(Product.objects.count(), 0) # Продукт должен быть удален diff --git a/pricing/urls.py b/pricing/urls.py new file mode 100644 index 0000000..1386bd9 --- /dev/null +++ b/pricing/urls.py @@ -0,0 +1,39 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from drf_yasg.views import get_schema_view +from drf_yasg import openapi +from rest_framework.permissions import AllowAny +from .views import ( + PriceTypeViewSet, PriceListViewSet, DiscountViewSet, + PriceChangeHistoryViewSet, PriceTagViewSet, DiscountHistoryViewSet, PriceListWithDiscountViewSet +) + +# Create the router for pricing-related views +router_pricing = DefaultRouter() +router_pricing.register(r'price-types', PriceTypeViewSet) +router_pricing.register(r'price-lists', PriceListViewSet) +router_pricing.register(r'discounts', DiscountViewSet) +router_pricing.register(r'price-change-history', PriceChangeHistoryViewSet) +router_pricing.register(r'price-tags', PriceTagViewSet) +router_pricing.register(r'discount-history', DiscountHistoryViewSet) +router_pricing.register(r'price-lists-with-discounts', PriceListWithDiscountViewSet) + +# Swagger schema view for pricing +schema_view_pricing = get_schema_view( + openapi.Info( + title="Store Management API - Pricing", + default_version='v1', + description="API for managing pricing in the franchise store", + terms_of_service="https://www.example.com/terms/", + contact=openapi.Contact(email="support@example.com"), + license=openapi.License(name="BSD License"), + ), + public=True, + permission_classes=(AllowAny,), +) + +urlpatterns = [ + path('', include(router_pricing.urls)), + path('swagger/', schema_view_pricing.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui-pricing'), + path('redoc/', schema_view_pricing.with_ui('redoc', cache_timeout=0), name='schema-redoc-pricing'), +] diff --git a/pricing/views.py b/pricing/views.py new file mode 100644 index 0000000..acbf9d9 --- /dev/null +++ b/pricing/views.py @@ -0,0 +1,46 @@ +from rest_framework import viewsets +from .models import Product, PriceType, PriceList, Discount, PriceChangeHistory, PriceTag, DiscountHistory, PriceListWithDiscount +from .serializers import ( + ProductSerializer, PriceTypeSerializer, PriceListSerializer, DiscountSerializer, + PriceChangeHistorySerializer, PriceTagSerializer, DiscountHistorySerializer, PriceListWithDiscountSerializer +) + +# ViewSet для модели Product +class ProductViewSet(viewsets.ModelViewSet): + queryset = Product.objects.all() + serializer_class = ProductSerializer + +# ViewSet для модели PriceType +class PriceTypeViewSet(viewsets.ModelViewSet): + queryset = PriceType.objects.all() + serializer_class = PriceTypeSerializer + +# ViewSet для модели PriceList +class PriceListViewSet(viewsets.ModelViewSet): + queryset = PriceList.objects.all() + serializer_class = PriceListSerializer + +# ViewSet для модели Discount +class DiscountViewSet(viewsets.ModelViewSet): + queryset = Discount.objects.all() + serializer_class = DiscountSerializer + +# ViewSet для модели PriceChangeHistory +class PriceChangeHistoryViewSet(viewsets.ModelViewSet): + queryset = PriceChangeHistory.objects.all() + serializer_class = PriceChangeHistorySerializer + +# ViewSet для модели PriceTag +class PriceTagViewSet(viewsets.ModelViewSet): + queryset = PriceTag.objects.all() + serializer_class = PriceTagSerializer + +# ViewSet для модели DiscountHistory +class DiscountHistoryViewSet(viewsets.ModelViewSet): + queryset = DiscountHistory.objects.all() + serializer_class = DiscountHistorySerializer + +# ViewSet для модели PriceListWithDiscount +class PriceListWithDiscountViewSet(viewsets.ModelViewSet): + queryset = PriceListWithDiscount.objects.all() + serializer_class = PriceListWithDiscountSerializer diff --git a/settings/__pycache__/__init__.cpython-310.pyc b/settings/__pycache__/__init__.cpython-310.pyc index c0f6ea3..d653309 100644 Binary files a/settings/__pycache__/__init__.cpython-310.pyc and b/settings/__pycache__/__init__.cpython-310.pyc differ diff --git a/settings/__pycache__/base.cpython-310.pyc b/settings/__pycache__/base.cpython-310.pyc index 77d06b7..db58c1b 100644 Binary files a/settings/__pycache__/base.cpython-310.pyc and b/settings/__pycache__/base.cpython-310.pyc differ diff --git a/settings/__pycache__/dev.cpython-310.pyc b/settings/__pycache__/dev.cpython-310.pyc index 7cd880a..ab580c7 100644 Binary files a/settings/__pycache__/dev.cpython-310.pyc and b/settings/__pycache__/dev.cpython-310.pyc differ diff --git a/settings/base.py b/settings/base.py index fe83006..3bb3108 100644 --- a/settings/base.py +++ b/settings/base.py @@ -56,6 +56,7 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'rest_framework', 'inventory', + 'pricing', ] ROOT_URLCONF = 'urls' diff --git a/urls.py b/urls.py index 4c7bb56..08dbe5f 100644 --- a/urls.py +++ b/urls.py @@ -3,5 +3,6 @@ from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), - path('api/', include('inventory.urls')), -] \ No newline at end of file + path('api/inventory/', include('inventory.urls')), # Inventory URLs + path('api/pricing/', include('pricing.urls')), # Pricing URLs +]