Docs: начальная структура проекта

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-25 14:59:46 +00:00
commit 1669c12182
56 changed files with 2085 additions and 0 deletions

0
app/users/__init__.py Normal file
View File

7
app/users/admin.py Normal file
View File

@@ -0,0 +1,7 @@
from django.contrib import admin
from .models import UserProfile
@admin.register(UserProfile)
class UserProfileAdmin(admin.ModelAdmin):
list_display = ("user", "employee")

7
app/users/apps.py Normal file
View File

@@ -0,0 +1,7 @@
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "users"
verbose_name = "Пользователи"

View File

@@ -0,0 +1,30 @@
# Generated by Django 5.2.11 on 2026-02-25 14:58
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('references', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('employee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_profiles', to='references.employee', verbose_name='Сотрудник (автор в документах)')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')),
],
options={
'verbose_name': 'Профиль пользователя',
'verbose_name_plural': 'Профили пользователей',
},
),
]

View File

31
app/users/models.py Normal file
View File

@@ -0,0 +1,31 @@
"""
Профиль пользователя: связь User → Сотрудник для поля «Автор» в документах.
"""
from django.conf import settings
from django.db import models
from references.models import Employee
class UserProfile(models.Model):
"""Профиль пользователя: привязка к справочнику Сотрудники."""
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="profile",
verbose_name="Пользователь",
)
employee = models.ForeignKey(
Employee,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="user_profiles",
verbose_name="Сотрудник (автор в документах)",
)
class Meta:
verbose_name = "Профиль пользователя"
verbose_name_plural = "Профили пользователей"
def __str__(self):
return f"{self.user.username}{self.employee or ''}"

10
app/users/urls.py Normal file
View File

@@ -0,0 +1,10 @@
from django.urls import path
from . import views
app_name = "users"
urlpatterns = [
path("", views.HomeView.as_view(), name="home"),
path("login/", views.ErpLoginView.as_view(), name="login"),
path("logout/", views.ErpLogoutView.as_view(), name="logout"),
]

10
app/users/utils.py Normal file
View File

@@ -0,0 +1,10 @@
"""Хелперы для пользователей."""
def get_author_employee(user):
"""Возвращает сотрудника (автора) для текущего пользователя, если привязан профиль."""
if not user or not user.is_authenticated:
return None
try:
profile = user.profile
return profile.employee if profile else None
except Exception:
return None

35
app/users/views.py Normal file
View File

@@ -0,0 +1,35 @@
"""
Вход, выход и главная страница.
"""
import logging
from django.contrib.auth.views import LoginView, LogoutView
from django.views.generic import TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin
logger = logging.getLogger(__name__)
class HomeView(LoginRequiredMixin, TemplateView):
"""Главная страница после входа: меню справочников и документов."""
template_name = "home.html"
login_url = "users:login"
class ErpLoginView(LoginView):
"""Страница входа."""
template_name = "registration/login.html"
redirect_authenticated_user = True
def form_valid(self, form):
logger.info("Вход пользователя: %s", form.get_user().username)
return super().form_valid(form)
class ErpLogoutView(LogoutView):
"""Выход."""
next_page = "users:login"
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
logger.info("Выход пользователя: %s", request.user.username)
return super().dispatch(request, *args, **kwargs)