from django.conf import settings from django.db import migrations, models import django.db.models.deletion GUEST_ID_START = 900_000_000_000_001 GUEST_ID_MAX = 999_999_999_999_999 def create_guest_id_sequence(apps, schema_editor): if schema_editor.connection.vendor != "postgresql": return with schema_editor.connection.cursor() as cursor: cursor.execute( """ CREATE SEQUENCE IF NOT EXISTS guests_guest_id_seq START WITH 900000000000001 INCREMENT BY 1 MINVALUE 900000000000001 MAXVALUE 999999999999999 NO CYCLE """ ) def drop_guest_id_sequence(apps, schema_editor): if schema_editor.connection.vendor != "postgresql": return with schema_editor.connection.cursor() as cursor: cursor.execute("DROP SEQUENCE IF EXISTS guests_guest_id_seq") class Migration(migrations.Migration): initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ("documents", "0015_delete_documentcomment"), ("employees", "0053_cleanup_department_role_permissions"), ] operations = [ migrations.RunPython(create_guest_id_sequence, drop_guest_id_sequence), migrations.CreateModel( name="Guest", fields=[ ("id", models.BigIntegerField(editable=False, primary_key=True, serialize=False)), ("last_name", models.CharField(max_length=150, verbose_name="Фамилия")), ("first_name", models.CharField(max_length=150, verbose_name="Имя")), ("patronymic", models.CharField(blank=True, max_length=150, verbose_name="Отчество")), ("birth_date", models.DateField(blank=True, null=True, verbose_name="Дата рождения")), ("phone", models.CharField(blank=True, max_length=64, verbose_name="Телефон")), ("email", models.EmailField(blank=True, max_length=254, verbose_name="Email")), ("organization", models.CharField(blank=True, max_length=255, verbose_name="Организация")), ("position", models.CharField(blank=True, max_length=255, verbose_name="Должность")), ("comment", models.TextField(blank=True, verbose_name="Комментарий")), ("is_active", models.BooleanField(default=True, verbose_name="Активен")), ("ldap_enabled", models.BooleanField(default=False, verbose_name="LDAP в Active OU")), ("ldap_username", models.CharField(blank=True, max_length=150, verbose_name="LDAP username")), ("ldap_upn", models.CharField(blank=True, max_length=255, verbose_name="LDAP UPN")), ("ldap_last_synced_at", models.DateTimeField(blank=True, null=True, verbose_name="Последняя LDAP синхронизация")), ("ldap_last_error", models.TextField(blank=True, verbose_name="Последняя LDAP ошибка")), ("created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано")), ("updated_at", models.DateTimeField(auto_now=True, verbose_name="Обновлено")), ("created_by", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="created_guests", to=settings.AUTH_USER_MODEL, verbose_name="Создал")), ], options={ "verbose_name": "Гость", "verbose_name_plural": "Гости", "ordering": ["last_name", "first_name", "id"], }, ), migrations.CreateModel( name="GuestVisit", fields=[ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("inviter_snapshot_name", models.CharField(blank=True, max_length=255, verbose_name="ФИО приглашающего на момент создания")), ("inviter_snapshot_email", models.EmailField(blank=True, max_length=254, verbose_name="Email приглашающего на момент создания")), ("purpose", models.TextField(verbose_name="Цель приглашения")), ("visit_comment", models.TextField(blank=True, verbose_name="Комментарий заявителя")), ("admin_comment", models.TextField(blank=True, verbose_name="Комментарий администратора")), ("status", models.CharField(choices=[("draft", "Черновик"), ("pending", "На рассмотрении"), ("needs_info", "Требуется информация"), ("approved", "Одобрено"), ("rejected", "Отклонено"), ("cancelled", "Отменено"), ("expired", "Истекло"), ("revoked", "Отозвано")], db_index=True, default="draft", max_length=24, verbose_name="Статус")), ("access_starts_at", models.DateTimeField(blank=True, null=True, verbose_name="Начало доступа")), ("access_expires_at", models.DateTimeField(blank=True, null=True, verbose_name="Окончание доступа")), ("all_day", models.BooleanField(default=True, verbose_name="Полные сутки")), ("unlimited", models.BooleanField(default=False, verbose_name="Бессрочно")), ("submitted_at", models.DateTimeField(blank=True, null=True, verbose_name="Отправлено на рассмотрение")), ("decided_at", models.DateTimeField(blank=True, null=True, verbose_name="Решение принято")), ("decision_comment", models.TextField(blank=True, verbose_name="Комментарий к решению")), ("cancelled_at", models.DateTimeField(blank=True, null=True)), ("cancel_reason", models.TextField(blank=True)), ("revoked_at", models.DateTimeField(blank=True, null=True)), ("revoke_reason", models.TextField(blank=True)), ("expired_at", models.DateTimeField(blank=True, null=True)), ("inviter_inactive", models.BooleanField(db_index=True, default=False)), ("created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано")), ("updated_at", models.DateTimeField(auto_now=True, verbose_name="Обновлено")), ("cancelled_by", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="cancelled_guest_visits", to=settings.AUTH_USER_MODEL)), ("decided_by", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="decided_guest_visits", to=settings.AUTH_USER_MODEL, verbose_name="Решение принял")), ("documents", models.ManyToManyField(blank=True, related_name="guest_visits", to="documents.document", verbose_name="Документы")), ("guest", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="visits", to="guests.guest", verbose_name="Гость")), ("host_department", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="guest_visits", to="employees.department", verbose_name="Отдел приглашающего")), ("inviter", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="guest_visits", to=settings.AUTH_USER_MODEL, verbose_name="Приглашающий")), ("revoked_by", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="revoked_guest_visits", to=settings.AUTH_USER_MODEL)), ], options={ "verbose_name": "Гостевой визит", "verbose_name_plural": "Гостевые визиты", "ordering": ["-created_at"], "permissions": [("view_all_guestvisit", "Может просматривать все гостевые визиты"), ("decide_guestvisit", "Может принимать решения по гостевым визитам"), ("manage_guestaccount", "Может управлять гостевыми учетками")], }, ), migrations.CreateModel( name="GuestVisitEvent", fields=[ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("event_type", models.CharField(choices=[("created", "Создано"), ("submitted", "Отправлено"), ("needs_info_requested", "Запрошена информация"), ("info_provided", "Информация предоставлена"), ("approved", "Одобрено"), ("rejected", "Отклонено"), ("decision_changed", "Решение изменено"), ("cancelled", "Отменено"), ("revoked", "Отозвано"), ("expired", "Истекло"), ("ldap_created", "LDAP создан"), ("ldap_updated", "LDAP обновлен"), ("ldap_enabled", "LDAP перемещен в Active OU"), ("ldap_disabled", "LDAP перемещен в Deactivated OU"), ("ldap_failed", "Ошибка LDAP"), ("ldap_skipped", "LDAP пропущен"), ("document_attached", "Документ прикреплен"), ("document_removed", "Документ удален"), ("inviter_inactive_detected", "Приглашающий неактивен")], db_index=True, max_length=48, verbose_name="Тип события")), ("from_status", models.CharField(blank=True, max_length=24)), ("to_status", models.CharField(blank=True, max_length=24)), ("comment", models.TextField(blank=True)), ("metadata", models.JSONField(blank=True, default=dict)), ("created_at", models.DateTimeField(auto_now_add=True)), ("actor", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="guest_visit_events", to=settings.AUTH_USER_MODEL, verbose_name="Автор события")), ("visit", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="events", to="guests.guestvisit", verbose_name="Визит")), ], options={ "verbose_name": "Событие гостевого визита", "verbose_name_plural": "События гостевых визитов", "ordering": ["-created_at", "-id"], }, ), migrations.AddIndex(model_name="guest", index=models.Index(fields=["last_name", "first_name"], name="guest_name_idx")), migrations.AddIndex(model_name="guest", index=models.Index(fields=["email"], name="guest_email_idx")), migrations.AddIndex(model_name="guest", index=models.Index(fields=["phone"], name="guest_phone_idx")), migrations.AddIndex(model_name="guest", index=models.Index(fields=["ldap_enabled"], name="guest_ldap_enabled_idx")), migrations.AddConstraint(model_name="guest", constraint=models.CheckConstraint(condition=models.Q(("id__gte", GUEST_ID_START), ("id__lte", GUEST_ID_MAX)), name="guest_id_in_guest_range")), migrations.AddIndex(model_name="guestvisit", index=models.Index(fields=["status", "created_at"], name="guestvisit_status_created_idx")), migrations.AddIndex(model_name="guestvisit", index=models.Index(fields=["inviter", "created_at"], name="guestvisit_inviter_created_idx")), migrations.AddIndex(model_name="guestvisit", index=models.Index(fields=["access_starts_at", "access_expires_at"], name="guestvisit_access_idx")), migrations.AddIndex(model_name="guestvisit", index=models.Index(fields=["guest", "status"], name="guestvisit_guest_status_idx")), migrations.AddConstraint(model_name="guestvisit", constraint=models.CheckConstraint(condition=models.Q(("unlimited", True), ("access_expires_at__isnull", True), ("access_starts_at__isnull", True), ("access_starts_at__lt", models.F("access_expires_at")), _connector="OR"), name="guest_visit_access_range_valid")), migrations.AddIndex(model_name="guestvisitevent", index=models.Index(fields=["visit", "-created_at"], name="guestevent_visit_created_idx")), migrations.AddIndex(model_name="guestvisitevent", index=models.Index(fields=["event_type", "-created_at"], name="guestevent_type_created_idx")), ]