Чаще всего от ИИ получают не тот макрос, который нужен, по одной причине: задачу поставили одной фразой. «Напишите макрос, который собирает спецификации в ведомость» модель поймёт по-своему, угадает формат ваших листов неправильно, и вы потратите час на переписку. Та же задача, описанная по шаблону на полстраницы, обычно даёт рабочий код с первой или второй попытки.
Авторский курс AI University. Названия программ и товарные знаки принадлежат их правообладателям. Код из уроков проверяйте на копиях файлов: ответственность за результат на инженере.
В этом уроке вы научитесь писать техническое задание для макроса, давать ИИ выдержки из справки API, читать сообщения об ошибках и проходить цикл «запуск, ошибка, исправление» без паники. Разберём, как ловить случаи, когда модель придумывает несуществующие методы, как защитить модели и файлы от неудачного запуска и где хранить свои макросы. В практикуме вы получите и отладите VBA-макрос для Excel, который собирает строки со всех листов-спецификаций в одну сводную ведомость с итогами.
Шаблон технического задания для макроса
Вы и так умеете писать технические задания, только обычно на изделие. Для макроса нужно то же самое: исходные данные, требования, ограничения, критерий приёмки. Модель не видит ваш экран и не знает, как у вас устроены файлы, поэтому всё, что для вас очевидно, для неё догадка.
1. Программа и версия: <Excel 2021 / Microsoft 365, КОМПАС-3D v23, SolidWorks 2024, nanoCAD 24>
2. Язык: <VBA / Python 3.12 / AutoLISP / C#>; где будет запускаться: <модуль книги, макрос САПР, командная строка>
3. Входные данные: что это за файлы или листы, как устроены, пример 3-5 строк
(обезличенный), какие бывают отклонения (пустые строки, объединённые ячейки,
шапка не в первой строке, количество текстом)
4. Что на выходе: какой файл или лист, какие столбцы, в каком порядке, пример
5. Ограничения: не изменять исходные данные; пробный режим или подтверждение
перед изменениями; журнал; без внешних библиотек (или: можно openpyxl)
6. Критерий приёмки: как я пойму, что работает (например, итог по болтам М10
совпадает с ручным подсчётом)
7. Формат ответа: код целиком одним блоком, комментарии на русском, после кода
инструкция по запуску по шагам
Пункт 1 важнее, чем кажется. API САПР меняются от версии к версии, и модель, не зная версии, смешает старые и новые вызовы. Пункт 3 решает половину проблем: если вы написали «шапка может быть в любой из первых 15 строк, столбцы могут идти в другом порядке», модель сразу напишет поиск шапки, а не жёсткое «столбец C всегда Наименование». Пункт 7 защищает от ответа «вот основная часть, остальное допишите сами».
Про конфиденциальность (подробно в уроке 1): пример данных в пункте 3 всегда выдуманный, той же формы, что настоящий. Модели не нужны реальные обозначения изделия, ей нужна структура.
Дайте ИИ кусок справки
Модели хорошо знают VBA для Excel и Python: примеров в интернете миллионы. API САПР они знают хуже, особенно API КОМПАС-3D, и чем реже встречается программа, тем увереннее модель придумывает. Лучшее средство: вставить в промпт фрагмент официальной справки по тем объектам, которые нужны.
Как это делается на практике. Найдите в справке API своей программы (справка SolidWorks API на help.solidworks.com, справка Autodesk на help.autodesk.com, SDK и справка АСКОН для КОМПАС-3D, developer.nanocad.ru для nanoCAD) страницы нужных методов: например, сохранение документа и чтение свойства. Скопируйте синтаксис, описание параметров и пример. В промпте напишите: «Используйте только методы из приведённой справки. Если для задачи нужен метод, которого здесь нет, не придумывайте его, а напишите, какой метод нужен, и я найду его в справке».
Эта одна фраза резко снижает число выдуманных вызовов. Модель перестаёт заполнять пробелы правдоподобными именами и начинает задавать вопросы. Для Excel VBA справку можно не вставлять, но полезно указать: «используйте объектную модель Excel из документации Microsoft, без надстроек».
Цикл «запуск, ошибка, исправление»
Первый запуск кода от ИИ часто заканчивается ошибкой. Это нормально и не говорит о том, что модель плохая или задача невыполнима. Профессиональные программисты тоже редко пишут код без единой ошибки с первого раза. Разница в том, что они спокойно проходят цикл:
1. Запустить на копии, на 2-3 файлах или одном листе.
2. Ошибка? Скопировать её текст целиком: номер, сообщение, строку кода.
3. Отправить ИИ: текст ошибки + что делали + что ожидали.
4. Получить исправленный код целиком, заменить старый, запустить снова.
5. Работает? Проверить результат по критерию приёмки, а не «на вид».
Ключевое в шаге 3: давайте ИИ текст ошибки, а не пересказ. «Макрос не работает» не даёт модели ничего. «Run-time error '13': Type mismatch, подсвечена строка qty = CDbl(qtyValue), на листе Опора в столбце Кол. есть значение "2 шт"» даёт ей всё, что нужно для исправления.
И ещё одно правило: просите код целиком. Если модель присылает «замените строки 40-45 на это», легко вставить не туда. Фраза «пришлите исправленный макрос целиком» экономит время.
Если после трёх-четырёх итераций ошибка не уходит или меняется по кругу, остановитесь. Начните новый чат, вставьте туда последнюю версию кода, ТЗ и текст ошибки. Длинная переписка с десятком версий путает модель так же, как путала бы человека.
Как читать сообщение об ошибке
В Python ошибка выводится как трейсбек (traceback), отчёт о том, где программа упала. Вот настоящий трейсбек, который мы получили, запустив скрипт проверки из практикума с опечаткой в имени файла (пути сокращены, служебные строки с подчёркиваниями убраны):
Traceback (most recent call last):
File "D:\ИИ-скрипты\check_specs.py", line 43, in <module>
wb = load_workbook(sys.argv[1], data_only=True)
File "C:\...\site-packages\openpyxl\reader\excel.py", line 346, in load_workbook
reader = ExcelReader(filename, read_only, keep_vba,
File "C:\...\site-packages\openpyxl\reader\excel.py", line 123, in __init__
self.archive = _validate_archive(fn)
File "C:\...\site-packages\openpyxl\reader\excel.py", line 95, in _validate_archive
archive = ZipFile(filename, 'r')
File "C:\...\Lib\zipfile\__init__.py", line 1367, in __init__
self.fp = io.open(file, filemode)
FileNotFoundError: [Errno 2] No such file or directory: 'нет_такого.xlsx'
Читать его нужно снизу вверх. Последняя строка говорит, что случилось: FileNotFoundError, файл не найден, и какой именно. Выше идёт цепочка вызовов: каждый блок File ..., line ... показывает файл и строку. Блоки из папки site-packages или Lib относятся к библиотекам, туда смотреть не нужно, ошибка почти никогда не в них. Ищите самый нижний блок из вашего файла: здесь это check_specs.py, строка 43. Вывод: скрипт упал при открытии книги, потому что файла с таким именем нет в текущей папке. Исправление не в коде, а в команде запуска.
Самые частые последние строки и что они обычно значат:
FileNotFoundError файла или папки нет: путь, опечатка, текущая папка
PermissionError файл занят (открыт в Excel или САПР) или нет прав на папку
ModuleNotFoundError не установлена библиотека: python -m pip install <имя>
KeyError: 'Наименование' нет такого ключа или столбца: заголовок назван иначе
ValueError значение не того вида: текст вместо числа
AttributeError у объекта нет такого метода или свойства (часто выдуманный метод)
IndentationError сбиты отступы при копировании кода
В VBA ошибка выглядит иначе: окно с номером и текстом, например Run-time error '9': Subscript out of range, и кнопки End и Debug. В русской версии Excel текст сообщения переведён, но номер ошибки тот же, и для ИИ номер важнее текста. Нажмите Debug: редактор откроется на строке, где макрос остановился, она подсвечена жёлтым. Скопируйте ИИ номер ошибки, текст и эту строку. Самые частые ошибки VBA: 9 (нет листа или элемента с таким именем или номером), 13 (несоответствие типа: например, текст «2 шт» превращают в число), 91 (переменная объекта не задана: забыли Set или объект не нашёлся), 1004 (общая ошибка Excel: неверный диапазон, защищённый лист, недопустимое имя листа).
Логирование: пусть макрос рассказывает, что делает
Макрос, который молча отработал, хуже макроса, который упал с ошибкой. Упавший хотя бы честно сообщил о проблеме. Молчаливый мог пропустить половину строк, и вы узнаете об этом от снабженцев.
Поэтому в ТЗ на любой макрос, который обрабатывает больше пары файлов, пишите: «вести журнал: что обработано, что пропущено и почему». В Excel журнал удобно писать на отдельный лист, как в практикуме ниже. В Python есть стандартный модуль logging, вот минимальный пример, который мы запускали:
import logging
logging.basicConfig(
filename="макрос.log", encoding="utf-8", level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger()
log.info("Старт. Папка: %s", "D:/Проект/копия")
log.warning("Лист %s, строка %d: количество «%s» не число", "Опора", 4, "2 шт")
log.info("Готово. Строк: %d, предупреждений: %d", 9, 1)
В файле макрос.log появятся строки с датой, временем, уровнем и сообщением:
2026-11-14 00:12:01,814 INFO Старт. Папка: D:/Проект/копия
2026-11-14 00:12:01,814 WARNING Лист Опора, строка 4: количество «2 шт» не число
2026-11-14 00:12:01,815 INFO Готово. Строк: 9, предупреждений: 1
В макросах САПР на VBA аналог логирования: Debug.Print (вывод в окно Immediate редактора, открывается Ctrl+G) или запись в текстовый файл. Итоговая строка «обработано столько-то, пропущено столько-то, предупреждений столько-то» должна быть в конце любого пакетного макроса. По ней сразу видно, что что-то пошло не так.
Как не сломать модели и файлы
Макрос работает быстрее вас, в том числе когда ошибается. Неудачный цикл сохранения может за минуту перезаписать двести чертежей. Три защиты, которые стоит требовать в каждом ТЗ:
- Копия. Первые запуски только на копии папки или книги. Для САПР это особенно важно: многие макросы открывают и сохраняют модели, а отменить сохранение уже нельзя. Если в организации есть PDM (ЛОЦМАН:PLM, SOLIDWORKS PDM, Vault), макросы, которые пишут в файлы, сначала обсудите с администратором.
- Пробный режим (dry-run). Макрос сначала только показывает, что собирается сделать: список файлов, старые и новые имена, что будет изменено. Делает по отдельной команде или ключу. Так устроен скрипт из урока 1.
- Подтверждение. Перед изменениями макрос спрашивает: «Будет изменено 47 файлов в папке такой-то. Продолжить?». В VBA это
MsgBoxс кнопками «Да» и «Нет». Особенно полезно, когда макрос работает с активным документом или книгой: подтверждение показывает, с каким именно.
Ещё два правила. Начинайте с 2-3 файлов, а не с полной папки. И не давайте макросу удалять что-либо: если старые файлы мешают, пусть он переносит их в папку «старое», удалите вы сами.
Когда ИИ придумывает методы API
Модель генерирует правдоподобный текст, и иногда правдоподобным оказывается метод, которого не существует или который удалили. Мы проверили два типичных случая на текущих версиях библиотек. Модели до сих пор иногда пишут df.append(...) для таблицы pandas, а этот метод удалён в pandas 2.0, результат запуска:
AttributeError: 'DataFrame' object has no attribute 'append'. Did you mean: '_append'?
Другой пример: метод ws.get_highest_row() из старых версий openpyxl. В версии 3.1 его нет, вместо него свойство ws.max_row. В API САПР таких случаев больше: модель может «вспомнить» метод из другой версии, из другой программы (перепутать API SolidWorks и Inventor) или просто сочинить имя по смыслу.
Как ловить:
AttributeErrorв Python, «Method or data member not found» или ошибка 438 «Object doesn't support this property or method» в VBA почти всегда означают выдуманный или устаревший метод. Отправьте ИИ ошибку и попросите: «этого метода нет в моей версии, найдите замену в официальной документации и дайте ссылку на страницу».- В редакторе VBA после точки появляется список доступных методов и свойств объекта (если переменная объявлена с конкретным типом, например
Dim ws As Worksheet). Если метода из кода нет в этом списке, это повод насторожиться. Там же есть обозреватель объектов (F2) с поиском по имени. - Перед первым запуском выполните в редакторе VBA команду Debug, Compile VBAProject. Она найдёт опечатки в именах переменных (при
Option Explicit) и часть неверных вызовов до запуска. - Для САПР ищите каждое незнакомое имя в справке API своей версии. Пять минут поиска дешевле, чем отладка макроса, который вызывает несуществующий метод в середине пакетной обработки.
Где хранить свои макросы
Через полгода у вас будет десяток скриптов, и через год вы не вспомните, какая версия рабочая. Немного порядка с самого начала:
- Одна папка, например
D:\ИИ-скрипты, внутри по программам:excel,kompas,solidworks,python. Каждый макрос в отдельном файле с понятным именем:СобратьВедомость.bas, а неModule1. - Шапка в начале каждого файла: что делает, на какой версии проверен, какие входные данные ждёт, дата последней правки. ИИ напишет такую шапку, если попросить.
- Рядом с макросом тестовые данные: маленькая копия книги или 2-3 файла, на которых вы его проверяли. Через полгода это лучшая документация.
- Git, если готовы потратить час на освоение (например, через GitHub Desktop или встроенную поддержку в VS Code). Он хранит историю версий: можно вернуться к вчерашней рабочей, если сегодняшняя правка всё сломала. Если git не для вас, хотя бы копируйте рабочую версию в папку
архивс датой в имени перед каждой правкой. - Модули VBA храните вне книги тоже: в редакторе VBA правой кнопкой по модулю, Export File. Книга может испортиться, а файл
.basостанется.
Практикум
Задача из жизни. Изделие состоит из нескольких сборочных единиц, спецификация каждой на отдельном листе книги Excel (выгрузка из САПР или набрано вручную). Снабжению нужна сводная ведомость: все строки в одном списке и итог по каждой позиции, сколько всего кронштейнов, болтов М10, подшипников на всё изделие. Листы оформлены по-разному: где-то шапка в третьей строке под заголовком, где-то в первой, столбцы в разном порядке, есть строки разделов «Детали», «Стандартные изделия» без количества, кое-где количество записано текстом «2 шт».
Промпт
Напишите макрос VBA для Excel <Microsoft 365 / Excel 2021> под Windows.
Я конструктор, программирую слабо: нужен модуль целиком, Option Explicit,
комментарии на русском, обработка ошибок, после кода инструкция по запуску.
Задача: собрать строки со всех листов-спецификаций активной книги в сводную ведомость.
Входные данные: на каждом листе спецификация изделия. Шапка в одной из первых
15 строк, над ней может быть заголовок. Столбцы: Поз., Обозначение, Наименование,
Кол., Примечание, но порядок на разных листах разный, точки в названиях могут
отсутствовать («Поз», «Кол»). Пример (данные выдуманные):
Поз. | Обозначение | Наименование | Кол. | Примечание
| | Детали | |
1 | АБВГ.301261.001 | Кронштейн | 4 |
| | Стандартные изделия | |
5 | | Болт М10-6gx40.58.016 ГОСТ 7798-70 | 16 |
Отклонения: пустые строки внутри таблицы; строки разделов без количества;
количество иногда текстом («2 шт»); ячейки с ошибками #Н/Д; скрытые листы
(их пропускать); листы без шапки (пропускать).
На выходе три листа (создать, если нет, очистить, если есть):
- «Сводная»: Лист, Поз., Обозначение, Наименование, Кол., Примечание; все строки
с количеством;
- «Итог»: Обозначение, Наименование, Кол. всего, Листы (где встречается);
суммировать по паре Обозначение + Наименование, сортировка по обозначению;
- «Журнал»: Лист, Строка, Замечание: пропущенные листы, нечисловое количество.
Ограничения: исходные листы не изменять; перед началом спросить подтверждение
с именем книги; без надстроек и внешних библиотек; в конце сообщение с числом
обработанных листов, строк, позиций и замечаний.
Критерий: итог по болтам равен сумме по всем листам, строка «2 шт» попадает
в журнал, а не ломает макрос.
Обратите внимание, как в промпте описаны отклонения. Без них модель почти наверняка написала бы макрос под «идеальный» лист и упала бы на первом же заголовке раздела.
Отладка: как это обычно выглядит
Первая версия, которую выдаёт модель на промпт попроще, часто падает на одних и тех же местах. Два типичных случая, которые полезно узнавать в лицо.
Run-time error '9': Subscript out of range на строке вида Set wsOut = wb.Worksheets("Сводная"). Макрос обращается к листу, которого ещё нет. Что сказать ИИ: «ошибка 9 на строке Set wsOut = ..., листа Сводная в книге нет. Создавайте лист, если его нет, и очищайте, если есть». В итоговой версии этим занимается функция PrepareSheet.
Run-time error '13': Type mismatch на строке qty = CDbl(...). На листе в столбце количества текст «2 шт», и превратить его в число нельзя. Что сказать ИИ: «ошибка 13, в Кол. бывает текст вроде "2 шт". Такие строки копируйте в сводную как есть, в итог не включайте и пишите в журнал». В итоговой версии стоит проверка IsNumeric и запись в журнал.
Результат
Проверенная по синтаксису версия: имена объектов, методов и констант (Worksheets.Add, Range.End, Range.Sort с параметрами Key1, Order1, Header, Scripting.Dictionary, xlSheetVisible) сверены с документацией Microsoft по VBA для Excel. Запустить VBA на нашем стенде мы не могли, поэтому логику макроса (поиск шапки, пропуски, суммирование) повторили на Python и прогнали на тестовой книге, скрипт проверки ниже. Работает в Excel для Windows, на Mac Scripting.Dictionary недоступен.
Option Explicit
' Модуль СобратьВедомость
' Собирает строки со всех видимых листов-спецификаций активной книги на лист «Сводная»,
' считает итог по паре «Обозначение + Наименование» на листе «Итог»,
' замечания пишет на лист «Журнал». Исходные листы не изменяет.
Private Const OUT_SHEET As String = "Сводная"
Private Const SUM_SHEET As String = "Итог"
Private Const LOG_SHEET As String = "Журнал"
Private Const SEARCH_ROWS As Long = 15 ' в скольких верхних строках искать шапку
Private logRow As Long
Public Sub CollectSpecs()
Dim wb As Workbook
Dim ws As Worksheet, wsOut As Worksheet, wsSum As Worksheet, wsLog As Worksheet
Dim hdrRow As Long, cPos As Long, cDes As Long, cName As Long, cQty As Long, cNote As Long
Dim r As Long, lastRow As Long, outRow As Long, sumRow As Long, sheetsDone As Long
Dim des As String, nm As String, key As String
Dim qtyValue As Variant, qty As Double
Dim totals As Object, sheetList As Object, k As Variant
Dim curSheet As String
On Error GoTo Fail
Set wb = ActiveWorkbook
' Подтверждение: макрос работает с активной книгой, пусть пользователь увидит, с какой
If MsgBox("Собрать ведомость из книги «" & wb.Name & "»?" & vbCrLf & _
"Листы «" & OUT_SHEET & "», «" & SUM_SHEET & "», «" & LOG_SHEET & _
"» будут очищены и заполнены заново.", vbYesNo + vbQuestion, "Сводная ведомость") <> vbYes Then
Exit Sub
End If
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Set wsOut = PrepareSheet(wb, OUT_SHEET)
Set wsSum = PrepareSheet(wb, SUM_SHEET)
Set wsLog = PrepareSheet(wb, LOG_SHEET)
wsOut.Range("A1:F1").Value = Array("Лист", "Поз.", "Обозначение", "Наименование", "Кол.", "Примечание")
wsSum.Range("A1:D1").Value = Array("Обозначение", "Наименование", "Кол. всего", "Листы")
wsLog.Range("A1:C1").Value = Array("Лист", "Строка", "Замечание")
logRow = 1
Set totals = CreateObject("Scripting.Dictionary") ' ключ -> сумма количества
Set sheetList = CreateObject("Scripting.Dictionary") ' ключ -> список листов
outRow = 1
For Each ws In wb.Worksheets
curSheet = ws.Name
If ws.Name = OUT_SHEET Or ws.Name = SUM_SHEET Or ws.Name = LOG_SHEET Then GoTo NextSheet
If ws.Visible <> xlSheetVisible Then
AddLog wsLog, ws.Name, 0, "скрытый лист, пропущен"
GoTo NextSheet
End If
hdrRow = FindHeader(ws, cPos, cDes, cName, cQty, cNote)
If hdrRow = 0 Then
AddLog wsLog, ws.Name, 0, "не найдена шапка с «Наименование» и «Кол.», лист пропущен"
GoTo NextSheet
End If
' последняя заполненная строка по столбцу наименования (и обозначения, если он есть)
lastRow = ws.Cells(ws.Rows.Count, cName).End(xlUp).Row
If cDes > 0 Then
If ws.Cells(ws.Rows.Count, cDes).End(xlUp).Row > lastRow Then lastRow = ws.Cells(ws.Rows.Count, cDes).End(xlUp).Row
End If
For r = hdrRow + 1 To lastRow
nm = CellText(ws.Cells(r, cName))
des = ""
If cDes > 0 Then des = CellText(ws.Cells(r, cDes))
qtyValue = ws.Cells(r, cQty).Value
If IsError(qtyValue) Then qtyValue = "ошибка в ячейке" ' #Н/Д, #ЗНАЧ! и т.п.
If des = "" And nm = "" Then GoTo NextRow ' пустая строка
If Trim$(CStr(qtyValue)) = "" Then GoTo NextRow ' заголовок раздела: «Детали», «Стандартные изделия»
' копируем строку в сводную как есть
outRow = outRow + 1
wsOut.Cells(outRow, 1).Value = ws.Name
If cPos > 0 Then wsOut.Cells(outRow, 2).Value = ws.Cells(r, cPos).Value
wsOut.Cells(outRow, 3).Value = des
wsOut.Cells(outRow, 4).Value = nm
wsOut.Cells(outRow, 5).Value = qtyValue
If cNote > 0 Then wsOut.Cells(outRow, 6).Value = ws.Cells(r, cNote).Value
' в итог только числовое количество
If IsNumeric(qtyValue) Then
qty = CDbl(qtyValue)
key = des & "|" & nm
totals(key) = totals(key) + qty ' нового ключа нет: Dictionary создаст его со значением Empty, Empty + qty = qty
If InStr(1, "; " & sheetList(key) & ";", "; " & ws.Name & ";") = 0 Then
If sheetList(key) = "" Then sheetList(key) = ws.Name Else sheetList(key) = sheetList(key) & "; " & ws.Name
End If
Else
AddLog wsLog, ws.Name, r, "количество «" & CStr(qtyValue) & "» не число, в итог не вошло"
End If
NextRow:
Next r
sheetsDone = sheetsDone + 1
NextSheet:
Next ws
' выгружаем итог
sumRow = 1
For Each k In totals.Keys
sumRow = sumRow + 1
wsSum.Cells(sumRow, 1).Value = Split(k, "|")(0)
wsSum.Cells(sumRow, 2).Value = Split(k, "|")(1)
wsSum.Cells(sumRow, 3).Value = totals(k)
wsSum.Cells(sumRow, 4).Value = sheetList(k)
Next k
If sumRow > 2 Then
wsSum.Range("A2:D" & sumRow).Sort Key1:=wsSum.Range("A2"), Order1:=xlAscending, _
Key2:=wsSum.Range("B2"), Order2:=xlAscending, Header:=xlNo
End If
wsOut.Columns("A:F").AutoFit
wsSum.Columns("A:D").AutoFit
wsLog.Columns("A:C").AutoFit
Done:
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
If curSheet <> "" Then
MsgBox "Готово. Листов обработано: " & sheetsDone & ", строк в сводной: " & (outRow - 1) & _
", позиций в итоге: " & (sumRow - 1) & ", замечаний: " & (logRow - 1), vbInformation, "Сводная ведомость"
End If
Exit Sub
Fail:
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
MsgBox "Ошибка " & Err.Number & ": " & Err.Description & vbCrLf & _
"Лист: " & curSheet & ", строка: " & r, vbCritical, "Сводная ведомость"
End Sub
' Ищет строку шапки: в ней должны быть «Наименование» и «Кол».
' Возвращает номер строки (0, если не нашли) и номера столбцов через параметры.
Private Function FindHeader(ws As Worksheet, cPos As Long, cDes As Long, cName As Long, _
cQty As Long, cNote As Long) As Long
Dim r As Long, c As Long, lastCol As Long, h As String
For r = 1 To SEARCH_ROWS
cPos = 0: cDes = 0: cName = 0: cQty = 0: cNote = 0
lastCol = ws.Cells(r, ws.Columns.Count).End(xlToLeft).Column
For c = 1 To lastCol
h = LCase$(Replace(CellText(ws.Cells(r, c)), ".", ""))
If h Like "поз*" And cPos = 0 Then cPos = c
If h Like "обозначение*" And cDes = 0 Then cDes = c
If h Like "наименование*" And cName = 0 Then cName = c
If h Like "кол*" And cQty = 0 Then cQty = c
If h Like "прим*" And cNote = 0 Then cNote = c
Next c
If cName > 0 And cQty > 0 Then
FindHeader = r
Exit Function
End If
Next r
FindHeader = 0
End Function
' Возвращает лист с нужным именем: очищает существующий или создаёт новый в конце книги
Private Function PrepareSheet(wb As Workbook, sheetName As String) As Worksheet
Dim ws As Worksheet
On Error Resume Next
Set ws = wb.Worksheets(sheetName)
On Error GoTo 0
If ws Is Nothing Then
Set ws = wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.Count))
ws.Name = sheetName
Else
ws.Cells.Clear
End If
Set PrepareSheet = ws
End Function
' Текст ячейки без пробелов по краям. Ячейка с ошибкой (#Н/Д, #ЗНАЧ!) даёт пустую строку,
' иначе CStr остановил бы макрос ошибкой 13 «Type mismatch»
Private Function CellText(cell As Range) As String
If IsError(cell.Value) Then
CellText = ""
Else
CellText = Trim$(CStr(cell.Value))
End If
End Function
' Пишет замечание на лист «Журнал»
Private Sub AddLog(wsLog As Worksheet, sheetName As String, rowNo As Long, text As String)
logRow = logRow + 1
wsLog.Cells(logRow, 1).Value = sheetName
If rowNo > 0 Then wsLog.Cells(logRow, 2).Value = rowNo
wsLog.Cells(logRow, 3).Value = text
End Sub
Что в этом коде стоит заметить. Поиск шапки (FindHeader) смотрит первые 15 строк и узнаёт столбцы по началу названия, поэтому «Кол.», «Кол» и «Количество» распознаются одинаково. Ячейки с ошибками обрабатываются отдельно (CellText), иначе макрос остановился бы на первой #Н/Д. Строки без количества считаются заголовками разделов и пропускаются. Подсчёт идёт через словарь: ключ «обозначение плюс наименование», поэтому болт без обозначения, но с полным наименованием по ГОСТ суммируется правильно. Обработчик ошибок Fail возвращает Excel нормальный режим пересчёта и показывает, на каком листе и строке случилась проблема.
Как запустить
- Сделайте копию книги со спецификациями. Работайте только с копией.
- Сохраните копию как книгу с поддержкой макросов: Файл, Сохранить как, тип «Книга Excel с поддержкой макросов (*.xlsm)». В обычном .xlsx макрос не сохранится.
- Нажмите Alt+F11, откроется редактор VBA. В меню Insert выберите Module. Вставьте код целиком в пустое окно модуля.
- Выполните Debug, Compile VBAProject. Если ничего не появилось, синтаксис в порядке.
- Вернитесь в Excel, нажмите Alt+F8, выберите
CollectSpecs, нажмите «Выполнить». Подтвердите имя книги в окне вопроса. - Проверьте результат. На листе «Журнал» должны быть замечания по пропущенным листам и нечисловому количеству. На листе «Итог» найдите две-три позиции и пересчитайте их вручную по исходным листам.
На нашей тестовой книге (лист «Рама» с шапкой в третьей строке, лист «Опора» с другим порядком столбцов и количеством «2 шт», лист «Заметки» без шапки и скрытый лист «Старое») Python-проверка дала такой итог, и с ним должен совпасть лист «Итог»:
[ошибка] Опора!строка 4: количество «2 шт» не число
[пропущен] Заметки: нет шапки
[пропущен] Старое: скрытый лист
Болт М10-6gx40.58.016 ГОСТ 7798-70 24
Гайка М10-6Н.5.016 ГОСТ 5915-70 16
Шайба 10 ГОСТ 11371-78 8
АБВГ.301261.001 Кронштейн 6
АБВГ.301261.002 Пластина опорная 2
Независимая проверка на Python
Хороший приём для любого макроса, который считает: проверить результат вторым способом. Этот скрипт читает ту же книгу без Excel и считает итоги той же логикой. Если итоги совпали, ошибиться одинаково в двух разных программах маловероятно. Запуск: python check_specs.py "D:\ИИ-скрипты\тест\спецификации.xlsm" (нужна библиотека openpyxl из урока 1).
# check_specs.py
# Независимая проверка макроса СобратьВедомость: считает итоги по всем листам-спецификациям
# той же логикой и печатает их. Сравните с листом «Итог», который построил макрос.
# Запуск: python check_specs.py <книга.xlsx или .xlsm>
import sys
from collections import defaultdict
from openpyxl import load_workbook
SKIP = {"Сводная", "Итог", "Журнал"} # листы, которые создаёт макрос
SEARCH_ROWS = 15 # в скольких верхних строках искать шапку
def norm(v):
"""Текст шапки: без точек и пробелов по краям, строчными буквами."""
return str(v).replace(".", "").strip().lower() if v is not None else ""
def find_header(ws):
"""Ищет строку, где есть «Наименование» и «Кол». Возвращает (строка, {поле: столбец})."""
for r in range(1, SEARCH_ROWS + 1):
cols = {}
for c in range(1, ws.max_column + 1):
h = norm(ws.cell(r, c).value)
for key, prefix in (("pos", "поз"), ("des", "обозначение"), ("name", "наименование"),
("qty", "кол"), ("note", "прим")):
if h.startswith(prefix) and key not in cols:
cols[key] = c
if "name" in cols and "qty" in cols:
return r, cols
return None, None
def to_number(v):
"""Число из ячейки: 2, 2.5, «2», «2,5». Всё остальное None."""
if isinstance(v, (int, float)):
return float(v)
try:
return float(str(v).strip().replace(",", "."))
except ValueError:
return None
wb = load_workbook(sys.argv[1], data_only=True)
totals = defaultdict(float)
for ws in wb.worksheets:
if ws.title in SKIP:
continue
if ws.sheet_state != "visible":
print(f"[пропущен] {ws.title}: скрытый лист")
continue
hdr, cols = find_header(ws)
if hdr is None:
print(f"[пропущен] {ws.title}: нет шапки")
continue
for r in range(hdr + 1, ws.max_row + 1):
des = str(ws.cell(r, cols["des"]).value or "").strip() if "des" in cols else ""
name = str(ws.cell(r, cols["name"]).value or "").strip()
qty_cell = ws.cell(r, cols["qty"]).value
if not des and not name:
continue # пустая строка
if qty_cell is None or str(qty_cell).strip() == "":
continue # заголовок раздела «Детали», «Стандартные изделия»
qty = to_number(qty_cell)
if qty is None:
print(f"[ошибка] {ws.title}!строка {r}: количество «{qty_cell}» не число")
continue
totals[(des, name)] += qty
for (des, name), q in sorted(totals.items()):
print(f"{des:18} {name:40} {q:g}")
Типичные ошибки и что сказать ИИ
Макросы не запускаются, вверху жёлтая или красная полоса про безопасность. Если книга пришла из интернета или по почте, Windows помечает её, и Excel блокирует макросы. Закройте книгу, в Проводнике щёлкните по файлу правой кнопкой, Свойства, отметьте «Разблокировать», откройте снова. Если макросы запрещены политикой предприятия, это решает администратор, а не вы.
Вместо русских букв в коде знаки вопроса. Редактор VBA работает в кодировке Windows, и кириллица в нём отображается только при русском языке для программ, не поддерживающих Юникод (Параметры Windows, Время и язык, Язык и регион, Административные языковые параметры). Если так настроить нельзя, попросите ИИ: «замените русские названия листов и сообщения на латиницу» или вставляйте код через буфер обмена, а не импортом .bas, сохранённого в UTF-8.
Compile error: Variable not defined. Сработал Option Explicit: в коде есть переменная без Dim, обычно опечатка. Отправьте ИИ имя переменной, которую подсвечивает редактор.
Итог не совпадает с ручным подсчётом. Сначала посмотрите «Журнал»: возможно, строка ушла как нечисловая. Затем «Сводную»: попали ли туда все строки листа. Частые причины: обозначение с лишним пробелом или латинской буквой вместо русской (тогда одна позиция разъезжается на две строки итога), шапка ниже 15-й строки. Что сказать ИИ: «в итоге позиция АБВГ.301261.001 разделилась на две строки, хотя должна быть одна. Нормализуйте обозначение: уберите пробелы, замените латинские буквы, похожие на русские».
Попробуйте сами
- Возьмите задачу из своей карты рутины (задание 1 урока 1) и напишите для неё ТЗ по шаблону из семи пунктов. Отправьте ИИ и посчитайте, сколько итераций потребовалось до рабочего результата. Критерий: ТЗ содержит версию программы, пример входных данных с отклонениями и критерий приёмки, а код заработал не позже чем за три итерации.
- Запустите макрос из практикума на копии своей книги со спецификациями (или на тестовой, собранной по описанию выше). Сверьте итог с Python-проверкой. Критерий: все позиции и количества совпадают, нечисловые значения попали в журнал.
- Попросите ИИ доработать макрос: добавить на лист «Итог» столбец «Единица измерения», если такой столбец есть в спецификациях, и не суммировать позиции с разными единицами. Пройдите цикл отладки, если появится ошибка. Критерий: в ответ на ошибку вы отправили номер, текст и подсвеченную строку, а не пересказ, и финальная версия проходит
Debug, Compile VBAProjectбез замечаний.
Проверьте себя
1. Python-скрипт упал с длинным трейсбеком. Откуда начинать чтение?
- С первой строки Traceback, там главная причина
- С последней строки (тип ошибки и сообщение), затем самый нижний блок из вашего файла
- С блоков из папки site-packages, ошибка обычно в библиотеке
- Читать не нужно, достаточно написать ИИ «скрипт не работает»
Показать ответ
Ответ: B. Последняя строка говорит, что случилось, а самый нижний блок из вашего файла показывает, на какой строке. Блоки библиотек почти никогда не содержат причину, и ИИ нужен полный текст, а не пересказ.
2. Макрос для SolidWorks от ИИ падает с ошибкой 438 «Object doesn't support this property or method». Что вероятнее всего произошло и что делать?
- Сломана установка SolidWorks, нужно переустановить программу
- Модель вызвала метод, которого нет у этого объекта в вашей версии: найти правильное имя в справке API и дать её фрагмент ИИ
- Не хватает прав администратора, нужно запустить SolidWorks от имени администратора
- Файл модели повреждён
Показать ответ
Ответ: B. Ошибка 438 в VBA и AttributeError в Python типичны для выдуманных или устаревших методов. Лечится сверкой со справкой API своей версии и требованием «используйте только методы из приведённой справки».
3. Какой набор требований в ТЗ лучше всего защищает модели от неудачного пакетного макроса?
- «Работай быстро и без лишних вопросов»
- Запуск на копии, пробный режим со списком изменений, подтверждение перед изменениями, журнал
- Удалять старые версии файлов, чтобы не путаться
- Запускать сразу на всей папке, чтобы быстрее увидеть ошибки
Показать ответ
Ответ: B. Копия, dry-run, подтверждение и журнал дают увидеть ошибку до того, как она перезапишет файлы. Начинать нужно с 2-3 файлов, а удаление оставлять себе.
Итоги
- ТЗ на макрос пишется по шаблону: программа и версия, язык, входные данные с примером и отклонениями, выход, ограничения, критерий приёмки, формат ответа. Пример данных всегда обезличенный.
- Для API САПР вставляйте в промпт фрагмент официальной справки и запрещайте модели придумывать методы, которых там нет.
- Ошибку отдавайте ИИ целиком: номер, текст, строку. Трейсбек Python читайте снизу вверх, в VBA нажимайте Debug и смотрите подсвеченную строку.
- AttributeError, ошибка 438 и «Method or data member not found» почти всегда означают выдуманный или устаревший метод: сверяйтесь со справкой своей версии.
- Защита файлов: копия, 2-3 файла для начала, пробный режим, подтверждение, журнал, никаких удалений из макроса.
- Храните макросы в одной папке с шапкой, тестовыми данными и историей версий, а результат расчётных макросов проверяйте вторым способом.