Автоматические и управляемые блокировки применительно к типовым конфигурациям 1С




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2024-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='\

39 Comments

  1. ZLENKO

    Какие полезные на практике выводы можно сделать на основе этой информации?

    В статье нет рецептов применения этих знаний, а сами по себе они интересны но ценности не представляют.

    Reply
  2. ids79

    Владимир, Вы правы, примеров практичскго применения в статье нет.

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

    Насколько это получилось, судить Вам :))

    Reply
  3. spectre1978

    Действительно, довольно неплохо собрано в кучку. А вот ИС сегодня глючит. Захотел поставить звездочку — не ставится. Тыкал-тыкал, и тут оно раз — и поставило сразу 8 звезд :)) Я было решил, что вам повезло, но после обновления страницы из восьми все же осталась только одна.

    Reply
  4. triviumfan
    может возникнуть deadlovk

    описка

    Reply
  5. ids79

    (4)Спасибо, исправил

    Reply
  6. ids79

    (5)Добрый вечер.

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

    Запрос, не возвращающий записей всегда будет выполняться быстрее.

    «Ну так потом придется заблокировать. А если не блокировать, то риск точно такой же, как и в старой схеме»

    Блокировать специально как раз не нужно, это делает сама СУБД при изменении данных в таблице итогов регистра накопления в момент записи движений. Нужно только отключить разделение итогов, если разделение включено для регистра, для этого и флаг: ЗаблокироватьДляИзменения.

    Reply
  7. bulpi

    (7)

    Запрос, не возвращающий записей всегда будет выполняться быстрее.

    ???? Да ну ? А Вы проверяли ? ИМХО, скорость выполнения запроса зависит от целой кучи факторов, среди которых кол-во возвращаемых записей на последнем месте.

    Блокировать специально как раз не нужно, это делает сама СУБД

    Какая разница, я или СУБД ? Я не понимаю, почему от перестановки слагаемых должна измениться сумма.

    Reply
  8. bulpi

    (7)

    ИМХО,

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

    В новой схеме движения не удаляются перед проведением, а сразу записываются новые. Потом проверяются в запросе результаты. Если все плохо, транзакция откатывается. Если все хорошо ( в большинстве случаев), то , получается 2 действия вместо 3, причем убрано 1 действие записи, т.е. очень затратное.

    Reply
  9. CSiER

    (9) про преимущества хорошо написано здесь (блок «Преимущества контроля остатков по новой методике»).

    Reply
  10. ids79

    (9) Добрый день

    Да Вы правы, упоустил этот момент. Наверно он действительно является самым значимым.

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

    Reply
  11. T_Guest

    А можете пояснить про режим разделения итогов и новой схемой контроля остатков.

    Я правильно понял: разделение итогов будет нормально работать только если система 1С 8.3 + MSSQL?

    Ведь если стоит Postgre, то разделение итогов бессмысленно поскольку в новом механизме контроля придется писать «НаборЗаписей. БлокироватьДляИзменения = Истина;» а это отключит режим разделения на момент записи.

    Reply
  12. bulpi

    (11)

    Попробуйте такую простую вещь. Создайте таблицу с миллионом записей из чисел от 1 до 1000000. Запустите 2 запроса, один из который выбирает по условию значение=0 (пустой результат), а второй по условию значение=1 (непустой результат).

    И «без необходимости соединения» — это о чем ? Запросы в первой схеме и во второй одинаковые.

    Reply
  13. bulpi

    (10)

    Хорошо то хорошо, но есть нюансы 🙂

    Написано так :

    Запрос, получающий данные по отрицательным остаткам, обращается только к одной таблице – нет необходимости делать левое соединение с данными документа и применять функцию «ЕСТЬNULL()»

    Ну да . Сначала так написали модуль, а потом сами же его критикуют. Никто не мешает написать в старой схеме абсолютно те же 2 запроса, что и в новой (это вместо одного! А может, все таки 1 запрос лучше?)

    Так что в чистом остатке остается 1 несомненное преимущество, о котором я написал.

    Reply
  14. ids79

    (13)

    Запросы в первой схеме и во второй одинаковые.

    В новой схеме не нужно соединять запрос к регистру с запросом к ТЧ документа. В старой нужно.

    Reply
  15. kolya_tlt

    Дмитрий Гончаров бы вас поправил и сказал, что новая методика — это не только запись и контроль отрицательного результата.

    Reply
  16. ids79

    (14)

    Так что в чистом остатке остается 1 несомненное преимущество, о котором я написал.

    Не могу не согласиться. Внесу исправление в статью

    Reply
  17. kolya_tlt

    (12) СУБД тут ни при чем, как в случае с разделением итогов, так и схеме контроля остатков

    Reply
  18. ids79

    (12)

    Добрый день.

    Я не совсем Вас понял.

    В версии 1С 8.3 работа с MSSQL поддерживает версионирование, аналогично PostgreSQL.

    Так что для 8.3 разницы не будет.

    (12)

    разделение итогов бессмысленно поскольку в новом механизме контроля придется писать «НаборЗаписей. БлокироватьДляИзменения = Истина;

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

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

    Reply
  19. ids79

    (16)Добрый день.

    Конечно не только.

    Здесь я все упрощаю, чтобы описать в рамках одной статьи

    Reply
  20. CSiER

    (14) думаю, что один запрос лучше как в плане быстродействия, так и в плане простоты кода.

    Reply
  21. Vovan1975

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

    Reply
  22. ids79

    Добрый день, Владимир.

    Если я правильно Вас понял, Вы, по сути, предлагаете старый вариант контроля…

    Но в этом случае придеться удалять существующие записи до получения остатков, чтобы они не попали в выборку, например при изменении времени проведения документа.

    Получаем основной минус старого режима контроля.

    Reply
  23. Vovan1975

    (23) неправильно.

    Алгоритм такой

    1)Получаете остатки на момент времени до (новых) движений документа.

    2)Получаете старые движения документа

    3) Если старые движение «входят» в остатки (ну например пользователь изменил дату документа) — исключаете их из остатков

    4) Добавляете к остаткам новые движения

    5)Проверяете результат.

    В итоге 2 запроса на чтение к бд.

    Reply
  24. acanta

    (24) в 8ке нет момента времени до новых движений документа!!

    Reply
  25. Vovan1975

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

    Reply
  26. HomeInc

    (24) Операция чтения остатков используя границу (до проведения документа) намного затратнее, чем получение самых последних остатков

    Reply
  27. Vovan1975

    (27) а при чем тут получение самых последних остатков? В так называемой новой методике один фиг получение остатков через границу документа(уже после записи движений), то есть получается и там и там одно и тоже.

    Reply
  28. ids79

    (28)

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

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

    Reply
  29. Vovan1975

    (29) неа, неверно.

    https://xn—-1-bedvffifm4g.xn--p1ai/articles/2017-02-12-two-methods-for-inventory-check/

    Обратите внимание, как передается момент времени – используется тип данных «Граница». Остатки нужно получить на момент времени сразу ПОСЛЕ текущего документа. Можно ли было получить остатки без границы, например, прибавив к дате документа 1 секунду? Нет! Ведь в одной секунде может быть большое число документов. Поэтому единственный правильный вариант – использовать вид границы «Включая».

    Reply
  30. Vovan1975

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

    Reply
  31. ids79

    (30) У указанном Вами примере, действительно так сделано.

    Я же говорю про типовые конфигурации, там на дату актуальности.

    По организациям также еще и на конец кажрого месяца следующего за проводимым документм, но это отдельная песня.

    Reply
  32. Vovan1975

    (32) это странно слышать, поскольку отсутсвие отрицательных итогов в актуальных еще не гарантирует их отстутсвие по времени между проводимым документом и таблицей актуальных итогов

    Reply
  33. Vovan1975

    + (33) при такой схеме можно также вычитать старые движения из остатков, полученных из таблицы актуальных остатков. Хотя лично мне такая метода представляется фигней.

    Reply
  34. CSiER

    (9) ещё один момент: с новой методикой уменьшается время блокировки таблиц в транзакции => меньше ожидание на блокировках => ускорение проведения.

    Reply
  35. acanta

    1с Ники с этой 8кой уперлись в блокировки при росте объема и интенсивности еще 7ки Не придумали ничего лучше чем убрать полный журнал. Мелкие разработчики дробили файлы на какие то периоды в рамках системы. Период хранения предполагал разные таблицы для каждого периода, неважно, год,это или день. Отсюда, именно из архитектуры хранения вытекает невозможность редактировать задним числом и необходимость сторно. Ужасная конечно, но если необходимость осознанная, то это уже свобода.

    Reply
  36. acanta

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

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

    Reply
  37. mrf

    Т.е. если по регистру нужен контроль остатка, нужно отключить использования разделения итогов как вредное?

    Reply
  38. ids79

    (38)Или отключить, или использовать НаборЗаписей.БлокироватьДляИзменения = Истина;

    По сути, это тоже отключение разделения данных, но для конкретного документа.

    Reply
  39. EMelihoff

    Лучше чем тут ИТС нигде, про блокировки не читал.

    Reply

Leave a Comment

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