KNOTTA research & development

Клиенты, объекты и приглашения

Модели Client, ContactPerson, ClientObject и ClientInvite — клиентская база и доступ контактных лиц.

Клиенты, объекты и приглашения

Client

Префикс: clnt_ Таблица: client Файл: garden/modules/client/models.py

Запись о компании-заказчике или физическом лице.

ПолеТипОписание
idKSUID, clnt_*Первичный ключ
nameCharField(255)Название (компании / ФИО физлица)
typeClientTypeindividual / organization
emailEmailField(nullable)Контактный email клиента в целом
phoneCharField(nullable)Контактный телефон
addressCharField(512, nullable)Произвольный адрес
tax_idCharField(64, nullable)ИНН (для организаций)

Подключает simple_history.HistoricalRecords() — все изменения пишутся в historical_client.

ContactPerson

Префикс: cprsn_ Таблица: contact_person Файл: garden/modules/client/models.py

Контактное лицо клиента — конкретный человек со стороны заказчика, с которым ведётся переписка и которому отправляются уведомления.

ПолеТипОписание
idKSUID, cprsn_*
clientFK → ClientНа какого клиента работает
first_name, last_nameCharField(255)
full_nameGeneratedFieldКонкатенация (хранится в БД)
positionCharField(255)Должность
emailEmailField(nullable)
phoneCharField(32, nullable)
preferred_languageCharField(2)ru / en
notify_work_startBooleanFieldDeprecated — использовать NotificationPreference
notify_report_readyBooleanFieldDeprecated — использовать NotificationPreference
userOneToOne → User, nullableПривязка к учётной записи (после принятия инвайта)

Связь с пользователем. Поле user (OneToOne) заполняется в момент принятия приглашения: создаётся учётная запись в Supabase, синхронизируется в Django, привязка User ↔ ContactPerson устанавливается. После этого ContactPerson.user.email — это login для входа в личный кабинет клиента.

При создании ContactPerson signal post_save создаёт записи NotificationPreference по умолчанию (топики: work_started, work_completed, report_ready; канал: email).

ClientObject

Префикс: cobj_ Таблица: client_object

Физическая площадка клиента, на которой выполняются работы.

ПолеТипОписание
idKSUID, cobj_*
clientFK → ClientКому принадлежит объект
nameCharField(255)Короткое название («Парк у входа», «Территория цеха №2»)
addressCharField(512)Произвольный адрес

Constraint uq_client_object_client_name — у одного клиента не может быть двух объектов с одинаковым названием.

ClientObject — это «то, к чему привязан наряд». Один клиент может иметь несколько объектов; на каждый создаётся свой ServiceVisit.

ClientInvite

Префикс: cinvt_ Таблица: client_invite

Приглашение конкретному контактному лицу. Один контакт может получить несколько приглашений (например, истёкшие + новое).

ПолеТипОписание
idKSUID, cinvt_*
contactFK → ContactPersonКому адресовано
clientFK → ClientДенормализация для индексов / фильтров
email_snapshotEmailFieldEmail на момент создания (snapshot — для аудита)
tokenCharField(128, unique)Одноразовый URL-safe токен (secrets.token_urlsafe(32))
statusInviteStatuspending / accepted / revoked / expired
invited_atDateTimeFieldКогда создано
expires_atDateTimeField(nullable)Срок действия
accepted_atDateTimeField(nullable)Когда принято
invited_byFK → User, nullableКто создал приглашение
accepted_by_userFK → User, nullableУчётка, принявшая приглашение
accepted_supabase_uidUUIDField(nullable)Supabase UID принявшего
channelCharField(32)email / telegram / link

Жизненный цикл:

pending ─▶ accepted   (получатель прошёл по ссылке и зарегистрировался)

   ├──▶ revoked       (отозвано вручную)

   └──▶ expired       (истёк expires_at)

После принятия — ContactPerson.user заполняется ссылкой на новую учётную запись User с ролью client.

Поток принятия приглашения

Загрузка диаграммы…

После принятия пользователь видит отчёты по объектам своего клиента.

На странице