<?php // Полная загрузка сервисных книжек, создан 2025-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='\
декартово произведение вроде бы вполне обходится без конструкций ВНУТРЕННЕЕ СОЕДИНЕНИЕ ПО ИСТИНА
select
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), ДЕНЬ, 3*(a-1)+b-1 ) as megafunction
, c as nomenkrlatura
from
(select 1 as a union select 2 union select 3) as t1,
(select 1 as b union select 2 union select 3) as t2,
(select ‘крокодил’ as c union select ‘красный’) as t3
——-
да и если уж на то пошло почему бы не пихать вместо двух одинаковыйх строчек вида (select 1 as a union select 2 union select 3) — таблицу значений с цифрами, сформированную в коде, в качестве параметра
Потому что изначально поставил себе цель сделать все одним запросом. Если делать табличку с цыферками — то ее перед запросом нужно сформировать, а при реализации на СКД — вообще такого сделать нельзя, только соединив два набора данных — объект (куда собственно таблица попадет) и запрос, но этот вариантуже реализован .
Что же к Декартовому произведению таблиц — я указал первоисточник, откуда его взял. Можете почитать, почему автор сделал так — просто он делает как раз то, что мне нужно, а далее я не копал
А почему было использовано 2 запроса к остаткам, а потом к движениям? Неужели нельзя было обойтись параметром виртуальной таблицы — метод дополнения.
(1)Формировать в табличку в коде не самый быстрый и гибкий метод. Практика показывает делать это в запросе намного лучше и вообще если есть возможность избавится от параметра лучше это сделать, оно и отлаживать удобней.
(3), вполне возможно. Я просто делал этот запрос по аналогии с регистрами сведений, когда из них нужно было получить такую же табличку(курсы валют, цены номенклатуры, …). Просто запрос работает да еще и не очень долго, а я этого и добивался. Если сделаете с методом дополнения с использованием 1 таблички — респект вам 🙂
А запрос точно рабочий? Запускаю в демке по позиции «Петр I Легкие» за период май 2007. При этом получаю в результате, что был на остатки только в последний день периода, хотя по ведомости есть приход от 5 мая 2007.
Запрос точно рабочий. Текст запроса, при вставке в пост потерял несколько символов. Ну и плюс, таблица дата, которую я выложил имеет ограниченность в 36 символов. Попробуйте скачать сам отчет или поменять разрядность таблицы дат.
(7) Проверил на самом отчете, количество выдает только на последний день периода, если был всего один приход за период.
(8) al_zzz, извините, не верно понял предыдущий комент. В тексте зпроса я выбираю НачальныйОстаток и соответственно, если был только один приход — в день прихода начальный остаток не изменится. Поменяйте его в тексте запроса на КонечныйОстаток(временная таблица Остатки) и все должно заработать верно.
Спасибо за выявленную неточность
Запрос прекрасно работает. Нужно только добавить одно условие для соединения двух таблиц. Иначе даты с остатками будут «обрезаны» по всем товарам. Пошевелите мозгами сами. Я и так хорошо подсказал.
Автору огромный респект!!!
(10), спасибо, очень приятно 🙂
Отчет точно рабочий?
Привожу пример. Меняем дату начала и цифры в отчете разные:
и
По-моему опечатка в коде.
Нужно заменить КоличествоНачальныйОстаток на КоличествоКонечныйОстаток. И тогда отчет заработает, остатки совпадут с «Ведомость по товарам организаций». Т.е. нужно:
(12), как я уже говорил вот здесь (9), да — я там выбирал начальный остаток. Уже не помню зачем. Спасибо за внимательный анализ:)
Хотя вот такая заковыка помогает получше узнать структура и принцип запроса
Да, действительно не прочитал комментарии. Спасибо за обработку, натолкнула на правильное решение.
(14), очень рад, что вам пригодилось
Во второй временной таблице «ТаблицаДата» условие должно быть следующим:
ГДЕ
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), ДЕНЬ, t.n) <= &КонецПериода ;
Последнее убирается при вставке в публикацию.
Извините
Обратите внимание!!! Запрос корректно отрабатывает не всех ситуациях, например попробуйте например сформировать за период 01.12.2011 по 06.01.2012
Вообщем нужно либо дорабатывать либо на помойку.
Анализируйте что выдает конкретно этот подзапрос
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), ДЕНЬ, t.n) КАК Дата
ИЗ
(ВЫБРАТЬ
6 * (t1.a — 1) + t2.b — 1 КАК n
ИЗ
(ВЫБРАТЬ
1 КАК a ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6) КАК t1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
1 КАК b ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6) КАК t2
ПО (ИСТИНА)) КАК t
ГДЕ
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачалоПериода, ДЕНЬ), ДЕНЬ, t.n) <= &КонецПериода
УПОРЯДОЧИТЬ ПО
Дата
(18) , если вы внимательно читали публикацию — то могли заметить, что в пунтке 2, где я описывал Декартово произведение таблиц — указано, что даный запрос возвращает 36 дней. Запрос вполне рабочий при определенных условиях. Выложен как вариант реализации данной задачи. Все расписано и указаны ссылки первоисточников, так что под свои нужды его очень быстро можно подстроить. Если же вы не в силах это сделать — обращайтесь к специалистам.
Или я не понял, или эта задача решается с использованием СКД.
Получение остатков на каждую дату решается установкой периода День.
(20) , возможно и решается. Но основной идеей этой публикации была реализация задачи БЕЗ использования каких-то инструментов кроме запроса.
Если вам при проведении какого-то документа понадобится данная табличка — вы же не будете СКД туда цеплять 🙂
(21) Согласен, если действительно не нужен отчет.
(20) aspirator23,
не решается, у вас будут остатки не на каждый день. Для этого и выдумывают виртуальную таблицу дат.
(21)
В отчетах тоже полезно, сталкивался с ситуацией, когда не мог реализвать остатки по периодам с помощью наборов данных, но это редкие случаи.
Еще дополнение — запрос не считает дни, когда товар пришел днем и в этот же день был продан. Я тупо подставил регистр продаж в подзапрос по остаткам 3 объединением. Может кто подскажет как по нормальному сделать подсчет таких дней (утром и вечером нет остатка по товару, остаток только днем)?
(23) , попробуйте брать НачальныйОстаток + Приход — по идее должно сработать.
Я выбираю остатки по контрагентам(задолженность), и у меня почему-то в таблице ОстаткиПериодами получаются нулевые периоды, за 1 апреля есть остаток, а за 2 нет….
Потому что у меня считаются не сами остатки а разность дт и кр, причем только но документам дата которых удовлетворяет определенному условию, и в результате некоторые строки в таблице остатки получаются с нулевым остатком
подскажите пожалуйста, может чего недопонимаю
но вот эта строчка
получает конец дня перед ОстаткиКонец.НачалоПериода. это верно?
я полагаю что должен быть конец дня ОстаткиКонец.НачалоПериода.
ещё раз возможно что то недопонимаю.
(27), попробую объяснить:
Как-то так
т.е. в таблице ОстаткиПериодами получаем строки двежений, а ДатаНачала и ДатаКонца это даты между движениями по этим позициям?
(29), Да. И если остаток не менялся за этот период, то мы можем на каждый день в этот промежуток поставить определенное значение.
Касательно объединения в таблице остатков, это необходимо, только на мой взгляд параметр должен быть немного другим:
(31), А разве послудеющее объединение
не подтянет эти изменения?
И вполне возможно, что для учета конечных остатков по дням ваш вариант лучше.
Но в примере я решил (на свою голову) выбрать начальные остатки по дням, поэтому ваш пример мне не покажет правильной картины, если было движение в в день
.
Спасибо за отзыв
простите за кучу правок, не ожидал, что так быстро ответят. Я пытался получить остатки на начало дня и активно испытывал Ваш запрос.
тогда нужно совсем удалить объединение с таблицей остатков иначе первой строкой будет остаток именно на начало дня
(32)
Я получил начальные остатки просто сместив дату в таблице остатков на день вверх, по другому у меня не получилось
(33) , так ведь начальный остаток и нужен на начало дня :).
Но если у вас все получилось согласно вашим ожиданиям — тогда все ОК.
так конечный остаток предыдущего дня и есть начальный остаток текущего или я что то не понимаю?
плюс ко всему, максимум непредсказуемо выберет либо начальный остаток, либо конечный
полезная штука. но поправьте уже обработку согласно замечаниям тут
Не рекомендую эту статью к использованию. Ну не должна такая простая задача выливаться в запрос из более сотни строк! Более правильное в методологическом смысле решение (опирающееся на понимание устройства виртуального регистра остатков) можно найти в публикацииЗапрос по остаткам регистра накопления на каждый день , а еще более короткое — в моем комментарии к этой же статье.
Ну сколько нужно повторять? Запрос написан без использования вспомогательных регистров для получения таблицы дат. Наилучшее решение (даже лучше вашего «короткого») — использовать производственный календарь, там точно есть все даты.
А относительно количества строк — я расширенно показал, как сделать поставленную задачу. И заданием этой статьи было поделиться знаниями. И думаю это понятнее, чем «вот такой запрос, только даты берите там, где у вас каждый день регистр изменяется (не в обиду автора упомянутой публикации)».
И на своей базе я не могу посмотреть тем запросом остатки на каждый день. Нет у меня такого регистра.
После этого вы будете утверждать что мой вариант плохой?
(37) ,что именно нужно поправить?
Применил этот метод к данным по своим остаткам.
Ошибку, с которой пока так и не поборолся нашёл в том, что не корректно считает остатки в первый день изменения.
Пример:
Есть входящий остаток 50 шт. Период построения запроса с 01.09 по 10.09.
Остаток менялся 05.09 на 45 шт и 08.09 на 30 шт.
В результате таблица остатков выглядит следующим образом:
01.09 — 00:00 | 04.09 — 23:59 | 50 шт — верно
05.09 — 00:00 | 07.09 — 23:59 | 50 шт — не верно ведь остаток на 06.09 должен быть 45 шт
08.09 — 00:00 | 08.09 — 23:59 | 45 шт — верно
09.09 — 00:00 | 09.09 — 23:59 | 30 шт — верно
(41) ABudnikov, в запросе вы берете НачальныйОстаток или КонечныйОстаток? Если брали запрос из публикации — то там Начальный остаток, о чем указано в статье и что соответствует вашим данным.
(42) беру НачальныйОстаток — так же как и в статье.
да и данные я понимаю как получились технически. Но получатся что логически остаток не верный для первой записи из соединения с таблицей остатков по дням — о чем я и написал
Конечный остаток на 05.09 и соответственно начальный на 06.09 у меня 45 шт.
А в результате работы запроса показывает что на 06.09 50 шт а не 45 шт.
(43) ABudnikov, не согласен. Запрос в статье все верно показывает. Смотрим на ваш пример, но я немного расширю таблицу:
Начало периода |Конец периода |Нач. ост.| Оборот|КонОст | Что берет запрос
01.09 — 00:00 | 04.09 — 23:59 | 50 шт | 0 | 50 шт | 50 шт
05.09 — 00:00 | 07.09 — 23:59 | 50 шт | -5 | 45 шт | 50 шт
08.09 — 00:00 | 08.09 — 23:59 | 45 шт | -15 | 30 шт | 45 шт
09.09 — 00:00 | 09.09 — 23:59 | 30 шт | 0 | 30 шт | 30 шт
(44) Хорошо а теперь если эту таблицу соединить с таблицей дат, то получается что на начало 06.09 остаток 50 шт, что не является правильным ответом, т.к. 05.09 произошло списание 5 шт
У Вас в статье написано: «Далее при соединении с таблицей дат, посмотрев в какой интервал остатков входит дата, получаем нужный остаток Главное правильно построить такую таблицу»
Так вот получается что таблица периодов не отражает всю действительность.
(45) ABudnikov, ну так выбирайте конечный остаток и будет вам счастье. Почему оно должно показать вам все правильно, если вы выбираете начальный остаток? Виртуальная таблица оборотов дает 4 даты — начало, конец и даты расходов. В день, когда был расход, начальный остаток не изменялся. Это касается 5 и 8 числа. Т.е. в вашей первоначальной таблице и 8 числа должно быть неправильно.
(46) 🙂 так задача состоит в том, чтоб получить начальные остатки. Да и 8-го тоже не правильный начальный остаток.
Т.к. у Вас в описано получение начальных остатков, я просто хотел указать что приведенный запрос не корректно решает описанную задачу.
Спасибо, за вашу публикацию!
Спасибо.
Весь фокус в формирование таблицы остатков по периодам.
Сам бы долго соображал что нужно:)
Имхо, ошибка в том, что Вы берете начальный остаток, а не конечный. Пример :
Товар1,
нач.остаток 01.01.14 — 5 шт, кон.остаток 3 шт.
нач.остаток 10.01.14 — 3 шт
У Вас получится, что с 02.01 по 09.01 нач.остаток 5. А на самом деле он 3!
(50), да, вы правы. С начальными остатками я перемудрил. Не работает. И если честно, то завис — не пойму как сделать 🙂
(47), да, вы были правы. Только сейчас до этого допер. Спасибо.
Автор молодец. Очень полезный код выложил. Важно лишь отметить что в том виде, что он изложен можно использовать период не более 36 дней. Ограничение прямо выходит из способа формирования таблицы дат путем «комбинирования» двух массивов по 6 чисел.
Ребята, вопрос: а чем вам не нравится стандартный механизм дополнения по периоду в итога? А потом выборку из результата запроса обходить с параметрами: ЗапросРезультат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, «ПолеПериод», «ВСЕ»);
Получаем остатки на каждый день таким образом без лишнего геморроя.)
Кроме того, если дат не очень много, можно объединять таблицы с последовательным увеличением периода виртуальной таблицы остатков и оборотов на один день. См. пример ниже:
Показать
(54) На момент написания публикации этот механизм давал пропуски, если не было движений по регистру. А здесь важно именно видеть остатки на каждый день.
Как сейчас обстоят дела — не знаю, не мониторил.
По-моему, за период в котором не было движений запрос не выбирает вообще ничего.
У вас так же?