среда, 11 декабря 2013 г.

1С v7.7 Быстрый метод создания периферийной базы УРБД SQL

Имеем сабж)

Рухнула как то периферийная БД. Соответственно возникла необходимость создания новой. База относительно не маленькая (22 Gb). Выгрузить периферийку штатными средствами не представляется возможным (ибо ограничение на выгрузку 2 Gb в 7.7 так и не пофиксили). Через сторонние выгрузки (плагин Romix`a) - долго. Задача стояла восстановить работу предприятия в кратчайшие сроки.

Итак, приступим:

1. Через конфигуратор в центральной базе создаем новую периферийную БД.

2. Через Enterprice Manager (SQL сервер 2000), в таблице _1SBDSET центральной базы, для новой базы меняем DBSTATUS c "N" на "C", и DBFMODE c "1" на "3" (активируем ее).

3. Отключаем (Detach Database) центральную базу в Enterprice Manager, копируем файлы базы данных .MDF и .LDF туда, где будет располагаться периферийная база и подключаем ее (Attach Database), (не забываем центральную тоже назад подключить).

4. Через Query Analizer периферийной базы запускаем скрипт. Меняем номер периферийной БД 1С и имя базы SQL):

use 

declare @Nom char(3)
declare @NomCentr char(3)

select @NomCentr = dbsign from _1SDBSET where dbstatus = 'M'
set @Nom = '048'              --  МЕНЯТЬ НОМЕР на НОВОЙ НОМЕР ПЕРИФ. БАЗЫ ЗДЕСЬ!!!! (в этом случае 048)

delete from _1SDBSET
where dbsign <> @NomCentr and dbsign <> @Nom

update _1SDBSET
set dbstatus = 'P'
where dbsign = @NomCentr 

update _1SDBSET
set dbstatus = 'M'
where dbsign = @Nom

update  _1SSYSTEM
set  DBSIGN = @Nom

truncate table _1SDWNLDS
truncate table _1SUPDTS

--DBCC SHRINKDATABASE(,5)  - при желании сожмем базу (раскомментировать в этом случае)
--exec sp_changedbowner '1cuser'                       - и если база поключилась с другим владельцем, сменим на нужного



Периыерийная и центральная БД готовы к работе! Done!  © maxis33

вторник, 26 ноября 2013 г.

просмотр ip-адресов компьютеров в локальной сети

Чтобы вывести список ip-адресов компьютеров в локальной сети, можно выполнить команду:

$nmap -sP 192.168.0.1-255

возможно придется предварительно установить nmap

четверг, 19 сентября 2013 г.

Команды редактора VI

VI - текстовый редактор - кошмар для случайного пользователя. Если его удается в конце-концов закрыть, то вздыхаешь с облегчением и твердой уверенностью, что больше не будешь запускать эту программу ни в коем случае.

Действительно, такой редактор нельзя просто открыть и начать работать требуется предварительная подготовка. Зато стоит раз испытать его в работе, и поражаешься, как мог обходиться без него раньше.

Консольные системы Unix не имеют в своем базовом комплекте ни одного нормального (еще раз подчеркну это слово - НОРМАЛЬНОГО) текстового редактора.

Поэтому системный администратор должен уметь пользоваться тем, что ему дают. А дают ему несравненный "Визуальный" экранный редактор файлов - Редактор VI.


Для работы, обычно достаточно знать всего несколько команд. Обычно для работы хватает таких:

vi /file_folder/filename - открыть файл в редакторе vi
vi /file_folder/filename ++25 - открыть файл в редакторе vi и перейти к строке 25.

i - включить режим внесения изменений
[Esc] - выйти из данного режима

u - undo (отмена сделанного изменения)
CTRL + R - redo (вернуть сделанное изменение)

:w [Enter] - сохранить внесенные изменения.

:q! [Enter] - выйти без сохранения изменений
:wq [Enter] - сохранить внесенные изменения и выйти.

dd - удалет строку на которой находится курсор
x - удалет символ на котором находится курсов


[y] - копирует строку в буфер
[P] - вставляет скопированную строку после той, на которой в текущий момент находится курсор


Поиск:

/text [Enter] - выполняет поиск "text" в тексте


Автозамена:

:%s/6201/6202/gc - ищет в документе вхождение 6201 и меняет (с подтверждением) на 6202 (замена порта).


Показать/Скрыть номера строк в редакторе vi:

:set nu - нумеровать строки
:set nonu - не нумеровать строки



!!!Чтобы редактор сохранил настройки, необходимо в файле ~/.vimrc
прописать строку, например set nu





Код взят на каком-то из девелоперских серверов. Допиливаем VI под себя:

vi ~/.vimrc

Достаточно удобно по F2 сохранять результат, а по F10 выходить.

1 set nocompatible
2
3 colorscheme desert
4
5 set novisualbell
6
7 set nu
8
9 set termencoding=utf-8
10
11 set hidden
12
13 set ch=1
14 set showcmd
15
16 syntax on
17
18 set autoindent
19 set smartindent
20
21 nmap <F2> :w<cr>
22 vmap <F2> <esc>:w<cr>i
23 imap <F2> <esc>:w<cr>i<right>
24
25 imap <Ins> <Esc>i
26
27 map <F10> :q!<cr>
28 vmap <F10> <esc>:q!<cr>
29 imap <F10> <esc>:q!<cr>

четверг, 12 сентября 2013 г.

Настройка разрешения экрана для видеокарт Intel 82945G/GZ в Ubuntu


Итак, сабж... Собственно, зачем это нужно?! Должно же из коробки работать. Работало... Но, в один прекрасный момент, по каким-то загадочным причинам перестало, есть такой глючок у интеловских видух. Т.е. в настойках можно установить только разрешение 800х600, что есть грустно.
Честно скажу, логи читать было лень, ибо возвращается все назад очень нехитрыми манипуляциями.


Генерируем интересующий нас Modeline:
#cvt 1280 1024 75

выхолп:

1280x1024 74.90 Hz (CVT 1.31M4) hsync: 80.30 kHz; pclk: 138.75 MHz
Modeline "1280x1024_75.00"  138.75  1280 1368 1504 1728  1024 1027 1034 1072 -hsync +vsync


Смотрим текущие настройки экрана:
#xrandr

выхлоп:

Screen 0: minimum 320 x 200, current 800 x 600, maximum 800 x 600
VGA1 connected 800x600+0+0 (normal left inverted right x axis y axis) 0mm x 0mm
800x600        60.3     56.2
640x480        59.9     59.9

Выставляем интересующее нас разрешение:
#export DISPLAY=:0
#xrandr --newmode "1280x1024_75.00"  138.75  1280 1368 1504 1728  1024 1027 1034 1072 -hsync +vsync
#xrandr --addmode VGA1 1280x1024_75.00
#xrandr --output VGA1 --mode 1280x1024_75.00

Примечание!!! В качестве параметров необходимо использовать "modeline", полученный в шаге 1 и VGA1, как видно из шага 2, ибо они специфичны для каждого компутера...

Создаем чистый конфиг Xorg`a, бекапим (если есть) текущий его конфиг и подкидываем новый:
#Xorg -configure
#/etc/init.d/gdm stop
#cp /etc/X11/xorg.conf /etc/X11/xorg.conf.origin
#cp ~/xorg.conf.new /etc/X11/xorg.conf

Приводим секцию "Monitor" к такому виду:
#mcedit /etc/X11/xorg.conf

Section "Monitor"
Identifier   "Monitor0"
VendorName   "Monitor Vendor"
ModelName    "Monitor Model"
Modeline     "1280x1024_75.00"  138.75  1280 1368 1504 1728  1024 1027 1034 1072 -hsync +vsync
Option       "PreferredMode" "1280x1024_75.00"
EndSection

Рестартим Xorg (или ребутим машину).

 Done.

вторник, 3 сентября 2013 г.

Организация RDP-сервера на базе Debian/Ubuntu

Итак, собсна, сабж...

Огромнейшее спасибо Kevin Cave за труды! Его скрипт очень автоматизирует установку и настройку RDP-сервера на базе Debian/Ubuntu.
Далее предлагаю вольный перевод статьи (оригинала) для тех, кто не очень дружит с буржуйским...

Качаем мега-скрипт:

$sudo apt-get install -y git
$git clone https://github.com/scarygliders/X11RDP-o-Matic.git
$cd X11RDP-o-Matic

Данный скрипт скачивает, собирает и устанавливает пакеты xrdp_0.7.0-1_<arch>.deb и x11rdp_0.7.0-1_<arch>.deb 

Запускаем инсталятор:

$sudo ./X11rdp-o-matic.sh --justdoit

!В случае ошибок сборки, докачиваем необходимые пакеты и запускаем инсталятор снова!

Параметры запуска скрипта:

--help : показать справку.

--justdoit : произвести полную компиляцию и установку всех пакетов по-умолчанию,
без вмешательства пользователя (Рекомендуется).

--nocpuoptimize : не изменять скрипт компиляции X11rdp для использования
более одного ядра процессора (если установлена, для сборки используется только одно ядро,
в противном случае - все доступные ядра процессора будут задействованы).

--nocleanup : не удалять исходники X11rdp / xrdp после установки (по-умолчанию удаляются).

--noinstall : не выполнять установку необходимых пакетов, а просто собрать их.

--nox11rdp  : собрать только xrdp, без "обертки" x11rdp (не рекомнедую, в связки очень достойно работают).

--bleeding-edge : использовать нестабильные репозитории исходных кодов.

Запускаем скрипт настройки клиентов:

$sudo ./RDPsesconfig

RDP-сервер готов!!! Для подключения, в принципе, можно использовать любой клиент с поддержкой RDPv5. Я рекомендую: Remmina (кроссплатформенный), KRDC

Стандартные виндузятные клиенты тоже подходят...

пятница, 30 августа 2013 г.

Настройка подключения через proxy в Ubuntu 12.04

Для работы через прокси-сервер в ubuntu 12.04 и выше нужно поправить 2 конфига:

Правим переменные окружения:
$sudo mcedit /etc/environment

Добавляем настройки на прокси в формате:

http_proxy="http://username:password@proxy:port/"
https_proxy="https://username:password@proxy:port/"
ftp_proxy="ftp://username:password@proxy:port/"
ftps_proxy="ftp://username:password@proxy:port/"

Например
http_proxy="http://192.168.10.2:8080/"
https_proxy="https://192.168.10.2:8080/"

Правим конфиг APT:

$sudo mcedit /etc/apt/apt.conf

Добавляем или изменяем следующие строчки, в формате по аналогии с окружением

Acquire::http::Proxy "http://username:password@proxy:port/";
Acquire::https::Proxy "http://username:password@proxy:port/";
Acquire::ftp::Proxy "http://username:password@proxy:port/";
Acquire::ftps::Proxy "http://username:password@proxy:port/";

Для исключения использования прокси-сервера в отношении каких-либо адресов/подсетей в environment добавляем:

no_proxy=localhost,127.0.0.1,192.168.10.0/24,www.exemple.com

Ребут.

пятница, 19 апреля 2013 г.

Описание таблиц SQL для 1С V8.x

Данное описание таблиц применимо к с следующим СУБД:
  • Microsoft SQL Server (клиент-серверный вариант информационной базы).
  • PostgreSQL (клиент-серверный вариант информационной базы).
  • IBM DB2  (клиент-серверный вариант информационной базы)
Основные таблицы:

Название таблицыОписание
Config Основная конфигурация информационной базы. Эта конфигурация соответствует реальной структуре данных и используется 1С:Предприятием 8.х в режиме Предприятия
ConfigSaveКонфигурация, редактируемая Конфигуратором.
Не сама основная конфигурация, а только отличия ее от конфигурации БД (т.е. только измененные объекты). Таким образом, если конфигурации базы данных и основная конфигурация совпадают, то таблица ConfigSave пустая. Конфигурация из ConfigSave переписывается в Config при выполнении "Обновления конфигурации базы данных" в Конфигураторе
FilesСодержит служебную информацию, например, о работе с хранилищем конфигурации
Params
Содержит параметры информационной базы. Среди них:
  • Список пользователей информационной базы. 
  • Национальные настройки информационной базы. 
  • Таблица соответствия объектов метаданных и объектов базы данных (таблиц, полей, индексов). 
_YearOffsetСмещение дат в базе данных. Эта таблица создается только при использовании Microsoft SQL Server
DBSchemaСодержит информацию о структуре базы данных 1С:Предприятия и определяет другие объекты базы данных, используемые данной информационной базой


Таблицы планов обмена, с установленным флагом "Распределенная информационная база":

Название таблицыОписание
_ConfigChangeRec Таблица регистрации изменений объектов конфигурации
_ConfigChangeRec_ExtPropsТаблица имен файлов измененных внешних свойств объектов конфигурации


Таблицы метаданных:

Название таблицыОписание
_Consts Константы.Содержит текущие значения всех констант, определенных в конфигурации
_ConstsChangeRecКонстанты. Таблица регистрации изменений констант. Создается, если хотя бы одна константа участвует хотя бы в одном плане обмена.
_Node<n> Планы обмена. Таблица плана обмена
_Node<n>_VT<k> Планы обмена. Табличная часть плана обмена, создается для каждой табличной части
_Reference<n> Справочники. Таблица справочника
_Reference<n>_VT<k> Справочники. Табличная часть справочника - для каждой табличной части
_ReferenceChangeRec<n> Справочники. Таблица регистрации изменений справочника. Создается, если справочник участвует хотя бы в одном плане обмена.
_Document<n> Документы. Таблица документов для каждого объекта метаданных "документ".
_Document<n>_VT<k> Документы. Табличная часть документа - для каждой табличной части каждого документа.
_DocumentChangeRec<n> Документы. Таблица регистрации изменений объекта метаданных типа "документ". Создается для каждого объекта метаданных типа "документ", если он участвует хотя бы в одном плане обмена.
_Sequence<n> Последовательности документов. Таблица регистрации документов - для каждой последовательности.
_SequenceBoundary<n> Последовательности документов. Таблица границ последовательности - для каждой последовательности.
_SequenceChangeRec<n> Последовательности документов. Таблица регистрации изменений последовательности. Создается для каждой последовательности, которая участвует хотя бы в одном плане обмена.
_DocumentJournal<n> Журналы документов. Таблица журнала документов, создается для каждого журнала документов.
_Enum<n> Перечисления. Таблица перечисления - по одной для каждого перечисления. 
_Chrc<n> Планы видов характеристик. Основная таблица плана видов характеристик.
_Chrc<n>_VT<k> Планы видов характеристик. Табличная часть плана видов характеристик - для каждой табличной части.
_ChrcChangeRec<n> Планы видов характеристик. Таблица регистрации изменений плана видов характеристик. Создается, если план видов характеристик участвует хотя бы в одном плане обмена.
_Acc<n> Планы счетов. Основная таблица плана счетов.
_Acc<n>_ExtDim<k> Планы счетов. Таблица видов субконто плана счетов, создается для плана счетов в том случае, если максимальное количество субконто больше нуля.
_Acc<n>_VT<k> Планы счетов. Табличная часть плана счетов, создается для каждой табличной части плана счетов. 
_AccChangeRec<n> Планы счетов. Таблица регистрации изменений плана счетов. Создается, если план счетов участвует хотя бы в одном плане обмена. 
_CalcKind<n> Планы видов расчета. Основная таблица плана видов расчета.
_CalcKind<n>_BaseCK Планы видов расчета. Таблица базовых видов расчета, создается для плана видов расчета в случае, если его свойство "Зависимость от базы" имеет значение, отличное от "Не зависит".
_CalcKind<n>_DisplacedCK Планы видов расчета. Таблица вытесняемых видов расчета, создается для плана видов расчета в случае, если у него установлен флаг "Использует период действия". 
_CalcKind<n>_LeadingCK Планы видов расчета. Таблица ведущих видов расчета - для каждого плана видов расчета.
_CalcKindDN<n> Планы видов расчета. Вспомогательная таблица для порядка вытеснения, создается, если у плана видов расчета установлен флаг "Использует период действия". 
_CalcKind<n>_VT<k> Планы видов расчета. Табличная часть плана видов расчета, создается для каждой табличной части.
_CalcKindChangeRec<n> Планы видов расчета. Таблица регистрации изменений плана видов расчета. Создается, если план видов расчета участвует хотя бы в одном плане обмена. 
_InfoReg<n> Регистры сведений. Таблица движений регистра сведений.
_InfoRegChangeRec<n> Регистры сведений. Таблица регистрации изменений регистра сведений. Создается, если регистр сведений участвует хотя бы в одном плане обмена.
_AccumReg<n> Регистры накопления. Таблица движений регистра накопления.
_AccumRegTotals<n> Регистры накопления. Таблица итогов регистра накопления, если регистр поддерживает остатки.
_AccumRegTurnovers<n> Регистры накопления. Таблица оборотов регистра накопления, если регистр поддерживает обороты. 
_AccumRegChangeRec<n> Регистры накопления. Таблица регистрации изменений регистра накопления. Создается, если регистр накопления участвует хотя бы в одном плане обмена.
_AccumRegOptions Регистры накопления. Таблица настроек хранения итогов регистров накопления одна на все регистры накопления. 
_AccntReg<n> Регистры бухгалтерии. Таблица движений регистра бухгалтерии
_AccntRegED<n> Регистры бухгалтерии. Таблица значений субконто регистра бухгалтерии, создается в том случае, если он ссылается на план счетов, у которого максимальное количество субконто больше нуля.
_AccTtl0<n> Регистры бухгалтерии. Таблица итогов по счету.
_AccTtl<i><n> Регистры бухгалтерии. Таблица итогов по счету с количеством видов субконто равным i, где i от 1 до максимального количества субконто. 
_AccTtlC<n> Регистры бухгалтерии. Таблица итогов оборотов между счетами, только для регистра бухгалтерии поддерживающего корреспонденцию. 
_AccntRegChangeRec<n> Регистры бухгалтерии. Таблица регистрации изменений регистра бухгалтерии. Создается, если регистр бухгалтерии участвует хотя бы в одном плане обмена. 
_AccntRegOptions Регистры бухгалтерии. Таблица настроек хранения итогов одна на все регистры бухгалтерии.
_CalcReg<n> Регистры расчета. Таблица движений регистра расчета.
_CalcRegActPer<n> Регистры расчета. Таблица фактических периодов действия для регистра расчета, создается, если у регистра расчета установлен флаг "Период действия".
_CalcRegChangeRec<n> Регистры расчета. Таблица регистрации изменений регистра расчета. Создается для каждого регистра расчета, участвующего хотя бы в одном плане обмена.
_CalcRegRecalc<n> Регистры расчета. Таблица перерасчета регистра расчета, создается для каждого перерасчета.
_CalcRegRecalcChangeRec<n> Регистры расчета. Таблица регистрации изменений перерасчета. Создается, если перерасчет участвует хотя бы в одном плане обмена.
_BPRoutePoint<n> Бизнес-процессы. Таблица точек маршрута бизнес-процесса для каждого бизнес-процесса.
_BusinessProcess<n> Бизнес-процессы. Основная таблица бизнес-процесса. 
_BusinessProcess<n>_VT<k> Бизнес-процессы. Табличная часть бизнес-процесса для каждой табличной части.
_BusinessProcessChangeRec<n> Бизнес-процессы. Таблица регистрации изменений бизнес-процесса. Создается для каждого бизнес-процесса, участвующего хотя бы в одном плане обмена.
_Task<n> Задачи. Основная таблица задачи.
_Task<n>_VT<k> Задачи. Табличная часть задачи для каждой табличной части.
_TaskChangeRec<n> Задачи. Таблица регистрации изменений в задачах. Создается для каждого объекта метаданных типа "задача", который участвует хотя бы в одном плане обмена. 


Структуру и название таблиц используемых для хранения данных в БД 1С 8.х можно посмотреть, выполнив следующий код на встроенном языке 1С:

 МассивИменМетаданных = Новый Массив();
 МассивИменМетаданных.Добавить("Справочник.Номенклатура");

 // Таблица значений, содержащая таблицы указанного объекта.
 Таблицы = ПолучитьСтруктуруХраненияБазыДанных(МассивИменМетаданных);
 Построитель = Новый ПостроительОтчета;
 Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(Таблицы);
 Построитель.ЗаполнитьНастройки();
 Построитель.Вывести(ЭлементыФормы.ПолеТабличногоДокумента1);
 Обновить();
 
 //Для Каждого Таблица Из Таблицы Цикл
 // ПоляТаблицы = Таблица.Поля;
 // Для Каждого Поле Из ПоляТаблицы Цикл
 //  // Просмотр свойств таблицы.
 // КонецЦикла; 

 // ИндексыТаблицы = Таблица.Индексы;
 // Для Каждого Индекс Из ИндексыТаблицы Цикл
 //  // Просмотр свойств индекса.
 // КонецЦикла;   
 //КонецЦикла;    

вторник, 9 апреля 2013 г.

Сканируем документы по сети. Или как расшарить сканер в Ubuntu?

Задача:
Организовать возможность сканировать документы по локальной сети.

Имеем:
Копутеры в разных подсетях (192.168.1.0/24, 192.168.2.0/24, 192.168.10.0/24) и МФУ Canon MF4010, подключенный по USB к 192.168.1.6

Обозначения:
scanserver - компьютер, к которому подключен сканер;
scanclient - компьютер, к которого необходимо сканировать по сети;


На scanserver:

Ставим недостающие пакеты:
$sudo apt-get install sane-utils

Правим конфиги:

$sudo mcedit /etc/sane.d/saned.conf

в самый конец файла добавляем IP-адрес scanclient 
(Если клиентов несколько, то можно указать диапозон IP-адресов (например, 192.168.1.1-192.168.1.4)
 или всю подсеть (например, 192.168.1.0/24))

192.168.1.0/24
192.168.2.0/24
192.168.10.0/24

$sudo mcedit /etc/inetd.conf

добавляем в него строку:
sane-port stream tcp nowait saned:saned /usr/sbin/saned saned

(если такая строка есть, но закомментирована - соответственно раскомментировать ее)


Рестартуем openbsd-inetd:
$sudo service openbsd-inetd restart


Если в системе еще нет группы scanner - создаем ее, и добавляем себя и пользователя saned в эту группу:
$sudo groupadd scanner
$sudo usermod -aG scanner user
$sudo usermod -aG scanner saned

Правим конфиг 40-libsane.rules:
$sudo mcedit /lib/udev/rules.d/40-libsane.rules

ищем свой сканер, должно быть что то типа:

# Canon imageCLASS MF4010 | Canon imageCLASS MF4018
ATTRS{idVendor}=="04a9", ATTRS{idProduct}=="26b4", ENV{libsane_matched}="yes"

приводим строку к такому виду:

# Canon imageCLASS MF4010 | Canon imageCLASS MF4018
ATTRS{idVendor}=="04a9", ATTRS{idProduct}=="26b4", ENV{libsane_matched}="yes", MODE="664", GROUP="scanner"


Если вашего сканера нет в файле, его можно добавить вручную по idVendor и idProduct, которые можно получить командой на scanserver:
$sane-find-scanner

в ответ должно быть что тот типа:
found USB scanner (vendor=0x04a9 [Canon Inc.], product=0x26b4 [MF4010]) at libusb:001:003

Теперь правим /etc/default/saned:
$sudo mcedit /etc/default/saned

устанавливаем RUN=yes

Рестартуем saned:
$sudo service saned restart

На этом настройка scanserver-a завершена...


На scanclient:

Ставим недостающие пакеты:
$sudo apt-get install sane-utils

Правим конфиг:

$sudo mcedit /etc/sane.d/net.conf

в самый конец файла добавляем IP-адрес scanserver
192.168.1.6 

Проверяем доступность сканера:
$sudo scanimage -Lq

В ответ должны получить что-то вроде:
device `net:192.168.1.6:pixma:04A926B4_SD3008250258Q' is a CANON Canon imageCLASS MF4010 multi-function peripheral

В конфиге /etc/sane.d/dll.conf проверяем наличие строки (раскаментируем/добовляем):
net


Если в системе еще нет группы scanner - создаем ее, и добавляем себя и пользователя saned в эту группу:
$sudo groupadd scanner
$sudo usermod -aG scanner user
$sudo usermod -aG scanner saned

На этом ВСЕ!!! Можно пробовать сканировать с помощью XSane или другой подходящей программой, используя расшаренный сканер.


ЗАМЕЧАНИЕ!!!
Если после настройки есть ругань на ограничение правах использования сканера, то на scanserver в конфиге /etc/inetd.conf следует заменить строку параметров:

sane-port stream tcp nowait saned:saned /usr/sbin/saned saned
заменить на:
sane-port stream tcp nowait root /usr/sbin/saned saned

Sony Xperia P + Ubuntu 12.04. Или подключаем Android 4.0+ к Ubuntu 12.04

Давно не было необходимости подключать свой телефон к копутеру по USB. А тут вот столкнулся. К своему удивлению обнаружил, что по-умолчанию 12-я убунта на умеет работать со смартами на android 4.0+ как с обычной флешкой по USB. Пришлось лечить.

Устанавливаем Go-mtpfs:

$sudo add-apt-repository ppa:webupd8team/unstable
$sudo apt-get update && sudo apt-get install go-mtpfs
Для монтирования устройства выполняем:
$go-mtpfs /media/MyAndroid

Для отмонтирования устройства выполняем:
$fusermount -u /media/MyAndroid



Убираем лишние значки с панели Gnome 3.

Значки типа: "специальные возможности", "индикатор раскладки клавиатуры" (у меня Xneur установлен), "индикатор батареи" мне не нужны. Было принято убрать лишнее с панели:

$sudo mcedit /usr/share/gnome-shell/js/ui/panel.js

закоментим оригинальные строки:

/*const STANDARD_STATUS_AREA_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'bluetooth', 'network', 'battery','userMenu'];
 const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'userMenu': imports.ui.userMenu.UserMenuButton
 };*/

и напишем как нам надо:

const STANDARD_STATUS_AREA_ORDER = [ 'volume', 'bluetooth', 'network'];
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
 'volume': imports.ui.status.volume.Indicator,
'userMenu': imports.ui.userMenu.UserMenuButton
 };

Перезагружаем Gnome (Alt+F2 —> r). Готово!

Свои ярлыки в меню Gnome 3 Ubuntu 12.04

Для того, чтобы создать собственный ярлык в главном меню Gnome 3 Ubuntu 12.04, например, для 1С:Предприетие 7.7, необходимо в /usr/share/applications/ создать desktop-файл такого содержания:

#mcedit /usr/share/applications/1cv77.desktop

[Desktop Entry]
Version=1.0
Type=Application
Terminal=false
Exec=env WINEPREFIX="/home/alah/.wine" wine start /Unix "/home/alah/wine_c/Program Files/1Cv77/BIN/1cv7s.exe"
Categories=Wine;
Icon[ru_RU]=/home/alah/.local/share/icons/hicolor/32x32/apps/1FDC_1Cv7s.0.png
Name[ru_RU]=1С:Предприятие 7.7
Icon=/home/alah/.local/share/icons/hicolor/32x32/apps/1FDC_1Cv7s.0.png
Name=1C:Enterprise 7.7

где

Exec - команда запуска приложения;
Categories - подкатегория, куда положить ярлык;
Icon - путь к иконке для ярлыка;
Name - имя ярлыка;

вторник, 19 февраля 2013 г.

FTP server за 15 минут!

Разворачиваем FTP-server за 15 минут на Ubuntu server 10.04

Ставим необходимые пакеты:
$ sudo apt-get install proftpd

Настраиваем:
$ sudo mcedit /etc/proftpd/proftpd.conf

#Тип сервера (выделенный)
ServerType                    standalone

#Чтобы предотвратить попытку положить или взломать демон proftpd командой "ls */../*/../*/..."
DenyFilter                      \*.*/

#Разрешить перезапись файлов
AllowOverwrite               on

#Разрешить "дозаливку" файлов при ошибках
AllowStoreRestart           on

#Максимальное кол-во подключений с одного хоста + сообщение при превышении ограничения
MaxClientsPerHost          2                     "too many connections by peer"

#Общее максимальное кол-во подключений
MaxInstances                 10

#Мыло админа
ServerAdmin                  admin@my.domen.com

#Имя сервера
ServerName                   "MY FTP SERVER"

#Замыкать всех в каталоге "/mnt/users/ftp"
DefaultRoot                    /mnt/users/ftp

#Удалить данные при прерывании сессии
DeleteAbortedStores       on

#Не показывать файлы пока они полностью не загружены на сервер
HiddenStores                  on

#Маска прав для вновь созданных файлов и каталогов
#первый параметр для файлов (666 - параметр = новый файл)
#второй параметр для каталогов (777 - параметр = новый каталог)
Umask                           022            022

#Root`y логиниться запрещено
RootLogin                       off

#Сообщение при удачном логине
DisplayLogin                   /etc/proftpd/.welcome.msg

#Запретить удаление файлов всем, кроме пользователя alah
<Limit WRITE>
          AllowUser            alah
          DenyAll
          IgnoreHidden       on
</Limit>

#Разрешить всем выкладывать файлы на сервер
<Limit STOR>
          AllowAll
</Limit>

#Глобальные настройки сервера для всех виртуальных
<Global>
           DefaultRoot              /mnt/users/ftp
           DeleteAbortedStores on
           HiddenStores            on
           Umask                     022          022
           RootLogin                 off
           DisplayLogin             /etc/proftpd/.welcome.msg
</Global>

#Настройки анонимных подключений
<Anonymous /mnt/users/ftp>
           #Юзер, от имени которого будем работать
           User                            ftp
           Group                          ftp
           UserAlias                     anonymous         ftp

           #Проверять наличие у юзера оболочки
           RequireValidShell         off

           #Просить пароль у анонимов
           AnonRequirePassword  off

           #Разрешить фнонимам презаписывать файлы
           AllowOverwrite            off

           #Максимальное кол-во анонимов
           MaxClients                  10             "Sorry, max %m users -- try again later"

           #Ограничить логины анонимов с определенных хостов
           <Limit LOGIN>
                      Order allow,deny
                      Allow from all
                      Deny from .microsoft.com, .msn.com
           </Limit>

           #Запретить анонима удалять файлы с сервера
           <Limit WRITE>
                      DenyAll
           </Limit>
</Anonymous>

На папку /mnt/users/ftp нужно дать права 777

Готово! Можно пользоваться.

P.S.

Если FTP-сервер за NAT`ом - необходимо добавить в конфиг следующие опции:

MasqueradeAddress ftp.mydomain.com  # FQDN вашего FTP
MasqueradeAddress 123.45.67.89         # внешний IP

PassivePorts            57000 58000            # диапазон портов для пассивного подключения

и разрешить диапазон портов пассивного подключения в IPTABLES:

# iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# iptables -A INPUT -p tcp --match multiport --dports 57000:58000 -j ACCEPT

ну и пробросить соответствующие порты на шлюзе.

четверг, 14 февраля 2013 г.

Настройка доступа к Firebird/interBase Server через ODBC. Ubuntu 12.04


Вдогонку к "Настройка доступа к Microsoft SQL Server через ODBC. Ubuntu 12.04" решил описать связку Linux->Firebird/InterBase.

Устанавливаем драйвер:

Установка необходимых пакетов:

$sudo apt-get install unixodbc unixodbc-bin unixodbc-dev odbcinst firebird2.5-super libfbclient2

Создаем необходимый симилинк:
#Для i386
$sudo ln -s /usr/lib/libfbclient.so.2 /usr/lib/libfbclient.so

#Для x86_64
$sudo ln -s /usr/lib/x86_64-linux-gnu/libfbclient.so.2 /usr/lib/x86_64-linux-gnu/libfbclient.so


На момент написания статьи последней версией драйвера была 2.0.1.152.
Последнюю версию на текущий момент см. тут

Качаем драйвер и копируем его куда следует:
#Для i386

$wget http://sourceforge.net/projects/firebird/files/firebird-ODBC-driver/2.0.1-Release/OdbcFb-LIB-2.0.1.152.i686.gz
$tar zxvf OdbcFb-LIB-2.0.1.152.i686.gz
$sudo cp -f libOdbcFb.so /usr/lib/odbc/
$sudo chmod +x /usr/lib/odbc/libOdbcFb.so

#Для x86_64
Для 64-битной системы драйвер оказался недоработанным (косяки работы с датой и "съедались" некоторые буквы у char`в). Пришлось собирать из svn свой.
$wget https://dl.dropbox.com/u/61986139/libOdbcFb.so.tar.gz
$tar zxvf libOdbcFb.so.tar.gz
$sudo cp -f libOdbcFb.so /usr/lib/x86_64-linux-gnu/odbc/
$sudo chmod +x /usr/lib/x86_64-linux-gnu/odbc/libOdbcFb.so



Собсна, переходим к конфигурированию...

Настраиваем драйвер:

$sudo mcedit /etc/odbcinst.ini 

и  добавляем секцию для Firebird/InterBase:

#Для i386
[Firebird]
Description = Firebird/InterBase(r) driver
Driver  = /usr/lib/odbc/libOdbcFb.so
Setup  = /usr/lib/odbc/libOdbcFb.so
FileUsage = 1
UsageCount = 4

#Для x86_64
[Firebird]
Description = Firebird/InterBase(r) driver
Driver  = /usr/lib/x86_64-linux-gnu/odbc/libOdbcFb.so
Setup  = /usr/lib/x86_64-linux-gnu/odbc/libOdbcFb.so
FileUsage = 1
UsageCount = 4



Настраиваем непосредственно систему ODBC. Создаем DSN.
Сделать это можно 2-мя способами:

1. Через GUI:

Используя мастер настройки:
$ODBCCreateDataSourceQ4

Используя ODBC администратор:
$ODBCManageDataSourcesQ4


2. Путем редактированию конфигов:

$sudo mcedit /etc/odbc.ini

Добавляем в него соответствующую секцию:

#Для i386
[MixPos]
Description = Firebird/InterBase(r) driver
Driver  = Firebird

#Если сервер Выньдовый, то путь к БД пишется в таком формате
#Dbname = 192.168.10.41:d:\mixpos\data\mixdata.fdb

#Если сервер на Linux, то путь к БД пишется в таком формате
Dbname  = 192.168.10.1:/mnt/base/mixpos/mixdata.fdb

User                                = SYSDBA
Password                         = masterkey
Role                                = 
CharacterSet                   = WIN1251
ReadOnly                        = No
NoWait                           = No
Dialect                            = 3
QuotedIdentifier              = Yes
SensitiveIdentifier           = No
AutoQuotedIdentifier       = No

#Для x86_64
[MixPos]
Description = Firebird/InterBase(r) driver
Driver  = Firebird

#Если сервер Выньдовый, то путь к БД пишется в таком формате
#Dbname = 192.168.10.41:d:\mixpos\data\mixdata.fdb

#Если сервер на Linux, то путь к БД пишется в таком формате
Dbname  = 192.168.10.1:/mnt/base/mixpos/mixdata.fdb

User                                = SYSDBA
Password                         = masterkey
Role                                = 
CharacterSet                   = WIN1251
ReadOnly                        = No
NoWait                           = No
Dialect                            = 3
QuotedIdentifier              = Yes
SensitiveIdentifier           = No
AutoQuotedIdentifier       = No


где:
Description - описание драйвера.
Driver - имя раздела из файла /etc/odbcinst.ini.
Dbname - путь к  базе данных на Firebird/InterBase сервере, к которой нужно получить доступ.
User и Password - пользователь, грантованный на работу с данной БД.
Role - роль, с которой осуществляется подключение к БД.

CharacterSet - кодировка БД.


Конфигурирование завершено...


Устанавливаем параметры драйвера в систему:

$sudo odbcinst -i -d -f /etc/odbcinst.ini

Устанавливаем настройки ODBC в систему:

$sudo odbcinst -i -s -l -f /etc/odbc.ini

!!!!ВАЖНО!!!!
Если в /etc/odbc.ini вносятся какие-либо изменения, процедуру установки ностроек ODBC в систему, необходимо повторить (sudo odbcinst -i -s -l -f /etc/odbc.ini)


Проверка установленных DSN:

$odbcinst -s -q

Проверка подключения к БД:

$isql MixPos

видим что то типа:

+---------------------------------------+
| Connected!                                 |
|                                                   |
| sql-statement                             |
| help [tablename]                        |
| quit                                            |
|                                                   |
+---------------------------------------+
SQL> 
 

Чтобы удалить параметры драйвера из системы:
$sudo odbcinst -u -d -n Firebird

Чтобы удалить регистрацию DSN из системы:
$sudo odbcinst -u -s -l -n MixPos

Во избежание конфликтов, лучше удалить локальный настройки ODBC:
$rm /home/name/.odbc.ini


Все, можно работать.

среда, 13 февраля 2013 г.

Настройка доступа к Microsoft SQL Server через ODBC. Ubuntu 12.04

Возникла недавно необходимость получить доступ к M$ SQL базам из Linux через ODBC.

Для организации связки Linux->M$ SQL, в качестве odbc-драйвера, был выбран - FreeTDS.

Установим необходимые пакеты:
$sudo apt-get install tdsodbc unixodbc unixodbc-bin unixodbc-dev odbcinst

Настройка FreeTDS.

$sudo mcedit /etc/freetds/freetds.conf

Добавляем новую секцию для M$ SQL сервера (название секции может быть любым):

[MSSQL]
host = sql.mydomen.ru
port = 1433
tds version = 8.0
client charset = UTF8

где:
host - доменное имя или IP сервера с SQL.
tds version - версия протокола TDS. Версия 8.0 для Microsoft SQL Server 2000 и выше.


Настройка ODBC.

Настраиваем драйвер:

$sudo mcedit /etc/odbcinst.ini 

и  добавляем секцию для FreeTDS:

#Для i386
[FreeTDS]
Description = FreeTDS
Driver  = /usr/lib/odbc/libtdsodbc.so
Setup  = /usr/lib/odbc/libtdsS.so
FileUsage = 1
UsageCount = 4


#Для x86_64
[FreeTDS]
Description = FreeTDS
Driver  = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so
Setup  = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so
FileUsage = 1
UsageCount = 4



Настраиваем непосредственно систему ODBC. Создаем DSN.
Сделать это можно 2-мя способами:

1. Через GUI:

Используя мастер настройки:
$ODBCCreateDataSourceQ4

Используя ODBC администратор:
$ODBCManageDataSourcesQ4


2. Путем редактированию конфигов:

$sudo mcedit /etc/odbc.ini

Добавляем в него соответствующую секцию:

#Для i386
[MSSQL]
Description = FreeTDS driver
Driver  = FreeTDS
Servername = MSSQL
Database = myDB
UID  = sa
PWD  = sa


#Для x86_64
[MSSQL]
Description = FreeTDS driver
Driver  = FreeTDS
Servername = MSSQL
Database = myDB
UID  = sa
PWD  = sa


где:
Description - описание драйвера.
Driver - имя раздела из файла /etc/odbcinst.ini.
Servername - имя раздела, описывающего сервер БД, из файла /etc/freetds/freetds.conf.
Database - имя базы данных на M$ SQL сервере, к которой нужно получить доступ.
UID и PWD - пользователь, заведенный на M$ SQL сервере и грантованный на работу с данной БД.


Конфигурирование завершено.


Устанавливаем параметры драйвера в систему:

$sudo odbcinst -i -d -f /etc/odbcinst.ini

Устанавливаем настройки ODBC в систему:

$sudo odbcinst -i -s -l -f /etc/odbc.ini

!!!!ВАЖНО!!!!
Если в /etc/odbc.ini вносятся какие-либо изменения, процедуру установки ностроек ODBC в систему, необходимо повторить (sudo odbcinst -i -s -l -f /etc/odbc.ini)


Проверка установленных DSN:

$odbcinst -s -q

Проверка подключения к БД:

$tsql -S MSSQL -U sa -P sa

видим что то типа:

locale is "ru_RU.UTF-8"
locale charset is "UTF-8"
using default charset "UTF8"
1> 

Чтобы удалить параметры драйвера из системы:
$sudo odbcinst -u -d -n FreeTDS

Чтобы удалить регистрацию DSN из системы:
$sudo odbcinst -u -s -l -n MSSQL

Во избежание конфликтов, лучше удалить локальный настройки ODBC:
$rm /home/name/.odbc.ini


Все, можно работать.

вторник, 12 февраля 2013 г.

Убрать индикатор раскладки клавиатуры Ubuntu 12.04

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

Делаем резервную копию файла:

$sudo cp /usr/lib/gnome-settings-daemon-3.0/libkeyboard.so /usr/lib/gnome-settings-daemon-3.0/libkeyboard.so.bak

 Открываем файл на редактирование любым HEX-редактором

$sudo ghex /usr/lib/gnome-settings-daemon-3.0/libkeyboard.so

Ищем кусок кода
FF 83 F8 01 0F

и меняем 01 на 02, должно получится так:


Сохраняем файл и перезаходим в сессию.

!!!ВАЖНО!!! Значок не будет отображаться, если у вас меньше 3 вариантов раскладки клавиатуры.

(с) http://help.ubuntu.ru/wiki/трей_в_unity#убираем_индикатор_раскадки_клавиатуры

понедельник, 4 февраля 2013 г.

Login под доменным пользователем Ubuntu 12.04

По-умолчанию unity-greeter в Ubuntu 12.04 позволяет входить в систему только под зарегистрированными пользователями. В случае, когда необходимо ввести Login незарегистрированного пользователя, например доменного, необходимо проделать такие вот манипуляции:

$sudo mcedit /etc/lightdm/lightdm.conf

там должно быть как-то так:

[SeatDefaults]
greeter-show-manual-login=true
user-session=gnome-classic
greeter-session=unity-greeter
autologin-user=

пятница, 1 февраля 2013 г.

isql to xml Linux

Столкнулся давича с необходимостью выгрузки результата консольного запроса к BD Firebird в XML.
Выполнить запрос к FB из консоли можно при помощи утилиты isql.
Оказалось, что линуховый isql штатными средствами этого не умеет (выньдовый, к стати, умеет цуко).

Погуглив вдоволь, нашел 2 варианта решения. Первый, быстрый и универсальный: не зависит от текста запроса и количества строк в результате запроса. Второй, рассчитан только на элементарные тексты запросов и "долгий" , при большом количестве строк в результате запроса.

Итак, вот, собственно, оба решения:


Вариант 1. Воспользоваться утилитой odbcsql (спасибо hellcat) :


Утилиту можно скачать здесь. Утилита кросс-платформенная. Не чувствительна к битности ОС. Инструкция по установке и необходимые драйвера в комплекте.

Принцип работы с qdbcsql такой:

$odbcsql DSN=mydatabaseDSN Query.sql Output.xml Error.log

где
        DSN=mydatabaseDSN  - DSN или строка подключения к БД
        Query.sql - файл, в котором лежит текст SQL-запроса, который необходимо выполнить
        Output.xml - файл, в который необходимо положить результат запроса (в формате XML)
        Error.log - файл, в котором будут лежать логи


Вариант 2. Добавляем в БД две хранимых процедуры (stored procedure):

XML_GENERATOR_FIELDS:


SET TERM ^ ;

CREATE PROCEDURE XML_GENERATOR_FIELDS(
FIELDS VARCHAR(1024),
SQL_RIGHT VARCHAR(1024))
RETURNS (
RECORD INTEGER,
XML_TEXT VARCHAR(1024))
AS
declare variable i integer;
declare variable len integer;
declare variable c char(1);
declare variable field varchar(100);
BEGIN

/* lenght fields */
len = char_length(fields);

/* loop in fields string */
i = 1;
field = ''; /* clear field */
while (i <= len) do
begin

/* current char */
c = substring(fields from :i for 1 );

/* if comma, select values of current field */
if (c = ',') then
begin

/* trim fields */
field = trim(:field);

/* order by record in the sp (xml_generator) */
record = 0;

/* values of current field */
for execute statement
cast( 'select ' || :field || ' from ' ||
:sql_right as varchar(1024))
into :xml_text do
begin

/* formatting the xml line */
/* <field>value</field>    */
xml_text = '<' || :field || '>' ||
:xml_text || '</' || :field || '>';

/* order by in xml_generator */
record = record + 1;
suspend;

end

/* clear field */
field = '';

end

else

/* char by char... */
field = field || c;

/* next char */
i = i + 1;

end

END^

SET TERM ; ^

XML_GENERATOR:

SET TERM ^ ;SET TERM ^ ;

CREATE PROCEDURE XML_GENERATOR(
PHEADER VARCHAR(1024),
PSQL VARCHAR(1024))
RETURNS (
XML_TEXT VARCHAR(1024))
AS
declare variable field varchar(30);
declare variable fields varchar(1024);
declare variable fields_begin smallint;
declare variable fields_end smallint;
declare variable from_end smallint;
declare variable sql_end smallint;
declare variable sql_right varchar(1024);
declare variable i integer;
declare variable records integer;
declare variable table_name varchar(30);
BEGIN

/* uppercase and trim psql parameter */
psql = upper(trim(psql));

/* extract fields from psql parameter */
fields_begin = position(' ',:psql)+1;
fields_end = position(' FROM ', psql);
fields = substring(psql from :fields_begin
for :fields_end-:fields_begin) ||
','; /* for extract the last field */

/* extract table name, where, order by, etc... */
from_end = fields_end + 6;
sql_end = char_length(psql)-from_end+1;
sql_right = substring(psql from :from_end for sql_end) || ' ';

/* extract only table name for "all fields" and */
/* open/close tags in xml */
table_name = substring(sql_right from 1
for position(' ',sql_right)-1);

/* if fields = '*', fields = "all fields" of the table */
if (fields = '*,') then
begin
fields = '';
for select trim(rdb$field_name) from rdb$relation_fields
where rdb$relation_name = :table_name order by rdb$field_id
into :field do fields = fields || :field || ',';
end

/* if pheader xml isnull... */
if (:pheader is null) then
pheader = '<?xml version="1.0" encoding="UTF-8" ?>';

/* returns pheader to client... */
xml_text = :pheader;
suspend;

/* open table */
xml_text = '<' || :table_name || '>';
suspend;

/* record count... */
execute statement
cast('select count(*) from ' || :sql_right as varchar(1024))
into :records;

/* fields from another stored procedure */
/* loop first to last record */
i = 1;
while ( i <= records ) do
begin

/* open record */
xml_text = '<RECORD>';
suspend;

/* fields and values in another stored procedure */
for select xml_text from xml_generator_fields(:fields, :sql_right)
where record = :i into :xml_text do
suspend;

/* close record */
xml_text = '</RECORD>';
suspend;

/* next record */
i = i + 1;

end

/* close table */
xml_text = '</' || :table_name || '>';
suspend;

END^

SET TERM ; ^

Собственно эти две процедуры генерируют нам на выходе результат запроса уже в готовой XML схеме.

Выгружаем результат запроса в XML:

Создаем SQL-скрипт и пишем в него запрос:
$touch Script.sql
$echo "SELECT XML_TEXT FROM XML_GENERATOR('<?xml version="1.0" encoding="windows-1251" ?>',' select a from ADMINUSERS' )"

Здесь, параметр 1 ('<?xml version="1.0" encoding="windows-1251" ?>') - это загаловок нашего XML-файла, а параметр 2 (' select a from ADMINUSERS') - это интересующий нас запрос

Далее даем шыйд команду на выполнение нашего запроса и сохранение его результатов в файл:
$isql MixPos -b -x0x0A < Script.sql > Result.xml

где

- "MixPos" - это DSN для нашей БД

- параметр "-b" отключает лишние комментарии командной строки isql, чтоб остался только результат запроса без дополнительной служебной информации

- параметр "-x0x0A" заменяет разделитель колонок в результате запроса на "Enter"

- "Script.sql" - это файл, где лежит тест запроса

- "Result.xml" - это необходимы нам XML-файл, куда вернется результат запроса



Проверим выполнение:

$isql MixPos -b -x0x0A < Script.sql > Result.xml

Получим такой вот Result.xml:
<ADMINUSERS>
         <RECORD>
                <ADMIN_ID>1</ADMIN_ID>
                <PASSWD>********</PASSWD>
                <PUBLISHED>P</PUBLISHED>
                <LOGIN>admin</LOGIN>
         </RECORD>
</ADMINUSERS>

пятница, 11 января 2013 г.

Описание таблиц SQL для 1С V77

Содержание:

1. Описание общих таблиц 1С V77
1.1. Общее описание хранения данных 1С.
1.1.1. Хранение ID объекта
1.1.2. Хранение даты
1.1.3. Хранение времени
1.2. Описание таблиц 1С
1.2.1. Таблица _1SCONNECT - Сессии (соединения)
1.2.2. Таблица _1SCONST - Константы
1.2.3. Таблица _1SCRDOC - Ссылки документов
1.2.4. Таблица _1SJOURN - Журналы
1.2.5. Таблица _1SDNLOCK - Номера документов
1.2.6. Таблица _1SSTREAM - Последовательность документов
1.2.7. Таблица _1SSYSTEM - Системная
1.2.8. Таблица _1SUIDCTL - Уникальности
1.2.9. Таблица _1SUSERS - Соединения
2. Подсистема справочников и констант
2.1. Предисловие
2.2. Введение
2.3. Описание полей таблиц справочников
2.4. Описание полей таблицы констант и периодических реквизитов справочников
2.5. Примеры запросов
2.5.1. Пример 1. Получение всех записей справочника.
2.5.2. Пример 2. Получение записей только групп справочника.
2.5.3. Пример 3. Получение записей только элементов (не групп) справочника.
2.5.4. Пример 4. Получение записей только непомеченных элементов справочника.
2.5.5. Пример 5. Получение записей элементов с периодическим реквизитом на максимальную дату.
2.5.6. Пример 6. Получение записей элементов с периодическим реквизитом на конкретную дату.
2.5.7. Пример 7. Получение значений непериодических реквизитов агрегатных типов.
2.5.8. Пример 8. Получение записей элементов справочников с их родителями.
2.5.9. Пример 9. Выборка элементов из подчиненного справочника.
3. Подсистема документов и регистров
3.1. Предисловие
3.2. Введение
3.3. Общее описание хранения данных 1С в подсистеме документов
3.3.1. Краткое описание таблиц
3.3.2. Примеры запросов
3.3.2.1. Пример 1. Получение всех документов одного вида со всеми реквизитами шапки.
3.3.2.2. Пример 2. То же, включая все общие и системные реквизиты.
3.3.2.3. Пример 3. То же, плюс все реквизиты табличной части.
3.3.2.4. Пример 4. То же, с фильтром по дате документа (за один день).
3.3.2.5. Пример 5. Получение документов родителей по подчиненному документу.
3.3.2.6. Пример 6. Получение списка подчиненных документов.
3.3.2.7. Пример 7. Получение списка только проведенных документов.
3.3.2.8. Пример 8. Получение списка документов, принадлежащих разным типам учета.
3.3.2.9. Пример 9. Получение списка документов по графе отбора.
3.4. Регистры
3.4.1. Общее описание подсиcтемы регистров
3.4.2. Рассмотрим примеры
3.4.2.1. Структура и описание полей таблиц регистров
3.4.2.1.1. Таблица RGxxx
3.4.2.1.2. Таблица RAxxx
3.4.2.2. Пример 1. Получение итогов на ТА или на конец периода.
3.4.2.3. Пример 2. Получение сумм прихода и расхода за один месяц (оборотов).
3.4.2.4. Пример 3. Получение итогов и остатков на произвольную дату.
3.4.2.5. Пример 4. Получение итогов и остатков на документ.
3.4.2.6. Пример 5. Получение остатков и оборотов за произвольный период.
3.4.2.7. Пример 6. Пересчет итогов по одному из регистров остатков.




1. Описание общих таблиц 1С V77

1.1. Общее описание хранения данных 1С.

Данная статья не является полным описанием и не претендует на оригинальность. Цель - облегчить понимание работы 1С, а также способствовать написанию прямых запросов к таблицам 1С. Статья написанная автором на основе личного опыта, а также той немногочисленной информации, которую можно раздобыть в Internet в свободном (и не очень) доступе. Возможно некоторые поля или таблицы могут измениться с выходом последующих релизов, но пока (до 25 релиза включительно) таких изменений не было замечено.


1.1.1. Хранение ID объекта

ID может иметь 3 представления (уровня) в зависимости от длины (количества значащих символов):

9 символов – определен тип и вид объекта (например «Справочник.Клиенты»), в ID включается только порядковый номер в 36-ричной системе исчисления. Под порядковый номер отводятся первые 6 символов, последние 3 символа зарезервированы под код базы УРБД.

13 символов – определен только тип объекта, вид не задан (например «Справочник»). Первые 4 символа – идентификатор вида (как он задан в метаданных), последующие 9 символов – по аналогии с предыдущим пунктом.

23 символа – не определен тип и вид объекта. В таком случае в первых 2 символах хранится тип объекта (будет рассмотрен ниже), следующие 13 символов формируются аналогично предыдущему пункту.

В некоторых случаях, при указании неопределенного типа объекта (длина ID кода 23 символа) создается дополнительное поле с символом «T» вначале (например, если в справочнике задан реквизит SP235 как неопределенный, то в таблице справочника будет создано еще одно поле TSP235). Рассмотрим поподробнее значения этого поля. Это поле по умолчанию заполняется пустой строкой (3 пробела).

Для невыбранного типа (заполняется по умолчанию): TSP235 = ' ' (3 пробела), при этом связанное неопределенное поле SP235 заполняется значением 'U ' (символ U и 22 пробела), что скорее всего означает «Undefined», т.е. неопределенный.

Для типов «Число»: связанное поле SP235 заполняется строковым значением числа, но с символом «N» в начале строки (например 'N 2' для целого числа 2). Возможные значения TSP235:

'F30' - число с плавающей запятой (float);

'320' - число, состоящее только из десятичных (0.99, т.е. Число(3,2)).

'A00' - целое число (int);

Для типов «Строка»: связанное поле SP235 заполняется строкой, но с символом 'S' слева (например 'SАбвгдежзклимно '). Возможные значения TSP235 в этом случае: количество знаков, определенных для строкового значения, например, '14' - количество знаков в строке, в 36-ричном формате (_IDToStr). Следует отметить, хотя 1С и назначает длину строки больше 22 символов, но фактически в этом поле можно хранить только 22 символа (1 разряд приходиться на обозначение типа поля 'S').

Для типов «Дата»: связанное поле SP235 заполняется строкой, но с символом 'D' слева (например 'D20050303 '). TSP235 - ' ' (3 пробела), т.е. поле пустое.

Для типов 1С (Справочник, Документ, Перечисление, Счет, и т.д.): связанное поле SP235 заполняется строкой включающий полный идентификатор объекта (его тип, вид, внутренний ИД код). Следует отметить, что для типов «Перечисление» нельзя не назначать вид, так как неназначение вида приведет к несовпадению типов и незаполнению неопределенного поля. Возможные значения TSP235:

'0 ' - был назначен только тип объекта, вид не назначен (например «НазначитьТип(«Справочник»)»)

'1 ' - был назначен как тип объекта, так и его вид (например «НазначитьТип(«Справочник.Клиенты»)»)

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


1.1.2. Хранение даты

Дата может храниться как в виде строки, так и в виде типа «Дата».

В случае хранения даты в стандартном формате (тип Дата), при добавлении такого поля в таблицу для базы в формате SQL - поле заполняется значением «1753-01-01 00:00:00.000».

В случае хранения даты в виде строки, дата сохраняется в формате «ГГГГММДД», т.е. 4 символа – год (0000-9999), 2 – месяц (01-12), 3 – день (01-31). Т.е. для даты 29.07.2005 поле будет равным '20050729'.


1.1.3. Хранение времени

Время может храниться в двух форматах: Числовое представление, Строковое представление.

В случае числового хранения времени оно отсчитывается от начала суток в десятитыcячных долях секунды. Т.е. фактически будет получено число: (Часы*3600+Минуты*60+секунды)*10000. Т.е. Для времени 19:05:36 – 687360000 (1С умеет учитывать время до 10000 долей секунды, как в случае с документами).

В случае числового хранения времени время с числового значения (Часы*3600+Минуты*60+секунды)*10000 переводиться в 36-ричный формат. Так, для времени 19:05:36 - BD8IDC.


Дополнительную информацию можно получить здесь:
http://www.mista.ru/articles1c/hare/article.8.html
http://www.mista.ru/articles1c/hare/article.11.html
http://www.mista.ru/articles1c/hare/article.12.html
http://www.mista.ru/articles1c/hare/article.13.html
http://1csql.ru/materials/articles/develop.html
http://www.gendin.ru/budni/b25.html
http://www.gendin.ru/budni/b26.html
http://gendin.ru/budni/files/1ctables.xls
http://web-team.narod.ru/index.html
http://www.sinor.ru/~my1c/knowhow/fields.html



1.2. Описание таблиц 1С

1.2.1. Таблица _1SCONNECT - Сессии (соединения)

Краткое описание: таблица предназначена для хранения уникального идентификатора сессии первого присоединившегося к базе данных пользователя в режиме предприятия (поэтому в таблице всегда только одна запись). Судя по всему, именно по наличию записи в этой таблицы и происходит верификация процедур при первом запуске в SQL.

Название поляОписание
CONNECTUUID GUID (уникальный идентификатор) первого сеанса, соединившегося с 1С в режиме предприятия. Тип - Строка(36).


1.2.2. Таблица _1SCONST - Константы

Краткое описание: таблица предназначена для хранения значений констант и периодических реквизитов справочников.

Название поляОписание
ROW_ID Порядковый номер записи в таблице. Тип - Число(int).
OBJID ID объекта периодического реквизита (справочника) или периодической константы, для непериодических констант это поле равно ‘     0   ’. Тип - Строка(9).
ID ID (идентификатор) константы или периодического реквизита справочника. Тип представлен в виде десятичного числа (_StrToID()). Тип - Число(int).
DATE Дата установки периодического реквизита. Для базы в формате SQL и для непериодического реквизита (или константы) поле заполняется значением '1753-01-01 00:00:00.000'. Тип - Дата (datetime для SQL).
VALUE Значение константы или периодического реквизита. Для неопределенных типов по умолчанию заполняется «U». Тип- Строка(255).
DOCID ID (идентификатор) документа (поле, связанное с полем IDDOC таблицы журналов (_1SJOURN)). Для значений установленных вручную или непериодических заполняется '     0   '.
TIME Время установки значения в десятичном формате ((Часы*3600+Минуты*60+секунды)*10000). Тип - Число(int).
ACTNO Номер движения документа (включая каждое движения по регистрам и запись периодических реквизитов за исключением проводок). В случае непериодического значения заполняется нулем. Тип - Число(int).
LINENO_ Номер строки документа (заполняется при вызове метода ПривязыватьСтроку(), если привязка не выполнена или непериодическое значение - заполняется нулем). Тип - Число(tinyint).
TVALUE Заполняется только для неопределенных реквизитов, для типов данных 1С (когда длина ID равна 23 символам). Тип - Строка(3).



1.2.3. Таблица _1SCRDOC - Ссылки документов

Краткое описание: таблица предназначена для хранения ссылок подчиненных документов и граф отборов реквизитов документов, которые не являются общими реквизитами. Для хранения ссылок документов в подчиненном документе должен быть реквизит (поле) в котором будет ссылка на документ-родитель. Без такой ссылки документ не считается подчиненным.

Название поляОписание
ROW_ID Порядковый номер записи в таблице. Номер проставляется автоматически при вводе новой строки. При изменении даты документа, введенного на основании, это поле «переприсваивается». Тип - Число(int).
MDID Номер графы отбора подчиненного документа. Тип - Число(int).
PARENTVAL Полный идентификатор документа родителя.
Поле, состоящее из комбинации:
  • код отбора
  • вид документа (IDDocDef в 36 системе)
  • идентификатор документа родителя (IDDoc)
Тип – Строка(23).
CHILD_DATE_TIME_IDDOC Дата + Время (в 36-ричном формате) + ИД подчиненного документа. Тип – Строка(23).
CHILDID ИД подчиненного документа. Тип – Строка(9).
FLAGS Тип – Число(1).

1.2.4. Таблица _1SJOURN - Журналы

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

Название поляОписание
ROW_ID Порядковый номер записи в таблице. Номер присваивается в порядке ввода документов. При изменении даты или времени – не изменяется. Тип - Число(4).
IDJOURNAL ID журнала документов, которому принадлежит документ (из метаданных). Тип - Число(4).
IDDOC ID документа в 36-ричной системе счисления. Тип - Строка(9).
IDDOCDEF ID вида документа. Номер документа из метаданных (номер, а не 36-ричное представление). Тип - Число(4).
APPCODE
Число, отражающее принадлежность документа к компонентам. Возможные значения:
  • 0 - не принадлежит никакому учету.
  • (1) 000001 - опер учет.
  • (2) 000010 - расчет.
  • (4) 000100 - бух учет.
  • (8) 001000 - движения периодических реквизитов.
  • (16) 010000 - существует документ операция у документа (документ может быть не проведен, но тогда должно быть выбрано "Создавать операцию - Всегда" в окне свойств документа).
  • (32) 100000 - Выполнен метод Операция.Записать(); в документе, принадлежащем бух. учету.
  • (20) 010100 - Это документ "Операция".
Возможна их комбинация, например 6 = 2+4 (бух. учет + расчет), 7 = все три компоненты. Тип - Число(2).
DATE_TIME_IDDOC Составное поле, обозначающее позицию документа во времени + ID документа.
  • 8 первых символов - дата документа в виде «ГГГММДД».
  • 6 следующих символов - время документа в 36-ричной системе исчисления, причем время рассчитывается от начала дня в сотых частях миллисекунды (10-5), т.е. время 00:00:01 будет равно 10000, или же в 36-ричной системе «7PS».
  • 9 последних символов - ID документа. Этот ID также составной, 6 символов - порядковый номер введенного документа в систему в 36-ричной системе (причем считаются не только сохраненные документы, а также те, которые не были сохранены), последние 3 символа - идентификатор базы УРИБ (если данные вводятся в распределенной ИБ).
Тип - Строка(23).
DNPREFIX Префикс номера документа. Тип - Строка(18). Для документов, у которых код числовой, это поле равно десятичному ID вида документа. Если нумерация в пределах периода - то также хранится и период в виде ГГГГММДД (например 2006 для нумерации в пределах года).
DOCNO Номер документа. Длина определяется максимальным номером всех документов. Тип - Строка (максимальное значение - 20).
CLOSED Флаг проведения документа. Для каждого набора компонент свое значение (аналогично полю APPCODE). Значение последнего байта отвечает за флаг проведения документа. Тип - Число(1).
ISMARK Флаг пометки на удаление. 0 - не помечен, 1 - помечен. Тип - Число(1,0) (бинарное значение).
ACTCNT Счетчик действий (движения) для документа (один документ может вызывать несколько движений регистров). Фактически хранит информацию о количестве движений по всем регистрам + записи периодических реквизитов (без учета проводок по бух. учету). Тип – Число(4).
VERSTAMP Количество изменений записи таблицы. Изменением считается любое действие "Изменить (открыть)" + действия при изменении структуры. Тип - Число(4).
RFxxx Флаг наличия движений по регистру "xxx". Тип - Число(1,0).
SPyyy Значение общих реквизитов документов, в которых установлено свойство «отбор». Если свойство «отбор» не установлено, то общие реквизиты хранятся в таблицах документов (DHxxx). Тип - Число, Строка, Дата.
TSPyyy Дополнение к общим реквизитам, заполняется только для неопределенных типов значений реквизитов отбора (используется совместно с полем «SPyyy»). Тип - Строка(3).
DSzzz Флаг принадлежности последовательности. ZZZ - десятичный ID последовательности. Принимает 3 значения: 0 - документ не принадлежит последовательности, 1 - документ находится на границе или за границей последовательности (или находился на ней), 2 - документ находится перед границей последовательности (устанавливается в момент проведения).

1.2.5. Таблица _1SDNLOCK - Номера документов

Краткое описание: таблица предназначена для хранения информации о зарезервированных номерах документов, т.е. тех. номерах документов, которые вводятся в данный момент в систему, но еще не сохранены (не записаны в таблицу журналов _1SJOURN)

Название поляОписание
DNPREFIX Идентификатор вида документа или справочника. Для документа также включает время (всегда 0 0). Тип – Строка(28).
DOCNO Зарезервированный номер. Тип – Строка(10) (определяется максимальным номером в системе, максимальное значение - 20).

1.2.6. Таблица _1SSTREAM - Последовательность документов

Краткое описание: таблица предназначена для хранения информации о последовательностях.

Название поляОписание
ID Идентификатор последовательности (числовое представление). Тип – Число(4).
DATE_TIME_DOCID Дата+Время+идентификатор документа, на котором установлена последовательность. Это поле аналогично полю DATE_TIME_IDDOC таблицы _1SJOURN. Тип – Строка(23).

1.2.7. Таблица _1SSYSTEM - Системная

Краткое описание: таблица предназначена для хранения информации о датах точки актуальности и рассчитанного периода бухгалтерских итогов, также в таблице хранится информация о параметрах УРБД.

Название поляОписание
CURDATE Дата точки актуальности. Тип - Дата (для SQL DateTime).
CURTIME Время точки актуальности. Время хранится в десятичном виде: (Часы*3600+Минуты*60+секунды)*10000. Преобразовав значение этого поля в 36-ричное значение, получим вторую подгруппу значений поля DATE_TIME_IDDOC (сред(DATE_TIME_IDDOC ,9,6)) таблицы 1SJOURN. Тип - Число(4).
EVENTIDTA ID документа, на котором установлена ТА. Тип - Строка(9).
DBSIGN Код базы УРИБ (ТекущаяИБКод()). Тип - Строка(3).
DBSETUUID GUID базы УРИБ. Тип - Строка(36).
SNAPSHPER Периодичность итогов регистров остатков (устанавливается в меню Операции - Управление оперативными тогами - Периодичность сохранения остатков). F – 5 дней. C – Декада (10 дней). T – 15 дней. M – месяц. Тип - Строка(1).
ACCDATE Дата актуальности бухгалтерских итогов. Тип - Дата (для SQL DateTime).
FLAGS Тип - Число(4).
Периодичность итогов регистров оборотов: «D» - День, «W» - Неделя, «C» - Декада, «M» - Месяц, «Q» - Квартал, «Y» - Год.



1.2.8. Таблица _1SUIDCTL - Уникальности

Краткое описание: таблица предназначена для хранения информации о последнем ID коде документов и справочников.

Название поляОписание
TYPEID Идентификатор вида справочника, или же 0 для всех документов (у всех документов сквозная нумерация ID). Тип – Число(4).
MAXID Максимальный используемый идентификатор (ID).

1.2.9. Таблица _1SUSERS - Соединения

Краткое описание: таблица предназначена для хранения информации о подключенных пользователях. В таблице всегда одна строка. Именно по наличию строки в этой таблице 1С судит об аварийном завершении программы и предлагает переиндексировать ИБ в случае dbf-версии.

Название поляОписание
USRSCNT Количество подключенных пользователей к 1С в режиме 1С предприятия.
NETCHGCN Счетчик действий пользователей, которые привели к изменению в базе данных (записи в таблицы). Счетчик учитывает количество записей в таблицы (т.е. в случае проведения документа с несколькими движениями учитывается каждое движение).

2. Подсистема справочников и констант

2.1. Предисловие

Данная часть предназначена для тех, кто пытается разобраться в структуре хранения данных в системе 1С версии 7.7. А также покажет, как можно получать данные напрямую из таблиц 1С, минуя программу 1С. Для понимания того, о чем идет речь в статье, необходимо понимать принципы работы 1С версии 7.7 и иметь начальные навыки работы с SQL Server Enterprise Manager и SQL Server Query Analyzer.


2.2. Введение

Что являют собой справочники? В понимании 1С - это объекты для хранения условно постоянной информации (константы - для хранения постоянной или очень редко изменяющейся информации).

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

Все таблицы справочников имеют почти одинаковые имена, эти таблицы именуются первыми двумя символами SC, далее следует десятичное представление справочника. Это десятичное представление - сквозная нумерация всех объектов внутри конфигурации (включая реквизиты и т.д.). В наших примерах таблица справочника будет иметь имя таблицы SC19, т.е. этот справочник был создан 19-ым по номеру в конфигурации среди всех объектов.


2.3. Описание полей таблиц справочников

Название поляОписание
ROW_ID Порядковый номер записи в таблице. Тип - Число(int).
ID ID элемента, тип «строка», по этому полю осуществляется связь с таблицами, где в качестве реквизита выбирается справочник, а также с таблицей констант (для периодических реквизитов). Нумерация сквозная, именно этот код должен быть уникальным в пределах таблицы. Тип - Char(9).
CODE Номер элемента (Код) справочника. Тип - Char(n), где n - длина номера справочника. Если длина кода = 0, это поле отсутствует.
DESCR Наименование элемента. Тип - Char(n), где n - длина номера справочника. Если длина кода = 0, это поле отсутствует.
ISMARK Флаг пометки на удаление элемента. Тип - bit. 0 - не помечен, 1 - помечен.
VERSTAMP Количество изменений записи таблицы. Изменением считается любое действие "Изменить (открыть)" + действия при изменении структуры. Тип - Integer.
SPххx Реквизит справочника(типы: Numeric, DateTime, Char(n), n=1:999).
TSPххx Дополнение к реквизиту, заполняется только для неопределенных типов значений (используется совместно с полем «SPххx»). Тип - Char(3).
PARENTID ID элемента, являющегося родителем (группой) для текущей записи (элемента). Поле связано с полем ID или же если родителя нет - заполнено пустым ID '     0   '. Тип - Сhar(9). Это поле появляется в таблице справочника лишь тогда, когда справочник имеет больше 1 уровня.
ISFOLDER Флаг того, что запись является элементом или группой. Для групп это поле равно 1, для элементов - 2. Тип - tinyint (0-255).
PARENTEXT ID элемента, являющегося владельцем (этот элемент подчинен владельцу с этим ID). Тип - Сhar(9). Это поле появляется в таблице справочника лишь тогда, когда справочнику установлено значение "Подчинен" одному из справочников системы.


Особое внимание надо уделить полям «TSP». Это поле создается лишь тогда, когда реквизит (измерение, ресурс) имеет неопределенный тип (длина ID кода 23 символа). Опытным путем было установлено, что это поле по умолчанию заполняется пустой строкой (3 пробела).

Для невыбранного типа (заполняется по умолчанию): TSP235 = ' ' (3 пробела), при этом связанное неопределенное поле SP235 заполняется значением 'U ' (символ U и 22 пробела), что скорее всего означает «Undefined», т.е. неопределенный.

Для типов «Число»: связанное поле SP235 заполняется строковым значением числа, но с символом «N» в начале строки (например 'N 2' для целого числа 2). Возможные значения TSP235:

'F30' - число с плавающей запятой (float);
'320' - число, состоящее только из десятичных (0.99, т.е. Число(3,2)).
'A00' - целое число (int);

Для типов «Строка»: связанное поле SP235 заполняется строкой, но с символом 'S' слева (например 'SАбвгдежзклимно '). Возможные значения TSP235 в этом случае: количество знаков, определенных для строкового значения, например, '14' - количество знаков в строке, в 36-ричном формате (_IDToStr). Следует отметить, хотя 1С и назначает длину строки больше 22 символов, но фактически в этом поле можно хранить только 22 символа (1 разряд приходиться на обозначение типа поля 'S').

Для типов «Дата»: связанное поле SP235 заполняется строкой, но с символом 'D' слева (например 'D20050303 '). TSP235 - ' ' (3 пробела), т.е. поле пустое.

Для типов 1С (Справочник, Документ, Перечисление, Счет, и т.д.): связанное поле SP235 заполняется строкой включающий полный идентификатор объекта (его тип, вид, внутренний ИД код). Следует отметить, что для типов «Перечисление» нельзя не назначать вид, так как неназначение вида приведет к несовпадению типов и незаполнению неопределенного поля. Возможные значения TSP235:

'0 ' - был назначен только тип объекта, вид не назначен (например «НазначитьТип(«Справочник»)»)

'1 ' - был назначен как тип объекта, так и его вид (например «НазначитьТип(«Справочник.Клиенты»)»)



2.4. Описание полей таблицы констант и периодических реквизитов справочников

Описание таблицы _1SCONST находится выше.

Необходимо только учесть, что значения периодических реквизитов элементов справочников хранятся в таблице _1SCONST с заполненным полем OBJID, которое равно полю ID таблицы справочника. Для констант же значение поля OBJID всегда равно ' 0 '. Также необходимо учесть, что выбрать все периодические значения всех реквизитов одного элемента (одной записи) невозможно. Дело в том, что в поле OBJID хранится краткий (строка 9 символов) ID, а такое значение ID не подразумевает определения вида справочника. Соответственно чтобы получить значение конкретного периодического реквизита надо знать десятичное значение реквизита (_StrToID). Т.е., выбрав только с условием по OBJID получим периодические реквизиты всех справочников, с таким ID, а не только одного. Но, поскольку нумерация всех ID объектов конфигурации (включая и все реквизиты) сквозная, то не может быть в двух разных справочниках реквизитов с одинаковым ID реквизита. Соответственно, необходимым условием для получения значений периодических реквизитов является как условие по полю OBJID (ID элемента справочника), так и по полю ID (десятичное значение ID реквизита справочника).


2.5. Примеры запросов

2.5.1. Пример 1

Получение всех записей справочника (кроме периодических реквизитов). SC19 - имя таблицы справочника.

SELECT * FROM SC19

2.5.2. Пример 2

Получение записей только групп справочника (кроме периодических реквизитов). ISFOLDER - флаг того, что это элемент или группа элементов.

SELECT * FROM SC19
WHERE
     ISFOLDER = 1

2.5.3. Пример 3

Получение записей только элементов (не групп) справочника (кроме периодических реквизитов).

SELECT * FROM SC19
WHERE
     ISFOLDER = 0

2.5.4. Пример 4

Получение записей только непомеченных элементов (не групп) справочника (кроме периодических реквизитов). ISMARK - флаг пометки элемента (или группы) на удаление.

SELECT * FROM SC19
WHERE
     ISFOLDER = 0
     AND ISMARK = 0

2.5.5. Пример 5

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

SELECT TabSpr.*, ISNULL(TabConst.VALUE,'')
FROM SC19 As TabSpr
LEFT OUTER JOIN _1SCONST As TabConst
          ON ((TabSpr.ID = TabConst.OBJID)
          AND (TabConst.ID = 101)
          AND (TabConst.DATE =
                   (SELECT MAX(TabConstl.DATE)
                    FROM
                        _1SCONST AS TabConstl
                    WHERE
                         TabConstl.OBJID = TabConst.OBJID
                         AND TabConstl.ID = TabConst.ID))
)

В этом примере могут возвращаться и значения NULL для тех реквизитов, для которых не были установлены периодические реквизиты:

SELECT TabSpr.*, (SELECT (TabConst.VALUE) 
                                FROM
                                       _1SCONST As TabConst
                                WHERE
                                       (TabSpr.ID = TabConst.OBJID)
                                       AND (TabConst.ID = 101)
                                       AND (TabConst.DATE =
                                       (SELECT MAX(TabConstl.DATE)
                                       FROM 
                                            _1SCONST AS TabConstl
                                       WHERE
                                             TabConstl.OBJID = TabConst.OBJID
                                             AND TabConstl.ID = TabConst.ID)
                                        )
                                )
FROM SC19 As TabSpr

Применение конструкции UNION. В первом запросе получаем список только тех записей, для которых есть установленные периодические реквизиты, а во втором - всех остальных:

SELECT TabSpr.*, ISNULL(TabConst.VALUE,'')
FROM SC19 As TabSpr
LEFT OUTER JOIN _1SCONST As TabConst
          ON ((TabSpr.ID = TabConst.OBJID) AND (TabConst.ID = 101))
WHERE
          (TabConst.DATE =
          (SELECT MAX(TabConstl.DATE)
          FROM _1SCONST AS TabConstl
          WHERE
                       (TabConstl.OBJID = TabConst.OBJID)
                       AND (TabConstl.ID = TabConst.ID)
            )
)
UNION ALL

SELECT TabSpr.*, ''
FROM SC19 As TabSpr
WHERE
           NOT EXISTS(
           SELECT *
           FROM _1SCONST As TabConst
           WHERE
                   (TabSpr.ID = TabConst.OBJID)
                   AND (TabConst.ID = 101)
)


2.5.6. Пример 6

Получение записей элементов с периодическим реквизитом, на конкретную дату.

Периодический реквизит получается на 11.03.2006. В данном примере условие TabConst.ID = 101 необходимо для отбора периодических значений только по реквизиту с десятичным ID кодом, равным 101.

SELECT TabSpr.*, ISNULL(TabConst.VALUE,'')
FROM SC19 As TabSpr
LEFT OUTER JOIN _1SCONST As TabConst
          ON ((TabSpr.ID = TabConst.OBJID)
                  AND (TabConst.ID = 101)
                  AND (TabConst.DATE =
                  (SELECT MAX(TabConstl.DATE)
                  FROM _1SCONST AS TabConstl
                  WHERE
                          TabConstl.OBJID = TabConst.OBJID
                          AND TabConstl.ID = TabConst.ID
                          AND TabConstl.DATE <= Convert(DateTime,'20060311',112)
                   )
                 )
)


2.5.7. Пример 7

Рассмотрим теперь пример получения значений непериодических реквизитов справочников, которые являются документами или элементами справочника. В системе 1С получение значений таких реквизитов осуществляется обращением к реквизитам и их атрибутам. Например, получение значение реквизита "Менеджер" элемента справочника "Контрагенты" (наименование менеджера). В 1С это легко реализуется, например, если СпрКонтрагенты является объектом "Справочник.Контрагенты" и спозиционирован на конкретном элементе (например "НайтиПоНаименованию("Иванов А. А.")") - то получение менеджера этого элемента осуществляется так: "СпрКонтрагенты.Менеджер.Наименование". Но в самой таблице справочника "Контрагенты" (допустим это таблица "SC191") в поле, отвечающем за реквизит "Менеджер" (например "SP10494") будут значение ID элемента справочника "Менеджеры", а не наименование менеджера (и это вполне нормально). Само же наименование менеджера хранится в другой таблице, это таблица "Сотрудники" (SC258).

Итак, для этого примера надо выполнить запрос, выполняющий 2 действия: 1 - позиционирование на элемент с наименованием "Иванов А. А.", 2 - получение наименования менеджера, для спозиционированного элемента справочника Контрагенты.

Первая часть запроса будет выглядеть так:

SELECT TabSpr.DESCR As Наименование
FROM SC191 As  TabSpr
WHERE
      TabSpr.DESCR = 'Иванов А. А.'

Но приведенный выше код содержит ошибку. Дело в том, что поле DESCR определено как Char, и его длина строго задана и равна длине наименования для справочника "Контрагенты". Например, если длина наименования 50 символов, то предыдущий запрос надо было написать так:

SELECT TabSpr.DESCR As Наименование
FROM SC191 As  TabSpr
WHERE
      TabSpr.DESCR = 'Иванов А. А.                                      '

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

Для первого случая пример запроса будет такой:

SELECT TabSpr.DESCR As Наименование
FROM SC191 As  TabSpr
WHERE
       RTRIM(TabSpr.DESCR) = 'Иванов А. А.'

Для второго случая пример запроса будет такой:

DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT TabSpr.DESCR As Наименование
FROM SC191 As  TabSpr
WHERE
       TabSpr.DESCR = @NAIM

Вторая часть запроса, получение имени менеджера. Необходимо сделать выборку из 2 таблиц. Это можно сделать как минимум двумя способами. В первом примере выборка из 2 таблиц с условием:

DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT TabSpr.DESCR As Наименование, TabManag.DESCR As Менеджер
FROM SC191 As  TabSpr, SC258 As TabManag
WHERE
      TabSpr.DESCR = @NAIM
      AND TabSpr.SP10494 = TabManag.ID

Во втором примере выборка из 2 таблиц с объединением. Этот код аналогичен предыдущему, за исключением того, что происходит объединение с условием, а не выборка из таблиц с условием:

DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT TabSpr.DESCR As Наименование, TabManag.DESCR As Менеджер
FROM SC191 As  TabSpr
INNER JOIN SC258 As TabManag
            ON TabSpr.SP10494 = TabManag.ID
WHERE
      TabSpr.DESCR = @NAIM

Но эти оба примера не совсем корректные. Дело в том, что запросы работают по полном объединении (или полном условии), т.е. в выборку попадут лишь те записи из таблицы Контрагентов, для которых есть записи в таблице Сотрудников (т.е. поле "Менеджер" справочника Контрагенты заполнено). Если же для элемента с наименованием 'Иванов А. А.' поле Менеджер пустое (в таблице находиться или NULL или значение ' 0 ', именно так 1С хранит значения невыбранных реквизитов), то в выборку не попадет строка таблицы, так как не выполняется условие на вхождение в таблицу Сотрудников (в таблице SC258 в столбце ID нет ни одной записи, для которой есть значение ' 0 ').

Первый пример будет выглядеть так:

DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT TabSpr.DESCR As Наименование, TabManag.DESCR As Менеджер
FROM SC191 As  TabSpr, SC258 As TabManag
WHERE
      TabSpr.DESCR = @NAIM
      AND TabSpr.SP10494 *= TabManag.ID

Второй пример выборки со связыванием 2 таблиц:

DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT TabSpr.DESCR As Наименование, TabManag.DESCR As Менеджер
FROM SC191 As  TabSpr
LEFT OUTER JOIN SC258 As TabManag
          ON TabSpr.SP10494 = TabManag.ID
WHERE
      TabSpr.DESCR = @NAIM


2.5.8. Пример 8

Рассмотрим пример получения значений записей элементов справочников с их родителями (группами).

Для примера возьмем тот же справочник "Контрагенты" (допустим это таблица "SC191"). Самый простой пример - получение родителя для каждого элемента (не для родителей).

SELECT TabSpr.DESCR As Наименование, TabSprGr1.DESCR As Родитель
FROM SC191 As  TabSpr
LEFT OUTER JOIN SC191 As TabSprGr1
          ON TabSpr.PARENTID = TabSprGr1.ID
WHERE
      TabSpr.ISFOLDER = 2

В этом коде условие TabSpr.ISFOLDER = 2 необходимо для отбора только элементов (не групп). Левое внешнее связывание применяется для того чтобы вывести все элементы, а не только те, у которых есть выбранные родители (в случае с INNER JOIN).

Рассмотрим теперь более сложный пример. Получим всех родителей для элементов (т.е. включая и родителей родителей). Для получения родителей надо знать максимальное количество родителей. Это число легко узнать с помощью команды "Метаданные.Справочник(х).КоличествоУровней". Приведем пример для значения количества уровней, равного 4.

SELECT TabSprGr3.DESCR As Родитель3, TabSprGr2.DESCR As Родитель2,
               TabSprGr1.DESCR As Родитель, TabSpr.DESCR As Наименование
FROM SC191 As  TabSpr
LEFT OUTER JOIN SC191 As TabSprGr1
          ON TabSpr.PARENTID = TabSprGr1.ID
          LEFT OUTER JOIN SC191 As TabSprGr2
                    ON TabSprGr1.PARENTID = TabSprGr2.ID
                    LEFT OUTER JOIN SC191 As TabSprGr3
                              ON TabSprGr2.PARENTID = TabSprGr3.ID
WHERE
      TabSpr.ISFOLDER = 2

В этом коде условие TabSpr.ISFOLDER = 2 необходимо для отбора только элементов (не групп). Количество внешних связываний равно количеству уровней справочника - 1 (в приведенном примере 3).

В результате выполнения данного запроса получаются данные по всем возможным родителям, даже если нет родителей у элемента 1 или 2, то все равно будет 3 колонки с родителями, но в таком случае в качестве родителя будет значение NULL. Для того чтобы в первой колонке всегда был родитель (если есть у элемента родитель), то необходимо выполнить вот такой запрос:

SELECT 
CASE
         WHEN TabSprGr3.DESCR IS NOT NULL THEN TabSprGr3.DESCR
         ELSE
         CASE WHEN TabSprGr2.DESCR IS NOT NULL THEN TabSprGr2.DESCR
         ELSE TabSprGr1.DESCR END END As Родитель3,
         CASE WHEN (TabSprGr2.DESCR IS NOT NULL)
                    AND (TabSprGr3.DESCR IS NOT NULL) THEN TabSprGr2.DESCR
         ELSE TabSprGr1.DESCR END As Родитель2,
         CASE WHEN (TabSprGr2.DESCR IS NOT NULL)
                    AND (TabSprGr3.DESCR IS NOT NULL) THEN TabSprGr1.DESCR
         ELSE NULL END As Родитель,
TabSpr.DESCR As Наименование
FROM SC191 As  TabSpr
LEFT OUTER JOIN SC191 As TabSprGr1
          ON TabSpr.PARENTID = TabSprGr1.ID
          LEFT OUTER JOIN SC191 As TabSprGr2
                    ON TabSprGr1.PARENTID = TabSprGr2.ID
                    LEFT OUTER JOIN SC191 As TabSprGr3
                              ON TabSprGr2.PARENTID = TabSprGr3.ID
WHERE
       TabSpr.ISFOLDER = 2

В этом коде условие TabSpr.ISFOLDER = 2 необходимо для отбора только элементов (не групп).

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


2.5.9. Пример 9

Следующий этап - выборка элементов из подчиненного справочника. Для примера возьмем справочник "Контрагенты" (SC191) и справочник "ДенежныеСчета" (SC146), подчиненный справочнику Контрагенты.

SELECT TabSpr.DESCR As Наименование, TabRS.SP143 As НомерСчета
FROM SC191 As  TabSpr
LEFT OUTER JOIN SC146 As TabRS
          ON TabSpr.ID = TabRS.PARENTEXT
WHERE
      TabSpr.ISFOLDER = 2

В этом коде условие TabSpr.ISFOLDER = 2 необходимо для отбора только элементов (не групп). Ведь в 1С подчиненные элементы справочника существуют только у элементов. В результат выборки попадут и помеченные на удаление подчиненные элементы. Если надо получить только непомеченные подчиненные элементы (расчетные счета), тогда текст запроса должен быть таким:

SELECT TabSpr.DESCR As Наименование, TabRS.SP143 As НомерСчета
FROM SC191 As  TabSpr
LEFT OUTER JOIN SC146 As TabRS
          ON TabSpr.ID = TabRS.PARENTEXT
          AND TabRS.ISMARK = 0
WHERE
      TabSpr.ISFOLDER = 2

В этом коде условие TabRS.ISMARK = 0 необходимо включать как раз в условие связывания, так как если включить условие в раздел WHERE - то те записи справочника Контрагенты, у которых есть запись в подчиненном справочнике и этот элемент помечен на удаление, вообще не попадут в результат запроса.




3. Подсистема документов и регистров

3.1. Предисловие

Данная часть предназначена для тех, кто пытается разобраться в структуре хранения данных в системе 1С версии 7.7. А также покажет, как можно получать данные напрямую из таблиц 1С, минуя программу 1С. Для понимания того, о чем идет речь в статье, необходимо понимать принципы работы 1С версии 7.7 и иметь начальные навыки работы с SQL Server Enterprise Manager и SQL Server Query Analyzer.

3.2. Введение

Следует учесть, что в статье рассматриваются только примеры для SQL формата базы данных. Для DBF формата есть некоторые особенности, и не все запросы буду идентичны как для DBF, так и для SQL формата баз. В любом случае для выполнения запросов к DBF необходимы или ODBC или OLEDB драйвера. Можно использовать любой драйвер, работающий с DBASE2 форматом DBF. Опыт показывает, что наиболее сопоставимы по тексту запросов драйвера Visual FoxPro (ведь MS SQL Server и MS Visual FoxPro принадлежат одной фирме, и есть вероятность, что тексты запросов будут унифицироваться в будущем). Я советую использовать драйвер OLE DB Visual FoxPro 9.0, так как в нем меньше ограничений и он поддерживает больше функций и методов по сравнению с ODBC Visual FoxPro 6.0. Скачать последний драйвер (OLE DB Visual FoxPro 9.0) можно по этой ссылке: http://www.microsoft.com/downloads/details.aspx?FamilyId=E1A87D8F-2D58-491F-A0FA-95A3289C5FD4&displaylang=en.

Для того, чтобы получить ID имен таблиц, достаточно просмотреть файл КаталогоИБ()+"1cv7.dds" ("1cv7.dd" для DBF формата базы).

Для получения ID объектов 1С можно воспользоваться компонентой 1С++ (http://www.1cpp.ru/) позволяющей получать ID объектов 1С прямо из 1С (например, преобразование значения ТекущийДокумент() в его ID (строка(9), или в длинную строку ИД (строка(13)), включая вид документа, или в самый длинный ID длиной 23 символа (строка(23)), включая ID типа, вида и самого объекта).

Также получение ID объектов рассмотрено в этой статье: http://www.sinor.ru/~my1c/knowhow/get_id.html

Как формируется ID код, можно почитать выше.


3.3. Общее описание хранения данных 1С в подсистеме документов

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




3.3.1. Краткое описание таблиц

DH - таблицы документов (реквизитов шапки). Создаются при первом добавлении реквизита шапки в документ.

DT – таблицы документов (реквизитов табличной части). Создаются при первом добавлении реквизита табличной части в документ.

_1SJOURN – таблица документов (общих и системных реквизитов). Именно это и есть таблица полного журнала документов. Все остальные журналы формируются по полному журналу с дополнительным отбором по типам документов.

_1SCRDOC – Таблица подчиненных документов. В этой таблице хранятся ссылки документов-родителей на подчиненные документы.

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

Данные документов хранятся в 2 таблицах: DHххх и DTххх, где ххх – десятичный идентификатор вида документа (в файле 1cv7.MD). Непосредственные номера таблиц документов можно посмотреть в файле 1cv7.dds (для dbf версии - 1cv7.dd).

В таблице DHххх – хранятся данные реквизитов шапки (за исключением общих реквизитов). Т.е. на каждый реквизит отведено минимум одно поле таблицы (может быть и 2 в случае неопределенного реквизита). Также в этой таблице хранятся данные по некоторым реквизитам табличной части (рассмотрим позже). В таблице DTххх – хранятся данные реквизитов табличной части (каждый реквизит минимум одно поле таблицы плюс системное поле LINENO_ - номер строки документа). Отдельно следует рассмотреть реквизиты табличной части типа Число (в понимании 1С), по которым установлен флаг «Итог по колонке» в свойствах реквизита. Итог по этим реквизитам хранится в таблице реквизитов шапки (DHххх), причем название поля совпадает для таблицы шапки (DH) и для таблицы табличной части (DT).

В таблице реквизитов шапки хранятся все реквизиты за исключением общих реквизитов и атрибутов документов (также и системных, таких как время, флаг проведения и удаления). Они все хранятся в одной общей таблице журналов _1SJOURN (1SJOURN для dbf). Описание полей таблицы _1SJOURN можно посмотреть выше.

Таблица журналов _1SJOURN, таблица реквизитов шапки DHххх и таблица реквизитов табличной части DTххх «связаны» по полю IDDOC. Связь эта «мнимая» (т.е. значения полей как бы равны для соответствующих строк), но это не стандартные связи SQL.

Поэтому необходимо учесть, что 1С не создает связей для ограничения целостности данных (связи между полями) средствами MS SQL. Также особое внимание надо уделить тому, что все поля создаются как NOT NULL, т.е. в них нельзя записывать значения типа NULL (видимо поэтому и нет связей). И если значение пустое (в понимании SQL должно быть NULL), то 1С заполняет это поле таким образом:

Для неопределенного типа: '   ' (3 пробела), при этом связанное поле TSP заполняется значением 'U                     '.

Для типа «число»: 0 (нулем).

Для типа «строка»: '          ' (строка с пробелами на всю размерность поля, в примере - 10 пробелов на значение Строка(10)).

Для типа «дата»: 01.01.1753. Именно эта дата является началом отсчета дат для типов smalDataTime в SQL.

Для типа «Справочник», «Документ», «Счет», «Календарь», «ВидРасчета» неопределенного вида: '   0     0   ' (строка(13)).

Для типа «Справочник», «Документ», «Счет», », «ВидРасчета» определенного вида, «Перечисление»: '     0   ' (строка(9)).

Для типа «Планы счетов», «Виды Субконто» неопределенного вида: '   0     ' (строка(9)).



3.3.2. Примеры запросов

3.3.2.1. Пример 1

Получение всех документов одного вида со всеми реквизитами шапки (кроме общих):

SELECT * FROM DH14


3.3.2.2. Пример 2

Получение всех документов одного вида со всеми реквизитами шапки (включая все общие и системные реквизиты):

SELECT TabJ.*, Tab1.*
FROM DH14 As Tab1
INNER JOIN _1SJOURN As TabJ
            ON (Tab1.IDDOC = TabJ.IDDOC)


3.3.2.3. Пример 3

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

SELECTTabJ.*, Tab1.*, Tab2.*
FROM DH14 As Tab1
INNER JOIN _1SJOURN As TabJ
            ON (Tab1.IDDOC = TabJ.IDDOC)
                      INNER JOIN DT14 As Tab2
                                  ON (Tab1.IDDOC = Tab2.IDDOC)


3.3.2.4. Пример 4

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



В этом примере необходимо учесть, что дата документа храниться в SQL и DBF формате базы по-разному. Но, в любом случае, дата документа храниться в таблице журналов _1SJOURN, рассмотрим пример для SQL базы:

SELECT TabJ.*, Tab1.*, Tab2.*
FROM DH14 As Tab1
INNER JOIN _1SJOURN As TabJ
            ON (Tab1.IDDOC = TabJ.IDDOC)
                    INNER JOIN DT14 As Tab2
                                 ON (Tab1.IDDOC = Tab2.IDDOC)
WHERE
               LEFT(TabJ.DATE_TIME_IDDOC,8) = '20050219'


3.3.2.5. Пример 5

Получение списка документов родителей по подчиненному документу (получение только общих реквизитов с отборами и системных реквизитов).

SELECT TabJ.*
FROM _1SCRDOC As TabRod
INNER JOIN _1SJOURN As TabJ
             ON (SUBSTRING(TabRod.PARENTVAL,7,9) = TabJ.IDDOC)
WHERE
              (TabRod.MDID = 0) /* только документы, без граф отбора*/
              AND
              (TabRod.CHILDID = '   6C3RK ')

где ' 6C3RK ' – ID документа родителя


3.3.2.6. Пример 6

Получение списка подчиненных документов (получение только общих реквизитов с отборами и системных реквизитов).

SELECT TabJ.*
FROM _1SCRDOC As TabRod
INNER JOIN _1SJOURN As TabJ
             ON (TabRod.CHILDID = TabJ.IDDOC)
WHERE
            (TabRod.MDID = 0) /*только документы, без граф отбора*/
            AND
            (TabRod.PARENTVAL = @ИдДокРодителя)
ORDER BY
             TabRod.CHILD_DATE_TIME_IDDOC

где @ИдДокРодителя – ID документа родителя (включая тип и вид документа, т.е. 23 символа), получить такой ID можно с помощью метода ЗначениеВСамуюДлиннуюСтрокуБД(ТекДок) класса MetaDataWork компоненты 1С++.


3.3.2.7. Пример 7

Получение списка только проведенных документов. Для отбора только проведенных документов можно воспользоваться значением поля CLOSED таблицы _1SJOURN. Так как первый бит его отвечает за то, проведен документ или нет, соответственно, сделав унарное умножение с 1, получим результат в виде 1 - проведен, 0 - непроведен.

SELECT TabJ.*, Tab1.*
FROM DH14 As Tab1
INNER JOIN _1SJOURN As TabJ
                    ON (Tab1.IDDOC = TabJ.IDDOC)
WHERE
             TabJ.CLOSED&1 = 1


3.3.2.8. Пример 8

Получение списка документов, принадлежащих разным компонентам (у которых включен флажок принадлежности к типам учета). Для отбора документов по типам учета применяется унарное умножение поля APPCODE с требуемым значением. Список значений для отбора по типам учета:

(1) 000001 - опер учет.

(2) 000010 - расчет.

(4) 000100 - бух учет.

(8) 001000 - движения периодических реквизитов.

(16) 010000 - существует документ операция у документа (Документ может быть непроведен, но тогда должно быть выбрано Создавать операцию - Всегда в окне свойства документа).

(32) 100000 - Выполнен метод Операция.Записать(); в документе, принадлежащем бух. учету.

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

SELECT TabJ.*, Tab1.*
FROM DH14 As Tab1
INNER JOIN _1SJOURN As TabJ
             ON (Tab1.IDDOC = TabJ.IDDOC)
WHERE
             TabJ.APPCODE&5 = 5


3.3.2.9. Пример 9

Получение списка документов по графе отбора.

Все графы отбора хранятся в 1С в таблице _1SCRDOC. Также в этой таблице хранятся и ссылки на подчиненные документы. Разница между подчиненными документами и графами отбора в том, что поле MDID для подчиненных документов равно 0, а для граф отбора - десятичному идентификатору графы отбора. Допустим, надо получить список документов с отбором по графе "Контрагент". Получим десятичный ИД этой графы отбора с помощью метода ИДОбъекта класса MetaDataWorks компоненты 1С++. К примеру так: ИДГрафы = глMDW.ИДОбъекта(Метаданные.ГрафаОтбора("Контрагент")), где глMDW = СоздатьОбъект("MetaDataWork"). Отбирать будем по столбцу PARENTVAL, в котором хранится полный идентификатор (23 символа) элемента справочника. Получить полный ИД необходимого элемента можно тем же путем, что и идентификатор для графы отбора, но немного другим методом. Например, отбираем по клиенту, значение которого хранится в переменной ВыбКлиент, тогда полный его идентификатор получаем как: IDКлиента = глMDW.ЗначениеВСамуюДлиннуюСтрокуБД(ВыбКлиент).

SELECT TabJ.IDDOC AS Документ, TabJ.IDDOCDEF AS Документ_вид,
                TabJ.DOCNO AS НомерДок
FROM dbo._1SCRDOC AS TabGraf (NOLOCK)
INNER JOIN _1SJOURN AS TabJ (NOLOCK)
             ON (TabJ.IDDOC = TabGraf.CHILDID)
WHERE
             MDID = 14730 /* ИД графы отбора*/
             AND
             PARENTVAL = 'B1  4U     2           ' /*23 ID элемента справочника*/
ORDER BY
             TabJ.DATE_TIME_IDDOC

где соответственно ИДГрафы = 14730, а IDКлиента = 'B1  4U     2          '.





3.4. Регистры

3.4.1. Общее описание подсистемы регистров


Физически регистры остатков состоят из двух таблиц: таблица остатков RGххх и таблица движений RAххх. В таблице движений хранятся все движения документов по регистрам. Список документов, которые сделали движения по регистру, можно получить, выбрав записи из таблицы журналов _1SJOURN с условием равенства поля RFxxx 1 (или не равно 0), например (в случае, если идентификатор регистра 16, т.е. таблица движений = RG16, а таблица остатков = RA16), для получения всех документов, сделавших движения по регистру, можно сделать так:

SELECT * FROM _1SJOURN
WHERE
            (RF16 = 0x1)
            AND
            (CLOSED&1=1)
            AND
            (APPCODE&1=1)

где CLOSED&1=1 – ограничение на выбор записей только проведенных документов. Фактически при корректных записях в базе, это условие лишнее, но при каких-то «глюках» таким условием можно «отловить» непроведенные документы с существующими движениями (к сожалению, такое может встречаться).

А вот условие APPCODE&1=1 означает отобрать документы, принадлежащие оперативному учету. Подробнее о полях таблицы _1SJOURN см. выше.


Рассмотрим более подробно, как хранятся данные в таблицах RG и RA.

В таблицу RA записываются все движения документов, с учетом флага прихода или расхода. Т.е. в таблице RA хранятся сведения о всех движениях документов, которые сделали движения по этому регистру. Соответственно методы "ДвижениеПриходВыполнить()/ДвижениеПриход()" записывают одну строку в таблицу RA с флагом приход (DEBKRED = 0), а методы "ДвижениеРасходВыполнить()/ДвижениеРасход()" также добавляют одну запись в таблицу RA, но уже с флагом расхода (DEBKRED = 1). Соответственно, если в регистре отражается приход и расход по складу, и самое первое движение по регистру есть первый приход на склад (т.е. это первое поступление на склад, до этого на складе ничего не было), то для получения остатка на складе нам надо сложить все приходы и вычесть все расходы. Хорошо, когда таких приходов немного, и с начала заполнения таблиц прошло пару месяцев. Теперь представим, что система работает пару лет. В таком случае для получения остатка на складе нам надо выполнить операцию сложения всех приходов и вычитания всех расходов с начала работы. А если еще учесть, что остатки нам надо считать каждый раз при формировании нового движения расхода (внесения новой записи в таблицу) (для контроля остатка или для расчета себестоимости), то налицо лишние операции сканирования всех записей таблицы RA. Для того чтобы не делать таких пересчетов, 1С разработали регистры остатков таким образом, что они состоят из двух таблиц. 1 - RA (хранятся все движения с флагом прихода или расхода), 2 - RG для хранения промежуточных итогов (как бы заблаговременно подсчитанные итоги по таблице RA за какой-то период).


3.4.2. Рассмотрим примеры

Рассмотрим получение остатков и оборотов по регистру остатков.
Период хранения остатков - месяц. Исходная таблица остатков (RG):

PERIODSP20SP22SP21
2005-02-01 00:00:00.000 '    AA   ' '    1A   ' 35.00
2005-03-01 00:00:00.000 '    AA   ' '    1A   ' 20.00

Исходная таблица движений (RA):

IDDOCLINENO_ACTNODEBKREDIDOCDEFDATE_TIME_IDDOCSP20SP22SP21
101012 '200502157579C0     1   ' '    AA   ' '    1A   ' 10.00
201012 '20050215759EHS     2   ' '    AA   ' '    1A   ' 10.00
601023 '200502157QOSK0     6   ' '    AA   ' '    1A   ' 15.00
701123 '200503013KLMO0     7   ' '    AA   ' '    1A   ' 15.00


Данное описание строится на регистрах остатков, регистры оборотов будут рассмотрены позже.
При описании таблицы остатков (RG) необходимо отметить важную особенность.
Особенность заключается в том, что записи в таблице сортируются по периодам остатков (колонка «PERIOD» и это поле является кластерным индексом (в состав индекса также всегда входит и 1 измерение регистра), по умолчанию периодичность регистров остатков - месяц, именно этот случай и рассматривается). В поле «PERIOD» хранится ДАТА НАЧАЛА периода (МЕСЯЦА). Исключением является период, совпадающий с точкой актуальности, в этом периоде все записи относятся не к концу месяца, а к времени или документу ТА (рассчитаны по этот документ, т.е. с учетом его движений, если документ последовательности проведен). Т.е. другими словами, в этой таблице всегда записи или на конец месяца, или на ТА.



3.4.2.1. Структура и описание полей таблиц регистров

3.4.2.1.1. Таблица RGxxx

Краткое описание: таблица предназначена для хранения итогов по периодам в разрезе по измерениям и по всем ресурсам регистра.

Название поляОписание
PERIOD Период остатков. Всегда равен началу периода (месяц для нашего примера). Тип - DateTime (для dbf - Date).
SPххx Измерения, ресурсы или атрибуты регистра (типы: Numeric, DateTime, Char(n), n=1:999).
TSPyyy Дополнение к измерению или реквизиту, заполняется только для неопределенных типов значений (используется совместно с полем «SPyyy»). Тип - Char(3).



3.4.2.1.2. Таблица RAxxx

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

Название поляОписание
IDDOC ID документа, тип «строка», по этому полю осуществляется связь с таблицей документов («1SJOURN» или «_1SJOURN» для SQL). Тип - Char(9).
LINENO_ Номер строки документа (то, что устанавливается методом «ПривязатьСтроку()»). Тип - SmallInt.
ACTNO Порядковый номер движения (это не номер строки, а именно движения). Тип - Integer.
DEBKRED Флаг прихода или расхода. 0 - приход, 1 - расход. Тип – bit.
IDOCDEF Поле присутствует лишь тогда, когда в свойствах регистра установлен флажок «Быстрая обработка движений». ID вида документа (из метаданных, причем номер, а не 36-ричное представление). Тип - Integer.
DATE_TIME_IDDOC Поле присутствует лишь тогда, когда в свойствах регистра установлен флажок «Быстрая обработка движений». Значения этого поля идентичны значениям поля «DATE_TIME_IDDOC» таблицы «_1SJOURN» (журналов). Тип - Char(23).
SPххx Измерения, ресурсы или атрибуты регистра (типы: Numeric, DateTime, Char(n), n=1:999).
TSPyyy Дополнение к измерению или реквизиту, заполняется только для неопределенных типов значений (используется совместно с полем «SPyyy»). Тип - Char(3).

Особое внимание надо уделить полям «TSP». Это поле создается лишь тогда, когда реквизит (измерение, ресурс) имеет неопределенный тип (длина ID кода 23 символа). Опытным путем было установлено, что это поле по умолчанию заполняется пустой строкой (3 пробела).

Для невыбранного типа (заполняется по умолчанию): TSP235 = '   ' (3 пробела), при этом связанное неопределенное поле SP235 заполняется значением 'U                      ' (символ U и 22 пробела), что скорее всего означает «Undefined», т.е. неопределенный.

Для типов «Число»: связанное поле SP235 заполняется строковым значением числа, но с символом «N» в начале строки (например 'N 2' для целого числа 2). Возможные значения TSP235:

'F30' - число с плавающей запятой (float);

'320' - число, состоящее только из десятичных (0.99, т.е. Число(3,2)).

'A00' - целое число (int);

Для типов «Строка»: связанное поле SP235 заполняется строкой, но с символом 'S' слева (например 'SАбвгдежзклимно '). Возможные значения TSP235 в этом случае: количество знаков, определенных для строкового значения, например, '14' - количество знаков в строке, в 36-ричном формате (_IDToStr). Следует отметить, хотя 1С и назначает длину строки больше 22 символов, но фактически в этом поле можно хранить только 22 символа (1 разряд приходиться на обозначение типа поля 'S').

Для типов «Дата»: связанное поле SP235 заполняется строкой, но с символом 'D' слева (например 'D20050303 '). TSP235 - ' ' (3 пробела), т.е. поле пустое.

Для типов 1С (Справочник, Документ, Перечисление, Счет, и т.д.): связанное поле SP235 заполняется строкой включающий полный идентификатор объекта (его тип, вид, внутренний ИД код). Следует отметить, что для типов «Перечисление» нельзя не назначать вид, так как неназначение вида приведет к несовпадению типов и незаполнению неопределенного поля. Возможные значения TSP235:

'0 ' - был назначен только тип объекта, вид не назначен (например «НазначитьТип(«Справочник»)»)

'1 ' - был назначен как тип объекта, так и его вид (например «НазначитьТип(«Справочник.Клиенты»)»)



3.4.2.2. Пример 1

Получение итогов на ТА или на конец периода (месяца в нашем случае).

Поскольку итоги в таблице RG хранятся на ТА или на конец месяца (для нашего примера), то для получения итогов не обходимо лишь указать условие по периоду, на который нам надо получить итоги.

SELECT TabRegOst.SP20 As Товар, Sum(TabRegOst.SP21) As КвоКонОст
FROM RG13 As TabRegOst
WHERE
           TabRegOst.PERIOD = @PERIODR
GROUP BY
           TabRegOst.SP20
ORDER BY
           TabRegOst.SP20

где @PERIODR – переменная типа DateTime, условие по которой служит для получения результата запроса на тот период, который нам необходим.

Например, если надо получить остаток на ТА, при этом ТА = «19.02.2006», то переменной @PERIODR необходимо присвоить значение начала месяца «01.02.2006».

SET @PERIODR = CONVERT(DateTime, '20060201',112)

Если необходимо рассчитать остаток на конец января, то переменной необходимо присвоить значение «01.01.2006»:

SET @PERIODR = CONVERT(DateTime, '20060101',112)

Нетрудно догадаться, что если необходимо получить остаток на конец января, то надо в качестве условия поставить начало февраля (ведь остаток на конец января есть остаток на начало февраля. Именно так и делает 1С при расчете остатка на конец периода).

Спросите, почему присвоение переменной @PERIODR происходит с помощью функции CONVERT, да и еще дата в таком странном формате? Это все дело привычки (дело в том, что в таблице журналов дата документа в поле DATE_TIME_IDDOC как раз храниться в виде строки ГГГГММДД).



3.4.2.3. Пример 2

Получение сумм прихода и расхода за один месяц (оборотов).

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

SELECT TabReg.SP20 As Товар, Sum(TabReg.SP21*((DEBKRED+1)%2)) As КвоПриход, 
             Sum(TabReg.SP21*DEBKRED) As КвоРасход
FROM RA13 As TabReg
INNER JOIN _1SJOURN As TabJ
             ON (TabReg.IDDOC = TabJ.IDDOC)
WHERE
            TabJ.DATE_TIME_IDDOC >= @PERIODN
            AND
            TabJ.DATE_TIME_IDDOC <= @PERIODK
GROUP BY
            TabReg.SP20
ORDER BY
            TabReg.SP20

где @PERIODN, @PERIODK – переменные типа Char(8), условия по которых служат для получения результата запроса на тот период, который нам необходим.

В нашем случае @PERIODN = «20060201», @PERIODK = «20060228».

Код для установки переменных:

SET @PERIODR = '20060201'
SET @PERIODR = '20060228'


3.4.2.4. Пример 3

Получение итогов и остатков на произвольную дату.

Поскольку итоги в нашем примере хранятся только на конец месяца или ТА, то необходимо выполнить 2 запроса, первый – для получения итога на начало периода (из примера 1), второй – для получения оборотов (из примера 2).

SELECT TMP.Товар As Товар, SUM(TMP.НачОст) As НачОст, SUM(TMP.Приход) As Приход, 
            SUM(TMP.Расход) As Расход, SUM(TMP.НачОст + TMP.Приход - TMP.Расход) As КонОст
FROM
        (
         SELECT TabRegOst.SP20 As Товар, TabRegOst.SP21 As НачОст, 0 As Приход, 0 As Расход
         FROM RG13 As TabRegOst
         WHERE
                      TabRegOst.PERIOD = @PERIODR
        
         UNION ALL

         SELECT TabReg.SP20 As Товар, 0 As НачОст, (TabReg.SP21*((DEBKRED+1)%2)) As Приход, 
                      (TabReg.SP21*DEBKRED) As Расход
         FROM RA13 As TabReg
         INNER JOIN _1SJOURN As TabJ
                      ON (TabReg.IDDOC = TabJ.IDDOC)
         WHERE
                      TabJ.DATE_TIME_IDDOC >= @PERIODN
                      AND
                      TabJ.DATE_TIME_IDDOC <= @PERIODK
          ) As TMP
GROUP BY
               TMP.Товар
ORDER BY
               TMP.Товар



где @PERIODR – переменная типа DateTime, условие по которой служит для получения результата запроса на период, меньший на 1 от рассчитываемого. В нашем случае для получения остатка на 20.02.2006 этой переменной необходимо присвоить значения начала января, т.е. «01.01.2006» (так как в случае условия на 01.02.2006 получим остаток или на конец февраля, или на точку актуальности):

SET @PERIODR = CONVERT(DateTime, '20060101',112)

@PERIODN, @PERIODK – переменные типа Char(8), условия по которым служат для получения результата в запросе за определенный период. Для нашего примера @PERIODN = «20060201», @PERIODK = «20060220»:

SET @PERIODR = '20060201'
SET @PERIODR = '20060220'

Следует отметить, что условие:

AND TabJ.DATE_TIME_IDDOC <= @PERIODK

предполагает получение остатка на начало дня, это связано с тем, что в поле DATE_TIME_IDDOC хранится не только дата в текстовом виде, но и время, и идентификатор документа. Так, чтобы получить строку представления начала дня, необходимо выполнить функцию СформироватьПозициюДокумента(<Дата>,<Час>,<Мин>,<Сек>,<ФлагКонцаСекунды>), которая и сформирует строку даты и времени в текстовом представлении. Например, для даты 14.03.2006 функция вернет значение '#20060314 0 0 ' (в нашем случае символ # надо убрать). Как видим, первый 0 - это 36 ричное представление времени, а вот второй 0 - это ID документа (невыбранного документа). Но, поскольку сравнению в условию поддается каждый символ, то любая пустая строка будет меньше любой заполненной строки (в нашем случае пробелами и 0). Поэтому, чтобы получить остаток на конец дня, надо или отсечь лишние символы в поле DATE_TIME_IDDOC, или же сравнивать с завтрашней датой. Я предпочитаю второй вариант, так как в таком случае нагрузка на сервер меньше (не требуется выполнять обрезание значений функцией Left), но приведу в качестве примера оба решения:

AND LEFT(TabJ.DATE_TIME_IDDOC,8) <= @PERIODK

или условие предыдущее, но значение переменной устанавливается на дату @PERIODK = «20060221»:

SET @PERIODR = '20060221'


3.4.2.5. Пример 4

Получение итогов и остатков на документ.

Задача аналогична задаче из предыдущего примера, за исключением того, что условие периода не по дату, а по позицию документа.

SELECT TMP.Товар As Товар, SUM(TMP.НачОст) As НачОст, SUM(TMP.Приход) As Приход, 
             SUM(TMP.Расход) As Расход, SUM(TMP.НачОст + TMP.Приход - TMP.Расход) As КонОст
FROM
            (
            SELECT TabRegOst.SP20 As Товар, TabRegOst.SP21 As НачОст, 0 As Приход, 0 As Расход
            FROM RG13 As TabRegOst
            WHERE
                         TabRegOst.PERIOD = @PERIODR
            UNION ALL
            SELECT TabReg.SP20 As Товар, 0 As НачОст,
                            (TabReg.SP21*((DEBKRED+1)%2)) As Приход,TabReg.SP21*DEBKRED) As Расход
            FROM RA13 As TabReg
            INNER JOIN _1SJOURN As TabJ
                         ON (TabReg.IDDOC = TabJ.IDDOC)
            WHERE
                         TabJ.DATE_TIME_IDDOC >= @PERIODN
                         AND
                         TabJ.DATE_TIME_IDDOC < @POZDOK
             ) As TMP
GROUP BY
           TMP.Товар
ORDER BY
           TMP.Товар



где @PERIODR – переменная типа DateTime, условие по которой служит для получения результата запроса на период, меньший на 1 от рассчитываемого. В нашем случае для получения остатка на 20.02.2006 этой переменной необходимо присвоить значения начала Января, т.е. «01.01.2006» (так как в случае условия на 01.02.2006 получим остаток или на конец февраля, или на точку актуальности):

SET @PERIODR = CONVERT(DateTime, '20060101',112)

где @PERIODN – переменная типа Char(8), условие по которой служит для получения результата в запросе на определенный период. Для нашего примера @PERIODN = «20060201».

SET @PERIODR = '20060201'

где @POZDOK – переменная типа char(23), позиция документа (получаемая методом ПолучитьПозицию()).

Следует отметить, что поле DATE_TIME_IDDOC может быть в таблице движений регистра (в случае установленного флага «Быстрая обработка движения» при конфигурировании настроек регистра), в этом случае связь с таблицей журналов (_1SJOURN) необязательна, и условие:

AND TabJ.DATE_TIME_IDDOC < @POZDOK

Может выглядеть вот так:

AND TabReg.DATE_TIME_IDDOC < @POZDOK

И строка:

INNER JOIN _1SJOURN As TabJ ON (TabReg.IDDOC = TabJ.IDDOC)

не нужна.


Т.е. Запрос будет выглядеть так:

SELECT TMP.Товар As Товар, SUM(TMP.НачОст) As НачОст, SUM(TMP.Приход) As Приход, 
             SUM(TMP.Расход) As Расход, SUM(TMP.НачОст + TMP.Приход - TMP.Расход) As КонОст
FROM
          (
          SELECT TabRegOst.SP20 As Товар, TabRegOst.SP21 As НачОст, 0 As Приход, 0 As Расход
          FROM RG13 As TabRegOst
          WHERE
                       TabRegOst.PERIOD = @PERIODR
          UNION ALL
          SELECT TabReg.SP20 As Товар, 0 As НачОст, (TabReg.SP21*((DEBKRED+1)%2)) As Приход, 
                      (TabReg.SP21*DEBKRED) As Расход
          FROM RA13 As TabReg
          WHERE
                       TabReg.DATE_TIME_IDDOC >= @PERIODN
                       AND
                       TabReg.DATE_TIME_IDDOC < @POZDOK
           ) As TMP
GROUP BY
            TMP.Товар
ORDER BY
            TMP.Товар


3.4.2.6. Пример 5

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

Приведенный в этом примере код составлен по таблицам конфигурации ПУБ для Украины. Запрос получает остатки и обороты в разрезе продукции за период с "15.01.2006" по "15.02.2006", при условии что период хранения остатков в регистрах остатков - Месяц.

-- Объявление переменных

Declare @PERIODR DateTime
Declare @PERIODN Char(8)
Declare @PERIODK Char(8)
Declare @PERIODN2 Char(8)
Declare @PERIODK2 Char(8)

-- присвоение переменным значений

SET @PERIODR = Convert(DateTime,'20051201',112)
SET @PERIODN = '20051201'
SET @PERIODK = '20060115'
SET @PERIODK2 = '20060215'

-- сам запрос

SELECT TMP.Продукция AS Продукция, SUM(TMP.НачОст) As НачОст, SUM(TMP.Приход) As Приход, 
             SUM(TMP.Расход) As Расход, SUM(TMP.НачОст + TMP.Приход - TMP.Расход) As КонОст
FROM
          (
          SELECT TabRegOst.SP1718 As Продукция, TabRegOst.SP1722 As НачОст, 0 As Приход, 0 As Расход
          FROM RG1714 As TabRegOst
          WHERE
                       TabRegOst.PERIOD = @PERIODR
          UNION ALL
          SELECT TabReg.SP1718 As Товар, (TabReg.SP1722*((DEBKRED+1)%2))- (TabReg.SP1722*DEBKRED) As НачОст,
                       0 As Приход, 0 As Расход
          FROM RA1714 As TabReg
          INNER JOIN _1SJOURN As TabJ
                       ON (TabReg.IDDOC = TabJ.IDDOC)
          WHERE
                       TabJ.DATE_TIME_IDDOC >= @PERIODN
                       AND
                       TabJ.DATE_TIME_IDDOC < @PERIODK
          UNION ALL
          SELECT TabReg2.SP1718 As Товар, 0 As НачОст, (TabReg2.SP1722*((DEBKRED+1)%2)) As Приход, 
                      (TabReg2.SP1722*DEBKRED) As Расход
          FROM RA1714 As TabReg2
          INNER JOIN _1SJOURN As TabJ
                       ON (TabReg2.IDDOC = TabJ.IDDOC)
          WHERE
                       TabJ.DATE_TIME_IDDOC >= @PERIODK
                       AND
                       TabJ.DATE_TIME_IDDOC < @PERIODK2
           ) AS TMP
GROUP BY
             TMP.Продукция



3.4.2.7. Пример 6

Пересчет итогов по одному из регистров остатков.

Данный код приведен как пример работы с регистрами.

/*Создадим процедуры по конвертации 10-36, 30-10
Проверим есть ли такие процедуры*/

IF EXISTS(
         SELECT name 
         FROM sysobjects
         WHERE
                  name = 'Convert10To36')
DROP Procedure 'Convert10To36'


CREATE PROCEDURE [Convert10To36] @Deci INT, @Res36 CHAR(9)
OUTPUT AS
          SET NOCOUNT ON
          DECLARE @j INT
          DECLARE @Arr36 CHAR(36)
SELECT @Arr36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
SELECT @Res36 = ''
SELECT @j = LOG(@Deci)/LOG(36) +1
while @j>0
begin
       SELECT @Res36 = LTRIM(RTRIM(@Res36)) + SUBSTRING(@Arr36, @Deci/POWER(36,@j-1) +1 ,1)
       SELECT @Deci = @Deci%POWER(36,@j-1)
       SELECT @j =@j-1
end
GO


IF EXISTS(
         SELECT name
         FROM sysobjects
         WHERE name = 'Convert36To10')
DROP Procedure 'Convert36To10'


CREATE PROCEDURE [Convert36To10] @Res36 CHAR(9), @Deci INT
OUTPUT AS
          SET NOCOUNT ON
          DECLARE @j INT
          DECLARE @Arr36 CHAR(36)
SELECT @Arr36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
SELECT @Deci = 0
SELECT @j = 1
while @j <= LEN(LTRIM(RTRIM(@Res36)))
begin
       if @j <> 1
       SELECT @Deci = @Deci*36
       SELECT @Deci = @Deci + CHARINDEX(SUBSTRING(LTRIM(RTRIM(@Res36)), @j,1),@Arr36) -1
       SELECT @j = @j+1
End
GO


DECLARE @DATE_POSL datetime
DECLARE @PERIOD_POSL datetime
DECLARE @TIME_POSL int
DECLARE @IDDOC_POSL char(9)
DECLARE @POZ_POSL char(23)
DECLARE @POZ_POSLMAX char(23)
DECLARE @DOC_POSL char(23)
DECLARE @Res36 char(9)

SELECT @DATE_POSL = Convert(Varchar(23),(SELECT MAX(CURDATE) FROM _1SSYSTEM),112)

/*Получаем дату ТА, время ТА в десятичном формате от начала суток*10000, ID 
документа последовательности*/
SELECT @DATE_POSL = MAX(CURDATE), @TIME_POSL = MAX(CURTIME), @IDDOC_POSL = MAX(EVENTIDTA)
FROM _1SSYSTEM

IF @TIME_POSL <> 0

/*Конвертируем время в 36 ричный вид*/
EXEC Convert10To36 @TIME_POSL, @Res36=@POZ_POSL OUTPUT
ELSE
SELECT @POZ_POSL= ''

/*Если ТА установлена не на документ - тогда время и ID документа будут равны 0*/
IF @IDDOC_POSL = '     0   ' SELECT @IDDOC_POSL= ''

/*Получаем позицию ТА*/
SELECT @POZ_POSL = Convert(Varchar(23),@DATE_POSL,112)+LTRIM(RTRIM(@POZ_POSL))+@IDDOC_POSL

/*Получаем документ ТА, если такой имеется (если ТА находится на документе)*/
SELECT @DOC_POSL = (SELECT DATE_TIME_IDDOC FROM _1SJOURN WHERE DATE_TIME_IDDOC = @POZ_POSL)

/*Получаем начало месяца даты ТА*/
SELECT @PERIOD_POSL = DATEADD(dd,1-DATEPART(dd,@DATE_POSL),@DATE_POSL)
SELECT @DATE_POSL,@TIME_POSL,@POZ_POSL,@DOC_POSL,@PERIOD_POSL

/*Заполняем временную таблицу периодами пересчета*/
SELECT DISTINCT RG8918.PERIOD As PERIOD, Convert(Varchar(8),RG8918.PERIOD,112) AS PERIODS,
(CASE WHEN RG8918.PERIOD <> @PERIOD_POSL 
          THEN Convert(Varchar(8),DATEADD(mm,1,RG8918.PERIOD),112)
          ELSE @POZ_POSL END) As PERIODPO
INTO ##TabPeriod FROM RG8918
GO

SELECT * FROM ##TabPeriod
ORDER BY PERIODS

/*Получаем курсор для обхода каждой строки временной таблицы ##TabPeriod*/
DECLARE MyCur cursor for

SELECT PERIOD, PERIODS, PERIODPO 
FROM ##TabPeriod
ORDER BY PERIOD

OPEN MyCur

/*переменные для периодов*/
DECLARE @PERIOD datetime
DECLARE @PERIODPRED datetime
DECLARE @PERIODS varchar(23)
DECLARE @PERIODPO varchar(23)

/*все делаем в транзакции*/

/*обходим в цикле по каждой строке таблицы (по каждому периоду)*/
FETCH NEXT FROM MyCur
INTO @PERIOD, @PERIODS, @PERIODPO
WHILE @@FETCH_STATUS = 0
BEGIN

           /*Получим дату предыдущего периода (месяца)*/
           SELECT @PERIODPRED = DATEADD(mm,-1,@PERIOD)
           PRINT convert(Varchar(8),@PERIODPRED,112)+'  #  '+@PERIODS+'  #  '+@PERIODPO
           SET HACT_ABORT ON
           BEGIN TRANSACTION

           /*очищаем текущий период*/
           DELETE FROM RG8918
           WHERE PERIOD = @PERIOD

           /*заполняем текущий период по пересчитанным данным*/
          INSERT RG8918

          SELECT  @PERIOD, T1.SP8908, T1.SP8909, T1.SP8910, T1.SP8911, T1.SP8912, T1.SP8913,
                        T1.SP8914, T1.SP8915, '', T1.SP11200, SUM(T1.SP8916), 0, 0, 0, 0
          FROM
                  (
                   SELECT  TabRegOst.SP8908 As SP8908, TabRegOst.SP8909 As SP8909, TabRegOst.SP8910 As SP8910,
                                TabRegOst.SP8911 As SP8911, TabRegOst.SP8912 As SP8912, TabRegOst.SP8913 As SP8913, 
                                TabRegOst.SP8914 As SP8914, TabRegOst.SP8915 As SP8915, TabRegOst.SP11200 As SP11200,
                                SUM(TabRegOst.SP8916) As SP8916
                   FROM RG8918 As TabRegOst (HOLDLOCK)
                   WHERE
                          TabRegOst.PERIOD = @PERIODPRED
                   GROUP BY
                              TabRegOst.SP8908,
                              TabRegOst.SP8909,
                              TabRegOst.SP8910,
                              TabRegOst.SP8911,
                              TabRegOst.SP8912,
                              TabRegOst.SP8913,
                              TabRegOst.SP8914,
                              TabRegOst.SP8915,
                              TabRegOst.SP11200
                   UNION ALL
                   SELECT  TabRegOb.SP8908 As SP8908, TabRegOb.SP8909 As SP8909, TabRegOb.SP8910 As SP8910,
                                TabRegOb.SP8911 As SP8911, TabRegOb.SP8912 As SP8912, TabRegOb.SP8913 As SP8913, 
                                TabRegOb.SP8914 As SP8914, TabRegOb.SP8915 As SP8915, TabRegOb.SP11200 As SP11200, 
                                SUM(TabRegOb.SP8916*(1-TabRegOb.DEBKRED*2)) As SP8916
                   FROM RA8918 AS TabRegOb (HOLDLOCK)
                   INNER JOIN _1SJOURN As TabJourn
                                  ON (TabRegOb.IDDOC = TabJourn.IDDOC)
                   WHERE
                           TabJourn.DATE_TIME_IDDOC BETWEEN @PERIODS AND @PERIODPO
                   GROUP BY
                                 TabRegOb.SP8908,
                                 TabRegOb.SP8909,
                                 TabRegOb.SP8910,
                                 TabRegOb.SP8911,
                                 TabRegOb.SP8912,
                                 TabRegOb.SP8913,
                                 TabRegOb.SP8914,
                                 TabRegOb.SP8915,
                                 TabRegOb.SP11200
                   ) AS T1
          GROUP BY
                    T1.SP8908,
                    T1.SP8909,
                    T1.SP8910,
                    T1.SP8911,
                    T1.SP8912,
                    T1.SP8913,
                    T1.SP8914,
                    T1.SP8915,
                    T1.SP11200
          HAVING 
                   SUM(T1.SP8916) <> 0
          COMMIT TRANSACTION

          FETCH NEXT FROM MyCur INTO @PERIOD, @PERIODS, @PERIODPO
END

CLOSE MyCur
DEALLOCATE MyCur

SELECT * FROM ##TabPeriod ORDER BY PERIODS
DROP TABLE ##TabPeriod


Оригинал статьи расположен по адресу http://www.script-coding.com/v77tables.html.