diff --git a/inventory/migrations/0002_contractor_position_truck_employee_supplycontract.py b/inventory/migrations/0002_contractor_position_truck_employee_supplycontract.py new file mode 100644 index 0000000..38c983c --- /dev/null +++ b/inventory/migrations/0002_contractor_position_truck_employee_supplycontract.py @@ -0,0 +1,86 @@ +# Generated by Django 5.1.4 on 2025-01-07 15:19 + +import django.contrib.auth.models +import django.contrib.auth.validators +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ('inventory', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Contractor', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='Наименование контрагента')), + ('address', models.TextField(verbose_name='Адрес')), + ('phone', models.CharField(max_length=20, verbose_name='Телефон')), + ('email', models.EmailField(max_length=254, verbose_name='Электронная почта')), + ], + ), + migrations.CreateModel( + name='Position', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='Наименование должности')), + ('hourly_rate', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Ставка в час')), + ('monthly_hours', models.IntegerField(verbose_name='Часы в месяц')), + ], + ), + migrations.CreateModel( + name='Truck', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('plate_number', models.CharField(max_length=20, verbose_name='Номерной знак')), + ('model', models.CharField(max_length=50, verbose_name='Модель')), + ('capacity', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Грузоподъемность (тонны)')), + ], + ), + migrations.CreateModel( + name='Employee', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('department', models.CharField(max_length=255, verbose_name='Подразделение')), + ('work_phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Рабочий телефон')), + ('personal_phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Личный телефон')), + ('email', models.EmailField(max_length=254, unique=True, verbose_name='Электронная почта')), + ('groups', models.ManyToManyField(blank=True, related_name='employee_groups', to='auth.group', verbose_name='Группы')), + ('user_permissions', models.ManyToManyField(blank=True, related_name='employee_permissions', to='auth.permission', verbose_name='Разрешения')), + ('position', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.position', verbose_name='Должность')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='SupplyContract', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('description', models.TextField(verbose_name='Описание')), + ('start_date', models.DateField(verbose_name='Дата начала')), + ('end_date', models.DateField(blank=True, null=True, verbose_name='Дата окончания')), + ('contractor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.contractor', verbose_name='Контрагент')), + ], + ), + ] diff --git a/inventory/models.py b/inventory/models.py index b0ad803..b6928e0 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -1,5 +1,5 @@ from django.db import models -from django.contrib.auth.models import AbstractUser +from django.contrib.auth.models import AbstractUser, Permission, Group # Справочник товаров class Product(models.Model): @@ -33,6 +33,19 @@ class Employee(AbstractUser): personal_phone = models.CharField(max_length=20, blank=True, null=True, verbose_name="Личный телефон") email = models.EmailField(unique=True, verbose_name="Электронная почта") + groups = models.ManyToManyField( + Group, + related_name='employee_groups', + blank=True, + verbose_name='Группы' + ) + user_permissions = models.ManyToManyField( + Permission, + related_name='employee_permissions', + blank=True, + verbose_name='Разрешения' + ) + def __str__(self): return self.get_full_name() diff --git a/inventory/urls.py b/inventory/urls.py index d9c180a..20311a5 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -4,6 +4,9 @@ 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) @@ -14,6 +17,21 @@ router.register(r'contractors', ContractorViewSet) router.register(r'supply-contracts', SupplyContractViewSet) router.register(r'trucks', TruckViewSet) +schema_view = get_schema_view( + openapi.Info( + title="Store Management API", + default_version='v1', + description="API for managing 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('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 diff --git a/settings/base.py b/settings/base.py index d62e02f..fe83006 100644 --- a/settings/base.py +++ b/settings/base.py @@ -1,3 +1,15 @@ +from pathlib import Path + +BASE_DIR = Path(__file__).resolve().parent.parent + +STATIC_URL = '/static/' +STATIC_ROOT = BASE_DIR / 'staticfiles' +STATICFILES_DIRS = [ + BASE_DIR / 'static', +] + +SECRET_KEY = 's3cr3t_k3y_g3n3r4t3d_h3r3' + DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', @@ -9,13 +21,41 @@ DATABASES = { } } +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + INSTALLED_APPS = [ - # 'django.contrib.admin', - # 'django.contrib.auth', - # 'django.contrib.contenttypes', - # 'django.contrib.sessions', - # 'django.contrib.messages', - # 'django.contrib.staticfiles', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', 'rest_framework', 'inventory', -] \ No newline at end of file +] + +ROOT_URLCONF = 'urls' diff --git a/settings/dev.py b/settings/dev.py index a535405..d39ec34 100644 --- a/settings/dev.py +++ b/settings/dev.py @@ -1,4 +1,6 @@ from .base import * DEBUG = True -ALLOWED_HOSTS = ["*"] \ No newline at end of file +ALLOWED_HOSTS = ["*"] + +INSTALLED_APPS.append("drf_yasg") diff --git a/urls.py b/urls.py new file mode 100644 index 0000000..4c7bb56 --- /dev/null +++ b/urls.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/', include('inventory.urls')), +] \ No newline at end of file