Эволюция кода: от простого сопоставления к интеллектуальной обработке данных

Анализ рефакторинга в системе 1С:Предприятие

Первый вариант: наивный подход к автоматизации

Исходная задача: Программист столкнулся с необходимостью автоматизировать заполнение документа “ПеремещениеЗапасов” данными из связанных расходных накладных. Основная цель – перенести заказы покупателей из накладных в документ перемещения.

🔧 Первоначальная реализация
1
&НаСервере
2
Процедура ЗаполнитьЗаказыПоРасходнымНакладным_3с(МассивНомеровНакладных)
3

4
//добавим префиксы и нули
5
ДокОбъект = РеквизитФормыВЗначение(“Объект”);
6
Префикс = “”;
7
ПрефиксацияОбъектовСобытия.УстановитьПрефиксИнформационнойБазыИОрганизации(ДокОбъект , Истина, Префикс);
8
МассивНомеровСПрефиксами = Новый Массив;
9
Для каждого НомерНакладной Из МассивНомеровНакладных Цикл
10
НомерНакладной = Префикс + СтроковыеФункцииКлиентСервер.ДополнитьСтроку(НомерНакладной, 11 – СтрДлина(Префикс), “0”, “Слева”);
11
МассивНомеровСПрефиксами.Добавить(НомерНакладной);
12
КонецЦикла;
13

14
Запрос = Новый Запрос;
15
Запрос.Текст =
16
“ВЫБРАТЬ
17
| РасходнаяНакладная.Ссылка КАК Ссылка,
18
| РасходнаяНакладнаяЗапасы.Номенклатура КАК Номенклатура,
19
| РасходнаяНакладнаяЗапасы.Характеристика КАК Характеристика,
20
| РасходнаяНакладнаяЗапасы.Заказ КАК Заказ,
21
| МАКСИМУМ(РасходнаяНакладнаяЗапасы.НомерСтроки) КАК НомерСтроки,
22
| СУММА(РасходнаяНакладнаяЗапасы.Количество) КАК Количество
23
|ПОМЕСТИТЬ ВТРасходныеНакладные
24
|ИЗ
25
| Документ.РасходнаяНакладная КАК РасходнаяНакладная
26
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РасходнаяНакладная.Запасы КАК РасходнаяНакладнаяЗапасы
27
| ПО (РасходнаяНакладнаяЗапасы.Ссылка = РасходнаяНакладная.Ссылка)
28
|ГДЕ
29
| РасходнаяНакладная.Организация = &Организация
30
| И РасходнаяНакладная.Номер В(&МассивНомеров)
31
|СГРУППИРОВАТЬ ПО
32
| РасходнаяНакладная.Ссылка,
33
| РасходнаяНакладнаяЗапасы.Номенклатура,
34
| РасходнаяНакладнаяЗапасы.Характеристика,
35
| РасходнаяНакладнаяЗапасы.Заказ
36
|;
37
|////////////////////////////////////////////////////////////////////////////////
38
|ВЫБРАТЬ
39
| ПеремещениеЗапасовЗапасы.НомерСтроки КАК НомерСтроки,
40
| ПеремещениеЗапасовЗапасы.Номенклатура КАК Номенклатура,
41
| ПеремещениеЗапасовЗапасы.Характеристика КАК Характеристика,
42
| ПеремещениеЗапасовЗапасы.Количество КАК Количество,
43
| ПеремещениеЗапасовЗапасы.ЗаказПокупателя КАК ЗаказПокупателя
44
|ПОМЕСТИТЬ ВТДанныеТЧ
45
|ИЗ
46
| &тзЗапасы КАК ПеремещениеЗапасовЗапасы
47
|;
48
|////////////////////////////////////////////////////////////////////////////////
49
|ВЫБРАТЬ
50
| ВТДанныеТЧ.НомерСтроки КАК НомерСтроки,
51
| ВТДанныеТЧ.Номенклатура КАК Номенклатура,
52
| ВТДанныеТЧ.Характеристика КАК Характеристика,
53
| ВТДанныеТЧ.Количество КАК Количество,
54
| ВТДанныеТЧ.ЗаказПокупателя КАК ЗаказПокупателяТекущий,
55
| ВТДанныеТЧ.НомерСтроки КАК КоличествоДублей,
56
| ВТРасходныеНакладные.Заказ КАК ЗаказПокупателя,
57
| ВТРасходныеНакладные.НомерСтроки КАК НомерСтрокиНакладной,
58
| ВТРасходныеНакладные.Ссылка КАК РасходнаяНакладная
59
|ИЗ
60
| ВТДанныеТЧ КАК ВТДанныеТЧ
61
| ЛЕВОЕ СОЕДИНЕНИЕ ВТРасходныеНакладные КАК ВТРасходныеНакладные
62
| ПО ВТДанныеТЧ.Номенклатура = ВТРасходныеНакладные.Номенклатура
63
| И ВТДанныеТЧ.Характеристика = ВТРасходныеНакладные.Характеристика
64
| И ВТДанныеТЧ.Количество = ВТРасходныеНакладные.Количество
65
|ИТОГИ
66
| КОЛИЧЕСТВО(КоличествоДублей)
67
|ПО
68
| НомерСтроки”;
69
Запрос.УстановитьПараметр(“МассивНомеров”, МассивНомеровСПрефиксами);
70
Запрос.УстановитьПараметр(“Организация”, Объект.Организация);
71
Запрос.УстановитьПараметр(“тзЗапасы”, Объект.Запасы.Выгрузить());
72

73
РезультатЗапроса = Запрос.Выполнить();
74

75
ВыборкаНомерСтроки = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
76

77
Пока ВыборкаНомерСтроки.Следующий() Цикл
78
Выборка = ВыборкаНомерСтроки.Выбрать();
79
Пока Выборка.Следующий() Цикл
80
Если ВыборкаНомерСтроки.КоличествоДублей > 1 Тогда
81
ОбщегоНазначения.СообщитьПользователю(“Дублирование строк: строка №” + Выборка.НомерСтроки + ” расх.накл: “+Выборка.РасходнаяНакладная + ” строка №” + Выборка.НомерСтрокиНакладной);
82
Продолжить;
83
КонецЕсли;
84
Если НЕ ЗначениеЗаполнено(Выборка.ЗаказПокупателя) Тогда
85
ОбщегоНазначения.СообщитьПользователю(“Не найден заказ клиента: строка №” + Выборка.НомерСтроки);
86
Продолжить;
87
КонецЕсли;
88
Если Выборка.ЗаказПокупателя <> Выборка.ЗаказПокупателяТекущий Тогда
89
Объект.Запасы[Выборка.НомерСтроки – 1].ЗаказПокупателя = Выборка.ЗаказПокупателя;
90
КонецЕсли;
91
КонецЦикла;
92
КонецЦикла;
93

94
КонецПроцедуры

❌ Критические недостатки первого подхода

1. Хрупкое сопоставление по точному количеству

ПО ВТДанныеТЧ.Номенклатура = ВТРасходныеНакладные.Номенклатура
    И ВТДанныеТЧ.Характеристика = ВТРасходныеНакладные.Характеристика
    И ВТДанныеТЧ.Количество = ВТРасходныеНакладные.Количество

Проблема: Если количества не совпадают точно – строка игнорируется. В реальности часто встречаются:

  • Округления при расчетах
  • Разное распределение количеств по документам
  • Технические погрешности

2. Отсутствие обработки составных заказов

Сценарий: В одной строке перемещения – 100 шт товара, а в расходных накладных:

  • Накладная №1: 40 шт (Заказ №123)
  • Накладная №2: 60 шт (Заказ №456)

Результат: Первый код не найдет соответствия и оставит строку без заказа.

3. Потеря данных при несовпадениях

Если НЕ ЗначениеЗаполнено(Выборка.ЗаказПокупателя) Тогда
    ОбщегоНазначения.СообщитьПользователю(“Не найден заказ клиента…”)
    Продолжить;
КонецЕсли;

Проблема: Сообщение пользователю, но полезная информация о наличии заказов в других накладных теряется.

4. Игнорирование бизнес-логики резервирования

Не учтено: При проставлении заказа нужно также резервировать товар.

🔧 Второй вариант: промышленное решение

Ключевые улучшения в переработанном коде:

🚀 Улучшенная реализация
1
&НаСервере
2
Процедура ЗаполнитьЗаказыПоРасходнымНакладным_3с(МассивНомеровНакладных)
3

4
//добавим префиксы и нули
5
ДокОбъект = РеквизитФормыВЗначение(“Объект”);
6
Префикс = “”;
7
ПрефиксацияОбъектовСобытия.УстановитьПрефиксИнформационнойБазыИОрганизации(ДокОбъект , Истина, Префикс);
8
МассивНомеровСПрефиксами = Новый Массив;
9
Для каждого НомерНакладной Из МассивНомеровНакладных Цикл
10
НомерНакладной = Префикс + СтроковыеФункцииКлиентСервер.ДополнитьСтроку(НомерНакладной, 11 – СтрДлина(Префикс), “0”, “Слева”);
11
МассивНомеровСПрефиксами.Добавить(НомерНакладной);
12
КонецЦикла;
13

14
// Свернём ТЧ
15
ТЗЗапасы = Объект.Запасы.Выгрузить();
16
ТЗЗапасы.Свернуть(“Номенклатура, Характеристика, ЕдиницаИзмерения, ПрослеживаемыйТовар, ПрослеживаемыйКомплект, УчитыватьВНУ, СтавкаНДС”, “Количество, Сумма, Всего”);
17
Объект.Запасы.Загрузить(ТЗЗапасы);
18

19
Запрос = Новый Запрос;
20
Запрос.Текст =
21
“ВЫБРАТЬ
22
| 0 КАК НомерЗапроса,
23
| РасходнаяНакладная.Ссылка КАК Ссылка,
24
| РасходнаяНакладнаяЗапасы.Номенклатура КАК Номенклатура,
25
| РасходнаяНакладнаяЗапасы.Характеристика КАК Характеристика,
26
| РасходнаяНакладнаяЗапасы.Заказ КАК Заказ,
27
| МАКСИМУМ(РасходнаяНакладнаяЗапасы.НомерСтроки) КАК НомерСтроки,
28
| СУММА(РасходнаяНакладнаяЗапасы.Количество) КАК Количество
29
|ПОМЕСТИТЬ ВТРасходныеНакладные
30
|ИЗ
31
| Документ.РасходнаяНакладная КАК РасходнаяНакладная
32
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РасходнаяНакладная.Запасы КАК РасходнаяНакладнаяЗапасы
33
| ПО (РасходнаяНакладнаяЗапасы.Ссылка = РасходнаяНакладная.Ссылка)
34
|ГДЕ
35
| РасходнаяНакладная.Организация = &Организация
36
| И РасходнаяНакладная.Номер В(&МассивНомеров)
37
|СГРУППИРОВАТЬ ПО
38
| РасходнаяНакладная.Ссылка,
39
| РасходнаяНакладнаяЗапасы.Номенклатура,
40
| РасходнаяНакладнаяЗапасы.Характеристика,
41
| РасходнаяНакладнаяЗапасы.Заказ
42
|;
43
|////////////////////////////////////////////////////////////////////////////////
44
|ВЫБРАТЬ
45
| 1 КАК НомерЗапроса,
46
| ПеремещениеЗапасовЗапасы.НомерСтроки КАК НомерСтроки,
47
| ПеремещениеЗапасовЗапасы.Номенклатура КАК Номенклатура,
48
| ПеремещениеЗапасовЗапасы.Характеристика КАК Характеристика,
49
| ПеремещениеЗапасовЗапасы.Количество КАК Количество,
50
| ПеремещениеЗапасовЗапасы.ЗаказПокупателя КАК ЗаказПокупателя
51
|ПОМЕСТИТЬ ВТДанныеТЧ
52
|ИЗ
53
| &тзЗапасы КАК ПеремещениеЗапасовЗапасы
54
|;
55
|////////////////////////////////////////////////////////////////////////////////
56
|ВЫБРАТЬ
57
| 2 КАК НомерЗапроса,
58
| ВТДанныеТЧ.НомерСтроки КАК НомерСтроки,
59
| ВТДанныеТЧ.Номенклатура КАК Номенклатура,
60
| ВТДанныеТЧ.Характеристика КАК Характеристика,
61
| ВТДанныеТЧ.Количество КАК Количество,
62
| ВТДанныеТЧ.ЗаказПокупателя КАК ЗаказПокупателяТекущий,
63
| ВТДанныеТЧ.НомерСтроки КАК КоличествоДублей,
64
| ВТРасходныеНакладные.Заказ КАК ЗаказПокупателя,
65
| ВТРасходныеНакладные.НомерСтроки КАК НомерСтрокиНакладной,
66
| ВТРасходныеНакладные.Ссылка КАК РасходнаяНакладная
67
|ИЗ
68
| ВТДанныеТЧ КАК ВТДанныеТЧ
69
| ЛЕВОЕ СОЕДИНЕНИЕ ВТРасходныеНакладные КАК ВТРасходныеНакладные
70
| ПО ВТДанныеТЧ.Номенклатура = ВТРасходныеНакладные.Номенклатура
71
| И ВТДанныеТЧ.Характеристика = ВТРасходныеНакладные.Характеристика
72
| И ВТДанныеТЧ.Количество = ВТРасходныеНакладные.Количество
73
|ИТОГИ
74
| КОЛИЧЕСТВО(КоличествоДублей)
75
|ПО
76
| НомерСтроки
77
|;
78
|////////////////////////////////////////////////////////////////////////////////
79
|ВЫБРАТЬ
80
| 3 КАК НомерЗапроса,
81
| ВТРасходныеНакладные.Номенклатура КАК Номенклатура,
82
| ВТРасходныеНакладные.Характеристика КАК Характеристика,
83
| ВТРасходныеНакладные.Количество КАК Количество,
84
| ВТРасходныеНакладные.Заказ КАК ЗаказПокупателя,
85
| ВТРасходныеНакладные.Ссылка КАК РасходнаяНакладная
86
|ПОМЕСТИТЬ ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп
87
|ИЗ
88
| ВТРасходныеНакладные КАК ВТРасходныеНакладные
89
| ЛЕВОЕ СОЕДИНЕНИЕ ВТДанныеТЧ КАК ВТДанныеТЧ
90
| ПО (ВТДанныеТЧ.Номенклатура = ВТРасходныеНакладные.Номенклатура)
91
| И (ВТДанныеТЧ.Характеристика = ВТРасходныеНакладные.Характеристика)
92
| И (ВТДанныеТЧ.Количество = ВТРасходныеНакладные.Количество)
93
|ГДЕ
94
| ВТДанныеТЧ.Номенклатура ЕСТЬ NULL
95
|;
96
|////////////////////////////////////////////////////////////////////////////////
97
|ВЫБРАТЬ
98
| 4 КАК НомерЗапроса,
99
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.Номенклатура КАК Номенклатура,
100
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.Характеристика КАК Характеристика,
101
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.Количество КАК Количество,
102
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.ЗаказПокупателя КАК ЗаказПокупателя,
103
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.РасходнаяНакладная КАК РасходнаяНакладная
104
|ИЗ
105
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп КАК ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп
106
|;
107
|////////////////////////////////////////////////////////////////////////////////
108
|ВЫБРАТЬ
109
| 5 КАК НомерЗапроса,
110
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.Номенклатура КАК Номенклатура,
111
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.Характеристика КАК Характеристика,
112
| СУММА(ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.Количество) КАК Количество
113
|ИЗ
114
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп КАК ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп
115
|СГРУППИРОВАТЬ ПО
116
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.Номенклатура,
117
| ВТРасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.Характеристика”;
118
Запрос.УстановитьПараметр(“МассивНомеров”, МассивНомеровСПрефиксами);
119
Запрос.УстановитьПараметр(“Организация”, Объект.Организация);
120
Запрос.УстановитьПараметр(“тзЗапасы”, Объект.Запасы.Выгрузить());
121

122
РезультатЗапроса = Запрос.ВыполнитьПакет();
123

124
ВыборкаНомерСтроки = РезультатЗапроса[2].Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
125
РасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп = РезультатЗапроса[4].Выгрузить();
126
РасходныеНакладныеБезСовпаденийПоКоличествуСгрупп = РезультатЗапроса[5].Выгрузить();
127

128
Пока ВыборкаНомерСтроки.Следующий() Цикл
129
Выборка = ВыборкаНомерСтроки.Выбрать();
130
Пока Выборка.Следующий() Цикл
131
Если ВыборкаНомерСтроки.КоличествоДублей > 1 Тогда
132
ОбщегоНазначения.СообщитьПользователю(“Дублирование строк: строка №” + Выборка.НомерСтроки + ” расх.накл: “+Выборка.РасходнаяНакладная + ” строка №” + Выборка.НомерСтрокиНакладной);
133
Продолжить;
134
КонецЕсли;
135
Если НЕ ЗначениеЗаполнено(Выборка.ЗаказПокупателя) Тогда
136
// Ниже подобные случаи (по количеству не совпало, возможно есть разбивка по документам или заказам) решаются отдельно, пока ничего не сообщаем, просто пропустим строку
137
Продолжить;
138
КонецЕсли;
139
Если Выборка.ЗаказПокупателя <> Выборка.ЗаказПокупателяТекущий Тогда
140
Объект.Запасы[Выборка.НомерСтроки – 1].ЗаказПокупателя = Выборка.ЗаказПокупателя;
141
Объект.Запасы[Выборка.НомерСтроки – 1].Резерв = Объект.Запасы[Выборка.НомерСтроки – 1].Количество;
142
КонецЕсли;
143
КонецЦикла;
144
КонецЦикла;
145
СтрокиСПустымиЗаказами = Объект.Запасы.НайтиСтроки(Новый Структура(“ЗаказПокупателя”, Документы.ЗаказПокупателя.ПустаяСсылка()));
146
Для Каждого СтрокаСПустымЗаказом Из СтрокиСПустымиЗаказами Цикл
147
СтруктураОтбора = Новый Структура(“Номенклатура, Характеристика”);
148
ЗаполнитьЗначенияСвойств(СтруктураОтбора, СтрокаСПустымЗаказом);
149
НайденныеСтрокиВРасходныхНакладныхСгрупп = РасходныеНакладныеБезСовпаденийПоКоличествуСгрупп.НайтиСтроки(СтруктураОтбора);
150
НайденныеСтрокиВРасходныхНакладныхНеСгрупп = РасходныеНакладныеБезСовпаденийПоКоличествуНеСгрупп.НайтиСтроки(СтруктураОтбора);
151
Если Не НайденныеСтрокиВРасходныхНакладныхСгрупп.Количество() Тогда
152
ОбщегоНазначения.СообщитьПользователю(“Не найдено в расходных накладных по строке №” + СтрокаСПустымЗаказом.НомерСтроки);
153
ИначеЕсли НайденныеСтрокиВРасходныхНакладныхСгрупп[0].Количество <> СтрокаСПустымЗаказом.Количество Тогда
154
ОбщегоНазначения.СообщитьПользователю(СтрШаблон(“В строка № %1 количество: %2, в расходных накладных количество: %3”, Выборка.НомерСтроки, СтрокаСПустымЗаказом.Количество, НайденныеСтрокиВРасходныхНакладныхСгрупп[0].Количество));
155
Для Каждого НайденнаяСтрокаВРасходныхНакладныхНеСгрупп Из НайденныеСтрокиВРасходныхНакладныхНеСгрупп Цикл
156
ОбщегоНазначения.СообщитьПользователю(СтрШаблон(Символы.Таб + “В расходной накладной %1 по заказу %2 количество: %3”, НайденнаяСтрокаВРасходныхНакладныхНеСгрупп.РасходнаяНакладная, НайденнаяСтрокаВРасходныхНакладныхНеСгрупп.ЗаказПокупателя, НайденнаяСтрокаВРасходныхНакладныхНеСгрупп.Количество));
157
КонецЦикла;
158
Иначе
159
Для Каждого НайденнаяСтрокаВРасходныхНакладныхНеСгрупп Из НайденныеСтрокиВРасходныхНакладныхНеСгрупп Цикл
160
НСтрЗапасы = Объект.Запасы.Добавить();
161
ЗаполнитьЗначенияСвойств(НСтрЗапасы, СтрокаСПустымЗаказом);
162
ЗаполнитьЗначенияСвойств(НСтрЗапасы, НайденнаяСтрокаВРасходныхНакладныхНеСгрупп, “Количество, ЗаказПокупателя”);
163
НСтрЗапасы.Резерв = НСтрЗапасы.Количество;
164
НСтрЗапасы.Сумма = СтрокаСПустымЗаказом.Сумма * НайденнаяСтрокаВРасходныхНакладныхНеСгрупп.Количество / СтрокаСПустымЗаказом.Количество;
165
НСтрЗапасы.Всего = СтрокаСПустымЗаказом.Всего * НайденнаяСтрокаВРасходныхНакладныхНеСгрупп.Количество / СтрокаСПустымЗаказом.Количество;
166
КонецЦикла;
167
Объект.Запасы.Удалить(СтрокаСПустымЗаказом);
168
КонецЕсли;
169
КонецЦикла;
170

171
КонецПроцедуры

📊 Сравнительная таблица эффективности

Критерий Первый код Второй код
Обработка точных совпадений ✅ Базовая ✅ Сохранена
Обработка составных заказов ❌ Не работает ✅ Автоматическая
Диагностика проблем ❌ Примитивная ✅ Детальная
Перерасчет финансовых данных ❌ Отсутствует ✅ Пропорциональный
Управление резервами ❌ Нет ✅ Интегрировано
Гибкость обработки ❌ Жесткая ✅ Адаптивная

💡 Профессиональные выводы

📈 Что изменилось в подходе:

1. От реактивного к проактивному

Было: обработка только идеальных случаев

Стало: предвосхищение различных сценариев использования

2. От изолированного к интегрированному

Было: решение одной узкой задачи

Стало: учет смежных бизнес-процессов

3. От простого к умному

Было: прямое сопоставление

Стало: многоуровневый анализ с эвристиками

🎯 Ключевые принципы качественной доработки:

  • Анализ полного жизненного цикла данных – понимание, как данные будут использоваться после обработки
  • Учет edge-cases – предвидение нестандартных сценариев вместо реакции на ошибки
  • Интеграция со смежными процессами – автоматизация связанных операций
  • Понятная диагностика – детальные сообщения, помогающие быстро исправить проблемы
  • Масштабируемость – архитектура, позволяющая легко добавлять новую логику
💡 Профессиональная рекомендация:
“Хороший код не просто решает задачу – он создает основу для будущего развития системы. Всегда задавайтесь вопросами: Что произойдет, если данные будут неидеальными? Какие смежные процессы затрагивает мое решение? Как пользователь поймет, что пошло не так? Насколько легко будет добавить новую логику через полгода?”

Такой подход превращает простое техническое задание в robust-решение, которое действительно упрощает бизнес-процессы, а не создает новые проблемы.