Работа разработчика 1С иногда похожа на увлекательный детективный сериал с непредсказуемым финалом. Думаю все испытывали этот сладостный момент, когда после долгих блужданий в дебрях кода истина наконец найдена! Вот и со мной так было не раз. Предлагаю вашему вниманию несколько историй на эту тему.
Ручной обмен
Много лет назад, кажется в 2014 году, пришел как-то к нам на поддержку новый клиент.
У клиента много баз, много интеграций между ними. База, где ведется основной учет, за несколько лет переписана до неузнаваемости. Причем в разные моменты ее дорабатывали разработчики очень разной квалификации. Никакой документации нет, технических заданий нет, спросить в случае чего не у кого. Плюс ко всему, совместно с переходом к нам на поддержку происходил также и переезд с собственных серверов в забугорский дата-центр (в компании была реструктуризация), что также добавляло изрядное количество проблем. Все это добро свалилось к нам одномоментно, практически без каких-либо нормальных процессов приемки-передачи. В общем, никогда такого не было, и вот опять!
Клиент большой, важный и требовательный (а есть другие?). Начинаем в ускоренном порядке въезжать в его бизнес-процессы, знакомиться с IT-службой, настраивать сервера, обрабатывать заявки. Вдруг, в какой-то момент встает обмен между двумя самыми главными, самыми важными информационными базами. Обмен полностью самописный. Никаких вам универсальных механизмов, правил обмена, EnterpriceData или чего-то подобного. Из одной базы COM-ом цепляемся в другую базу, собираем данные и погнали. Только код, только Hardcore!
Итак, обмен не работает. Ошибка абсолютно невразумительная и непонятная: «в данной транзакции уже происходили ошибки». ОК, уже происходили, понятно, вернее - не понятно, дальше что?
Ладно, открываю конфигуратор в боевой базе (нет времени собирать какие-то тестовые стенды), ставлю точки останова, запускаю обмен. Выявил процедуру, где возникает ошибка. Уже хорошо. В этой процедуре также ставлю точку останова и иду построчно в отладчике. Пытаюсь уже здесь выловить злосчастную строчку, где все падает, и… обмен проходит без ошибок. Что за чертовщина? Но времени разбираться нет. Отписываемся клиенту, что обмен прогрузили, и едем дальше разруливать заявки.
Но на следующий день (обмен ходил раз в сутки ночью) все опять повторяется. Регламентное задание не отработало. Та же ошибка. Снова конфигуратор, снова точки останова, отладка, и снова обмен проходит без ошибок.
Ближе к вечеру разворачиваю тестовые базы на нужную дату. Запускаю обмен – ошибка! Ставлю в конфигураторе режим «Остановка по ошибке». Ошибка есть, остановки нет! Иду отладчиком построчно – нет ошибок! Начинаю верить в магию и параллельные миры. Еду домой, много думаю.
Следующий день уже знакомо начинается с того, что ночью обмен не прошел. Надо понимать, что все это происходило в режиме аврала. Сидеть несколько часов с задачей нет никакой возможности. В результате составили расписание: каждое утро дежурный разработчик открывал рабочий конфигуратор и прогонял обмен руками в режиме отладки. Это продолжалось пару недель, пока не разгребли более срочные задачи и не смогли заняться этой проблемой детально.
Причина же оказалась в следующем: в процессе обмена шла запись некоторых служебных данных в некоторый служебный регистр, причем регистр был периодический с периодом в 1 секунду. Когда обмен запускался регламентным заданием, происходила запись в этот регистр одинаковых данных, соответственно обмен падал с ошибкой «запись не уникальна». Когда шли отладкой, между итерациями проходило больше одной секунды и все служебные данные записывались в регистр без ошибок. Остановка по ошибке не срабатывала, т.к. как весь обмен был написан «в попытке», а до этого момента в вышестоящей процедуре также была исключительная ситуация, которая на результат обмена не влияла, но не давала отловить ошибку записи в регистр (привет разработчику, который это написал).
Возникает вопрос, почему же раньше все работало? А помните про переезд в дата-центр? Раньше сервера были старые, медленные и ситуаций записи в регистр в рамках одной секунды не возникало. А новые сервера работали так быстро, что между итерациями цикла (где и была запись) проходило меньше секунды.
Магия здесь оказалась ни при чем. Виноваты гигагерцы.
Грузим грузим грузим остатки
Внедряли мы как-то 1С:ERP в одной крупной торговой сети.
Еще в самом начале проекта появилась задача по загрузке остатков (куда ж без нее?) из действующей системы заказчика (не 1С). В качестве формата передачи данных выбрали Excel, т. к. в этом случае не требовалась доработка исходной системы.
Итоговая задача сводилось к следующему: необходимо было прочитать файл с остатками, получившиеся данные распознать, затем свернуть строки таблицы по набору полей «Организация, Склад, Подразделение, Номенклатура, Характеристика и т. д.» с суммированием количества и стоимости. Далее, для каждого набора «Организация, Подразделение, Склад» создавался отдельный документ «Ввод начальных остатков» с заполнением табличной части. Причем для простоты тестирования и отладки, а также чтобы не плодить лишние документы при каждой загрузке одного и того же файла, существующие документы, отвечающие параметрам поиска, перезаписывались. На форме обработки была соответствующая «галочка». В общем, все как обычно.
Написал обработку, ее протестировали на тестовых примерах. Все работает.
Ближе к старту, клиент начал постепенно скидывать файлы с остатками для загрузки. Консультант грузит, документы создаются, все хорошо.
Сверяем итог в 1С с первоисточником – не сходится. Клиент выгружает файлы Excel еще раз, уверяет, что файлы созданы правильно. Консультант грузит файлы, сверяет каждый файл, все загружается верно. Общий результат все равно не сходится. Файлы огромные (общий объем под 200000 строк), грузятся долго. Поиск ошибки затягивается. Старт под угрозой, задача получает наивысший приоритет. Каждое звено цепи работает без ошибок, но остатки в двух системах никак не хотят сойтись.
Как часто бывает, причина лежала на поверхности, но ее почему-то сразу никто не увидел. Дело в том, что клиент предоставил несколько файлов с остатками. И в разных файлах существовали строки с одинаковыми наборами «Организация, Подразделение, Склад» (и даже с одинаковой номенклатурой). Соответственно, следуя алгоритму, обработка загрузки пересоздавала некоторые документы, которые были созданы ранее при загрузке данных из других файлов.
Загрузили файлы с небольшим смещением по времени (поиск происходил также и по дате документа) и остатки сошлись.
Широка страна моя родная
«Зарплата и управление персоналом» версии 2.5. 11 распределенных информационных баз. Каждый филиал работает в своей базе, итоговые данные сливаются в центральный офис.
Только что внедрен новый функционал – очень хитрый и сложный расчет оплаты сверхурочных часов.
Приходит заявка – данные расчета переработок по одному сотруднику рассчитались неправильно.
Конфигуратор, отладка, получаю итоговый запрос, консоль запросов, вставлю в нужные места фильтры по сотруднику, выполняю, получаю результат. Расписываю, как и почему получены суммы, отдаю задачу. По всему получается, что все рассчитано верно.
Консультант связывается с клиентом. Вместе решают, что да, все верно. Но вскоре задача возвращается: в филиале при расчете суммы другие. Так. Уже интересней.
Мы разрабатываем на серверах центрального офиса, все разработочные / тестовые являются копией центральной. С обменами уже давно никаких проблем не было.
Подключаюсь к базе филиала, делаю расчет, действительно, по одному сотруднику данные не сходятся с центром. Консоль запросов, фильтр по сотруднику – результат другой. Хм… Очевидно, дело в отличающихся исходных данных. Но где именно?
Как я говорил, алгоритм расчета нетривиальный, запрос под две тысячи строк, куча таблиц (во общем, как обычно и бывает в ЗУП). Начинаю плавно подниматься по запросу снизу-вверх, сравнивая значения временных таблиц. Где-то в середине запроса нахожу таблицу, с которой начинаются несоответствия. Иду дальше и натыкаюсь на регистр, ошибку в котором я совсем не ждал…
Дело в том, что этот филиал находится в Казани. А в Татарстане есть национальный праздник с плавающей датой – «Курбан-байрам», который в 2016 году выпал на 12 сентября. Соответственно, в производственном календаре это был выходной день, и работа в этот день считалась переработкой. А в центральной базе, где был общий Российский производственный календарь – это рабочий день.
Описываю всю ситуацию. Отдаю задачу консультанту. Консультант звонит клиенту. Где-то в глубине души начинает зарождаться очень нехорошее предчувствие. И действительно… Задача приходит на доработку:
«Реализовать в системе возможность хранения нескольких производственных календарей»
Но это уже совсем другая история…
P. S. Смотрите также:
Комментарии (7)
alexhott
26.10.2022 15:44+2Tavalik Автор
26.10.2022 16:24В современном ЗУП 3.1 эта проблема решена.
pae174
26.10.2022 20:43+5Я однажды наблюдал со стороны модуль планирования на заводе, в котором была учтена посменная работа нескольких представителей (или выходцев) из двух противоборствующих банд. Их нельзя было в одном цеху ставить в одну смену, и выработку ихнюю нельзя было доверить другой банде. А вы тут про календарь какой-то, фу, нисмишнодаже.
Radisto
27.10.2022 05:40Нетривиальная задача! А как матрица банд и их совместимость задавалась? Это ведь динамичные социальные структуры)))
Honomer
В Конфигураторе же надо остановка по ошибке. Включили и ждем, пока упадёт. Зачем по шагам ходить было?
Tavalik Автор
При хорошем стиле программирования так и должно быть. Но если у вас вложенные попытки - исключения, то остановка будет работать только при первой ошибке. В данном случае, запись в регистры была второй ошибкой, после какой-то незначительной ошибки, связанной толи с проверкой данных, толи еще с чем.
aspid-crazy
Проблема не во вложенных попытках как таковых. Проблема в том что транзакция испорчена. Не каждая ошибка в транзакции приведет к такому эффекту. Например, обращение к несуществующей процедуре в попытке не приведет к "протуханию" транзакции. А вот ошибки, связанные с обращением к данным - приведут, даже если исключение перехвачено.
В этом случае, любое следующее действие внутри транзакции приведет к "В данной транзакции уже происходили ошибки". Т.е. сама ошибка возникла ранее, но выстрелила в каком-то рандомном коде, который попытался выполнить действие в контексте протухшей транзакции.
Это делает дебаггинг этой ошибки одним из самых неприятных.
Полезно будет упомянуть, что на ИТС есть стандарт описывающий работу с транзакциями , рекомендую к ознакомлению.