Представим переписку, которая знакома почти любому агентству. В понедельник дизайнер отправил клиенту макет лендинга. Во вторник клиентка ответила: «Выглядит отлично, берём в работу». В среду менеджер поправил заголовок первого экрана, заменил фотографию и выложил файл с тем же названием, только с припиской «финал». В пятницу верстка готова, а в понедельник приходит письмо: «Мы этого не согласовывали, там другая картинка». Кто прав? Скорее всего, обе стороны. Клиентка согласовала то, что видела во вторник. Агентство сдало то, что лежало в папке в пятницу. Между этими двумя фактами нет связи, и никакая вежливость её не восстановит.
Проблема не в людях. Решение клиента («да», «нет», «с правками») живёт в одном месте, результат работы в другом, задача в третьем. Клиентский портал нужен ровно для того, чтобы эти три вещи перестали разъезжаться. Ниже разберём, как собрать такой портал на EspoCRM, которую можно развернуть на собственном VPS: кто в нём клиент, из каких записей состоит один цикл согласования, как отделить внутренние споры команды от клиентской переписки и как проверить, что клиент видит только своё.
Сразу уточним границы. Портальная модель доступа в EspoCRM описана в документации, и на неё можно опираться. А вот «журнал согласований» как готовой функции в продукте нет: это решение, которое мы проектируем из пользовательских сущностей, связей и, при желании, доработки. Кнопка «Согласовано» в таком журнале фиксирует решение внутри процесса агентства, но юридической подписью сама по себе не является. Если договор требует особой формы подтверждения, этот вопрос решается отдельно.
Кто такой клиент в системе: портальный пользователь, а не сотрудник с урезанными правами
Первая развилка кажется техничной, но определяет всё остальное. Можно завести клиенту обычную учётную запись и ограничить её ролью. А можно использовать отдельный механизм портала. Для внешних людей правильнее второй путь: документация EspoCRM о порталах описывает портальных пользователей как отдельную категорию, и такой пользователь обязательно связывается с конкретным Portal. Это даёт понятную границу: сотрудники работают во внутреннем интерфейсе, клиенты заходят через собственный вход со своим набором разделов.
Дальше важно, к чему привязан этот человек. В CRM агентства есть компания-заказчик (Account) и конкретные люди в ней (Contact). Портальный пользователь связывается с контактом и, через него, с аккаунтом. Эта связь не формальность: именно она позволяет сказать системе «покажи этому человеку всё, что относится к его компании». Если контакт не привязан к аккаунту, дальнейшие правила видимости будут опираться на пустоту.
Тут стоит принять одно организационное решение. Кто на стороне клиента вправе согласовывать? Бывает, что у компании три контакта: маркетолог ведёт проект, директор подписывает бюджет, стажёр получает уведомления. Если всем троим дать одинаковые права, «согласовал стажёр» станет поводом для спора через месяц. Поэтому полезно заранее различать роли внутри клиента: кто видит и комментирует, а кто принимает решение. Как именно реализовать такое различие (отдельной ролью, отдельным полем у контакта или процессом на стороне агентства), зависит от вашей схемы. Важно, чтобы это было записано в системе, а не жило в памяти менеджера.
Два уровня видимости: account и contact
В документации по порталам описаны уровни доступа, которые привязывают видимость к записям клиента. Уровень account означает: пользователь видит записи, связанные с его аккаунтом. Уровень contact уже: только записи, связанные с ним лично. Уровень own относится к записям, созданным самим пользователем.
Для агентства это даёт три практических режима:
- account подходит для проектов, задач и документов, которые касаются всей компании-заказчика: коллега клиента должен видеть тот же макет и тот же статус.
- contact подходит для личных вещей: например, счетов, адресованных конкретному человеку, или персональных уведомлений.
- own подходит для обращений, которые клиент сам создал: запросов на доработку, вопросов, комментариев.
Типичная ошибка новичка: поставить account везде «чтобы не мучиться». Тогда стажёр клиента увидит коммерческие условия, которые не должен был видеть. Обратная ошибка: поставить own везде, и директор не найдёт макет, который загрузил менеджер агентства, потому что клиент его «не создавал».
Один цикл согласования: задача, версия и решение как связанные записи
Теперь главное. Чтобы фраза «я этого не согласовывал» перестала быть проблемой, решение клиента должно ссылаться не на проект вообще и не на папку, а на конкретную версию результата. Для этого нужны три типа записей и связи между ними.
- Задача (например, «Макет лендинга»): что делаем и для какого проекта. Задача относится к аккаунту клиента.
- Версия результата (назовём её сущностью «Версия»): конкретный файл или ссылка, номер версии, дата, автор. Версия связана с задачей и тоже с аккаунтом.
- Решение (сущность «Согласование»): вердикт клиента, комментарий, кто принял, когда. Решение связано с конкретной версией, а не с задачей.
Эту схему придётся создать в конструкторе сущностей как пользовательские типы: «Версия» и «Согласование». И здесь начинается деталь, на которой чаще всего ломаются самодельные порталы. Документация по порталам отдельно говорит: для пользовательских типов сущностей нужны настоящие отношения с Account или Contact, настроенные для ACL. Недостаточно завести текстовое поле «Клиент» или поле с названием, похожим на «Account». Система проверяет права по связям между записями, а не по тому, что поле называется похоже. Если у «Версии» нет настоящей связи с аккаунтом, портальный пользователь с уровнем account просто не увидит такие записи, а в худшем случае вы начнёте «чинить» ситуацию, раздав права на уровне «все».
Поэтому порядок работы такой: сначала создаём сущность, затем добавляем отношение к Account (и при необходимости к Contact), и только потом настраиваем права ролей. Если сделать наоборот, легко получить в портале пустые списки и ложный вывод, что «портал не работает».
Рабочий пример: две версии макета
Вернёмся к лендингу и посмотрим, как бы выглядела та же история в журнале. Условный пример, без привязки к реальному проекту.
| Запись | Содержание | Связана с |
|---|---|---|
| Задача «Макет лендинга» | Статус «На согласовании клиента» | Аккаунт клиента |
| Версия 1 | Файл, загруженный во вторник; в описании: «Фото команды, заголовок А» | Задача |
| Согласование 1 | Вердикт «Согласовано», контакт-маркетолог, комментарий «Берём в работу» | Версия 1 |
| Версия 2 | Файл, загруженный в среду; в описании: «Заменено фото, заголовок Б» | Задача |
| Согласование 2 | Пока отсутствует | Версия 2 |
В понедельник спор решается одним взглядом: согласована версия 1, версия 2 не согласована, потому что у неё нет записи решения. Менеджер агентства не должен был считать версию 2 принятой: статус задачи вернулся бы в «На согласовании», как только появилась новая версия. Вот это правило и есть смысл всей конструкции: новая версия сбрасывает согласование.
Как его реализовать? Вручную, дисциплиной менеджера, это возможно, но хрупко. Надёжнее настроить автоматизацию: при создании новой «Версии» у задачи статус меняется на «На согласовании». В EspoCRM для подобной логики есть механизмы автоматизации и возможность доработки, но конкретный набор доступных инструментов зависит от редакции и версии, поэтому проверьте его в своей установке. Для нашей схемы это именно проектное решение: журнал и сброс статуса не поставляются готовыми, их собирает агентство.
Что должно быть в записи решения
Чтобы запись действительно отвечала на вопрос «кто и что согласовал», в ней нужны следующие поля:
- ссылка на версию (обязательная связь, а не текст);
- вердикт: согласовано, согласовано с правками, отклонено;
- контакт клиента, принявший решение (связь с Contact);
- комментарий клиента в свободной форме;
- дата и время создания записи.
Обратите внимание на вердикт «согласовано с правками». Он самый коварный. «Берём, но поменяйте кнопку» легко прочесть как «согласовано» и забыть про оговорку. Если вердикт с правками порождает новую задачу, связанную с этим же решением, оговорка не потеряется: в журнале останется и сам вердикт, и то, что из него вытекло.
Файлы: версия должна быть неизменной
Версия, на которую ссылается решение, не должна тихо меняться. Если менеджер может заменить файл в записи «Версия 1» уже после согласования, вся схема превращается в красивую декорацию: клиент подтвердил один файл, а в записи лежит другой. Поэтому правило простое: загруженный файл версии не заменяется, любое изменение это новая версия. Реализовать это можно правами роли (запретом редактирования после создания для обычных сотрудников) или процессом. Какой способ выбрать, зависит от размера команды. В маленькой студии достаточно договорённости и аккуратного названия версий; в большой лучше закрепить правило правами.
Объём хранения файлов растёт быстрее, чем кажется: каждая правка макета это ещё один файл. Для небольшого агентства поэтому имеет значение диск сервера. Если планируете хранить много больших исходников, смотрите на тарифы с запасом по NVMe, например линейку VPS MAATRIX: Ba (1 vCPU, 2 ГБ RAM, 40 ГБ NVMe), Uab (2 vCPU, 4 ГБ, 80 ГБ), Heka (4 vCPU, 8 ГБ, 160 ГБ) и выше. Это ресурсы конфигурации, а не обещание ёмкости: сколько клиентов и файлов выдержит ваша установка, покажет только замер на ваших данных. Тяжёлые исходники часто удобнее держать отдельно, например в Nextcloud, а в версии хранить ссылку, но тогда нужно заранее продумать, кто и как получает доступ к этой ссылке.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.
Два разговора, которые нельзя смешивать: команда и клиент
Теперь о вещи, которая губит портальные проекты не хуже кривых прав. Допустим, дизайнер пишет в задаче: «Клиент опять хочет логотип больше, это бред, предлагаю сделать как есть и посмотреть, заметит ли». Если лента задачи видна клиенту, следующим сообщением будет разговор о доверии, а не о логотипе.
Решение в том, чтобы разделить два потока на уровне данных, а не на уровне «будем осторожнее». Есть несколько способов.
- Отдельная сущность для внутренних заметок. Внутренние замечания команды хранятся в записях, которые у портальных ролей закрыты полностью. Клиент их не видит ни в списках, ни в связанных панелях.
- Клиентская переписка в отдельном потоке. Комментарии, адресованные клиенту, пишутся в связанных записях (например, «Комментарий к версии»), у которых портальные роли имеют доступ на чтение и создание.
- Разные права на стрим. В ролях EspoCRM права включают, среди прочего, право stream, то есть доступ к ленте записи. Как описано в документации по ролям, роль задаёт create, read, edit, delete и stream с уровнями all, team, own и no. Для клиентов лента сущностей, где команда обсуждает работу, должна быть закрыта.
Вариант с отдельной сущностью для внутренних заметок надёжнее остальных. Он не зависит от того, не забыл ли кто-то при отправке сообщения переключить «видно клиенту», а ошибка прав видна сразу: у клиента пустой раздел. Вариант с флажком «видимость» дешевле, но каждый раз требует человеческого внимания.
Практическое правило для команды: в клиентском комментарии пишем так, будто клиент прочтёт его при нас. Во внутренней заметке пишем всё, что нужно для работы. Если мысль начинается со слов «клиент опять», она идёт во внутреннюю заметку и там же остывает.
Проверяем, что клиент видит именно то, что должен
Права в портале не проверяют на глаз по схеме. Их проверяют на конкретном пользователе. Тут есть тонкость, которую легко пропустить: роли портала и дополнительные роли складываются. По документации по порталам, дополнительные Portal Roles объединяются с ролями самого портала. А если ролей несколько, как объясняет описание ролей, более разрешающее правило имеет приоритет.
Практический вывод неприятный и важный: добавить дополнительную роль «на минутку» значит расширить доступ, а не заменить его. Допустим, директору клиента выдали дополнительную роль, чтобы он смог видеть счета. Если в этой роли случайно оставили чтение всех «Версий» на уровне all, он увидит версии других заказчиков, и «ограничивающие» настройки основной роли ничего не изменят, ведь побеждает более разрешающее правило. Отсюда правило: дополнительные роли должны добавлять узкие права, а не повторять широкие.
Как проверять? В карточке пользователя есть раздел Access: по документации по ролям он показывает эффективные разрешения. Это лучший инструмент для спокойной проверки, потому что показывает итог после объединения ролей, а не каждую роль по отдельности. Предлагаем такой порядок.
- Заведите два тестовых аккаунта-заказчика и по одному портальному пользователю в каждом.
- Создайте у каждого аккаунта задачу с версией и решением.
- В разделе Access каждого пользователя посмотрите эффективные права для задач, версий и согласований.
- Войдите в портал под первым пользователем и убедитесь, что записи второго аккаунта не видны ни в списках, ни по прямой ссылке на запись.
- Повторите проверку после любого изменения ролей, особенно после добавления дополнительных.
Шаг с прямой ссылкой нужен, потому что скрыть пункт меню и закрыть доступ к записи это разные вещи. Если у пользователя остаётся право чтения, спрятанная вкладка ничего не защищает.
Отдельно проверьте права на создание и редактирование решений. Клиент должен уметь создать запись «Согласование» для версии своего аккаунта, но не должен редактировать её после создания и тем более удалять. Иначе журнал превратится в черновик, который переписывается задним числом, причём любой стороной. Права edit и delete для портальной роли на сущность «Согласование» разумно закрыть, оставив create и read.
Связь списков с реальностью: на что смотреть в уровнях доступа
| Сущность | Чтение клиентом | Создание | Правка и удаление |
|---|---|---|---|
| Задача | account | обычно нет | нет |
| Версия | account | нет | нет |
| Согласование | account | да | нет |
| Комментарий к версии | account | да | own, если нужно править свои |
| Внутренняя заметка | нет | нет | нет |
Это отправная точка, а не догма. Агентство, где клиент сам заводит задачи, откроет создание задач. Агентство с одним контактом на аккаунт может заменить account на contact. Главное, что каждая ячейка таблицы должна быть осознанным решением и подтверждаться проверкой через Access.
Что делать, когда правка приходит после «согласовано»
Идеальный цикл красив, но настоящая жизнь любит добавки. Клиент согласовал версию 1, команда уже вёрстает, и вдруг приходит: «А можно в футере другой телефон?» Что происходит в журнале?
Есть соблазн открыть согласованную запись и поправить. Не делайте этого. Согласование относится к версии 1, и версия 1 остаётся такой, какой её видел клиент. Новое пожелание это новая работа, у которой три возможных формы.
- Мелкая правка в рамках задачи. Создаётся версия 2 с описанием «изменён телефон в футере». Старая версия и её согласование остаются как есть, а у задачи снова статус «На согласовании». Клиент подтверждает именно версию 2.
- Правка, меняющая объём работ. Это уже новая задача, возможно, с отдельным обсуждением сроков и стоимости. Связь с исходной задачей полезна, но решение по ней отдельное.
- Спорная правка. Если команда считает, что запрос выходит за рамки договорённости, это вопрос для менеджера, а не для статуса задачи. Журнал в таком споре честно показывает, что было согласовано и когда.
Такой порядок делает историю линейной. Через полгода по задаче видно: версия 1 согласована тогда-то таким-то контактом, потом появилась версия 2 из-за телефона, она согласована тем-то. Никто не спрашивает «а что мы тогда имели в виду». Всё записано рядом с файлом.
Есть и обратная ситуация: правка приходит от человека, который не вправе принимать решения. Допустим, стажёр клиента пишет в комментарии «Давайте поменяем цвет». Это комментарий, а не решение. Пока ответственный контакт не создал запись согласования для версии, задача не меняет статус. Различие между обсуждением и решением и есть то, ради чего мы разводили эти сущности.
Закрытие проекта и то, что остаётся после
Когда работа завершена, проект закрывается, но журнал остаётся. Что делать с порталом? Несколько вопросов, которые стоит решить заранее.
- Доступ клиента. Пусть закрытые задачи остаются доступными для чтения: клиенту часто нужно вернуться к тому, что согласовывали. Если проект закончился совсем, доступ можно ограничить, но решение принимайте осознанно, а не по умолчанию.
- Финальный статус. Закрытие проекта разумно сопровождать итоговой записью: какие версии приняты, какие правки закрыты, что осталось за рамками. Это короткий документ, а не отчёт на десять страниц.
- Хвосты. Если после закрытия приходит новая правка, это новая задача в том же аккаунте, а не воскрешение старой. Закрытые записи остаются закрытыми.
Если у вас много проектов и клиентов, прикиньте, как долго нужно хранить журналы и файлы. Сроки хранения определяются договором и внутренними правилами агентства, а не возможностями системы. Это отдельный разговор с юристом или руководителем, а не техническая настройка.
Где всё это поселить: VPS под клиентский портал
EspoCRM с порталом хорошо подходит для собственного сервера: данные клиентов, версии и журнал решений лежат у вас, а не у стороннего сервиса. На VPS MAATRIX есть root-доступ, NVMe-диски и процессоры AMD EPYC. В публичном предложении перечислены локации UK, US, RU и FR; оплата картой РФ или через СБП, а VPS создаётся автоматически через пару минут после оплаты. Какая локация и какой образ доступны в момент заказа, проверяйте в форме заказа: наличие может отличаться.
Выбор локации зависит от того, где живут ваши клиенты и что говорят требования по данным. Сама по себе страна сервера не делает проект соответствующим закону, это вопрос отдельной проверки. Для небольшого портала с десятком клиентов начинать можно с тарифа поскромнее и увеличивать ресурсы, когда замеры покажут нехватку; число клиентов, которое выдержит конкретная конфигурация, зависит от файлов, количества одновременных пользователей и настроек, и заранее его не назвать. Если вам важна альтернатива с другой моделью данных, посмотрите, как похожая задача решается в Baserow: клиентский портал в Baserow с доступом только к своим данным. Подходы различаются, и сравнить их полезно до выбора платформы.
Один вопрос, на который портал должен отвечать сразу
Вернёмся к понедельнику, с которого всё началось. Клиентка пишет: «Мы этого не согласовывали». Менеджер открывает задачу «Макет лендинга» и видит три строки.
- Версия 1, загружена во вторник: согласована контактом-маркетологом, комментарий «Берём в работу».
- Версия 2, загружена в среду: заменены фото и заголовок, согласования нет.
- Статус задачи: «На согласовании», сборка версии 2 не должна была уходить в вёрстку.
Разговор меняется. Вместо «вы же согласовали» менеджер говорит: «Вы согласовали версию от вторника. В среду мы выложили версию 2, и она ждёт вашего решения. Вот обе, сравните». А внутренний вопрос «почему вёрстку начали с неподтверждённой версии» остаётся в внутренней заметке команды, куда клиент не заглядывает.
Вот что в итоге даёт такой портал: по каждому решению видно, кто согласовал именно эту версию и что ещё осталось сделать. Не потому, что система следит за людьми, а потому что решение, файл и задача связаны настоящими отношениями, а права разведены так, чтобы клиент видел своё, команда спорила про своё, а новая версия всегда начинала круг заново.