v8.2 Управляемые формы: Динамические элементы формы и переопределяемые события или как изменить поведение и внешний вид управляемой формы программно без лишних хлопот




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2026-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

57 Comments

  1. MarSeN

    Хочу уточнить, то в примере используются переопределение и установка событий &НаКлиенте, но механизм расчитан на все события формы и элементов. т.е. пожно переопределять и устанавливать события как &НаКлиенте так и &НаСервере

    Reply ↓
  2. TitanLuchs

    В реальной жизни уже пробовали тестировать? Как-нибудь отражается на быстродействии, тормозов нет?

    Reply ↓
  3. MarSeN

    (2) Все написанное в обоих статьях с успехом применяется в разрабатываемой и уже внедренной конфигурации УТ11.

    Тормозов нет

    Reply ↓
  4. Evil Beaver

    А в 8.3 обещают подписку на события форм. Тогда даже модуль формы менять не придется.

    Reply ↓
  5. MarSeN

    (4) скорее бы, но пока мы жевем реальностью и приходится как-то выкручиваться ))

    Reply ↓
  6. wunderland

    В стандартных тоже иногда применяется динамическое формирование формы, например в УНФ форма элемента для спр. ФизЛица. Но здесь намного красивее. Мне понравилось, возьму на вооружение…

    Reply ↓
  7. ivs200999

    (4) Обещают? Это было бы прекрасно, если было бы правдой.

    (1) С какой целью создание реквизитов, элементов и назначение обработчиков реализовано тремя вызовами процедур общего модуля? Для наглядности? Не лучше ли сделать один вызов?

    Reply ↓
  8. MarSeN

    (7) для того чтобы не перебирать макет если там нет настроек для этой формы. И конечно так нагляднее.

    Reply ↓
  9. MarSeN

    (9) Эх…

    Цитирую:

    «Данная статья и прикрепленная конфигурация направлена на то чтобы продемострировать возможности возможности платформы 8.2».

    Никто не говорит о том что это ноу-хау. Есть гораздо продуманные системы по «программному добавлению элементов». И подходов множество. Речь совсем не об этом. А о точ то есть такие возможности и, как вариант, этот механизм.

    НО — я еще не встречал механизма который бы давал возможность модифицировать формы (элементы/реквизиты) + их поведение (переопределение событий) в одном флаконе.

    Если бы подобный механизм/функционал был хоть бы в одной конфе — статьи бы тут не было.

    Попробуйте в пару строк распространить механизм документа Установка цен на всю конфу. Для внедрения моего механихма надо перенести 1 общий модуль и одну обработку.

    Reply ↓
  10. MarSeN

    (11) Я только за чтоб в комментах обсуждалось и приводились примеры других реализаций. Но, желательно, не в таком тоне.

    Спасибо за понимание.

    Reply ↓
  11. internetname

    Управляемые формы рулят. Кстати, когда там рабочая версия 8.3 появится?

    Reply ↓
  12. MarSeN

    (13) сам жду с нетерпением )

    Reply ↓
  13. ArtfulCrom

    (13) ) хотел бы я взглянуть в глаза человеку ЗНАЮЩЕМУ ответ на этот вопрос )

    Статья хорошая. Спасибо. Побольше бы таких простых и безхистростных «учебников» для отрастающего поколения

    Reply ↓
  14. marochkin

    Спасибо за метод, давно не видел столь грамотного способа. Сам делал нечто подобное (переопределение действий с сохранением вызова типового) для элементов форм. Чувствуется опыт программирования не только в 1С 😉

    Скорее всего сталкивались со следующим вопросом, очень интересует как решали!?

    БСП входит в состав УТ 11. В своих объектах тоже очень хочется пользоваться плюшками БСП. Самые простые примеры: — структура подчиненности, движения документов, виды объектов доступа и права.

    При обновлении БСП, в классическом варианте нужно снимать с поддержки некоторые объекты и снимать галки при обновлении. Как поступить в случае когда в УТ добавляются новые документы и удаляются старые. Вариантов кроме как делать для своих объектов каждый раз руками некий перечень действий, не нашел.

    Очень интересен комментарий автора статьи на этот счет!

    Reply ↓
  15. MarSeN

    (16) Спасибо за высокую оценку.

    Я сам пока не заморачивался по «таким» вопросам, так как методику разработал и начал применять относительно недавно. Но на вскидку думаю но структуре подчиненности и движениям документа или другим командам можно просто существующую комманду скопировать в «свою» некую обработку и на нее назначить нужные объекты. при этом больше править ничего не нужно будет. А вот в с составом объектов метаданных, к сожалениию, решений нет. (

    По ролям я делаю на все объекты новые роли. Если нужно убрать на сущ. роли часть прав то копирую нужную — ее правлю и потом в профиле перевыбираю свою вместо преджней.

    Можно устроить мозговой штурм при желании )

    Reply ↓
  16. marochkin

    (17) Да тоже думал над копированием команд, но в некоторых случаях возможно придется копировать объекты целиком, поэтому это пока не сложилось в вариант решения вопроса ))

    Насчет прав я не про роли. А про возможность использовать RLS шаблоны самой БСП. Там нужно в нескольких объектах изменять типы, примеры: ПВХ.Виды объектов доступа, некоторые подписки на события, типы измерений некоторых регистров.

    Reply ↓
  17. MarSeN

    (18) К сожалению приходится нсимать с поддержки и править ручками то что нельзя «отнаследовать». В каждом случае приходится решать проблемку конкретно. Иногда, если есть такая возможность правильнее упростить задачу или решить ее бругим путем. Но 1С в некоторых случаях не оставляет нам другого пути, как решать задачу «В лоб» (

    Reply ↓
  18. ivanov660

    Интересный подход. Хотел бы спросить по поводу ограничения доступа на уровне реквизитов, проблем с этим не было? Или задачу динамического размещения необходимо создавать в привилегированном режиме, или данный функционал не зависит от этой особенности?

    Reply ↓
  19. MarSeN

    (20) ivanov660

    Все «динамические» модификации делаются в привелегированном режиме. С правами реквизитов объекта «только на просмотр» все работает корректно. Если отсутствуют права на чтение то будет ошибка. Я делал этот механизм на УТ11 и у в моих задачах не было необходимости запрещать чтение реквизитов.

    но не проблема проверить перед добавлением элемента на форму права пользователя на чтение динамически добавляемых реквизитов.

    Reply ↓
  20. Kaval88

    Интересный подход. Хорошо применялась для моих прикладных задач.

    Reply ↓
  21. MarSeN

    (22) Kaval88

    По моему мнению, на данный момент, это наиболее «полный» вариант из имеющихся на инфостарте включающий подписки, элементы формы и реквизиты.

    Задумываюсь над тем чтобы перевести настройку в режим предприятия посредством обработки — визуально чтоб можно было все делать.

    Reply ↓
  22. MarSeN

    (22) я рад, что данная разработка будет применена не только в моих решениях )

    Reply ↓
  23. irishka77

    Спасибо, ко времени.Начала работать с бух3.0.

    Reply ↓
  24. MarSeN

    (25) irishka77,

    всегда пожалуйста )

    Reply ↓
  25. HIgor

    События &НаСервере пробовали переопределять? У меня ошибки посыпались (хотя мог и я недоглядеть что-нибудь, вместо макета справочник дополнительный завел). &НаКлиенте — все отлично переопределяется.

    Reply ↓
  26. MarSeN

    (27) HIgor,

    Да, должно быть в примере. на документе Заказ. Кажись итоги там подчитываются

    Reply ↓
  27. pbazeliuk

    На версии УТ 11.1.2.8 и выше — не сработает переопределение событий, например в документе реализация товаров услуг.

    Reply ↓
  28. Evgen.Ponomarenko

    Годится, откладываю статью в «закладки»! Вернусь после командировки — разберу по винтикам 🙂

    Reply ↓
  29. MarSeN

    (29) pbazelyuk,

    А на версии ниже работает??

    Думаю, что проблема не в версиях.

    Код в студию )) или в личку.

    Reply ↓
  30. mister_tula

    Интересное решение, но есть большое сомнение в эффективности его применения к изменению типовых конфигураций. Очень большой объем кода приходится вставлять в модули форм, может проще и быстрее вставить «визуальные» элементы и описать на месте все обработчики? Форму-то всё равно придётся скопировать, а потом исправлять! Или Вы меняете существующую форму?

    На первый взгляд вижу только некую «классификацию-напоминалку» внесенных изменений в макете «ДинамическиеЭлементыФорм» и «ДинамическиеРеквизитыФорм»?!! Какое мнение автора? Хотелось бы обсудить!

    Reply ↓
  31. MarSeN

    (32) mister_tula,

    Меняется модуль формы и реквизиты. Вставляются только шаблоны обработчиков и строчка вызова общего модуля.

    Все что в статье описано как пример и работает на демо конфе — в статье отражено ввиде принтскринов. Думаю это не большие изменения.

    или я не понял Вашего вопроса?

    готов обсудить.

    я не совсем понял о чем Вы «Какое мнение автора?»

    Reply ↓
  32. mister_tula

    Возможно Вы меня не совсем поняли. Шаблоны обработчиков необходимо вставлять в форму, сами обработчики в общий модуль. Я пишу о том, что текста то-полно!! Может всё это сразу вставить в форму и будет проще! Зачем такие сложности? Я не вижу достоинств Вашего решения перед более простым. Возможно я ошибаюсь. Я пытаюсь практически применить Ваши решения и пока не заметил преимуществ. Поэтому и хотел с обсудить. Если , конечно, у Вас есть время.

    Reply ↓
  33. MarSeN

    (34) mister_tula,

    Да, давайте обсудим.

    Макеты можно добавлять в конкретные объекты — тогда нудно переписать общий модуль подключений чтоб смотрел модули объектов.

    конечно же все тексты обработчков надо хранить в общих модулях.

    Давайте я помогу разглядеть их — достоинства, но если только Вы их хотите увидеть. В противном случае лучше поступать так:

    Может всё это сразу вставить в форму и будет проще!

    какие у Вас лично проблемы возникают с применением данного метода?

    Reply ↓
  34. mister_tula

    Проблем с «простым» изменением форм — никаких. Я копирую исходную форму, называю её myФормаЭлемента, назначаю основной и вношу все изменения непосредственно в модуль формы.

    конечно же все тексты обработчков надо хранить в общих модулях

    . В данном случае зачем мне это делать?

    Если я воспользуюсь Вашей методикой:

    шаблоны обработчиков вставляю в мою форму myФормаЭлемента, затем в общем модуле их описываю. Дописываю метод ПриСозданииНаСервере. Если необходимо добавить доп.реквизиты — добавляю в любом случае. Описываю все изменения в шаблоне ДинамическиеЭлементыФорм. Мне кажется манипуляций даже больше. Объективно, в чем преимущества? Пока я вижу только два:

    1) текстовое описание добавляемых реквизитов в шаблоне ДинамическиеЭлементыФорм и ДинамическиеРеквизитыФорм, помогает разобраться в омуте внесенных изменений, СОГЛАСЕН.

    2) если вдруг типовая форма изменилась, мне придется ее скопировать вновь и добавить блок по Вашей методике, по моей методике путь немного длиннее — вставляю реквизиты «руками» и привязываю обработчики событий. Не вижу разницы.

    Возможно несколько примитивно я описал. Но всё-таки есть другие достоинства?

    Reply ↓
  35. mister_tula

    Если не сложно, напишите, как действуете Вы для изменения типовой формы, чтобы потом было легче обновляться. Может я не тем путем хожу (:

    Reply ↓
  36. MarSeN

    (37) mister_tula,

    В статье вроде все подробно описано, ну да ладно

    1. снимаю форму с поддержки

    2. добавляю нужные реквизиты

    3. в макете для элементов прописываю куда и какой реквизит поместить

    4. в при создании на сервере прописываю вызов метода для динамической вставки элементов

    ———————-

    обычные реквизиты без обработчиков готовы.

    если нужны обработчики как на новые так и на существующие элементы.

    1. Прописываю обработчик в макете. если большой текст обработчика — перенаправление в макете в общий модуль

    2. Снимаю, если еще не снята, форму с поддержки

    3. вставляю подключаемые команды (тексты которые я привел с статье и есть в демке) в модуль формы

    4 . подключаю при создании на сервере вызов метода подключения обработчика.

    с реквизитами форм все аналогично

    что это дает?

    При обновлении я вижу что форма поменялась и мне не надо смотреть что там поменялось. Я просто в существующую форму добавляю свои вставки и все. если таковых много — пользуюсь KDiff3

    и никаких заново выкладываний на форму, проверок старого и нового функционала в случае если вы делаете свою форму основной и тп.

    Reply ↓
  37. mister_tula

    СПасибо огромное за разъяснение, я был не прав, использую копирование форм. Ваш метод ГОРАЗДО привлекательнее.

    А KDiff3 по-моему это какая-то прога для сравнения файлов? А что она может интегрироваться как-то с 1С?

    Reply ↓
  38. MarSeN

    (39) mister_tula,

    KDiff3 — программа которая намного удобнее чем то что предлагает нам 1С

    В ней есть возможность сравнивать и объединять до 3-х файлов одновременно. Широко применял для 7.7 в связке с GComp

    Нет. она не интегрирована. Нужно выгрузить модули из 1С ч/з выгрузить модули (есть до версии 8.3) или просто ручками создать 2 текстовых файла старого и нового модуля. Объединить и ручками вернуть обратно.

    если выгружать из 1С то потом загрузить обратно. Все просто.

    Спасибо

    Reply ↓
  39. mister_tula

    Супер, буду пробовать.

    Reply ↓
  40. MarSeN

    (41) mister_tula,

    поделитесь результатами экспериментов )

    Reply ↓
  41. zqzq

    Глянул демо-конфу, достаточно красиво и главное просто реализовано (в отличии от соседней темы где внешние обработки и черт ногу сломит). Единственное, повторяющийся код типа

     МассивИнструкций = ДинамическиеЭлементыКлиентСервер.ПолучитьИнструкцииПереопределаемыхОбработчиков(ЭтаФорма, «ПослеЗаписи»);
    
    Для Каждого Инструкция из МассивИнструкций Цикл
    Выполнить(Инструкция);
    КонецЦикла; 

    можно было бы заменить на одну строчку типа

    Выполнить(ДинамическиеЭлементыКлиентСервер.ПолучитьВсеИнструкцииПереопределаемыхОбработчиков(ЭтаФорма, «ПослеЗаписи»)); 
    Reply ↓
  42. MarSeN

    (43) zqzq,

    Спасибо

    Нет, Ваш код не подходит. Цикл сделан специально, так как подсистема способна нетолько добавлять и заменять сущеструюшие обработчики, но и выполнять новый обработчик до или после существующего.

    Reply ↓
  43. zqzq

    (44) ну так загнать массив инструкций в многострочную строку в общем модуле и её уже выполнять на форме.

    Reply ↓
  44. MarSeN

    (45) zqzq,

    ) Согласен, ступил. Спасибо.

    Reply ↓
  45. knasa

    Здравствуйте MarSeN.

    Подскажите пожалуйста, а можно ли на основе Вашей разработки полностью создать таблицуформы и определить в ней колонки. Наподобии как если бы я сделал это в ручную на управляемой форме?

    Reply ↓
  46. MarSeN

    (47) Да, можно.

    это есть в примере

    Reply ↓
  47. It-developer

    Не знаю, не знаю. Пробовал динамически добавлять элементы, но в обычные формы. Довольно геморройное дело. Слишком много кода.

    Наверно, для каких-то целей такая реализация может подойти. Например, когда у тебя пару сотен узлов, которые ты обновлять не хочешь.

    Но опять же, писать кучу всяких обработчиков в самой форме нужно (не считая события ПриСозданииНаСервере) 🙁

    И потом … в макетах куча описалова по обработчикам, элементам, реквизитам. Приколол макет с реквизитами — куча свойств формы в макете 🙂 — в конце проекта в этом макете будут все свойства реквизитов форм :))

    Не знаю как автору, но мне было бы неудобно больше всего не видеть визуально изменяемую форму.

    Я поставил плюс, т. к. сама идея мне лично интересна

    Reply ↓
  48. MarSeN

    (49)

    Я объединение делал через KDiff 3 при этом использовал 3 версии текстов (cf) — конфа поставщика, новая версия, и текущая версия.

    при этом кадиф автоматом предлагает, если нет изменений между поставщиком и новой от поставщика, вставить код от текущей версии

    Reply ↓
  49. mikhailv

    (4) Evil Beaver, обещанного 3 года ждут. Исходя из даты сообщения, 01.02.2016 должно случиться чудо:)

    Reply ↓
  50. Cyberhawk

    1. Я правильно понимаю, что переопределить обработчик ПриСозданииНаСервере не получится?

    2. Также желательно описать (и добавить в демо-конфигурацию) переопределение обработчика, получающего на вход больше одного параметра

    Reply ↓
  51. MarSeN

    (52)

    1. Можно переопределить обработчик ПриСозданииНаСервере, но я не заморачивался тогда по причине отсутствия надобности.

    2. Принцип всегда один, но количество параметров зависит от конкретного обработчика. Тут особо описывать нечего. Я всегда брал типовой и на основе него делал переопределеное.

    Reply ↓
  52. check2

    Сергей, просто супер!

    А почему все элементы в один макет? не лучше ли было бы лепить макеты к самому объекту: три макета. Если очень много дорабатываемых объектов, то чуть-чуть выиграем на сканировании табличного документа и… самое главное… я в задании на разработку описываю табличку что во что должно вставиться… Мы просто копируем табличку из задания на разработку и разработчику нужно вставить несколько обработчиков в форму… Щастье… Беру демку, переделаю под себя… Идея просто блестящая.

    Спасибо!

    UPD: Ещё бы и код обработчиков генерился… из обработки… алмаз…

    Reply ↓
  53. check2

    Ещё вопрос, если поле имя оставить пустым — синоним в заголовок из реквизита подтянется? Или пусто будет?

    Reply ↓
  54. MarSeN

    (54)

    Да, можно было и так сделать, но я ориентировался на минимум изменений в типовых объектах

    Кот обработчиков, если я правильно понял что имеется ввиду, не может быть сгенерирован. Его можно попробовать в обработке разместить, но не весь, только для серверных вызовов. Для клиентских только в общем модуле.

    Прошу прощения если не точно ответил. Писал давно и уже многое не помню

    Reply ↓
  55. MarSeN

    (55)

    Это я не помню. Если такое возможно (платформой и по коду), то я должен был прописать.

    Reply ↓
  56. MarSeN

    Не думал, что данная разработка еще актуальна )

    Reply ↓
  57. check2

    (58) Ещё как актуальна, мы её допилили, добавили работу с существующими элементами — перемещение, скрытие, удаление. Добавили работу с группами, кнопками. Сделали дополнительно обработку — генератор форм. Т.е. в рунтайме правим табличку — получаем форму с новыми вставленными элементами. Очень помогает в процессе написания проектных решений чтобы не просто описывать будущую форму, а заскринить, выделить новые элементы. Затем таблички сохраняем к заданию на разработку.- кодеру достаточно только заменить пути в элементах форм на реквизиты объекта. Как результат — снижение трудоёмкости работ при проектировании.

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *