СКД: Корректный расчет остатков по нескольким регистрам




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

25 Comments

  1. Сурикат

    Как альтернатива предлагаемого решения — наборы данных

    Reply
  2. Armando

    На ИТС описан алгорит получения остатка. В виде кода, т.к. ИС съедает отступы

    Полем остатка с точки зрения макета компоновки данных является то, у которого в роли проставлен признак Остаток.
    
    10.8.1. Расчет итогов по полям остатка
    Если в макете компоновки данных в некотором наборе данных присутствует поле начального остатка, то в наборе данных также должно присутствовать соответствующее ему поле конечного остатка, и наоборот.
    
    Все поля-периоды, описанные в наборе данных, должны иметь непрерывную нумерацию, начинающуюся с единицы.
    
    Для корректного расчета итогов по полям источника данных данные должны удовлетворять следующему правилу: в данных должна соблюдаться уникальность значений полей-периодов и полей-измерений, т. е. данные не должны содержать строк с одинаковыми значениями полей-периодов и полей-измерений.
    
    При расчете итогов по полям-остаткам используется следующий алгоритм:
    
    ● если требуется осуществить расчет итога поля остатка для группировки по полю-периоду:
    
    ● если по всем полям-периодам уже была осуществлена группировка:
    
    ● для каждой комбинации полей измерений, по которым осуществлялась группировка:
    
    ● получается запись, ближайшая к текущему периоду;
    
    ● если полученная запись была на текущий период, то из данной записи будут получаться начальные и конечные остатки;
    
    ● иначе, если полученная запись имеет предыдущий период, конечный остаток записи будет использован как начальный и конечный остаток;
    
    ● иначе начальный остаток полученной записи будет использоваться как начальный и конечный остаток;
    
    ● иначе (группировка еще не произведена по всем полям-периодам):
    
    ● для каждой комбинации полей измерений, по которым осуществлялась группировка:
    
    ● получаются первая и последние записи, у которых поля использованных периодов равны текущему периоду;
    
    ● если записи найдены, то первая запись будет использоваться как начальный остаток, последняя – как конечный;
    
    ● если записи не найдены, то получается ближайшая запись и ее остатки используются как начальные остатки и конченые остатки в зависимости от того, предшествует ли найденная запись текущему периоду;
    
    ● иначе (не группировка по полю-периоду):
    
    ● первые по хронологии записи для неиспользованных полей-измерений будут использоваться в качестве записей начального остатка, последние – в качестве конечного.

    Показать

    https://its.1c.ru/db/v8310doc#bookmark:dev:TI000000628

    Reply
  3. echo77

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

    Reply
  4. Сурикат

    (3)

    я Объединения, а не для отдельных наборов данных в него входящих — получим тоже самое

    Просто такую же проблему у меня получилось решить именно объединением наборов… Причем в наборах поля ресурсов не пересекались.

    Но может ситуация была немного другая =)

    Reply
  5. logarifm

    Во-первых чтобы СКД корректно рассчитывала остаток надо обязательно в ресурсах указывать поля Нач.Кон остатки

    Reply
  6. logarifm

    Прекрасно делается через Набор данных Объединение только там есть интересный момент следует учесть Значения разных ресурсов регистров под разныими именами!

    Reply
  7. logarifm

    А вот это диво-дивное и чудо чудное решается как с помощью Набора данных Объединение так и с помощью просто Объеденить в запросе. Но с одной особенностью, что роли необходимо самому указать и проверить для Измерений чтобы были измерениями, а для остатков остатками. Но можно упороться на одну интересную ошибку особенно если использовали к примеру одно поле КонОст и остатка и партии во тут получим ошибку.

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

    Оговорюсь только , что это делалось для конфигурашки на обычных формах 10..х под CRM.

    Reply
  8. echo77

    (7) Все хорошо, пока не добавить группировку по регистратору.

    При добавлении группировки — остаток по партиям для Номенклатуры и для Склада — 0

    Эти грабли я и описал в статье, и как их поборол

    p.s. В отчете, что вы скинули, нет никакого смысла делать объединение наборов, т.к. вы ничего не объединяете. И не совсем понятно, почему так сложно рассчитываются ресурсы

    Reply
  9. alexz69

    Спасибо большое, за вашу статью. Такая же проблема была, только я использовал три регистра в самописной конфигурации. Пришел к выводу, что при добавлении третьего регистра плывет остаток по конечному остатку по измерениям. Хотя по регистратору всё нормально. Проверял все роли, делал и в одном запросе и в объединении — ничего не помогало. Благодаря добавлению раздела всё стало на свои места. Нутром чувствовал, что СКД путает. Очень выручили )).

    Reply
  10. mitia.mackarevich

    Для остатков по регистратору вы использовали вспомогательное поле период секунда?

    Reply
  11. echo77

    (10) Да, псевдоним у поля — Период

    Reply
  12. mitia.mackarevich

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

    Reply
  13. rozer

    (12) да на итс уже как 100 лет есть это https://its.1c.ru/db/metod8dev/content/3093/hdoc — если регистратор то надо секунду добавлять и роли полей поставить правильно

    Reply
  14. echo77

    (12)

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

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

    Цель статьи — заострить внимание на том, что задачу решить можно, путем введения нового разреза и установкой роли — Измерение для этого разреза.

    Ничего сверхестественного, просто тонкость, которую нужно понимать

    Reply
  15. alanchik

    Спасибо автору!!! Столкнулся я с проблемой этих остатков, когда объединял два регистра накопления, день бился не добился результата, пока не наткнулся на эту статью. Интуитивно было понятно, что надо что-то делать с разделами учета, потому что по-отдельности запросы считали корректно, а при объединении нет, но как сочинить сие действо мозгов не хватило)))

    Reply
  16. Noveng

    У меня несколько иная задача(. есть начальный остаток по регистру товары на складах и приход по нему материала. Но расход высчитывается как нормативный. Берется перемещаема продукция. по ней ищется спецификация, выбирается материал и рассчитывается нормативный расход. Общего из двух запросов получается только склад и номенклатура. А вот конечный остаток вычисляется как начальный остаток +приход-нормативный расход. Не получается подружить конечный остаток ни с одним периодом из регистра. Группировка склад и номенклатура — всё нормально, но как только добавляешь регистратор, день…остатки разлетаются. Искусственное измерение типа — раздела учета не помогает….

    Reply
  17. echo77

    (16) И не поможет. Вам в итоге нужно получить выборку вида:

    Регистратор НачальныйОстаток, Приход, Расход, КонечныйОстаток

    В одной строке выборки должен присутствовать начальный и конечный остаток.

    Reply
  18. rom-x

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

    Reply
  19. echo77

    (18) И не нужно группировать регистратор с каким-то еще полем — посмотрите скриншот с настройкой структуры.

    Reply
  20. rom-x

    (19) Я хотел сделать также, как на картинке перед заголовком Немного красоты.

    Reply
  21. rom-x

    (19) Так как это сделать?

    Reply
  22. echo77

    (21) Это не группировка Регистратор, Раздел. Это детальные записи, в которых выведены эти два поля.

    Reply
  23. TSSV
  24. Team leader

    (7) взял на вооружение +1

    Reply
  25. German_Tagil

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

    в консоли все нормально работало-

    решил перенести в отчет

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

    теперь немного понятно

    Reply

Leave a Comment

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