1c77-dev
Разработка и доработка под 1С:Предприятие 7.7 (она же "77", "семерка"): правка и создание модулей кода (.1s, кодировка CP1251), форм (.frm), объектов конфигурации (1Cv7.MD), внешних обработок и отчетов (.ert), плюс разбор и сборка .ert / 1Cv7.MD через GComp. Используй ВСЕГДА, ког
Install
npx skills add https://github.com/Desko77/claude-code-skills-1c/tree/main/skills/1c77-dev
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install desko77-claude-code-skills-1c@llmmart
git clone https://github.com/Desko77/claude-code-skills-1c.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole desko77/claude-code-skills-1c collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Разработка под 1С 7.7 (1c77-dev)
Доработка кода, форм, метаданных и внешних обработок 1С:Предприятия 7.7. Версия 7.7 кардинально отличается от 8.x, и привычные для 8.x приемы тут ломаются. Этот скилл собирает рабочие приемы, особенности синтаксиса и готовые скрипты.
Чем 7.7 отличается (читать обязательно)
- Нет EDT и .bsl/.mdo. Конфигурация хранится в бинарном
1Cv7.MD, внешние обработки - в бинарном.ert. Редактируемые исходники получают разбором через GComp (текстовые.1s,.frm,.mdp,.mxl). - Кодировка везде CP1251. Файлы
.1s/.frm/.mdp- не UTF-8. Инструмент Grep (ищет по UTF-8-байтам) кириллицу в них не находит. Read показывает кракозябры. Читать/править - только через Python сencoding='cp1251'(см.scripts/cp1251_io.py) либо через MCP-сервер1c77-metadata. - Синтаксис строже и беднее 8.x. Нет типа Булево; число в условии
Еслине приводится к логическому; многих функций 8.x (ЗначениеЗаполнено, штатнойПустаяСтрокакак Булево) нет или ведут себя иначе. Это источник большинства ошибок компиляции. Подробно:references/syntax-gotchas-77.md.
Карта навигации
Читай нужный файл по ситуации, не загружай все сразу:
| Когда | Файл |
|---|---|
| Пишешь/правишь BSL-код 7.7, ловишь ошибку компиляции "Выражение должно иметь логический тип" и т.п. | references/syntax-gotchas-77.md |
Нужно понять структуру .1s / .frm / .mdp / .mxl / .ert, что внутри обработки |
references/file-formats-77.md |
Надо разобрать .ert в исходники или собрать обратно |
references/gcomp.md |
| Анализ метаданных конфигурации, поиск реквизита, проверка запроса 7.7, окружение | references/mcp-and-tools.md |
Скрипты в scripts/ (запуск с --help):
cp1251_io.py- чтение/запись/grep по.1sв CP1251 (когда обычный Grep слеп).analyze_module.py- инвентаризация модуля:СоздатьОбъект, типы метаданных, глобальныегл*-функции, обращения к реквизитам.patch_by_marker.py- идемпотентная вставка блока кода по маркеру-якорю (для повторяемых правок).build_ert.py/unpack_ert.py- обертки GComp (сборка/разбор.ert) через PowerShell.
Базовый workflow доработки
Понять объект. Метаданные и код существующих объектов конфигурации смотри через MCP
1c77-metadata(get_object,get_module,search_field,validate_query) - это быстрее и точнее ручного парсинга. Для внешней обработки.ertсначала разбери ее GComp (unpack_ert.py), затем работай с.1s/.frm. Детали:references/mcp-and-tools.md,references/gcomp.md.Править код только через CP1251. Используй
scripts/cp1251_io.pyили Python напрямую (Path.read_text(encoding='cp1251')/write_text(..., encoding='cp1251', newline='')). Никогда не правь.1sинструментом, который пишет UTF-8 - сломаешь кодировку всего файла. Для повторяемых вставок (одинаковый блок в N мест) используйpatch_by_marker.py- он идемпотентен.Сверить синтаксис 7.7 ДО сборки. Прогони мысленно (или поиском) по чек-листу особенностей из
references/syntax-gotchas-77.md. Самая частая ошибка: голый вызов функции или число в условииЕсли/ИначеЕслибез операции сравнения. Проверка запросов -validate_queryMCP1c77-metadata.Собрать
.ertи проверить. Сборка - только рабочим GComp через PowerShell (build_ert.py). Перед сборкой - бэкап.ert. После - убедиться, что timestamp/размер изменились (GComp молча не пересобирает при битых аргументах). Финальная компиляция BSL и проверка - при открытии в самой 7.7.
Самопроверка перед сборкой .ert
Чек-лист прогонять по СВОЕМУ сгенерированному коду ДО вызова gcomp:
- В условиях
Если/Поканет голых вызовов функций или чисел - каждое сравнение явное и в скобках:(Ф(х)=1) И (у=2). -
ПустаяСтрока/ПустоеЗначениесравнивать с 1/0. - Каждая вызываемая процедура/функция объявлена ВЫШЕ точки вызова или в глобальном модуле - проверить греп-поиском определения.
-
Симв(), неСимвол(); тернарник?(усл,а,б)с явными сравнениями. - Нет обращений к несуществующим у 7.7 методам 8.x.
Детали и полный список - references/syntax-gotchas-77.md.
Типовые ошибки компиляции ("Выражение должно иметь логический тип", "Процедура не обнаружена", "Плохой тип переменной") - сначала прогнать этот чек-лист, потом лезть в отладку.
Критические правила (ключевые отличия синтаксиса, детали в references)
- Логический тип в
Если.Если ПустаяСтрока(Х) Тогда-> ошибка "Выражение должно иметь логический тип". Нужно сравнение:Если ПустаяСтрока(Х) = 1 Тогда, аНЕ ПустаяСтрока(Х)->ПустаяСтрока(Х) = 0. То же для тернарника:?(ПустоеЗначение(Х) = 1, "", ...). Полный список -references/syntax-gotchas-77.md. - CP1251. Grep по кириллице в
.1sмолчит (UTF-8 vs CP1251). Не делай вывод "совпадений нет" по Grep - ищи черезcp1251_io.py grepили MCP. - GComp запускать только из PowerShell. Из Bash кириллица в путях бьется, gcomp молча выходит с ложным
успехом и ничего не собирает. Рабочий exe - в
C:\Program Files\GComp\Release\(сGComp.dllрядом), "голый" exe из каталогов проекта не запускается. Детали -references/gcomp.md. - Минимальный diff при багфиксе. Правь только код, связанный с багом. Не переформатируй CP1251-файл целиком - рискуешь кодировкой и шумным diff.
Проверка качества
После правки BSL 7.7 - прогон через MCP 1c-naparnik (ask_1c_ai) для оценки и validate_query
(1c77-metadata) для запросов. EDT-валидация и bsl-platform-help к 7.7 неприменимы (это 8.x).
Files (claude-code-skills-1c)
-
references
-
file-formats-77.md 9.2 KB
# Форматы файлов 1С 7.7 Что внутри бинарных `.md`/`.ert` и как устроены текстовые исходники после разбора GComp. Все текстовые файлы - в кодировке **CP1251**. ## Содержание 1. [Бинарные контейнеры: 1Cv7.MD и .ert](#1-бинарные-контейнеры) 2. [Состав разобранной обработки .ert](#2-состав-разобранной-обработки-ert) 3. [Модули .1s](#3-модули-1s) 4. [Формы .frm](#4-формы-frm) 5. [Метаданные .mdp](#5-метаданные-mdp) 6. [Макеты .mxl](#6-макеты-mxl) 7. [Прочие служебные файлы](#7-прочие-служебные-файлы) --- ## 1. Бинарные контейнеры - **`1Cv7.MD`** - вся конфигурация (метаданные + код + формы + макеты + права) в одном бинарном файле формата Compound Document (как старый .doc). Внутри - потоки в скобочном синтаксисе, CP1251. - **`.ert`** - внешняя обработка/отчет. В 7.7 это единый формат (в отличие от 8.x, где `.epf`/`.erf`). Тот же Compound Document, несет форму, модуль, макет и (часто) копию метаданных конфигурации. Напрямую эти файлы не редактируются. Их **разбирают** GComp в дерево текстовых файлов, правят, **собирают** обратно. См. `references/gcomp.md`. Альтернатива для чтения метаданных без разбора - MCP `1c77-metadata`, который парсит `1Cv7.MD` и отдает объекты/модули через API (см. `references/mcp-and-tools.md`). ## 2. Состав разобранной обработки .ert После `unpack_ert.py` (GComp `-d`) каталог обработки содержит примерно: ``` <Обработка>/ ├── МодульФормы.1s # код обработки (CP1251) - основная цель правок ├── Форма.frm # диалог формы (CP1251) ├── Структура.mdp # метаданные: реквизиты обработки + нередко копия всей конфигурации ├── Таблица.mxl # макет табличного документа (печать/вывод) ├── ОбъектыМетаданных.txt # реестр объектов (Справочник.X, Документ.Y ...) с числовыми ID ├── gcomp.ini # параметры разбора (язык, флаги) ├── gcomp.ver # версия GComp, которой разбирали └── ПорядокОбъектов.ord # порядок объектов для детерминированной сборки ``` Размер `Структура.mdp` может быть сопоставим с размером всего `.ert` - это значит обработка несет полную метаструктуру конфигурации (нужна для разбора путей реквизитов вроде `спр.Товар.Артикул`). При сборке такую структуру сохраняют как есть (без `--truncate-mms`), иначе обработка может потерять сведения о типах. Если структура не нужна - `--truncate-mms` уменьшает результат. ## 3. Модули .1s Обычный текст BSL 7.7 в CP1251. Имена в типовых распаковках: - `ГлобальныйМодуль.1s` - глобальный модуль конфигурации (общие функции, в т.ч. `ПустаяСтрока` и пр.). - `Документы/<Имя>/МодульПроведения.1s`, `.../МодульФормы.1s`. - `Справочники/<Имя>/МодульФормы.1s`. - `Обработки/<Имя>/МодульФормы.1s`, для внешней обработки - просто `МодульФормы.1s`. Правка - только через CP1251 (`scripts/cp1251_io.py`). Обычный Grep по кириллице в `.1s` ничего не находит (он ищет UTF-8-байты). Синтаксис - см. `references/syntax-gotchas-77.md`. ## 4. Формы .frm Скобочный текстовый формат (CP1251). Описывает диалог: параметры окна и список элементов управления. ``` Диалог: { Параметры формы: { Параметры: { Ширина: 322 Высота: 263 Заголовок: " " } } Элементы: { BUTTON: { Заголовок: "1. Выгрузить склады" ИдентификаторМетаданных: -1 ДопФлаги: Flag0, Flag1, КартинкаПоЦентру, Flag28, Flag30 X: 7 Y: 38 Ширина: 148 Высота: 14 ПорядокОбхода: 4200 Формула: "Демо_ВыгрузитьСклады()" // обработчик: вызов процедуры модуля Слой: Основной } // другие элементы: TEXT, EDIT, CHECKBOX, GROUP, TABLE и т.д. } } ``` Ключевое для правок: - **Кнопка вызывает процедуру** через поле `Формула: "ИмяПроцедуры()"`. Добавить кнопку = вписать блок `BUTTON` с координатами `X/Y/Ширина/Высота`, уникальным `ПорядокОбхода` и `Формула`, плюс саму процедуру в `.1s`. - Координаты в диалоговых единицах; следи, чтобы новые элементы не накладывались (проверяй визуально после открытия в 7.7). - Правка через CP1251. Для добавления повторяющихся элементов удобен `patch_by_marker.py`. ## 5. Метаданные .mdp Скобочный текстовый формат (CP1251). Описание объектов конфигурации/обработки: реквизиты, табличные части, свойства. Шапка несет служебные счетчики (`Next ID`, `Версия`). ``` Main Metadata Stream: { MainDataContDef: { Next ID: 9144 Версия: 10009 ... } Описание задачи: { { Идентификатор: "..." Комментарий: "7.70.927" ... } } Справочники: { Банки: { Синоним: "Банки (классификатор БИК)" Длина кода: ... Длина наименования: ... Количество уровней: 1 // реквизиты, табличные части ... } } Документы: { ... } Регистры: { ... } } ``` Ручная правка `.mdp` рискованна (числовые ID, счетчики, ссылки между объектами). Для **анализа** структуры объекта лучше MCP `1c77-metadata` (`get_object` - реквизиты и табличные части без ручного парсинга). Менять метаданные надежнее в самой 7.7 (Конфигуратор), а не правкой `.mdp` руками. Если правишь `.mdp` - не нарушай скобочную структуру и отступы, иначе сборка GComp упадет или даст битый объект. ## 6. Макеты .mxl Табличный документ (печатные формы, вывод обработок). После разбора - текстовый, но громоздкий. Для точечной работы с печатью обычно достаточно знать имена областей и параметров. Глубокая правка `.mxl` руками неудобна - чаще проще отредактировать макет в 7.7. ## 7. Прочие служебные файлы - `gcomp.ini` - флаги разбора (язык Rus, `no-parse-forms`, `more-subfolders` и т.п.). При сборке влияет на трактовку форм/папок. - `gcomp.ver` - версия GComp. Полезно собирать той же версией, что разбирали. - `*.ord` - порядок объектов для детерминированной сборки. - `ОбъектыМетаданных.txt` - человекочитаемый реестр объектов с ID; удобен для быстрой проверки, какие объекты есть и как называются. -
gcomp.md 6 KB
# GComp - разбор и сборка .ert / 1Cv7.MD GComp - утилита для декомпиляции/компиляции бинарных файлов 1С 7.7 (`1Cv7.MD`, `.ert`) в текстовые исходники и обратно. Версия в проекте - 2.2.15.beta. (`.erf` - формат 8.x, к 7.7 не относится.) ## Рабочий GComp (критично) Запускать **только полную установку** (exe + `GComp.dll` + `MFC42.DLL` рядом): ``` C:\Program Files\GComp\Release\gcomp.exe ← основной, рабочий ``` Запасные комплекты (если основной недоступен): - `C:\Tools\gcomp\` - `C:\1Cv77\BIN\` (рядом с платформой) **Не использовать "голый" gcomp.exe** из каталогов проекта (например `Конфигурации\...\KonturEDI\АнализКонфигурации\gcomp.exe`) - рядом нет `GComp.dll`, и он падает с `STATUS_DLL_NOT_FOUND` (`EXIT=-1073741515` / `0xC0000135`). Это компонент сторонних обработок, не наш. В каталоге Release также есть `vgcomp.exe` - GUI-версия (визуальная), для скриптовой работы не нужна. ## Запускать только из PowerShell Из Bash кириллица в путях (`ВыгрузкаИзТис77`, `PUBID_NNNN-...`) **бьется**: gcomp получает битые аргументы, ничего не находит и **молча выходит с EXIT 0** - создается ложное впечатление успеха, а файл не меняется. Поэтому: - Запуск - через PowerShell (нативная Windows-кодировка), либо через `scripts/build_ert.py` / `scripts/unpack_ert.py`, которые вызывают gcomp правильно. - После сборки **обязательно** проверь, что у `.ert` изменился timestamp/размер. Не меняется - значит аргументы не дошли. ## Команды GComp штатно не печатает usage (выдает `exit 53` без вывода); справка ниже извлечена из бинарника. ### Разбор (декомпиляция) ``` gcomp -d -F <файл.ert> -DD <каталог_назначения> ``` - `-d` - режим разбора. - `-F <файл>` - что разбирать (`.ert`, или опустить `-F` для `1cv7.md` в текущем каталоге). - `-DD <каталог>` - куда положить исходники. Без `-DD` имя каталога формируется как `SRC\<ИмяБезРасширения>`. - Предупреждение GComp: разбор в непустой каталог может затереть данные - разбирай в чистый каталог. ### Сборка (компиляция) ``` gcomp -c -F <файл.ert> -DD <каталог_исходников> ``` - `-c` - режим сборки. - `-F <файл>` - целевой `.ert` (перезаписывается молча - делай бэкап заранее). - `-DD <каталог>` - откуда брать исходники. Без `-DD` берется `SRC\<ИмяБезРасширения>`. ### Полезные опции - `--external-report` - явно указать, что работаем с внешним отчетом/обработкой (`.ert`). Обычно определяется по расширению `-F`, но можно задать явно. - `--meta-data` - работаем с файлом конфигурации (`1cv7.md`). - `--truncate-mms` - при сборке `.ert` заменить поток `Main MetaData Stream` на пустой (уменьшает размер, если копия метаданных конфигурации в обработке не нужна). По умолчанию НЕ труначить, если обработка опирается на типы реквизитов из этой структуры. - `--no-parse-dialogs` - выгрузить формы как есть, без обработки (для проблемных диалогов). - `--more-subfolders` - разбивать на каталоги объекты, которые по умолчанию идут одним `.mdp`. - `-v` / `-vv` - подробный вывод (для диагностики сборки). ## Безопасный порядок работы 1. **Бэкап** целевого `.ert` (gcomp перезаписывает молча): `Copy-Item <файл.ert> <файл.ert>.bak_<дата>` 2. Сборка из исправленного каталога исходников (`build_ert.py` или PowerShell напрямую). 3. Проверка: timestamp и размер `.ert` изменились; при сомнении - разобрать собранный `.ert` во временный каталог и сверить, что правка на месте (round-trip). 4. Финальная компиляция BSL и проверка - при открытии обработки в 1С 7.7 (gcomp пакует, но не проверяет синтаксис BSL так, как платформа). ## Пример (PowerShell) ```powershell $g = "C:\Program Files\GComp\Release\gcomp.exe" $out = "E:\...\src\PUBID_NNNN_ert\ВыгрузкаИзТис77.ert" $src = "E:\...\src\PUBID_NNNN_ert\PUBID_NNNN-ВыгрузкаИзТис77" Copy-Item $out "$out.bak" -Force & $g -c -F $out -DD $src "EXIT=$LASTEXITCODE" Get-Item $out | Select-Object Name,Length,LastWriteTime ``` Целевое имя выходного `.ert` для проекта миграции - `ВыгрузкаИзТис77.ert` (без префикса `PUBID_NNNN-`); каталоги исходников `PUBID_NNNN_ert/` не переименовываются - это маркер источника. -
mcp-and-tools.md 6.7 KB
# MCP и инструменты для 1С 7.7 Чем анализировать метаданные и код 7.7, как проверять качество, какое окружение нужно. ## Содержание 1. [MCP 1c77-metadata - анализ конфигурации](#1-mcp-1c77-metadata) 2. [Проверка качества BSL](#2-проверка-качества-bsl) 3. [Распаковка конфигурации (oscript/v7src)](#3-распаковка-конфигурации) 4. [Fallback: ручной CP1251](#4-fallback-ручной-cp1251) 5. [Что НЕ применять к 7.7](#5-что-не-применять-к-77) --- ## 1. MCP 1c77-metadata Сервер анализа `1Cv7.MD` (Compound Document + скобочный синтаксис в CP1251). Дает API к справочникам, документам, регистрам, перечислениям, модулям - **вместо** ручного парсинга `.mdp`/`.1s` через Python+CP1251 или слепого Grep. - **Адрес:** `http://localhost:<порт>` (Project MCP; порт задается при установке сервера, типовой - 8009). - **Запуск:** `start.cmd` из каталога установки сервера (читает `1Cv7.MD` из его подкаталога `data/`). Фоновый вариант - `start-hidden.vbs`. Сервер ставится отдельно, документация и установка: https://github.com/ivanarama/77MCP. - **Проверка доступности:** `get_configuration_info` (вернет имя и версию загруженной конфигурации). Инструменты (по задачам): | Задача | Инструмент | |--------|-----------| | Проверить, что конфигурация загружена | `get_configuration_info` | | Обзор объектов по типу (Справочник/Документ/Регистр/Перечисление/Журнал/Константа/Отчет/ВидРасчета) | `list_objects` | | Реквизиты + табличные части объекта | `get_object` | | Найти ВСЕ объекты с реквизитом по имени (трассировка использования) | `search_field` | | Код модуля объекта (без возни с CP1251) | `get_module` | | Глобальный модуль | `get_global_module` | | Форма объекта | `get_form` | | Пакетная выборка метаданных нескольких объектов | `get_objects_batch` | | Список модулей | `list_modules` | | Поиск по именам/синонимам/комментариям | `search` | | Поиск текста по модулям | `search_in_modules` | | Проверка запроса 7.7 на ошибки | `validate_query` | | Проверка пути реквизита (`Товар.Артикул`) | `validate_field_path` | | Сменить/перезагрузить файл конфигурации | `reload_configuration` | Типовые сценарии: - "Где трогается реквизит X" (миграция/рефакторинг) -> `search_field` (точнее и полнее, чем Grep по `.1s`). - Перед написанием кода, работающего с объектом -> `get_object` (структура) + `get_module` (существующий код). - Перед сборкой `.ert` с запросом -> `validate_query` для каждого запроса. **Когда сервер недоступен или конфигурация не загружена** - fallback на распакованные `.mdp` + Python CP1251 (см. п.3-4). Для очень большого глобального модуля `get_module` может быть тяжелым - тогда точечный Grep через `cp1251_io.py grep` по конкретному `.1s`. ## 2. Проверка качества BSL - **`ask_1c_ai`** (MCP `1c-naparnik`) - оценка качества написанного BSL. Прогоняй после правок кода. Учитывай, что Напарник ориентирован на 8.x - особенности 7.7 он может не отловить; финальная истина - компиляция в самой 7.7 и чек-лист из `references/syntax-gotchas-77.md`. - **`validate_query`** (MCP `1c77-metadata`) - для запросов именно 7.7 (язык запросов отличается от 8.x). ## 3. Распаковка конфигурации Распаковать `1Cv7.MD` в дерево `.1s`/`.mdp` можно двумя путями: - **GComp** (`-d --meta-data`, см. `references/gcomp.md`) - универсально, рекомендуется. - **oscript + v7src** - утилита v7src на OneScript: ``` oscript <путь>/v7src.os extract -bin <каталог_с_1Cv7.MD> -src <каталог_исходников> ``` OneScript: `C:\Program Files\OneScript\bin\oscript.exe`. Скрипт `v7src.os` - внешняя зависимость (в проекте может лежать как `src/v7src.os`; если нет - использовать GComp). ## 4. Fallback: ручной CP1251 Когда MCP недоступен - читать/искать/править через Python с `encoding='cp1251'`. Это инкапсулировано в `scripts/cp1251_io.py` (подкоманды `read`, `grep`, `replace`). Главное помнить: - Обычный Grep по кириллице в `.1s`/`.frm`/`.mdp` **не работает** (UTF-8 vs CP1251) - не делать вывод "нет совпадений" по нему. - Запись - с `newline=''`, чтобы не превратить CRLF в LF и не сломать файл для 7.7. - Read-инструмент покажет кракозябры; для осмысленного чтения используй `cp1251_io.py read` или MCP. ## 5. Что НЕ применять к 7.7 7.7 - не 8.x. Неприменимо (это инструменты для 8.x, дадут мусор или ошибку): - AI-EDT (`ai-edt`) - работает с `.bsl`/`.mdo`, не с 7.7. - `bsl-platform-help` - API платформы 8.x. - Скиллы `1c-meta-*`, `1c-form-*`, `1c-cf*`, `1c-epf-*`, `1c-erf-*` - формат 8.x (XML, управляемые формы). - `v8unpack` - для CF/CFE/EPF (8.x), не для `.ert`/`.md` (7.7). Для 7.7 используем: GComp (разбор/сборка), MCP `1c77-metadata` (анализ), `cp1251_io.py` (правка), `ask_1c_ai` (оценка, с поправкой на 8.x-уклон). -
syntax-gotchas-77.md 10.7 KB
# Особенности синтаксиса 1С 7.7 Отличия 7.7 от 8.x, на которых ломается код, написанный по привычке 8.x. Большинство всплывает только при компиляции в самой 7.7 (ни EDT, ни внешние линтеры 8.x их не ловят). ## Содержание 1. [Логический тип в условиях](#1-логический-тип-в-условиях) - самая частая ошибка 2. [Нет типа Булево](#2-нет-типа-булево) 3. [Функции, которых нет или они другие](#3-функции-которых-нет-или-они-другие) 4. [Безопасный доступ к реквизитам](#4-безопасный-доступ-к-реквизитам) 5. [Идиомы выгрузки значений (str/num/bool/date/ref)](#5-идиомы-выгрузки-значений) 6. [Работа с объектами через СоздатьОбъект](#6-работа-с-объектами-через-создатьобъект) 7. [Строки, конкатенация, пустые значения](#7-строки-конкатенация-пустые-значения) 8. [Чек-лист перед сборкой](#8-чек-лист-перед-сборкой) --- ## 1. Логический тип в условиях В 7.7 в `Если <выр> Тогда` / `ИначеЕсли <выр> Тогда` / `Пока <выр> Цикл` выражение должно быть **результатом операции сравнения или логической операции**. Голый вызов функции или число логическим выражением не считается, даже если функция возвращает 0/1. ``` // ОШИБКА компиляции: "Выражение должно иметь логический тип" Если ПустаяСтрока(текОКПО) Тогда ... КонецЕсли; // после НЕ: "Ожидается логическое выражение" ИначеЕсли НЕ ПустаяСтрока(текКПП) Тогда ... // ПРАВИЛЬНО - явное сравнение Если ПустаяСтрока(текОКПО) = 1 Тогда ... КонецЕсли; ИначеЕсли ПустаяСтрока(текКПП) = 0 Тогда ... ``` Это касается **любой** функции в условии, не только `ПустаяСтрока`. Если функция возвращает число-флаг - сравнивай его явно (`= 1`, `<> 0`, `> 0`). Тот же принцип в тернарном операторе (см. п.5): условие внутри `?(...)` тоже должно быть сравнением: `?(ПустоеЗначение(Х) = 1, "", СокрЛП(Х))`. Почему так: в 7.7 нет автоприведения числа к логическому. Компилятор разбирает условие как логическое выражение и требует операцию отношения. Привычка из 8.x (`Если Функция() Тогда`) дает ошибку. ## 2. Нет типа Булево В 7.7 нет литералов `Истина`/`Ложь` как типа Булево (в большинстве релизов). Логика выражается числами: `0` - ложь, не ноль - истина, но **только** как результат сравнения/логической операции в условии. Флаги в реквизитах хранятся как `Число(1.0)` со значениями 0/1. - Присваивание флага: `тз.Флаг = ?(спр.Флаг = 1, 1, 0);` (нормализация к 0/1). - Проверка флага: `Если спр.Флаг = 1 Тогда`, а не `Если спр.Флаг Тогда`. ## 3. Функции, которых нет или они другие - `ПустаяСтрока(Стр)` - в типовых 7.7 обычно определена в глобальном модуле и возвращает **число** 0/1 (не Булево). В условии требует сравнения (см. п.1). Если в конкретной базе ее нет - проверяй пустоту через `СокрЛП(Стр) = ""`. - `ЗначениеЗаполнено(...)` - функции 8.x в 7.7 нет. Аналоги: `ПустоеЗначение(Знач) = 1` (для значений ссылочного/любого типа, глобальная), `СокрЛП(Стр) = ""` (для строк), `Объект.Выбран() = 1` (для ссылок). - Тернарный оператор `?(Условие, ЗначениеЕслиИстина, ЗначениеЕслиЛожь)` - есть, но `Условие` обязано быть сравнением. - Нет `Новый` для большинства объектов - вместо `Новый` используется `СоздатьОбъект("Тип")` (см. п.6). - Работа с файлами: нет объектов 8.x. Текст пишут через `ТекстовыйДокумент`/`СоздатьОбъект("Текст")` или внешнюю компоненту `FileSystemObject` (Scripting) для скорости на больших объемах. Если не уверен, есть ли функция в конкретной базе - проверь глобальный модуль через MCP `1c77-metadata` (`get_global_module` / `search_in_modules`), не полагайся на знание 8.x. ## 4. Безопасный доступ к реквизитам Реквизит может отсутствовать в конкретной редакции конфигурации (особенно при доработках под клиента). Обращение к несуществующему реквизиту - ошибка времени выполнения. Чтобы выгрузка не падала на "чужой" базе, оборачивай чтение необязательных реквизитов в `Попытка/Исключение`: ``` тзСтрока.Поле = ""; Попытка тзСтрока.Поле = СокрЛП(спр.Поле); Исключение КонецПопытки; ``` Инициализируй переменную значением по умолчанию ДО `Попытка`, чтобы при отсутствии реквизита осталось корректное пустое значение. ## 5. Идиомы выгрузки значений Шаблоны присваивания. Тип колонки ТЗ создается через `тз.НоваяКолонка("Имя","Тип"[,длина[,точность]])`, заполнение - в цикле обхода справочника: | Вид данных в 7.7 | Колонка ТЗ | Присваивание (внутри Попытка/Исключение) | |------------------|-----------|------------------------------------------| | Строка | `НоваяКолонка("Х","Строка",длина)` | `тз.Х = СокрЛП(спр.Х);` | | Число | `НоваяКолонка("Х","Число",прец,точн)` | `тз.Х = спр.Х;` | | Дата | `НоваяКолонка("Х","Дата")` | `тз.Х = спр.Х;` | | Флаг (Число 1.0) | `НоваяКолонка("Х","Число",1)` | `тз.Х = ?(спр.Х = 1, 1, 0);` | | Ссылка -> код | `НоваяКолонка("Х","Строка",12)` | `тз.Х = ?(ПустоеЗначение(спр.Х) = 1, "", СокрЛП(спр.Х.Код));` | | Перечисление -> имя | `НоваяКолонка("Х","Строка",50)` | `тз.Х = СокрЛП(спр.Х);` (имя значения) | | Ссылка/строка (смешанно) | `НоваяКолонка("Х","Строка",100)` | `тз.Х = СокрЛП("" + спр.Х);` | Генерация таких блоков для десятков полей автоматизируется - см. `scripts/patch_by_marker.py` (вставка по якорю). ## 6. Работа с объектами через СоздатьОбъект ``` спр = СоздатьОбъект("Справочник.Номенклатура"); спр.ВыбратьЭлементы(); Пока спр.ПолучитьЭлемент() = 1 Цикл // = 1, не голый вызов Если спр.ЭтоГруппа() = 1 Тогда Продолжить; КонецЕсли; // ... работа с спр.Реквизит КонецЦикла; тз = СоздатьОбъект("ТаблицаЗначений"); тз.НоваяКолонка("Код","Строка",9); ``` Запрос - через `СоздатьОбъект("Запрос")` и текст на языке запросов 7.7 (отличается от 8.x; проверяй через `validate_query` MCP `1c77-metadata`). ## 7. Строки, конкатенация, пустые значения - Конкатенация - оператор `+`. Приведение значения к строке: `"" + Значение`. - Обрезка пробелов: `СокрЛП` (с обеих сторон), `СокрЛ`, `СокрП`. - Пустая ссылка/значение: `ПустоеЗначение(Знач)` возвращает число (`= 1` если пусто). - Подстроки: `Лев(Стр, N)`, `Прав(Стр, N)`, `Сред(Стр, Поз, Длина)`, поиск `Найти(Стр, Подстр)`. - Длина строки: `СтрДлина(Стр)`. ## 8. Чек-лист перед сборкой Пройди по списку перед `build_ert.py` (быстрее, чем ловить на компиляции в 7.7): - [ ] Все `Если`/`ИначеЕсли`/`Пока` содержат сравнение, а не голый вызов функции/число. - [ ] `НЕ Функция(...)` заменено на `Функция(...) = 0` (или обернуто в скобки с явным сравнением). - [ ] Тернарники `?(...)` имеют сравнение в условии. - [ ] Необязательные реквизиты читаются через `Попытка/Исключение` с инициализацией по умолчанию. - [ ] Нет функций/конструкций 8.x (`Новый`, `ЗначениеЗаполнено`, объекты файлов 8.x). - [ ] Запросы проверены `validate_query` (MCP `1c77-metadata`). - [ ] Файл сохранен в CP1251, переводы строк не побиты (CRLF).
-
-
scripts
-
analyze_module.py 6.3 KB
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Анализ модуля 1С 7.7 (.1s, CP1251): инвентаризация + линтер особенностей синтаксиса. Две подкоманды: inventory <file> - что использует модуль: - СоздатьОбъект("Тип") - с какими объектами работает - упоминания типов метаданных (Справочник.X, Документ.Y, Регистр.Z, Перечисление, Журнал) - глобальные функции гл* (специфика конкретной базы) - объявленные процедуры/функции модуля lint <file> - ищет частые ошибки 7.7, которые всплывают только при компиляции: - условие Если/ИначеЕсли/Пока без операции сравнения (голый вызов функции/число) -> в 7.7 это "Выражение должно иметь логический тип" Линтер эвристический (не полноценный парсер), но ловит самый частый класс ошибок. Примеры: python analyze_module.py inventory МодульФормы.1s python analyze_module.py lint МодульФормы.1s """ import argparse import re import sys from pathlib import Path ENC = "cp1251" def lines_of(path): return Path(path).read_text(encoding=ENC, errors="replace").split("\n") def is_comment(line): return line.lstrip().startswith("//") def cmd_inventory(a): lines = lines_of(a.file) print("=== СоздатьОбъект(тип) ===") seen = set() rx = re.compile(r'СоздатьОбъект\("([^"]+)"') for i, l in enumerate(lines, 1): m = rx.search(l) if m and m.group(1) not in seen: seen.add(m.group(1)) print(f" {i}: {m.group(1)}") print("\n=== Типы метаданных (Справочник./Документ./Регистр./Перечисление./Журнал.) ===") seen = set() rx = re.compile(r"\b(Справочник|Документ|Регистр|Перечисление|Журнал)\.([А-Яа-яA-Za-z][\w]*)") for l in lines: if is_comment(l): continue for m in rx.finditer(l): seen.add(f"{m.group(1)}.{m.group(2)}") for k in sorted(seen): print(f" {k}") print("\n=== Глобальные функции гл* ===") seen = set() rx = re.compile(r"\b(гл[А-ЯA-Z][\w]*)\s*\(") for l in lines: if is_comment(l): continue for m in rx.finditer(l): seen.add(m.group(1)) for k in sorted(seen): print(f" {k}") print("\n=== Объявленные процедуры/функции ===") rx = re.compile(r"^\s*(Процедура|Функция)\s+([А-Яа-яA-Za-z_][\w]*)", re.IGNORECASE) for i, l in enumerate(lines, 1): m = rx.match(l) if m: print(f" {i}: {m.group(1)} {m.group(2)}") # Условие между ключевым словом и Тогда/Цикл COND_RX = re.compile(r"\b(?:Если|ИначеЕсли)\b(.*?)\bТогда\b", re.IGNORECASE) WHILE_RX = re.compile(r"\bПока\b(.*?)\bЦикл\b", re.IGNORECASE) # наличие оператора сравнения в условии (=, <>, <, >, <=, >=) HAS_CMP = re.compile(r"[=<>]") # есть ли вообще вызов функции или идентификатор (чтобы не ругаться на пустое) HAS_CALL = re.compile(r"[А-Яа-яA-Za-z_][\w.]*\s*\(|[А-Яа-яA-Za-z_]") def check_condition(cond): """True - условие выглядит подозрительным (нет сравнения, но есть содержимое).""" c = cond.strip() if not c: return False # убрать строковые литералы, чтобы = внутри "" не считалось сравнением и наоборот c_nostr = re.sub(r'"[^"]*"', "", c) if HAS_CMP.search(c_nostr): return False # есть сравнение - ок if HAS_CALL.search(c_nostr): return True # есть содержимое, но нет сравнения - подозрительно return False def cmd_lint(a): lines = lines_of(a.file) problems = 0 for i, l in enumerate(lines, 1): if is_comment(l): continue for rx in (COND_RX, WHILE_RX): for m in rx.finditer(l): if check_condition(m.group(1)): kw = "Пока/Цикл" if rx is WHILE_RX else "Если/ИначеЕсли" print(f" {i}: [{kw}] условие без сравнения -> '{m.group(1).strip()}'") print(f" строка: {l.strip()}") problems += 1 if problems: print(f"\n[{problems} подозрительных условий] " "В 7.7 условие должно содержать сравнение (= 1 / <> 0 и т.п.), " "иначе 'Выражение должно иметь логический тип'.", file=sys.stderr) else: print("[чисто] подозрительных условий не найдено.", file=sys.stderr) return problems def main(): # Вывод содержит кириллицу. Без явного переключения печать падает с UnicodeEncodeError # везде, где консоль не в UTF-8: сборочный агент, чужая локаль. sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) sub = p.add_subparsers(dest="cmd", required=True) pi = sub.add_parser("inventory", help="инвентаризация модуля") pi.add_argument("file") pi.set_defaults(func=cmd_inventory) pl = sub.add_parser("lint", help="линтер особенностей синтаксиса 7.7") pl.add_argument("file") pl.set_defaults(func=cmd_lint) a = p.parse_args() a.func(a) if __name__ == "__main__": main() -
build_ert.py 4.9 KB
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Сборка (компиляция) .ert / 1Cv7.MD из исходников через GComp (7.7). Вызывает gcomp.exe напрямую через subprocess - Python на Windows передает Unicode-аргументы корректно, кириллица в путях не теряется (в отличие от Bash, где gcomp молча выходит с EXIT 0 и ничего не собирает). Делает бэкап целевого файла (gcomp перезаписывает молча) и проверяет, что результат реально изменился (timestamp/размер). Если не изменился - сигнализирует об ошибке: значит аргументы не дошли до gcomp. Примеры: python build_ert.py --out ВыгрузкаИзТис77.ert --src ./PUBID_NNNN-ВыгрузкаИзТис77 python build_ert.py --out report.ert --src ./report_src --truncate-mms """ import argparse import os import shutil import subprocess import sys import time from datetime import date from pathlib import Path DEFAULT_GCOMP_CANDIDATES = [ r"C:\Program Files\GComp\Release\gcomp.exe", r"C:\Tools\gcomp\gcomp.exe", r"C:\1Cv77\BIN\gcomp.exe", ] def find_gcomp(explicit): if explicit: return explicit env = os.environ.get("GCOMP_EXE") if env and Path(env).exists(): return env for c in DEFAULT_GCOMP_CANDIDATES: if Path(c).exists() and (Path(c).parent / "GComp.dll").exists(): return c return None def run_gcomp(gcomp, args): cmd = [gcomp] + args print("RUN:", " ".join(f'"{a}"' if " " in a else a for a in cmd), file=sys.stderr) proc = subprocess.run(cmd, capture_output=True) for stream in (proc.stdout, proc.stderr): if stream: try: print(stream.decode("cp866")) except Exception: print(stream.decode("cp1251", errors="replace")) return proc.returncode def stat_of(path): if Path(path).exists(): st = Path(path).stat() return (st.st_size, st.st_mtime) return None def main(): # Вывод содержит кириллицу. Без явного переключения печать падает с UnicodeEncodeError # везде, где консоль не в UTF-8: сборочный агент, чужая локаль. sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) p.add_argument("--out", required=True, help="целевой .ert (или 1cv7.md)") p.add_argument("--src", required=True, help="каталог исходников (-DD)") p.add_argument("--meta-data", action="store_true", help="собираем файл конфигурации 1cv7.md") p.add_argument("--external-report", action="store_true", help="явно указать .ert") p.add_argument("--truncate-mms", action="store_true", help="заменить Main MetaData Stream на пустой") p.add_argument("--no-backup", action="store_true", help="не делать бэкап целевого файла") p.add_argument("--gcomp", help="путь к gcomp.exe (иначе автопоиск рабочего)") p.add_argument("--extra", nargs=argparse.REMAINDER, help="доп. флаги GComp как есть") a = p.parse_args() out = Path(a.out).resolve() src = Path(a.src).resolve() if not src.is_dir(): sys.exit(f"Каталог исходников не найден: {src}") gcomp = find_gcomp(a.gcomp) if not gcomp: sys.exit("Рабочий gcomp.exe не найден. Укажи --gcomp <путь к Release\\gcomp.exe>.") before = stat_of(out) if before and not a.no_backup: bak = f"{out}.bak_{date.today().strftime('%Y%m%d')}" shutil.copy2(out, bak) print(f"Бэкап: {bak}", file=sys.stderr) args = ["-c", "-F", str(out), "-DD", str(src)] if a.meta_data: args.append("--meta-data") if a.external_report: args.append("--external-report") if a.truncate_mms: args.append("--truncate-mms") if a.extra: args += a.extra rc = run_gcomp(gcomp, args) print(f"EXIT={rc}", file=sys.stderr) if rc != 0: sys.exit(rc) time.sleep(0.2) after = stat_of(out) if after is None: sys.exit("ОШИБКА: целевой файл не создан.") if before is not None and after == before: sys.exit("ОШИБКА: файл не изменился (size/mtime те же). Аргументы не дошли до GComp - проверь пути.") print(f"OK: {out.name} -> {after[0]} байт, mtime обновлен.", file=sys.stderr) if __name__ == "__main__": main() -
cp1251_io.py 4.8 KB
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Чтение / поиск / замена в файлах 1С 7.7 (.1s, .frm, .mdp) в кодировке CP1251. Зачем: обычный Grep ищет по UTF-8 байтам и кириллицу в CP1251-файлах НЕ находит, а Read показывает кракозябры. Этот скрипт - корректный доступ к таким файлам. Подкоманды: read <file> [--start N] [--count M] печать строк с номерами grep <file> <pattern> [-i] [-C N] поиск регуляркой (номера строк) replace <file> <old> <new> [--regex] [--count K] [--dry-run] замена Запись всегда в CP1251 с newline='' - оригинальные переводы строк (обычно CRLF) не искажаются, иначе файл может стать нечитаемым для 7.7. Примеры: python cp1251_io.py grep Модуль.1s "ПустаяСтрока" -C 1 python cp1251_io.py read Модуль.1s --start 1500 --count 40 python cp1251_io.py replace Модуль.1s "ПустаяСтрока(Х) Тогда" "ПустаяСтрока(Х) = 1 Тогда" """ import argparse import re import sys from pathlib import Path ENC = "cp1251" def read_text(path): return Path(path).read_text(encoding=ENC, errors="replace") def read_raw(path): # newline='' - сохранить исходные переводы строк для последующей записи with open(path, encoding=ENC, newline="") as f: return f.read() def write_raw(path, text): with open(path, "w", encoding=ENC, newline="") as f: f.write(text) def cmd_read(a): lines = read_text(a.file).split("\n") start = max(1, a.start) end = len(lines) if a.count is None else min(len(lines), start - 1 + a.count) for i in range(start, end + 1): print(f"{i}\t{lines[i - 1]}") def cmd_grep(a): flags = re.IGNORECASE if a.ignore_case else 0 rx = re.compile(a.pattern, flags) lines = read_text(a.file).split("\n") hits = [i for i, l in enumerate(lines, 1) if rx.search(l)] for i in hits: lo = max(1, i - a.context) hi = min(len(lines), i + a.context) for j in range(lo, hi + 1): mark = ":" if j == i else "-" print(f"{j}{mark}\t{lines[j - 1]}") if a.context: print("--") print(f"[{len(hits)} совпадений]", file=sys.stderr) def cmd_replace(a): text = read_raw(a.file) if a.regex: new, n = re.subn(a.old, a.new, text, count=(a.count or 0)) else: total = text.count(a.old) n = total if a.count is None else min(a.count, total) new = text.replace(a.old, a.new, a.count if a.count is not None else -1) print(f"[замен: {n}]", file=sys.stderr) if a.dry_run: print("[dry-run, файл не изменен]", file=sys.stderr) return if n: write_raw(a.file, new) print("[записано]", file=sys.stderr) def main(): # Вывод содержит кириллицу. Без явного переключения печать падает с UnicodeEncodeError # везде, где консоль не в UTF-8: сборочный агент, чужая локаль. sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) sub = p.add_subparsers(dest="cmd", required=True) pr = sub.add_parser("read", help="печать строк файла") pr.add_argument("file") pr.add_argument("--start", type=int, default=1) pr.add_argument("--count", type=int, default=None) pr.set_defaults(func=cmd_read) pg = sub.add_parser("grep", help="поиск регуляркой") pg.add_argument("file") pg.add_argument("pattern") pg.add_argument("-i", "--ignore-case", action="store_true") pg.add_argument("-C", "--context", type=int, default=0) pg.set_defaults(func=cmd_grep) prp = sub.add_parser("replace", help="замена подстроки/регулярки") prp.add_argument("file") prp.add_argument("old") prp.add_argument("new") prp.add_argument("--regex", action="store_true", help="трактовать old как regex, new как шаблон замены") prp.add_argument("--count", type=int, default=None, help="максимум замен (по умолчанию все)") prp.add_argument("--dry-run", action="store_true", help="только посчитать, не писать") prp.set_defaults(func=cmd_replace) a = p.parse_args() a.func(a) if __name__ == "__main__": main() -
patch_by_marker.py 4.8 KB
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Идемпотентная вставка блока кода по маркеру-якорю в файл 1С 7.7 (.1s, CP1251). Зачем: повторяемые правки (вставить одинаковый блок колонок/присваиваний в обработку выгрузки, добавить элемент в форму и т.п.) удобнее делать привязкой к стабильному фрагменту-якорю, а не к номерам строк. Идемпотентность: если блок уже стоит рядом с маркером - повторно не вставляется, скрипт можно прогонять много раз. Маркер должен встречаться ровно один раз (или используй --all для всех вхождений). Запись в CP1251 с сохранением переводов строк. Примеры: # вставить блок после строки-якоря python patch_by_marker.py Модуль.1s --marker 'тз.НоваяКолонка("Комментарий","Строка",300);' \\ --after --block-file new_columns.txt # вставить текст напрямую перед якорем, проверить идемпотентность python patch_by_marker.py Форма.frm --marker 'Элементы:' --after --block ' BUTTON: { ... }' """ import argparse import sys from pathlib import Path ENC = "cp1251" def read_raw(path): with open(path, encoding=ENC, newline="") as f: return f.read() def write_raw(path, text): with open(path, "w", encoding=ENC, newline="") as f: f.write(text) def main(): # Вывод содержит кириллицу. Без явного переключения печать падает с UnicodeEncodeError # везде, где консоль не в UTF-8: сборочный агент, чужая локаль. sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) p.add_argument("file") p.add_argument("--marker", required=True, help="текст-якорь, к которому привязываемся") g = p.add_mutually_exclusive_group() g.add_argument("--after", action="store_true", help="вставить блок ПОСЛЕ маркера (по умолчанию)") g.add_argument("--before", action="store_true", help="вставить блок ПЕРЕД маркером") src = p.add_mutually_exclusive_group(required=True) src.add_argument("--block", help="текст блока для вставки") src.add_argument("--block-file", help="файл с текстом блока (читается как CP1251)") p.add_argument("--all", action="store_true", help="вставлять у всех вхождений маркера (иначе требуется уникальный)") p.add_argument("--dry-run", action="store_true", help="только показать, не писать") a = p.parse_args() text = read_raw(a.file) block = a.block if a.block is not None else Path(a.block_file).read_text(encoding=ENC) cnt = text.count(a.marker) if cnt == 0: sys.exit("Маркер не найден - проверь точное написание (CP1251, пробелы, кавычки).") if cnt > 1 and not a.all: sys.exit(f"Маркер встречается {cnt} раз. Уточни маркер или используй --all.") before = a.before # идемпотентность: блок уже стоит со стороны вставки от маркера? if before: if (block + a.marker) in text: print("[идемпотентно] блок уже перед маркером, пропуск.", file=sys.stderr) return new = text.replace(a.marker, block + a.marker, (0 if a.all else 1) or 1) if a.all \ else text.replace(a.marker, block + a.marker, 1) else: if (a.marker + block) in text: print("[идемпотентно] блок уже после маркера, пропуск.", file=sys.stderr) return new = text.replace(a.marker, a.marker + block) if a.all \ else text.replace(a.marker, a.marker + block, 1) inserted = (len(new) - len(text)) print(f"[вставлено {1 if not a.all else cnt} раз, +{inserted} символов]", file=sys.stderr) if a.dry_run: print("[dry-run, файл не изменен]", file=sys.stderr) return write_raw(a.file, new) print("[записано]", file=sys.stderr) if __name__ == "__main__": main() -
unpack_ert.py 3.8 KB
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Разбор (декомпиляция) .ert / 1Cv7.MD в текстовые исходники через GComp (7.7). Вызывает gcomp.exe напрямую через subprocess: Python на Windows передает Unicode-аргументы корректно (CreateProcessW), поэтому кириллица в путях не бьется - в отличие от запуска из Bash. По умолчанию ищет рабочий GComp в стандартных местах (с GComp.dll рядом). "Голый" gcomp.exe из каталогов проекта не годится - падает с STATUS_DLL_NOT_FOUND. Примеры: python unpack_ert.py ВыгрузкаИзТис77.ert --dest ./src_unpacked python unpack_ert.py 1Cv7.MD --meta-data --dest ./conf_src """ import argparse import os import subprocess import sys from pathlib import Path DEFAULT_GCOMP_CANDIDATES = [ r"C:\Program Files\GComp\Release\gcomp.exe", r"C:\Tools\gcomp\gcomp.exe", r"C:\1Cv77\BIN\gcomp.exe", ] def find_gcomp(explicit): if explicit: return explicit env = os.environ.get("GCOMP_EXE") if env and Path(env).exists(): return env for c in DEFAULT_GCOMP_CANDIDATES: if Path(c).exists(): # рядом должна быть GComp.dll, иначе exe не запустится if (Path(c).parent / "GComp.dll").exists(): return c return None def run_gcomp(gcomp, args): cmd = [gcomp] + args print("RUN:", " ".join(f'"{a}"' if " " in a else a for a in cmd), file=sys.stderr) proc = subprocess.run(cmd, capture_output=True) for stream in (proc.stdout, proc.stderr): if stream: try: print(stream.decode("cp866")) except Exception: print(stream.decode("cp1251", errors="replace")) return proc.returncode def main(): # Вывод содержит кириллицу. Без явного переключения печать падает с UnicodeEncodeError # везде, где консоль не в UTF-8: сборочный агент, чужая локаль. sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) p.add_argument("file", help="исходный .ert или 1Cv7.MD") p.add_argument("--dest", help="каталог назначения (-DD). Без него GComp создает SRC\\<имя>") p.add_argument("--meta-data", action="store_true", help="работаем с файлом конфигурации 1cv7.md") p.add_argument("--external-report", action="store_true", help="явно указать .ert") p.add_argument("--gcomp", help="путь к gcomp.exe (иначе автопоиск рабочего)") p.add_argument("--extra", nargs=argparse.REMAINDER, help="доп. флаги GComp как есть") a = p.parse_args() src = Path(a.file).resolve() if not src.exists(): sys.exit(f"Файл не найден: {src}") gcomp = find_gcomp(a.gcomp) if not gcomp: sys.exit("Рабочий gcomp.exe не найден. Укажи --gcomp <путь к Release\\gcomp.exe>.") args = ["-d", "-F", str(src)] if a.dest: Path(a.dest).mkdir(parents=True, exist_ok=True) args += ["-DD", str(Path(a.dest).resolve())] if a.meta_data: args.append("--meta-data") if a.external_report: args.append("--external-report") if a.extra: args += a.extra rc = run_gcomp(gcomp, args) print(f"EXIT={rc}", file=sys.stderr) if rc != 0: sys.exit(rc) print("Разбор завершен.", file=sys.stderr) if __name__ == "__main__": main()
-
-
SKILL.md 10.4 KB
--- name: 1c77-dev description: >- Разработка и доработка под 1С:Предприятие 7.7 (она же "77", "семерка"): правка и создание модулей кода (.1s, кодировка CP1251), форм (.frm), объектов конфигурации (1Cv7.MD), внешних обработок и отчетов (.ert), плюс разбор и сборка .ert / 1Cv7.MD через GComp. Используй ВСЕГДА, когда задача касается 1С 7.7: файлы .1s / .ert / .frm / .mdp / 1Cv7.MD; глобальный модуль, модули проведения и формы 7.7; синтаксис 7.7 (нет типа Булево; ПустаяСтрока; СоздатьОбъект; тернарник ?(); ошибка компиляции "Выражение должно иметь логический тип"); обход справочников и документов 7.7, выгрузка через ТаблицаЗначений или FileSystemObject; сборка или разбор внешних обработок 7.7 (gcomp); миграция данных с 7.7; поиск и правка кириллицы в .1s (обычный grep ее не находит из-за CP1251). Применяй даже без явного "7.7", если речь о старой конфигурации с файлами .1s/.ert/.md или о ТиС / Торговле / Бухгалтерии 7.7 / УПП 7.7. НЕ использовать для 1С 8.x: EDT, .bsl, .mdo, .epf, .erf, .cf, .cfe, управляемые формы, расширения конфигурации (CFE), СКД, Конвертация данных (КД) 2.x, конфигурации УНФ / ERP / ЗУП / УТ / БП 3.x - для них есть отдельные 1c-* скиллы. Не применять и к общим задачам с кодировкой CP1251 вне 1С 7.7 (например конвертация csv-файлов). --- # Разработка под 1С 7.7 (1c77-dev) Доработка кода, форм, метаданных и внешних обработок 1С:Предприятия 7.7. Версия 7.7 кардинально отличается от 8.x, и привычные для 8.x приемы тут ломаются. Этот скилл собирает рабочие приемы, особенности синтаксиса и готовые скрипты. ## Чем 7.7 отличается (читать обязательно) - **Нет EDT и .bsl/.mdo.** Конфигурация хранится в бинарном `1Cv7.MD`, внешние обработки - в бинарном `.ert`. Редактируемые исходники получают разбором через **GComp** (текстовые `.1s`, `.frm`, `.mdp`, `.mxl`). - **Кодировка везде CP1251.** Файлы `.1s`/`.frm`/`.mdp` - не UTF-8. Инструмент Grep (ищет по UTF-8-байтам) кириллицу в них **не находит**. Read показывает кракозябры. Читать/править - только через Python с `encoding='cp1251'` (см. `scripts/cp1251_io.py`) либо через MCP-сервер `1c77-metadata`. - **Синтаксис строже и беднее 8.x.** Нет типа Булево; число в условии `Если` не приводится к логическому; многих функций 8.x (`ЗначениеЗаполнено`, штатной `ПустаяСтрока` как Булево) нет или ведут себя иначе. Это источник большинства ошибок компиляции. Подробно: `references/syntax-gotchas-77.md`. ## Карта навигации Читай нужный файл по ситуации, не загружай все сразу: | Когда | Файл | |-------|------| | Пишешь/правишь BSL-код 7.7, ловишь ошибку компиляции "Выражение должно иметь логический тип" и т.п. | `references/syntax-gotchas-77.md` | | Нужно понять структуру `.1s` / `.frm` / `.mdp` / `.mxl` / `.ert`, что внутри обработки | `references/file-formats-77.md` | | Надо разобрать `.ert` в исходники или собрать обратно | `references/gcomp.md` | | Анализ метаданных конфигурации, поиск реквизита, проверка запроса 7.7, окружение | `references/mcp-and-tools.md` | Скрипты в `scripts/` (запуск с `--help`): - `cp1251_io.py` - чтение/запись/grep по `.1s` в CP1251 (когда обычный Grep слеп). - `analyze_module.py` - инвентаризация модуля: `СоздатьОбъект`, типы метаданных, глобальные `гл*`-функции, обращения к реквизитам. - `patch_by_marker.py` - идемпотентная вставка блока кода по маркеру-якорю (для повторяемых правок). - `build_ert.py` / `unpack_ert.py` - обертки GComp (сборка/разбор `.ert`) через PowerShell. ## Базовый workflow доработки 1. **Понять объект.** Метаданные и код существующих объектов конфигурации смотри через MCP `1c77-metadata` (`get_object`, `get_module`, `search_field`, `validate_query`) - это быстрее и точнее ручного парсинга. Для внешней обработки `.ert` сначала разбери ее GComp (`unpack_ert.py`), затем работай с `.1s`/`.frm`. Детали: `references/mcp-and-tools.md`, `references/gcomp.md`. 2. **Править код только через CP1251.** Используй `scripts/cp1251_io.py` или Python напрямую (`Path.read_text(encoding='cp1251')` / `write_text(..., encoding='cp1251', newline='')`). Никогда не правь `.1s` инструментом, который пишет UTF-8 - сломаешь кодировку всего файла. Для повторяемых вставок (одинаковый блок в N мест) используй `patch_by_marker.py` - он идемпотентен. 3. **Сверить синтаксис 7.7 ДО сборки.** Прогони мысленно (или поиском) по чек-листу особенностей из `references/syntax-gotchas-77.md`. Самая частая ошибка: голый вызов функции или число в условии `Если`/`ИначеЕсли` без операции сравнения. Проверка запросов - `validate_query` MCP `1c77-metadata`. 4. **Собрать `.ert` и проверить.** Сборка - только рабочим GComp через PowerShell (`build_ert.py`). Перед сборкой - бэкап `.ert`. После - убедиться, что timestamp/размер изменились (GComp молча не пересобирает при битых аргументах). Финальная компиляция BSL и проверка - при открытии в самой 7.7. ## Самопроверка перед сборкой .ert Чек-лист прогонять по СВОЕМУ сгенерированному коду ДО вызова gcomp: - [ ] В условиях `Если`/`Пока` нет голых вызовов функций или чисел - каждое сравнение явное и в скобках: `(Ф(х)=1) И (у=2)`. - [ ] `ПустаяСтрока`/`ПустоеЗначение` сравнивать с 1/0. - [ ] Каждая вызываемая процедура/функция объявлена ВЫШЕ точки вызова или в глобальном модуле - проверить греп-поиском определения. - [ ] `Симв()`, не `Символ()`; тернарник `?(усл,а,б)` с явными сравнениями. - [ ] Нет обращений к несуществующим у 7.7 методам 8.x. Детали и полный список - `references/syntax-gotchas-77.md`. Типовые ошибки компиляции ("Выражение должно иметь логический тип", "Процедура не обнаружена", "Плохой тип переменной") - сначала прогнать этот чек-лист, потом лезть в отладку. ## Критические правила (ключевые отличия синтаксиса, детали в references) - **Логический тип в `Если`.** `Если ПустаяСтрока(Х) Тогда` -> ошибка "Выражение должно иметь логический тип". Нужно сравнение: `Если ПустаяСтрока(Х) = 1 Тогда`, а `НЕ ПустаяСтрока(Х)` -> `ПустаяСтрока(Х) = 0`. То же для тернарника: `?(ПустоеЗначение(Х) = 1, "", ...)`. Полный список - `references/syntax-gotchas-77.md`. - **CP1251.** Grep по кириллице в `.1s` молчит (UTF-8 vs CP1251). Не делай вывод "совпадений нет" по Grep - ищи через `cp1251_io.py grep` или MCP. - **GComp запускать только из PowerShell.** Из Bash кириллица в путях бьется, gcomp молча выходит с ложным успехом и ничего не собирает. Рабочий exe - в `C:\Program Files\GComp\Release\` (с `GComp.dll` рядом), "голый" exe из каталогов проекта не запускается. Детали - `references/gcomp.md`. - **Минимальный diff при багфиксе.** Правь только код, связанный с багом. Не переформатируй CP1251-файл целиком - рискуешь кодировкой и шумным diff. ## Проверка качества После правки BSL 7.7 - прогон через MCP `1c-naparnik` (`ask_1c_ai`) для оценки и `validate_query` (`1c77-metadata`) для запросов. EDT-валидация и `bsl-platform-help` к 7.7 неприменимы (это 8.x).
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.