Клиенты, объекты и приглашения
Модели Client, ContactPerson, ClientObject и ClientInvite — клиентская база и доступ контактных лиц.
Клиенты, объекты и приглашения
Client
Префикс: clnt_
Таблица: client
Файл: garden/modules/client/models.py
Запись о компании-заказчике или физическом лице.
| Поле | Тип | Описание |
|---|---|---|
id | KSUID, clnt_* | Первичный ключ |
name | CharField(255) | Название (компании / ФИО физлица) |
type | ClientType | individual / organization |
email | EmailField(nullable) | Контактный email клиента в целом |
phone | CharField(nullable) | Контактный телефон |
address | CharField(512, nullable) | Произвольный адрес |
tax_id | CharField(64, nullable) | ИНН (для организаций) |
Подключает simple_history.HistoricalRecords() — все изменения пишутся в historical_client.
ContactPerson
Префикс: cprsn_
Таблица: contact_person
Файл: garden/modules/client/models.py
Контактное лицо клиента — конкретный человек со стороны заказчика, с которым ведётся переписка и которому отправляются уведомления.
| Поле | Тип | Описание |
|---|---|---|
id | KSUID, cprsn_* | |
client | FK → Client | На какого клиента работает |
first_name, last_name | CharField(255) | |
full_name | GeneratedField | Конкатенация (хранится в БД) |
position | CharField(255) | Должность |
email | EmailField(nullable) | |
phone | CharField(32, nullable) | |
preferred_language | CharField(2) | ru / en |
notify_work_start | BooleanField | Deprecated — использовать NotificationPreference |
notify_report_ready | BooleanField | Deprecated — использовать NotificationPreference |
user | OneToOne → User, nullable | Привязка к учётной записи (после принятия инвайта) |
Constraint uq_contact_person_client_email гарантирует, что у одного клиента не будет двух контактных лиц с одинаковым email. Аналогично для телефона. Между разными клиентами совпадения допустимы.
Связь с пользователем. Поле 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
Физическая площадка клиента, на которой выполняются работы.
| Поле | Тип | Описание |
|---|---|---|
id | KSUID, cobj_* | |
client | FK → Client | Кому принадлежит объект |
name | CharField(255) | Короткое название («Парк у входа», «Территория цеха №2») |
address | CharField(512) | Произвольный адрес |
Constraint uq_client_object_client_name — у одного клиента не может быть двух объектов с одинаковым названием.
ClientObject — это «то, к чему привязан наряд». Один клиент может иметь несколько объектов; на каждый создаётся свой ServiceVisit.
ClientInvite
Префикс: cinvt_
Таблица: client_invite
Приглашение конкретному контактному лицу. Один контакт может получить несколько приглашений (например, истёкшие + новое).
| Поле | Тип | Описание |
|---|---|---|
id | KSUID, cinvt_* | |
contact | FK → ContactPerson | Кому адресовано |
client | FK → Client | Денормализация для индексов / фильтров |
email_snapshot | EmailField | Email на момент создания (snapshot — для аудита) |
token | CharField(128, unique) | Одноразовый URL-safe токен (secrets.token_urlsafe(32)) |
status | InviteStatus | pending / accepted / revoked / expired |
invited_at | DateTimeField | Когда создано |
expires_at | DateTimeField(nullable) | Срок действия |
accepted_at | DateTimeField(nullable) | Когда принято |
invited_by | FK → User, nullable | Кто создал приглашение |
accepted_by_user | FK → User, nullable | Учётка, принявшая приглашение |
accepted_supabase_uid | UUIDField(nullable) | Supabase UID принявшего |
channel | CharField(32) | email / telegram / link |
Жизненный цикл:
pending ─▶ accepted (получатель прошёл по ссылке и зарегистрировался)
│
├──▶ revoked (отозвано вручную)
│
└──▶ expired (истёк expires_at)После принятия — ContactPerson.user заполняется ссылкой на новую учётную запись User с ролью client.
Поток принятия приглашения
После принятия пользователь видит отчёты по объектам своего клиента.