Claude Skill

1c77-dev

Разработка и доработка под 1С:Предприятие 7.7 (она же "77", "семерка"): правка и создание модулей кода (.1s, кодировка CP1251), форм (.frm), объектов конфигурации (1Cv7.MD), внешних обработок и отчетов (.ert), плюс разбор и сборка .ert / 1Cv7.MD через GComp. Используй ВСЕГДА, ког

LLM Mart · 0 points · 13 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download Desko77-claude-code-skills-1c-skills_1c77-dev-eb281b4.zip · 27 KB
Part of desko77/claude-code-skills-1c — 48 skills

Install

skills CLI npx skills add https://github.com/Desko77/claude-code-skills-1c/tree/main/skills/1c77-dev
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install desko77-claude-code-skills-1c@llmmart
Git 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 доработки

  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).

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.

No comments yet.

Reviews (0)

No reviews yet.

Related