13. Best Practices и типичные ошибки
13.1. Общая концепция
Этот раздел — сводка рекомендаций по всем предыдущим разделам. Следуя этим правилам, вы избежите 80% типичных проблем при разработке на DSL.
13.2. Именование и структура файлов
Правила именования:
| Объект | Рекомендация | Пример |
|---|---|---|
| Коллекции | Существительное, ед. число | Клиент, Сотрудник, Товар |
| Документы | Существительное, отражающее операцию | ЗаказНаряд, ПоступлениеТоваров |
| Роли | Существительное, обозначающее должность | Менеджер, Кладовщик, Бухгалтер |
| Процедуры | Глагол + существительное | РассчитатьЗарплату, ОтправитьУведомление |
| Отчёты | Существительное, что показывает | ОстаткиТоваров, ДолгиКлиентов |
| Поля | Существительное, с большой буквы | Наименование, ДатаНачала |
❌ Неправильно: коллекция_клиенты (транслит), proc1 (бессмысленно), ЗаказНаряд123 (цифры)
✅ Правильно: Клиенты, РассчитатьСкидкуПоКлиенту, ЗаказНаряд
Структура DSL-файла (рекомендуемый порядок блоков):
# ========== 1. РОЛИ И ПОЛЬЗОВАТЕЛИ ==========
РОЛЬ ... КОНЕЦ_РОЛИ
ПОЛЬЗОВАТЕЛЬ ... КОНЕЦ_ПОЛЬЗОВАТЕЛЯ
# ========== 2. НАСТРОЙКИ И КОНСТАНТЫ ==========
НАСТРОЙКИ ... КОНЕЦ
КОЛЛЕКЦИЯ СистемныеКонстанты ... КОНЕЦ
# ========== 3. СПРАВОЧНИКИ ==========
КОЛЛЕКЦИЯ Организации ... КОНЕЦ
КОЛЛЕКЦИЯ Склады ... КОНЕЦ
# ========== 4. ОСНОВНЫЕ СПРАВОЧНИКИ ==========
КОЛЛЕКЦИЯ Клиенты ... КОНЕЦ
КОЛЛЕКЦИЯ Номенклатура ... КОНЕЦ
# ========== 5. ДОКУМЕНТЫ ==========
ДОКУМЕНТ ПоступлениеТоваров ... КОНЕЦ
ДОКУМЕНТ ЗаказНаряд ... КОНЕЦ
# ========== 6. ЖУРНАЛЫ ==========
ЖУРНАЛ ОстаткиТоваров ... КОНЕЦ
ЖУРНАЛ Взаиморасчеты ... КОНЕЦ
# ========== 7. ОТЧЁТЫ ==========
ОТЧЁТ СкладскиеОстатки ... КОНЕЦ
# ========== 8. ПРОЦЕДУРЫ ==========
ПРОЦЕДУРА ... КОНЕЦ_ПРОЦЕДУРЫ
# ========== 9. API ==========
API ... КОНЕЦ_API
# ========== 10. РАСПИСАНИЯ ==========
РАСПИСАНИЕ ... КОНЕЦ_РАСПИСАНИЯ
# ========== 11. ПЕЧАТНЫЕ ФОРМЫ ==========
ШАБЛОН ... КОНЕЦ13.3. Безопасность (Роли и RLS)
✅ Правильно:
РОЛЬ Менеджер
ДОСТУП ЗаказНаряд ЧТЕНИЕ, ДОБАВЛЕНИЕ, ИЗМЕНЕНИЕ
ОГРАНИЧЕНИЕ_ДОСТУПА ЗаказНаряд ГДЕ Менеджер = ТЕКУЩИЙПОЛЬЗОВАТЕЛЬ()
КОНЕЦ_РОЛИ❌ Ошибка 1: Забыли про RLS в прямых SQL-запросах
ПРОЦЕДУРА ОпаснаяПроцедура
ВЫЧИСЛИТЬ Заказы = ЗАПРОС("SELECT * FROM ЗаказНаряд") # Пользователь увидит ВСЕ заказы!
КОНЕЦ_ПРОЦЕДУРЫИсправление:
ПРОЦЕДУРА БезопаснаяПроцедура
ВЫЧИСЛИТЬ ТекПользователь = ТЕКУЩИЙПОЛЬЗОВАТЕЛЬ().id
ВЫЧИСЛИТЬ Заказы = ЗАПРОС("SELECT * FROM ЗаказНаряд WHERE Менеджер = " + ТекПользователь)
КОНЕЦ_ПРОЦЕДУРЫ❌ Ошибка 2: Слишком широкие права — давайте только необходимые права.
❌ Ошибка 3: Простой пароль — используйте сложные пароли.
13.4. Логика (ЛОГИКА и процедуры)
✅ Правильно:
ДОКУМЕНТ ЗаказНаряд
ЛОГИКА
ПЕРЕД СОХРАНЕНИЕМ
ЕСЛИ ПУСТО(data.Клиент) ТО ОШИБКА "Клиент обязателен" КОНЕЦ
КОНЕЦ
ПРИ СОХРАНЕНИИ
ВЫЧИСЛИТЬ data.ИтогоСумма = СУММА(data.Работы, "Сумма")
КОНЕЦ
КОНЕЦ
КОНЕЦ❌ Ошибка 1: Рекурсивный вызов
ПРИ ИЗМЕНЕНИИ Сумма
ВЫЧИСЛИТЬ data.Сумма = data.Сумма * 1.1 # Бесконечный цикл!
КОНЕЦИсправление: использовать флаг.
❌ Ошибка 2: Тяжёлые вычисления в ПРИ ИЗМЕНЕНИИ — перенесите в ПРИ СОХРАНЕНИИ.
❌ Ошибка 3: Не проверяете существование объекта
ВЫЧИСЛИТЬ Клиент = ПРОЧИТАТЬ("Клиенты", ID)
ЗАПОЛНИТЬ Клиент.Наименование = "Новое имя" # Ошибка, если Клиент = ПУСТОИсправление:
ВЫЧИСЛИТЬ Клиент = ПРОЧИТАТЬ("Клиенты", ID)
ЕСЛИ ПУСТО(Клиент) ТО ОШИБКА "Клиент не найден" КОНЕЦ13.5. Работа с данными (CRUD)
✅ Правильно:
ПРОЦЕДУРА СоздатьКлиентаЕслиНет(Телефон, Имя)
ВЫЧИСЛИТЬ Существующий = НАЙТИ("Клиенты", "Телефон = '" + Телефон + "'")
ЕСЛИ ПУСТО(Существующий) ТО
ВЫЧИСЛИТЬ Новый = СОЗДАТЬ("Клиенты")
ЗАПОЛНИТЬ Новый.Наименование = Имя
ЗАПОЛНИТЬ Новый.Телефон = Телефон
ВЫЧИСЛИТЬ _ = ЗАПИСАТЬ(Новый)
ВОЗВРАТ Новый.id
КОНЕЦ
ВОЗВРАТ Существующий.id
КОНЕЦ_ПРОЦЕДУРЫ❌ Ошибка 1: Не экранируете кавычки — используйте 'ООО "Ромашка"' вместо "ООО "Ромашка"".
❌ Ошибка 2: Конкатенация пользовательского ввода в SQL — экранируйте или используйте параметризованные запросы.
❌ Ошибка 3: Не проверяете результат ЗАПИСАТЬ
13.6. Журналы и движения
✅ Правильно: включайте КОНТРОЛЬ_ОСТАТКОВ ДА для складских журналов.
❌ Ошибка 1: Не включили контроль остатков — опасно, можно уйти в минус.
❌ Ошибка 2: Неправильное указание полей в ДВИЖЕНИИ — ПОЛЯ Склад = Склад (без data.).
❌ Ошибка 3: Забыли про _type при ручном расчёте остатков — используйте SUM(_type * Количество).
13.7. Отчёты
✅ Правильно:
ОТЧЁТ ПродажиПоКлиентам
ПАРАМЕТР Клиент ВЫБОР Клиенты
ЗАПРОС "
SELECT К.Наименование, SUM(З.ИтогоСумма) as Сумма
FROM ЗаказНаряд З
JOIN Клиенты К ON З.Клиент = К.id
WHERE 1=1
{{ЕСЛИ Клиент}} AND З.Клиент = '{{Клиент}}' {{КОНЕЦ}}
GROUP BY К.id
"
КОЛОНКА Наименование СТРОКА
КОЛОНКА Сумма ЧИСЛО
КОНЕЦ❌ Ошибка 1: Забыли кавычки для строковых параметров — WHERE Наименование = '{{Наименование}}'.
❌ Ошибка 2: Не используете {{ЕСЛИ}} для необязательных параметров — иначе пустой параметр сломает запрос.
❌ Ошибка 3: Нет псевдонимов для колонок — используйте AS.
13.8. Печатные формы
✅ Правильно: всегда добавляйте <meta charset="utf-8"> и @page { margin: ...; }.
❌ Ошибка 1: Нет meta charset — русские буквы будут крякозябрами.
❌ Ошибка 2: Слишком широкие таблицы — избегайте white-space: nowrap.
❌ Ошибка 3: Забыли про печать — всегда задавайте поля через @page.
13.9. API
✅ Правильно: используйте сложные пароли и схемы валидации.
❌ Ошибка 1: Простой пароль — ПАРОЛЬ "123".
❌ Ошибка 2: Нет схемы для POST — любой мусор попадёт в БД.
❌ Ошибка 3: Не обрабатываете ошибки в процедуре для API — возвращайте понятный JSON с status: error.
13.10. Расписания и фоновые задачи
✅ Правильно: КРОН "0 0 3 * * *" (в 3 часа ночи).
❌ Ошибка 1: Слишком частое расписание — */1 * * * * * создаёт высокую нагрузку.
❌ Ошибка 2: Нет обработки ошибок — используйте ПОПЫТКА...ИСКЛЮЧЕНИЕ.
❌ Ошибка 3: Не учитываете, что фоновая задача не возвращает результат — пишите результат в БД.
13.11. Производительность
❌ Плохо (запрос в цикле):
ЦИКЛ ПО СписокТоваров КАК Товар
ВЫЧИСЛИТЬ Остаток = ЗАПРОС("SELECT SUM(_type * Количество) FROM Журнал_Остатки WHERE Товар = " + Товар.id)
КОНЕЦ✅ Хорошо (один запрос с GROUP BY):
ВЫЧИСЛИТЬ Остатки = ЗАПРОС("
SELECT Товар, SUM(_type * Количество) as Остаток
FROM Журнал_Остатки
WHERE Товар IN (" + СписокID + ")
GROUP BY Товар
")❌ Плохо (НАЙТИ_ВСЕ без фильтра): НАЙТИ_ВСЕ("ЗаказНаряд", "") — может быть миллион записей.
✅ Хорошо (всегда добавляйте фильтр или лимит): НАЙТИ_ВСЕ("ЗаказНаряд", "Дата >= date('now', '-30 days')").
13.12. Отладка
Полезные приёмы:
- Используйте
ВЫВЕСТИдля логирования начала и конца процедур - Логируйте SQL-запросы перед выполнением
- Проверяйте промежуточные значения
- Логируйте ошибки в отдельную таблицу для аудита
13.13. Чек-лист перед вводом в эксплуатацию
- Все ли роли имеют минимально необходимые права?
- Есть ли RLS-ограничения для ролей, которые должны видеть не все данные?
- Включен ли
КОНТРОЛЬ_ОСТАТКОВ ДАдля складских журналов? - Все ли
ПОЛЕимеют правильные типы и модификаторы? - Нет ли бесконечных рекурсий в
ЛОГИКА? - Все ли процедуры обрабатывают ошибки (особенно
HTTP_ЗАПРОС)? - Нет ли в расписаниях слишком частых CRON-интервалов?
- Есть ли у API-эндпоинтов сложные пароли?
- Все ли SQL-запросы в отчётах экранируют параметры?
- Все ли шаблоны содержат
<meta charset="utf-8">? - Сделан ли бэкап базы данных перед применением изменений?
13.14. Заключение
Данная документация охватывает все основные аспекты разработки на DSL платформы 3ERP:
- Лексическая структура и типы данных
- Модель безопасности (роли, пользователи, RLS)
- Коллекции и документы
- Бизнес-логика (ЛОГИКА)
- Скриптовый движок (процедуры)
- Журналы и движения
- Отчёты и диаграммы
- Печатные формы (ШАБЛОН)
- API и вебхуки
- Расписания и фоновые задачи
- Отладка и диагностика
- Best Practices и типичные ошибки
Следуя рекомендациям из этого руководства, вы сможете создавать надёжные и производительные конфигурации для платформы 3ERP.