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




Принцип обмена данными из 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='\

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 не будет опубликован. Обязательные поля помечены *