(Основные понятия)
(Интерфейс программы)
(Программирование на встроенном языке)
Проектирование базы
База предназначена для хранения и обработки данных. Данные могут быть различными, но для удобства работы с ними они должны быть как-то классифицированы и упорядочены. Простейший вариант классификации данных - это отнести их все к одному типу, в таком случае их можно хранить в одной таблице. Таблица в этом случае подобна таблице в обычном смысле, у нее есть столбцы и строки. Количество столбцов таблицы остается постоянными при добавлении туда новых данных. Когда добавляется новая строка, то в каждый столбец добавляется какое-то одно из значений, составляющих новые данные. Это значит, что при создании таблицы были заранее определены типы и количество ее столбцов, поскольку было известно, какие данные предполагалось хранить в ней.
Конечно, даже такой простой пример показывает, что необходимо бывает иногда преобразовывать классификацию данных, то есть, например, добавлять в таблицу новые столбцы, поскольку не всегда можно заранее предусмотреть все требования, которые могут быть предъявлены к базе. Возможность такого преобразования предусмотрена в программе, однако в рабочем режиме классификация данных вполне может оставаться неизменной.
При работе с компьютером необходимо формально определить, что составляет то или иное понятие. По определению, таблица состоит из полей и записей. Поле соответствует столбцу таблицы. Поле имеет тип, то есть тип значения, которое туда заносится, а так же имя. Запись соответствует строке таблицы. При проектировании базы определяются ее поля, при работе с базой преобразуются ее записи (то есть их можно добавлять, удалять, изменять ).
Тип поля может быть числом, датой, строкой, а так же ссылкой на другую таблицу. Если исключить ссылки, то такое определение таблицы соответствует общепринятому в реляционных базах данных.
Понятно, что одной таблицы реально может быть недостаточно, в общем база может состоять из многих таблиц. Таблицы, конечно, могут быть независимыми, однако часто удобно установить связь между таблицами. Так же как в обычном языке мы определяем одни понятия через другие, мы можем определять и таблицы. Если, например, база предназначена для хранения информации о продажах, подразумевается, что туда будут заносится данные типа "вчера было продано 100 розеток". Если слово "вчера" вполне можно характеризовать как "дата", "100" как "число", то "розетки" являются "товаром". Можно, конечно, считать, что это просто строка. Однако данные типа "розетка стоит 100 рублей" тоже бывает необходимо хранить в базе. Так что в рассмотреном примере удобнее сначала создать специальную таблицу "товары", содержащую поля "название товара" как строка и "цена товара" как число, а затем определить поле "товар" таблицы "продажи" через таблицу "товары". Такой тип поля называется ссылкой.
Когда в таблицу вводятся данные, то дата, число и строка могут вводится с клавиатуры, а ссылка должна выбираться из списка. Это удобнее в том смысле, что меньше вероятность ошибки. В общем в работе с компьютером существует принцип неизбыточности данных. Это значит, что конкретная информация должна храниться только в одном месте. Для приведенного выше примера это означает следующее. Мы, вообще говоря, знаем, что товар называется "розетка". Это знание мы фиксируем один раз записью в таблице "товары". Если бы мы записывыли каждый раз наименоваие товара как строку в таблице "продажи", мы могли бы ошибиться и один раз написать "розтка", а другой раз "розитка". Для компьютера это совершенно разные значения, и при обработке данных такая ошибка может привести к путанице. Напротив, если ошибка произошла в таблице "товары", то достаточно исправить ее в одном месте.
Возможности работы со счетами
В программе предусмотрены специальные возможности для ведения бухгалтерии. Вообще говоря, любую базу данных можно приспособить к хранению бухгалтерской информации. Однако возможности программы "Механический баланс" рассчитаны на простую и гибкую работу по преобразованию информации, которая была классифицирована в формализме баз данных, в бухгалтерскую информацию. Это не обязательно означает, что в базе данных необходимо хранить всю информацию о бухгалтерии, включая баланс. Часто средства для ведения бухгалтерии оказываются удобными просто потому, что предоставляют другой срез проблемы, которую решает база данных, и позволяют обобщить и по другому упорядочить хранящуюся там информацию, не преобразовывая при этом структуры таблиц и не меняя записей.
Бухгалтерская информация обычно представляет собой план счетов и совокупость проводок между ними. Информацию о конкретной проводке естественно сопоставить записи в некоторой таблице. С другой стороны, могут существовать таблицы для записи каких-то произведенных действий, и эти действия можно классифицировать с точки зрения бухгалтерии как хозяйственную операцию. В этом случае каждой записи таблицы должны соответствовать проводки, возможно их должно быть несколько.
О плане счетов. Обычно существует совокупость счетов, как, скажем, счет "50" или счет "62". У некоторых счетов могут быть субсчета. Кроме того, по некоторым счетам может вестись аналитический учет. Аналитический учет имеет тип, например, учет по товарам, и совокупость значений, соответственно, совокупость различных товаров. Тип аналитического учета может быть одинаковым у нескольких счетов. У счета может быть несколько типов аналитического учета, например, учет по товарам и учет по покупателям. Аналитический учет не обязательно соответствует глобальному счету, он может соответствовать и субсчету.
В программе это обобщено следующим образом. Для любого аналитического
учета существует ключ, представляющий его тип, и значение, соответствующее
конкретному значению данного типа. Например, можно записать товар =
"розетки", здесь ключом является товар, а значением - розетки.
Каждая проводка на счет, на котором ведется аналитический учет по товарам,
должна иметь такой атрибут, причем значение может меняться, а ключ должен
оставаться постоянным. Далее, план субсчетов на счете приравнен к аналитическому
учету, то есть у него тоже есть ключ учета и значение - название субсчета.
Наконец, сам план счетов можно представить таким же образом, то есть у
него есть ключ, один на всех, например, счет, и значение для каждого
счета, например, "50". Таким образом, полное определение счета,
на который совершается проводка, должно представлять из себя просто список
ключей и их значений. Например,
{ счет = "41", подразделение = "склад 1", товар = "розетки" }.
Заметим, что по данным из базы о хозяйственных операциях, если информация о них является достаточно полной, можно составить бухгалтерскую информацию, а обратное действие представлятся затруднительным. В программе, соответственно, принято, что хранится только необходимая информация о хозяйственных операциях, а бухгалтерская информация в необходимом объеме получается автоматически как результат обработки базы. Для этого каждой таблице можно приписать, каким проводкам соответствуют записи в ней. Понятно, что конкретная проводка должна получаться как результат обработки конкретной записи, однако шаблон для проводок пишется для всей таблицы, и в этом шаблоне конкретные характеристики проводки, то есть счета дебета и кредита, сумма и дата, выражаются через поля таблицы. При обработке базы вместо названий полей подставляются конкретные их значения для текущей записи, и получившаяся таким образом проводка далее учитывается по необходимости. Таблице может соответствовать несколько шаблонов проводок, а может - ни одного. Шаблоны проводок можно изменить, не меняя записей таблицы.
Полученная таким образом бухгалтерская информация далее в программе может использоваться двумя способами. Во-первых, существуют средства для построения стандартных отчетов, принятых в бухгалтерии, таких как отчет по оборотам счета или главная книга. Во-вторых, данные по оборотам или остаткам на счете могут использоваться в запросах произвольным образом, то есть их можно занести в таблицу, распечатать в произвольном формате, экспортировать и.т.д.
Понятие о запросах
В базе данных, кроме списка таблиц с их данными и свойствами, может храниться список запросов. У каждого запроса есть текст, который должен представлять из себя последовательность инструкций на встроенном алгоритмическом языкя программирования. Запросы не зависят друг от друга, и связаны с таблицами базы только посредством того, что в запросах могут быть указаны имена таблиц и полей для обработки.
Запросы могут выполнять следующие функции:
(а) Организация интерфейса для облегчения внесения информормации в таблицы
Все данные базы можно изменять и без помощи запросов, а просто редактируя записи таблиц. Однако в языке запросов предусмотрены достаточно широкие возможности для упрощения этой работы и сведения ее к выполнению действий, понятных и удобных с точки зрения задач, которые должна решать база.
(б) Подготовка документов для печати
(в) Экспорт и импорт данных из базы
Для выборки данных из базы и изменения данных в базе в языке запросов предусмотрены средства, подобные выражениям SQL в реляционных базах ( выражения SELECT, INSERT, UPDATE и DELETE ), а так же функции для выборки бухгалтерской информации. При организации интерфейса можно строить диалоговые окна либо открывать окна для редактирования таблиц с отборам и сортировкой данных для этих окон. При подготовке документов для печати можно подготовить достаточно произвольный документ, выбирая шрифты, оформление и расположение текста. Наконец, язык запросов обладает гибкоститью алгоритмического языка при организации преобразования данных.
Обмен данных между базами
Программа "Механический баланс", в общем, может работать в сети, но
в ней разрешается держать открытым файл конкретной базы данных только одному
пользователю. Для того, чтобы не возникало проблем при ведении учета на
нескольких компьютерах, общую задачу ведения учета следует разбить на блоки
и создать несколько баз данных, по одной для каждого блока, а также организовать
схему обмена данными между базами.
(Программирование на встроенном языке)
Алгоритмы
Когда человек подсчитывает на калькуляторе "3 + 2", он выполняет действия:
"Нажать 3"
"Нажать +"
"Нажать 2"
"Нажать ="
В результате на индикаторе высвечивается сумма.
В принципе, любой компьютер работает с такими алгоритмами. Если надо
его запрограммировать на выполнение каких-то действий, то описывать ему
их надо в этом стиле.
Иногда алгоритм рисуют в виде схемы. Например,
Часто оказывается, что в алгоритме последовательность дальнейших действий зависит от выполнения некоторого условия ( Например, чтобы число на индикаторе калькулятора было меньше 1 ). Если условие выполняется, то затем следует совершить какие-то одни действия, иначе - другие. Такое условие на схеме принято обозначать ромбиком:
В этом примере в узле А проверяется условие и, если оно выполняется,
то исполнение программы идет по одному направлению, а если нет - по другому
направлению.
Примеры алгоритмов
Если в запросе написать
x = 3 + 2
то число x будет равно пяти. Это принятая в программе форма записи
простых алгоритмов. То же самое можно записать и иначе:
x = 3
x = x + 2
После выполнения первой строчки x будет равно 3, как и на калькуляторе
после нажатия "3+" на индикаторе горит "3". После выполнения второй строчки
x будет равно 5.
Дальше можно, например, написать
y = 2
x = ( y + 1 ) * x
После выполнения этого алгоритма x будет равно 15, а y - 2.
В данных примерах шагом алгоритма, соответствующим квадратику, является
вычисление выражения слева от знака равенства и присвоение его переменной
справа от знака равенства.
Чтобы записать условный алгоритм (ветвление), рассмотрим пример
if a < 2 then
x = 5
y = x + 1
else
x = 3
y = 4
end_if
z = y - 1
Если (if) число a было меньше двух, то (then) выполняются две верхние
операции ( x = 5,
y = x + 1 = 6 ), иначе (else) - две нижние ( x = 3, y = 4 ). Слово
end_if означает, что дальше команды опять выполняются по очереди. Таким
образом, если a было меньше двух, то z будет равно 5, иначе - 3.
Блок else - необязательный. Например, можно записать
x = 3
if a == 2 then x = x - 1 end_if
y = x * 4
В этом примере, если число a было равно двойке, то выполнится операция x = x - 1 и в результате будет x = 2, y = 8, иначе останется x = 3 и y будет равно 12.
Следует обратить внимание, что в последнем примере употреблено сочетание
символов "==". Оно введено, чтобы отличить сравнение двух чисел от присваивания
значения некоторой переменной. Если написать
"if a = 2 then...", это было бы ошибкой.
Типы переменных
Переменные в запросах могут быть не только числами, как это было в приведенных выше примерах. Например, можно записать
x = "123"
x = x + "456"
Здесь x считается строкой. Строки - это такой же тип, как и числа. Для задания строки необходимо заключить ее в кавычки. Так, чтобы задать строку 789 надо записать "789". Строки можно складывать, в этом случае вторая строка присоединяется к первой с конца. В приведенном примере переменная x в конце будет иметь значение "123456".
В общем, переменные в запросах могут быть числами, строками, датами или таблицами.
Функции
В математике мы пишем x = sin(y). Здесь sin - название функции. В программе
тоже используются функции, которые позволяют получать значения по определенным
данным.
В примере
x = str( 5 )
y = val( x )
функция str определяет строку по заданному числу, то есть значением x будет строка "5". Функция val, наоборот, преобразовывает строку в число, то есть значением переменной y будет число 5.
Еще пример.
d = now()
x = day( d )
Функция now (теперь) позволяет получить текущую дату, то есть переменная d имеет тип "дата", а ее значение - сегодняшнее число. Функция day(день) определяет день месяца в заданной дате как число, то есть значением x будет число, равное сегодняшнему дню.
С помощью функций можно также требовать от запроса выводить значения на экран и получать результат, который определит человек, заказавший исполнение запроса (пользователь). Например, можно записать
y = 5
x = messagebox( "y = " + str( y ) )
Здесь функция messagebox во время исполнения запроса выбрасывает сообщение - строку, которая предварительно вычисляется в соответствии с выражением в скобках. Пользователь должен увидеть строку "у = 5". Он может нажать "ОК" или "Отменить" и в зависимости от этого определится значение переменной x и запрос будет выполняться дальше.
Переменная x в приведенном выше примере будет числом. Однако для нас важно, нажал ли пользователь "ОК", и не важно, чему конкретно равно это число. Для этого случая предусмотрены так называемые константы. Можно записать
if x != scbtn_ok then exit() end_if
Здесь полученное от вызова функции messagebox значение x сравнивается со специальным числом, обозначаемым scbtn_ok, и, если x не равно ему (сочетание != означает "не равно"), вызывается функция exit (выход), которая останавливает выполнение запроса. А функция messagebox устроена таким образом, что она возвращает число scbtn_ok, если пользователь нажал "ОК", и другое число, если пользователь нажал что-либо еще, например - "отменить".
Так же, можно передавать функции специальное число, указывающее, что функция должна сделать. Ту же функцию messagebox можно записать и иначе:
x = messagebox( "хорошо?", "как дела?", scmb_yesno )
Такая запись означает, что заголовок сообщения должен быть "Как дела?", текст сообщения - "хорошо?" и в окошке должны быть две кнопки - "да" и "нет" ( это как раз и указывает третий аргумент функции messagebox - число scmb_yesno ).
Пример простой программы
С полученными сведениями можно составить и выполнить простой запрос, приведенный ниже:
d = now()
d1 = d + 1
s = "завтра будет " + str( d1, scdf_long )
r = messagebox( s, "Сообщение", scmb_okcancel )
if r == scbtn_ok then
r = messagebox( "Вы нажали ОК" )
else
r = messagebox( "Вы не нажали ОК" )
end_if
r = messagebox( str( r ) )
Словами это можно сказать так:
"Переменной d присвоить дату, равную сегодняшнему числу"
"Переменной d1 присвоить дату, на один день большую, чем дата d"
"Сформировать строку, состоящую из строк "завтра будет " и строковой
записи даты d1 (причем месяц записан словом) и присвоить ее переменной
s"
"Показать строку s на экране с заголовком "сообщение" и двумя
кнопками и присвоить переменной r, какую кнопку нажал пользователь, увидев
результат"
"Если r соответствует нажатию ОК, тогда"
"Показать
на экране строку "Вы нажали ОК" (и результат запомнить в r)"
"иначе (r не соответствует нажатию ОК)"
"Показать на
экране строку "Вы не нажали ОК" (и результат запомнить в r)"
(затем, так или иначе)
"Показать на экране строку, в которой записано численное значение переменной
r (и результат опять же запомнить в r)"
При исполнении этого запроса на экране должно быть нарисовано окошко, в котором написано завтрашнее число, и двумя кнопками. Если нажать ОК, появится окошко (сообщение) "Вы нажали ОК"; если нажать "Отменить", появится сообщение "Вы не нажали "ОК". После этого появится окошко с каким-то числом и запрос завершится.
Ввод данных в запрос
В большинстве случаев запрос должен выполняться не сам по себе, а для него необходимо, чтобы пользователь задавал для него какие-то данные. Например, можно просто посчитать 2 * 2, но интереснее написать программу, которая запрашивает у пользователя некоторое число, скажем x, и сама умножает его на 2. Это делается так:
x = inputnumeric( "Введите число", 2 )
y = 2 * x
r = messagebox( "2 * " + str( x ) + " = ", str( y ) )
Функция inputnumeric выбрасывает на экран окошко, в котором позволяет ввести число. Первый аргумент функции - заголовок окошка ( строка, в данном примере - "Введите число") . Второй аргумент - начальное значение для числа, которое можно менять в поле ввода (редактировать).
В данном примере введенное число присваивается переменной x. Функция messagebox показывает в конце строку типа "2 * 3 = 6", определенную с использованием переменных x и у.
А если нужно написать программу, в которую вводится несколько чисел? Можно несколько раз вызывать функцию inputnumeric. При исполнении такого запроса будут несколько раз появляться окошки, в которые по очереди нужно будет вводить числа. Но есть способ вывести один раз окошко, в которое можно ввести несколько чисел, потому что там будет несколько полей ввода. Такие окошки называются формами, и для их вызова используется функция showform (показать форму), а так же процедуры описания элементов формы( чтобы знать, какие поля должны быть в форме). Рассмотрим пример:
x = 2
y = 2
editnumber( x, "Первое число" )
editnumber( y, "Второе число" )
if showform( "Вычисление произведения" ) == scbtn_cancel
then exit() end_if
r = messagebox( "Произведение равно " + str( x * y ) )
Запись editnumber( a, s ) говорит программе, что в будущей форме должно быть поле, в котором редактируется переменная a, и у этого поля должен быть заголовок - строка s. Так можно записать несколько раз, такие поля накапливаются, и функция showform показывает их все сразу.
У окошка, показываемого функцией showform, кроме определенных выше полей ввода всегда будут еще две кнопки - "ОК" и "Отменить". В данном примере, если нажато "Отменить", запрос сразу завершится, иначе будет выброшено окошко, в котором вычислено произведение двух введенных чисел. Аргумент функции showform - заголовок окошка (формы).
Чтение данных из базы
Для выборки данных из базы используется инструкция SELECT (выбрать). Допустим, в базе есть таблица "товары" с полями "название" и "цена". Тогда, чтобы извлечь данные из этой таблицы в запрос, надо записать
ds = select название, цена from товары
Здесь ds - название переменной, куда осуществляется выборка; select (выбрать) и from (из) - ключевые слова. Переменная ds здесь имеет тип "набор данных", который можно представлять себе как таблицу, у которой есть записи и поля. Можно дальше, например, записать
res = messagebox( "Товар " + ds.название + " стоит " + str( ds.цена ) )
то есть, как видно, чтобы получить значение поля набора данных, нужно после имени набора данных написать точку и имя поля.
Однако у набора данных может быть много записей. Значение поля в наборе данных берется для текущей записи, так что приведенный пример соответствует названию и цене первого товара в таблице товары. Чтобы перебрать все товары, надо записать
dsbegin( ds )
while dsnext( ds ) do
res = messagebox( "Товар " + ds.название + "
стоит " + str( ds.цена ) )
end_while
Процедура dsbegin устанавливает текущую запись в наборе данных на начало. Процедура dsnext переводит текущую запись в наборе данных на одну вперед.
В этом примере для перебора записей используется так называемый цикл.
Его можно схематитически изобразить так:
В узле, изображаемом ромбиком, проверяется некоторое условие. Если
оно выполняется, то алгоритм идет по правой ветке и опять возвращается
к проверке условия. Так будет повторяться, пока условие выполнено.
Функция dsnext возвращает значение "Истинно" или "Ложно", которое можно
использовать в условии цикла, в зависимосто от того, достигла ли текущая
запись в наборе данных конца или нет. Ключевое слово whille - это начало
цикла; затем идет условие цикла (dsnext(ds)), затем - ключевое слово do,
затем до ключевого слова end_while следуют инструкции для выполнения в
цикле. В нашем примере в цикле будет выбрасываться сообщение.
Варианты инструкции SELECT
Допустим, в базе есть также таблица "продажи" с полями "товар", "дата" и "количество", как описано в разделе "Проектирование базы". Если требуется отобрать в таблице продаж только продажи розеток, можно записать
ds = select дата, количество from продажи where товар.наименование == "розетки"
Здесь where - ключевое слово, после которого идет условие отбора. В условии отбора можно записывать любое логическое выражение. В данном случае поле "товар" таблицы "продажи" было ссылкой на таблицу "товары" и в таблице "товары" было поле "наименование", т.е. запись "товар.наименование" означает наименование конкретного товара. Еще пример:
ds1 = select handle from товары
listsingle( ds1, "Выберите товар" )
if showform() == scbtn_cancel then exit() end_if
ds = select товар.наименование as имя, количество * товар.цена
as сумма from продажи
where товар == ds1.handle
Здесь в первой строчке в переменную ds1 отбирается список всех товаров, причем handle здесь - ключевое слово, означающее ссылку на запись таблицы. Во второй строчке заказывается просмотр списка ds1 в форме, причем в списке требуется выбрать единственный элемент. В третьей строчке показывается форма. После этого в наборе данных ds1 осталась единственная запись - номер ссылки для этого товара. В условии отбора при формировании переменной ds это условие сравнивается со значением поля "товар", которе тоже является ссылкой. Таким образом, будут отобраны только записи для выбранного в форме товара.
Ключевое слово as означаен переименование поля. Причем в качестве поля
может стоять не только имя поля в таблице, но и выражение из имен полей
и переменных. В приведенном примере в результате в наборе данных ds будут
поля "имя" - строка и "сумма" - число.
Еще пример:
ds = select товар.наименование as имя, sum( количество * товар.цена
)as сумма
from продажи group by товар
Здесь group by - ключевое слово, указывающее, что записи надо сгруппировать. В данном случае все записи с совпадающим значением поля "товар" будут объединены. Поле "имя" при этом останется неизменным для каждого товара, а в поле "сумма" будет подсчитана сумма выражений "количество * товар.цена" для каждой группируемой записи. Здесь sum - ключевое слово, относящееся к так называемым агрегатным функциям. Агрегатная функция указывает, каким способом надо группировать объединяемые поля.
Записи при выборке можно также сортировать, для этого используется ключевое слово order by. Например, можно записать
ds1 = select handle from товары order by наименование desc
listsingle( ds1, "Выберите товар" )
if showform() == scbtn_cancel then exit() end_if
При этом в показываемом списке товаров они будут отсортированы по наименованию.
Ключевое слово desc означает, что надо сортировать по убывающей.
Изменение данных в базе
Существует три варианта для изменения данных базы: добавить записи в таблицу, удалить записи из таблицы либо изменить записи в таблице. Соответственно этому применяются интструкции INSERT (вставить), DELETE (удалить) и UPDATE (изменить).
Вставлять в таблицу можно отдельную запись либо набор данных. Варианты инструкции INSERT могут быть следующие:
insert into товары values( "розетки", val( "330" ) )
insert into товары fields (наименование, цена) values ( "розетки",
330 )
insert into товары
select "новые " + наименование,
100 + цена from товары where наименование == "розетки"
Первый вариант добавляет в таблицу запись с определенными значениями для всех полей; в списке значений выражения перечисляются в порядке следования полей в таблице и их тип должен совпадать с типом поля. Второй вариант может добавлять в таблицу запись, где определены только поля, перечисленные в списке после ключевого слова fields; в списке значений выражения перечисляются в порядке следования полей в списке fields. Третий вариант добавляет в таблицу набор данных (это может быть не только инструкция select, но и определенная ранее переменная). Количество полей и порядок их типов в наборе данных должны совпадать с количеством и порядком типов полей в таблице.
При удалении записей может используеться ключевое слово where, как в инструкции SELECT, определяющее, какие записи будут удалены (можно, конечно, удалить и все записи таблицы, тогда никакого условия не нужно):
delete from товары
delete from товары where товар.цена < 100
Синтаксис инструкции UPDATE не совсем совпадает с принятым в SQL и похож на синтаксис инструкции SELECT:
update цена + 100 as цена from товары where наименование == "розетки"
то есть выражение для поля, вместо того, чтобы заносится в набор данных, сразу присваивается полю в таблице, с названием, определенным после ключевого слова as. В данном примере цена розеток будет увеличена на 100.
Извлечение бухгалтерской информации
Когда по записям таблиц можно определить проводки, то естественно представить себе алгоритм, который прокручивает все записи базы, составляет из них проводки, отбирает нужные и как-то использует. Эту операцию можно проделать через интерфейс программы, где предусмотрены возможности создания документов на основе отобранных проводок. Эту операцию можно проделать и в запросе, для этого в языке запросов существует ряд встроенных функций. Самый простой пример - подсчитать обороты между двумя счетами за заданный интервал. Для этого существует функция turnoversum:
оборот = turnoversum( date( "01.01.91" ), date( "01.02.91" ), "счет = '50'", "счет = '51'" )
Поскольку можно, кроме суммы проводки, определить еще и количество, с целью ведения количественного учета, то существует еще и функция turnovernum.
Если считать, что в базу занесена вся деятельность предприятия с момента основания, то, проанализировав все проводки раньше заданной даты, можно получить остаток по счету. Для того, чтобы не хранить устаревшую информацию, в программе можно задавать и хранить список остатков на счетах на заданную дату, в специальной встроенной таблице. Так или иначе, функция получения остатков на счете существует и называется restsum / restnum:
остаток = restsum( date( "01.02.91" ), "счет = '51'" )
Счета в этих функциях могут задаваться не только с указанием всей аналитики, но и обобщенно (хотя при определении проводок все ключи аналитики должны указываться). Например, при соответствущем плане счетов не будет ошибкой записать следующие три варианта обращения к базе:
остаток1 = restsum( now(), "счет = '45', товар = 'розетки', покупатель
= 'сидоров'" )
остаток2 = restsum( now(), "счет = '45', товар = 'розетки'" )
остаток3 = restsum( now(), "счет = '45'" )
При этом понятно, что остаток1 <= остаток2 <= остаток3.
Поскольку при каждом вызове функции restsum или turnoversum происходит чтение всей базы, то актуален вопрос скорости работы запроса. В программе предусмотрен вариант, когда можно накопить отдельные обращения типа подсчета оборотов по счету и затем один раз прокрутить базу и определить все накопленные запросы. Для этого существуют процедуры setturnoversum/setternovernum/setrestsum/setrestnum и процедура calcturn. Вот пример их использования:
ds = select наименование, 0 as остаток from товары
dsbegin( ds )
while dsnext( ds ) do
str = "счет = '41', товар = '" + ds.наименование
+"'"
setrestsum( ds.остаток, now(), str )
end_while
calcturn()
report()
Процедура setrestsum в данном примере устанавливает, что процедура calcturn
должна занести в поле "ds.остаток" ( в соответствующую запись ) остаток
по заданому счету на заданную дату. Таким образом, один раз прокрутив базу,
можно подготовить отчет с остатками по всем товарам.