[РЕШЕНО] Конфигурация с нуля. СОМ, синхронизация, разделение данных

Тема в разделе "Конфигурирование на платформе "1С:Предприятие 8"", создана пользователем roofless, 17 фев 2015.

  1. TopicStarter Overlay
    roofless
    Offline

    roofless Опытный в 1С

    Регистрация:
    27 июн 2014
    Сообщения:
    262
    Симпатии:
    7
    Баллы:
    29
    Всем привет.
    Не буду загружать вас фрагментами кода, а буду загружать ТЗ:

    Что имеем: 1С УТ 11.1.6.24 на 1С:Предприятие 8.3 (8.3.4.482), клиент-сервер, БД на MS SQL 2012. Всё развернуто на WinServer 2008 R2 Standart, который крутится на виртуалке. Доступ по RDP
    Нужно: создать конфигурацию, которая получает заказы из основной базы, раскидывает заказы по пользователям в зависисмости от склада.
    Рабочее место кладовщика имеет простой и понятный интерфейс. Кладовщик видит номер заказа, дата, срок выполнения (сортировка по этому столбцу), статус (просмотрен, не просмотрен, в работе, отложено, завершено, требуется согласование). Кладовщик видит только те товары, который должны быть отгружены с его склада. В подсистеме возможно изменение статуса заказа и изменение количества товара. Статус и количество отправляются в основную базу.

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

    Свою базу сделал файловой.
    Решил, что данные моя база у главной будет брать через COM-соединение. В том варианте, который имеется сейчас все документы "ЗаказКлиента" грузятся из источника, а если документ с таким номером уже существует в моей базе, тогда он тупо удаляется, а вместо него тупо создается новый.Подключение и загрузка реализовано регламентным заданием с интервалом 5 минут.

    Собственно, первая проблема: подключение к источнику, запрос документов за текущий год, удаление и создание новых занимает 60-70 секунд. даже, если я использую хэширование COM-соединения. На мой взгляд, это очень долго, если в итоге в мою тестовую базу попадает 7 документов.

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

    Третья, и главная, это разделение данных. Если документ открыт пользователем в приемнике для редактирования, а в это время регламентное задание загружает новую версию, то нужно как-то разрешать конфликт.

    Может я вообще не тем путём пошёл?

    Буду рад любым комментариям.
  2. nomad_irk
    Offline

    nomad_irk Гуру в 1С

    Регистрация:
    20 окт 2008
    Сообщения:
    9.685
    Симпатии:
    1.011
    Баллы:
    204
    60-70 секунд через COM - нормально, думаю, т.к. происходит по сути работа в удаленной базе, т.е. нужно дать доступ ко всем метаданным удаленной БД, а это - время.

    Делайте хранилище ссылок на измененные объекты и складируйте в него. Смысла в проверке изменения каждого реквизита мало, т.к. затраты на проверку будут несоизмеримы с "выхлопом" от такой проверки. Поэтому тупо регистрируйте объект, если он притерпел запись в БД и не важно, чего там поменяли в реквизитах.

    Вот вы и подошли к основной проблеме он-лайн синхронизации данных :) Не разрешишь никак, т.к. при открытии формы объекта пользователем объект либо блокируется к изменению и тогда обмен умрет по причине конфликта блокировок, либо обмен таки пройдет, но пользователь не увидит изменения сразу, внесет какие-то свои изменения и потом будет удивлятся тому, что в итоге в реквизитах измененного им объекта содержатся вообще какие левые по мнению пользователя данные.
  3. TopicStarter Overlay
    roofless
    Offline

    roofless Опытный в 1С

    Регистрация:
    27 июн 2014
    Сообщения:
    262
    Симпатии:
    7
    Баллы:
    29
    потестил куски кода на время, проблема не в СОМ, а вот в этом куске

    Код:
        //обрабатываем результат
        Пока ЗаказыСписок.Следующий() Цикл
         
            ДокументМодуля = ЗаказыСписок.Ссылка.ПолучитьОбъект();
         
            //если документ с таким номером уже загржуался, то удаляем его
            РезультатПоиска = Документы.Заказ.НайтиПоНомеру(ДокументМодуля.Number);
         
            Если НЕ ЗначениеЗаполнено(РезультатПоиска) Тогда
                          Иначе
                РезультатПоиска.ПолучитьОбъект().Удалить();
                         
            КонецЕсли;
         
            //создаем новый документ в модуле
            Док = Документы.Заказ.СоздатьДокумент();
            Док.Номер = ДокументМодуля.Number;
            Док.Дата = ДокументМодуля.Date;
            Док.ДатаОтгрузки = ДокументМодуля.ДатаОтгрузки;
            Док.Контрагент = ДокументМодуля.Контрагент.НаименованиеПолное;
            Док.Комментарий = ДокументМодуля.Комментарий;
         
            КоличествоПозиций = ДокументМодуля.Товары.Count()-1;
            //перебираем ТЧ документа
            Для НомерСтроки = 0 По КоличествоПозиций Цикл
             
                СписокПозиций = ДокументМодуля.Товары.Get(НомерСтроки);
                ДокСтрока = Док.Товары.Добавить();
                ДокСтрока.Номенклатура = СписокПозиций.Номенклатура.НаименованиеПолное;
                ДокСтрока.Количество = СписокПозиций.Количество;
             
                //пробуем достучаться до перечисления
                Попытка
                НомерПеречисления = СоединениеС1С.Перечисления.СкладыКапитан.Индекс(СписокПозиций.Номенклатура.СкладКапитан);     
                ИмяПеречисления = СоединениеС1С.Метаданные().Перечисления.СкладыКапитан.EnumValues.Get(НомерПеречисления).Name;
    
                Если ИмяПеречисления="ШтучныйТовар" Тогда
                    ДокСтрока.Склад = Перечисления.Склад.СкладСоленойПродукции
                ИначеЕсли ИмяПеречисления="ХК"
                    Тогда ДокСтрока.Склад = Перечисления.Склад.СкладКопченойПродукции
                ИначеЕсли ИмяПеречисления="СМ"
                    Тогда ДокСтрока.Склад = Перечисления.Склад.СкладСМПродукции
                КонецЕсли;
            Исключение
            КонецПопытки;
         
            КонецЦикла;
         
            Попытка
                //пишем документ
                Док.Записать(РежимЗаписиДокумента.Проведение);
            Исключение
             
                Сообщить("не гуд");
            КонецПопытки;
        КонецЦикла
    выполняется 95% от общего времени

    а виноваты обращения к перечислениям
    Снимок.JPG
    в базе-источнике?
    у меня второй случай. хоть бы придумать, как пользователю сообщать, а-ля microsoft alarm "на сервере появился новый документ, открыть его?"
    Последнее редактирование: 17 фев 2015
  4. nomad_irk
    Offline

    nomad_irk Гуру в 1С

    Регистрация:
    20 окт 2008
    Сообщения:
    9.685
    Симпатии:
    1.011
    Баллы:
    204
    Так понимаю, львиную долю от 95% времени занимает время выполнения
    РезультатПоиска = Документы.Заказ.НайтиПоНомеру(ДокументМодуля.Number);
    ?

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

    Оповещение пользователя можно делать с использованием метода ОбработкаОповещения для формы.
  5. TopicStarter Overlay
    roofless
    Offline

    roofless Опытный в 1С

    Регистрация:
    27 июн 2014
    Сообщения:
    262
    Симпатии:
    7
    Баллы:
    29
    неа, вот эти строки дольше всего:

    Код:
     НомерПеречисления = СоединениеС1С.Перечисления.СкладыКапитан.Индекс(СписокПозиций.Номенклатура.СкладКапитан);     
    ИмяПеречисления = СоединениеС1С.Метаданные().Перечисления.СкладыКапитан.EnumValues.Get(НомерПеречисления).Name;
    10 секунд на документ

    я так понял что не вариант работать с перечислениями по COM, если так сложно и долго получать наименование. буду вместо него делать справочник в источнике
  6. nomad_irk
    Offline

    nomad_irk Гуру в 1С

    Регистрация:
    20 окт 2008
    Сообщения:
    9.685
    Симпатии:
    1.011
    Баллы:
    204
    А у объекта
    СписокПозиций.Номенклатура.СкладКапитан вообще никаких реквизитов нет, если к нему напрямую обратится?
  7. TopicStarter Overlay
    roofless
    Offline

    roofless Опытный в 1С

    Регистрация:
    27 июн 2014
    Сообщения:
    262
    Симпатии:
    7
    Баллы:
    29
    нет, особенность работы с перечислениями по СОМ:(
  8. nomad_irk
    Offline

    nomad_irk Гуру в 1С

    Регистрация:
    20 окт 2008
    Сообщения:
    9.685
    Симпатии:
    1.011
    Баллы:
    204
    значит будет только так медленно работать.
  9. nickpugachev
    Offline

    nickpugachev Профессионал в 1С Команда форума

    Регистрация:
    28 май 2012
    Сообщения:
    3.397
    Симпатии:
    155
    Баллы:
    104
    насколько высока вероятность изменения состава и порядка значений перечисления? если перечисления подконтрольны вам (вы доработки производите), то можно один раз вычислить номера значений перечислений и использовать их - сократит время обработки в 2 раза примерно - будет выполняться одна строка кода вместо двух. Либо попробовать работать не с именем элемента перечисления, а с его представлением, просто приводя значение перечисления в удаленной базе к строке, возможно будет быстрее
  10. TopicStarter Overlay
    roofless
    Offline

    roofless Опытный в 1С

    Регистрация:
    27 июн 2014
    Сообщения:
    262
    Симпатии:
    7
    Баллы:
    29
    по поводу отслеживания изменений:

    1) сделал в источнике РС, в который буду писать признак модифицированности док-та "Заказ клиента"
    2) добавил подписку на событие ПриЗаписи() документа ЗаказКлиента
    3) при выполнении события пишу в РС ссылку на документ и число (=1)
    4) при загрузке документов из источника смотрю в регистр, если там есть ссылка на документ, то загружаю этот документ
    5) после загрузки всех документов очищаю РС в источнике

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

    можно ли программно заблокировать запись в РС, пока производится чтение-запись по СОМ?
    надеюсь, понятно изложил
  11. nomad_irk
    Offline

    nomad_irk Гуру в 1С

    Регистрация:
    20 окт 2008
    Сообщения:
    9.685
    Симпатии:
    1.011
    Баллы:
    204
    можно, это называется управляемая блокировка данных.
  12. TopicStarter Overlay
    roofless
    Offline

    roofless Опытный в 1С

    Регистрация:
    27 июн 2014
    Сообщения:
    262
    Симпатии:
    7
    Баллы:
    29
    спасибо, но можно ли её вызвать через СОМ?

    фрагмент такой:

    Код:
    ЗапросОчистка = СоединениеС1С.NewObject("Запрос");
        ЗапросОчистка.Текст = "ВЫБРАТЬ
        |*
        |Из
        |РегистрСведений.ЗаказМодифицированность КАК ЗаказМодифицированность";
       
    НачатьТранзакцию(РежимУправленияБлокировкойДанных.Управляемый);
    
        Выборка = ЗапросОчистка.Выполнить().Выбрать();
       
        Если Выборка.Количество() > 0 Тогда
           
            Пока Выборка.Следующий() Цикл
                Попытка
                    РегистрОчистка = СоединениеС1С.РегистрыСведений.ЗаказМодифицированность.CreateRecordSet();
                    РегистрОчистка.Прочитать();
                    РегистрОчистка.Очистить();
                    РегистрОчистка.Записать();
                Исключение
                    ОтменитьТранзакцию();
                КонецПопытки;
            КонецЦикла;   
        Иначе
           
        КонецЕсли;
  13. nomad_irk
    Offline

    nomad_irk Гуру в 1С

    Регистрация:
    20 окт 2008
    Сообщения:
    9.685
    Симпатии:
    1.011
    Баллы:
    204
    То что началась транзакция - это все хорошо, но еще надо бы заблокировать данные в таблице РС, чтобы они были не изменяемые на время выполнения транзакции.

    Это надо курить БлокировкуДанных.

    То что вы делаете - это какой-то ужас. Зачем запросом читать что-то из РС, потом бежать по каждой строчке выборки из запроса и делать все то, что делается?

    Проще и быстрее сделать:
    Код:
    Если Выборка.Следующий() Тогда
    РегистрОчистка = СоединениеС1С.РегистрыСведений.ЗаказМодифицированность.CreateRecordSet();
    РегистрОчистка.Записать();
    КонецЕсли;
    И транзакцию вы завершаете как-то странно: начало транзакции за пределами цикла, а отменяется транзакция на каждой итерации цикла.
  14. TopicStarter Overlay
    roofless
    Offline

    roofless Опытный в 1С

    Регистрация:
    27 июн 2014
    Сообщения:
    262
    Симпатии:
    7
    Баллы:
    29
    спасибо, сильно не ругайтесь, я еще только учусь
    что нагуглил, то и вставил( т.к. оно отработало успешно, больше не задумывался

    насчет транзакций тоже понял

    но вот с блокировкой никак(
    Код:
        Если Выборка.Количество() > 0 Тогда
           
            НачатьТранзакцию(РежимУправленияБлокировкойДанных.Управляемый);
            текБлокировка = СоединениеС1С.NewObject("БлокировкаДанных");
            тбдЖурнал = текБлокировка.Add("РегистрСведений.ЗаказМодифицированность");
            тбдЖурнал.Mode = СоединениеС1С.РежимБлокировкиДанных.Исключительный;
            текБлокировка.Заблокировать();
           
            Пока Выборка.Следующий() Цикл
                Попытка
                    РегистрОчистка = СоединениеС1С.РегистрыСведений.ЗаказМодифицированность.CreateRecordSet();
                    РегистрОчистка.Записать();
                Исключение
                КонецПопытки;
            КонецЦикла;
           
            ЗафиксироватьТранзакцию();
        Иначе
           
        КонецЕсли;
    {ОбщийМодуль.РегламентноеЗадание.Модуль(59)}: Ошибка при вызове метода контекста (Заблокировать)
    текБлокировка.Заблокировать();
    по причине:
    Произошла исключительная ситуация (1C:Enterprise 8.3.4.482): Использование блокировки допустимо только внутри транзакции в режиме управляемых блокировок!
    Последнее редактирование: 24 фев 2015
  15. TopicStarter Overlay
    roofless
    Offline

    roofless Опытный в 1С

    Регистрация:
    27 июн 2014
    Сообщения:
    262
    Симпатии:
    7
    Баллы:
    29
    получилось, рабочий вариант выглядит так:
    Код:
        Если Выборка.Count() > 0 Тогда
           
            СоединениеС1С.НачатьТранзакцию();
            текБлокировка = СоединениеС1С.NewObject("БлокировкаДанных");
            тбдЖурнал = текБлокировка.Add("РегистрСведений.ЗаказМодифицированность");
            тбдЖурнал.Mode = СоединениеС1С.РежимБлокировкиДанных.Исключительный;
            текБлокировка.Заблокировать();
           
            Пока Выборка.Следующий() Цикл
                Попытка
                    РегистрОчистка = СоединениеС1С.РегистрыСведений.ЗаказМодифицированность.CreateRecordSet();
                    РегистрОчистка.Записать();
                Исключение
                КонецПопытки;
            КонецЦикла;
           
            СоединениеС1С.ЗафиксироватьТранзакцию();
        Иначе
           
        КонецЕсли;
    
  16. nomad_irk
    Offline

    nomad_irk Гуру в 1С

    Регистрация:
    20 окт 2008
    Сообщения:
    9.685
    Симпатии:
    1.011
    Баллы:
    204
    Цикла не нужно делать, это вызовет многократные транзакции записи в РС.
    --- Объединение сообщений, 24 фев 2015 ---
    Значит для конфигурации/РС используются неуправляемые блокировки.
  17. nickpugachev
    Offline

    nickpugachev Профессионал в 1С Команда форума

    Регистрация:
    28 май 2012
    Сообщения:
    3.397
    Симпатии:
    155
    Баллы:
    104
    для этого есть планы обмена, они делают то же самое, но лучше
    перед загрузкой устанавливаете изменениям очередной номер сообщения обмена и после этого работаете с теми документами, у которых он меньше или равен текущему, после чего удаляете из плана обмена изменения с номерами сообщения меньше либо равными текущему. в этом случае изменения, сделанные во время обмена, останутся нетронутыми
  18. TopicStarter Overlay
    roofless
    Offline

    roofless Опытный в 1С

    Регистрация:
    27 июн 2014
    Сообщения:
    262
    Симпатии:
    7
    Баллы:
    29
    спасибо, рассмотрю и этот вариант