ФБП · Язык программирования руководство пользователя

Язык программирования
«Финансы без проблем»

Руководство пользователя: отчётные формы, операторы, функции, виды операций и проводки, лента фактов, запросы к серверу.

Подготовлено на основе оригинальных руководств «Финансы без проблем» © Хакерс Дизайн (hdru.com): «Руководство пользователя. Однопользовательские версии» и «Руководство пользователя. Сетевая версия».
Пользуйтесь оглавлением слева или поиском вверху страницы (Ctrl+K).

1. Введение: что за язык и где он выполняется

«Финансы без проблем» (ФБП) — бухгалтерская система, в которой почти всё поведение определяется не жёстко зашитым кодом, а программами на встроенном языке ФБП, которые пишет сам пользователь. На этом языке описывают:

  • отчётные формы (файлы *.rpt) — отчёты, списки, диалоги, документы;
  • виды операций и проводки (дерево cas.rul) — что происходит со счетами при регистрации хозяйственной операции;
  • файлы-коэффициенты — алгоритмы расчёта сумм проводок;
  • первичные документы (формы, связанные с операциями).

Это руководство учит именно программированию: как устроены формы, какие есть операторы и функции, как обращаться к счетам и оборотам, как строить диалоги и отчёты. Установка программы и повседневная работа бухгалтера здесь не рассматриваются.

1.1. Где «живут» программы на языке ФБП

У языка ФБП есть несколько точек приложения, и важно сразу понимать, где какой код выполняется:

МестоФайлЧто там пишут
Отчётная формаимя.rptТекст отчёта с операторами и функциями; выполняется при запуске формы.
Файл-коэффициентимя.rptАлгоритм, который вычисляет сумму проводки; вызывается из листа дерева видов операций.
Лист дерева видов операцийcas.rulПроводки (дебет, кредит, коэффициент/формула), псевдопроводки.
Общие определенияfirst.rpt, library.rpt, *.libПеременные и подпрограммы, доступные всем формам.
Первичный документимя.rpt → *.txaФорма печатного документа, связанного с операцией.

1.2. Кто выполняет программы: сервер и клиент

В сетевой версии ФБП есть ФБП:Сервер — единственный процесс, который хранит данные и выполняет все программы на языке ФБП: регистрирует операции, пересчитывает баланс, выполняет отчётные формы. ФБП:Клиент — «тонкий»: он лишь показывает ответы сервера и передаёт ему запросы. Поэтому форма, написанная вами, всегда работает «внутри» сервера и имеет прямой доступ ко всем данным учёта.

Обмен клиента с сервером происходит не по сетевым протоколам, а через файлы в общем «каталоге обмена». Для программиста на языке форм это почти неважно, но для понимания диагностики и директив сервера пригодится — см. главу «Язык запросов к серверу».

1.3. Главная «магия» ФБП: виртуальный пересчёт

Если вы изменили лист дерева видов операций — например, добавили проводку с расчётом налога, — то интерпретация всех ранее введённых операций этого вида изменится сразу: остатки и обороты счетов пересчитаются автоматически. Данные (операции) и правила (дерево, формы) в ФБП независимы, поэтому настройку можно менять «на живую», не переформатируя базу.

1.4. Как выглядит программа: первый пример

Отчётная форма — это обычный текстовый файл. Строки текста попадают в отчёт как есть, строки-операторы выполняются, а форматные вставки из знаков ^ заменяются значениями переменных. Вот законченная форма:

* форма hello.rpt — первая программа
X = 2 + 2
Привет! Дважды два = ^^^^X

Остаток на счете 50 (Касса): ^^^^^^^^^^[ea 50]

Запустив такую форму, вы получите примерно следующее:

Привет! Дважды два =    4

Остаток на счете 50 (Касса):      12500.00

Здесь видны сразу три базовых элемента языка: комментарий (строка со звёздочки), оператор присваивания и вызов функции [ea 50] — остаток счёта. Подробно всё это разбирается в главах «Переменные и выражения» и «Функции».

1.5. Условные обозначения в этом руководстве

  • [ea X] — функции языка записываются в квадратных скобках;
  • · — средняя точка (символ с кодом 250), служебный разделитель в строках директив и уточнений;
  • //250// — способ записать любой символ его кодом (ровно три цифры), если сам символ неудобно вводить;
  • в примерах строки, начинающиеся с *, — комментарии;
  • пометка вида с 3.07 — версия, начиная с которой возможность доступна.
Исходные тексты форм исторически пишутся в кодировке CP866 (DOS). Если ваши русские буквы в форме превращаются в «кракозябры» — почти наверняка дело в кодировке файла.

1.6. Как читать это руководство

Если вы новичок — идите подряд от главы «Быстрый старт»: каждая глава опирается на предыдущие. Если нужен конкретный оператор или функция — пользуйтесь поиском вверху страницы (клавиши Ctrl+K) или оглавлением слева; главы 9–11 образуют полный справочник функций, глава «Примеры и приёмы» — сборник готовых решений.

Все примеры из руководства можно сразу проверить: создайте файл probe.rpt в каталоге данных, наберите пример и запустите форму (в клиенте — директивой R probe, в однопользовательской версии — из меню файлов форм клавишей Enter).

2. Быстрый старт: ваша первая форма за 10 минут

Эта глава — практикум «с нуля до первого работающего отчёта». Мы установим программу (если она ещё не установлена), создадим файл формы, наберём простейшую программу, запустим её и научимся реагировать на сообщения об ошибках. Дальше по книге всё будет только дополнять эти базовые навыки.

2.1. Если программа ещё не установлена

Чтобы попробовать «Финансы без проблем» в работе, не нужно ничего, кроме архива с сайта производителя и десяти минут времени.

Для DOS:

  1. Перепишите с сайта http://hdru.com/russian/ultrah.zip и разархивируйте утилитой pkunzip с ключом -d.
  2. Заведите рабочий каталог, например FIN, и скопируйте туда файлы fine.exe, finebody.ovl, fin.hlp, install.exe.
  3. В архиве есть каталоги с примерами настроек (например, NSBU — пример под украинское законодательство). Скопируйте все файлы выбранного примера прямо в рабочий каталог.
  4. Запустите программу командой fine.exe.

Для Windows: перепишите архив http://hdru.com/russian/w_ultrah.zip, разархивируйте его и запустите finsetup.exe, затем следуйте указаниям установщика.

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

2.2. Создаём файл формы

Формы хранятся в обычных текстовых файлах с расширением *.rpt в рабочем каталоге данных. Создавать и запускать их удобнее всего прямо из программы, в режиме «Файлы форм». Перейти в него можно через меню Документы → Файлы форм или клавишами Alt+F4.

В этом режиме показывается список файлов: имена и первые строки отчётных форм, первичных документов, файлов-коэффициентов, форм-историй и файлов общих определений. Действия над файлами выполняются клавишами:

КлавишаДействие
Enterвыполнить (запустить) форму
F4редактировать форму
F6переименовать форму
F7создать новую форму
F8удалить форму

Нажмите F7, задайте имя, например hello — программа создаст файл hello.rpt и откроет встроенный текстовый редактор.

2.3. Основные клавиши редактора

Редактор форм простой и «досовский» по духу, но всё необходимое в нём есть. Минимум, которым вы будете пользоваться постоянно:

КлавишаДействие
стрелки, Home / Endперемещение курсора; в начало / конец строки
Ctrl+Home / Ctrl+Endв начало / конец файла
Insвкл/выкл режим вставки (на экране — знак <Ins>)
Enterвставить строку (в режиме вставки)
Ctrl+Yудалить строку
Delудалить символ
F7поиск по образцу от текущей позиции
F9вкл/выкл режим рисования линий (для рамок в отчётах)
F2записать текст в файл
F5печатать текст
Начиная с версии ultraH-II для DOS в редакторе работают блоки: выделение — Shift+стрелки, копирование в буфер — Ctrl+C, вставка — Ctrl+V. Там же появился текстовый режим 80x50 (его программа наследует, например, при запуске из Far Manager).

2.4. Первая форма: комментарий, присваивание, форматная вставка

Форма — это текстовый файл, в котором смешаны три вещи: обычный текст (он попадёт в отчёт как есть), операторы (они выполняются, но ничего не печатают сами) и форматные вставки из знаков ^ (вместо них в отчёт подставляются значения переменных). Наберите в hello.rpt такой текст:

* Первая форма: знакомимся с языком
B=234.1; A=B+12.2
Результат вычисления: ^^^^^^A^^^

Разберём по строкам:

  • Строка, начинающаяся со звёздочки *, — комментарий: она никуда не выводится и нужна только для тех, кто читает форму.
  • B=234.1; A=B+12.2 — два оператора присваивания в одной строке, разделённые точкой с запятой. После выполнения A станет равным 246.3. Текст после оператора в той же строке тоже считается комментарием.
  • В третьей строке слова «Результат вычисления:» — обычный текст, а ^^^^^^A^^^ — форматная вставка. Шесть знаков ^ перед именем переменной отводят шесть позиций под целую часть числа, три знака после — три позиции под дробную часть.

Запустите форму (как — см. следующий подраздел) и в отчёте появится:

Результат вычисления:    246.300

Число выровнено по правому краю отведённого поля: перед 246.300 стоят три пробела, добирающие целую часть до шести знаков.

Если число не помещается в отведённый формат, вся вставка заполняется знаками ^ — это видимый признак переполнения, а не ошибка. Просто увеличьте количество ^ перед именем переменной.
Форматные вставки умеют выводить и длинные строки «в несколько этажей» (через так называемые растекатели), а знак \ в конце строки подавляет перевод строки при выводе. Всё это подробно разобрано в главе «Текст и вывод».

2.5. Запуск формы

Способ запуска зависит от того, в какой версии вы работаете.

В однопользовательской версии всё просто: в режиме «Файлы форм» подведите селектор к имени формы и нажмите Enter — форма выполнится, а результат появится на экране и одновременно попадёт в файл report.txt в рабочем каталоге (его можно открыть любым текстовым редактором).

R форма [·ответ· …] — выполнить форму

В сетевом клиенте форму запускают директивой R, набранной в строке запроса к серверу. форма — имя файла без расширения .rpt. Если форма задаёт вопросы (оператор ? или функции ввода), ответы можно передать заранее — после имени, обрамив каждый знаками · (символ с кодом 250, вводится Alt+250 на цифровой клавиатуре).

R HELLO
R 10 ·3·

Вторая директива выполняет форму 10.rpt; первый же вопрос в ней получит ответ 3 автоматически. Подробно язык запросов описан в главе «Язык запросов к серверу».

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

2.6. Форма с вводом значения

Отчёт становится в сто раз полезнее, если перед построением спросить у пользователя параметры — дату, сумму, название. Для этого есть функции ввода; вот две самые ходовые.

[ir s, nn, mm] — ввод числа с 3.1

Выводит вопрос s и ждёт число. nn — значение по умолчанию (число или формула, подставляется, если просто нажать Enter), mm — количество знаков после десятичной точки.

A=[ir 'Введите номер поручения:',X,0]

[is s, sd, mm] — ввод строки с 3.1

Выводит вопрос s и ждёт строку длиной не более mm символов; sd — значение по умолчанию.

A=[is 'Введите название фирмы:',A,20]

Соберём форму с вводом целиком:

* Форма ask.rpt — спрашиваем и считаем
S=[ir 'Введите сумму:', 100, 2]
N=[is 'Кому платим?', ' ', 30]
Получатель: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^N
Сумма: ^^^^^^^^^^^S^^
T=S*1.1
Сумма с надбавкой 10%: ^^^^^^^^^^^T^^
В ранней Windows-версии тексты вопросов и значения по умолчанию в функциях ввода могут быть только константами: S=[is 'Введите строку', 20] работает, а вариант с переменными внутри вызова — нет. Если форма «не спрашивает» — проверьте, что аргументы записаны константами.

Есть и более простое средство — оператор вопроса:

? переменная строка

Он выводит строку на экран и запрашивает значение переменной. Но функции [ir]/[is] удобнее: у них есть значения по умолчанию и контроль формата. Все функции ввода и оформления диалогов (включая меню [im] и выбор субсчёта [ia]) подробно описаны в главе «Функции: ввод и оформление».

2.7. Если синтаксическая ошибка

Ошибаться в первых формах будете много — это нормально, язык сообщает об ошибках вполне дружелюбно. Если при выполнении формы встретилась ошибка, программа сама переходит в режим редактирования текста формы: на экране появляется сообщение, а курсор встаёт на место ошибки. Исправьте строку, запишите файл (F2) и запустите снова.

Две опции делают охоту за ошибками ещё проще:

  • «Синтаксис подсвечен» — анализатор непрерывно просматривает текст и раскрашивает операторы, комментарии, обычный текст и ошибочные места разными цветами;
  • «Сначала все ошибки» — полный анализ, включая проверку существования счетов, к которым обращается форма. Если опция выключена, проверяется только формальный синтаксис (скобки, запятые), а предупреждения об отсутствующих счетах появятся уже при выполнении.
В Windows-версии сообщение об ошибке появляется в нижней полосе главного окна. Клавиша Alt+E (или кнопка «Где ошибка») активирует окно с ошибкой и ставит на неё селектор; повторное нажатие может показать ещё одно окно с дополнительной информацией — например, при делении на нуль первое нажатие ведёт в дерево видов операций, второе — в журнал операций. Клавиша Pause останавливает и возобновляет фоновые вычисления.

Некоторые сообщения анализатора говорят сами за себя, например «@-обращение к несуществующему счету» (счёт не описан в списке счетов — введите его и его субсчета) или «Неправильно указан вид операции» (дерево изменилось после ввода операций — укажите вид операции заново). Полный перечень сообщений и способы реакции на них собраны в главе «Приложения».

2.8. Куда идти дальше

Теперь вы умеете создавать, редактировать и запускать формы. Дальше путь такой:

Хочу…Глава
Разобраться в типах файлов форм, общих определениях first.rpt, библиотеках3. Файлы форм и общие файлы
Красиво оформить вывод: форматные вставки, растекатели, вывод в файл4. Текст и вывод
Понять переменные, выражения и типы данных5. Переменные и выражения
Условия, циклы, подпрограммы — все операторы языка6. Операторы
Получать остатки и обороты счетов: [ea], [od] и десятки других9. Функции: данные учёта
Все функции ввода и оформления: [ir], [is], [im], [ia]10. Функции: ввод и оформление
Научить программу делать проводки: дерево видов операций14. Виды операций и проводки
Управлять сервером запросами: директивы R, O и другие16. Язык запросов к серверу
Готовые решения и приёмы из практики20. Примеры и приёмы
Не откладывайте практику: после каждой главы открывайте свой hello.rpt и пробуйте новые конструкции в нём. Форма из этой главы — отличный полигон для экспериментов.

3. Файлы форм: создание, компиляция, общие файлы

Программы на языке ФБП хранятся в обычных текстовых файлах с расширением .rpt в рабочем каталоге. В этой главе разбирается «хозяйственная» сторона вопроса: как создавать и редактировать формы, как система проверяет и выполняет их текст, какие служебные файлы (FIRST.RPT, LIBRARY.RPT, PROTO.RPT, библиотеки *.lib) позволяют не повторять общий код в каждой форме, и как отправить результат формы не на экран, а в файл.

3.1. Режим «Файлы форм»

Все тексты на языке ФБП — отчётные формы, первичные документы, файлы-коэффициенты, формы-истории, файлы общих определений — собраны в одном списке. Чтобы попасть в него, выберите в меню Документы → Файлы форм или нажмите Alt+F4 (либо щёлкните соответствующую иконку мышью). Для каждого файла показываются его имя и первая строка — поэтому принято начинать форму со строки-комментария с понятным названием.

КлавишаДействие в режиме «Файлы форм»
Enterвыполнить (запустить) форму
F4редактировать форму
F6переименовать форму
F7создать новую форму
F8удалить форму

3.2. Встроенный редактор

Форму можно набрать и во внешнем редакторе (это простой текстовый файл в кодировке CP866 — см. предупреждение в главе «Введение»), но встроенный редактор удобнее: он связан с анализатором синтаксиса и при ошибке выполнения сам показывает место сбоя.

КлавишаДействие в редакторе
Ctrl+Home / Ctrl+Endперейти в начало / конец файла
Home / Endперейти в начало / конец строки
стрелкиперемещение курсора
Insвключить/выключить режим вставки (на экране — индикатор <Ins>)
Enterвставить строку (в режиме вставки)
Ctrl+Yудалить строку
Delудалить символ
F2записать текст в файл
F5напечатать текст
F7поиск по образцу от текущей позиции
F9включить/выключить режим рисования линий (есть знак-индикатор на экране)
Начиная с версии ultraH-II для DOS в редакторе работают блоки: Shift+стрелки выделяют блок, Ctrl+C копирует его в буфер, Ctrl+V вставляет из буфера. Там же появилась поддержка текстового режима 80x50 (он наследуется, например, при запуске из Far Manager; в этом режиме программа не загружает свой шрифт, в отличие от режима «как в Windows» при 80x25).

3.3. Интерпретатор и компилятор

Когда вы запускаете форму, её текст не «читается построчно» в момент вывода, как в классических пакетных файлах. В ранних версиях работал интерпретатор, а начиная с версии 3.1 форма сначала компилируется в промежуточный код, который затем выполняет специальная машина — это сделано ради быстродействия. Внешне поведение осталось прежним: как у интерпретатора старых версий.

Если во время выполнения формы возникает ошибка, система переходит к редактированию текста формы: на экран выводится сообщение, а курсор указывает место ошибки.

В сетевой версии компиляцией форм управляет ФБП:Сервер (команда W FORM; подробности — в главе «Язык запросов к серверу»).

С версии Сервера 3.05 команда W FORM перекомпилирует только изменённые формы, а FIRST.RPT, PROTO.RPT и первичные документы не перекомпилируются вовсе — после их правки требуется перезагрузка сервера (W ON, W OFF). Учтите это, меняя общие определения «на живую».
С версии Сервера 3.12 файлы *.rpt можно шифровать (технология Enigma): расширение сохраняется, шифрованные и обычные файлы допустимо смешивать. Первая строка формы остаётся видимой — поэтому по-прежнему делайте её осмысленным комментарием.

3.4. Синтаксический анализатор и его сообщения об ошибках

Проверка текста выполняется непрерывно, ещё до запуска формы. Две опции управляют анализатором:

  • «Синтаксис подсвечен» — анализатор постоянно просматривает текст форм и дерево видов операций, раскрашивая разными цветами операторы, комментарии, текст формы и ошибочные места;
  • «Сначала все ошибки» — полный анализ, включая проверку существования всех упомянутых счетов. Если опция выключена, проверяется только формальный синтаксис (скобки, запятые и т.п.), а предупреждения об отсутствующих счетах появятся лишь при компиляции или выполнении.

Компилятор дерева видов операций обнаруживает большинство формальных ошибок «на первой секунде» попытки построить баланс. Дальше поведение зависит от версии:

  • В DOS-версии, если операции уже введены, а дерево затем изменено, при построении оборотной ведомости (Alt+F3) несоответствующие операции автоматически отыскиваются в журнале, на них ставится селектор, а внизу экрана появляется сообщение «Неправильно указан вид операции». Такие операции нужно заново «перебить» (или удалить и ввести снова).
  • В Windows-версии (многооконной) изменения в окне «Счета…» приводят к тому, что отсутствующие счета перечёркиваются и перекрашиваются в окне «Дерево…»; изменения в «Дерево…» цветом выделяют ставшие несоответствующими операции в окне «Операции…». Сообщение об ошибке выводится в нижней полосе главного окна; кнопка «Где ошибка» (мышью) или Alt+E (с клавиатуры) активирует окно с ошибкой и ставит на неё курсор. Повторное нажатие может открыть ещё одно окно с дополнительной информацией: например, при делении на нуль в операции первое нажатие покажет место в «Дерево…», второе — в «Операции…». Клавиша Pause останавливает и возобновляет фоновые вычисления.

Полный перечень сообщений анализатора с пояснениями:

СообщениеПричина и что делать
ПУСТАЯ СТРОКА В ОПИСАНИИ ПРОВОДКИВ листе дерева видов операций и проводок есть пустая строка — удалите её.
НЕПРАВИЛЬНО ОПИСАНА ПРОВОДКАНеправильное описание проводки в листе дерева: проверьте порядок «дебетуемый счёт, кредитуемый счёт, коэффициент».
НЕПРАВИЛЬНЫЙ КОЭФФИЦИЕНТ В ОПИСАНИИ ПРОВОДКИНеправильно задано числовое выражение коэффициента в описании проводки.
@-ОБРАЩЕНИЕ К НЕСУЩЕСТВУЮЩЕМУ СЧЕТУ@-обращение ссылается на счёт, не описанный в списке счетов. Нужно ввести новый счёт и его субсчета.
@-ОБРАЩЕНИЕ К СЧЕТУ БЕЗ СУБСЧЕТОВ@-обращение должно ссылаться на счёт, имеющий субсчета: либо субсчета ещё не созданы, либо упомянут не тот счёт.
НЕИЗВЕСТНЫЙ (ИЛИ СОСТОЯЩИЙ ИЗ СУБСЧЕТОВ) ДЕБЕТУЕМЫЙ СЧЕТ / …КРЕДИТУЕМЫЙ СЧЕТСчёт не описан в списке счетов либо состоит из субсчетов, но @-обращение не применяется — для такого счёта обращайтесь через @.
НЕТ ОПИСАНИЙ ПРОВОДОК ДЛЯ ЭТОЙ ОПЕРАЦИИНет листа с описаниями проводок для данного вида операции — добавьте лист в дерево.
НЕПРАВИЛЬНО УКАЗАН ВИД ОПЕРАЦИИВетвь дерева изменилась после того, как виды операций были указаны; при редактировании операции заново укажите вид.
В ОПИСАНИИ ПРОВОДКИ НЕТ ССЫЛКИ НА ФАЙЛ-КОЭФФИЦИЕНТВозникает при редактировании дерева по F4, если текущее описание проводки не содержит коэффициента или содержит числовой коэффициент.
Это сообщения о согласованности дерева, счетов и форм. Полный список всех сообщений об ошибках и аварийных завершениях приведён в главе «Приложения».

3.5. Файлы-коэффициенты

Файл-коэффициент — это обычный файл .rpt, содержащий алгоритм на языке форм, который вычисляет сумму проводки. Он нужен там, где сумма не совпадает с суммой операции: расчёт налогов, процентов, амортизации и т.п. Файл-коэффициент вызывается из листа дерева видов операций — имя файла дописывается в строке проводки после счетов (подробно о дереве — глава «Виды операций и проводки»).

Обмен данными с вызвавшей проводкой идёт через предопределённые переменные:

ПеременнаяСмысл
$на входе — сумма операции; в эту же переменную файл-коэффициент возвращает вычисленную сумму проводки (то есть $ — и передаваемый, и возвращаемый параметр)
$Dобозначение дебетуемого счёта
$Кобозначение кредитуемого счёта

Создать файл-коэффициент можно двумя способами: в режиме «Файлы форм» клавишей F7 или прямо из дерева проводок — дописать в строке проводки после пробела имя файла-коэффициента и нажать F4.

Пример. К проводке @70 68-3 (удержание подоходного налога) дописано имя файла-коэффициента TAX. Начало этого файла:

* Подоходный налог с физических лиц
*
D=[AK $D]        Доход, начисленный сотруднику с начала года
U1=[AP $D,68-3]  Уже удержанный подоходный налог (город)
U2=[AP $D,68-31] Уже удержанный подоходный налог (федеральный)
G=D*0.01         вычеты в Пенсионный фонд 1%
M=[ed MIN]       Подлежащие вычету минимумы
P=[ep @70]       Сумма льгот, накопленная к моменту начисления
if D in 0..10000 D=D-P-2*M; L=M*2
elseif D in 10000..30000 D=D-P-M; L=M
else D=D-P; L=0
endif
*
* ... далее — расчёт налога по шкале и итоговое присваивание:
$=J2

Здесь видно несколько важных приёмов файлов-коэффициентов: строки со звёздочки — комментарии (комментарий допускается и в конце оператора, после выражения); обращения к данным учёта записываются в квадратных скобках ([AK …], [AP …], [ed …], [ep …] — см. главу «Функции: данные учёта»); несколько операторов в одной строке разделяются точкой с запятой; результат возвращается присваиванием $=….

Файл общих определений FIRST.RPT перед файлами-коэффициентами не выполняется (см. ниже) — поэтому коэффициент не должен полагаться на переменные, установленные в FIRST.RPT. Общие подпрограммы для коэффициентов размещайте в LIBRARY.RPT (раздел 3.8).

3.6. Файл общих определений FIRST.RPT

Если в рабочем каталоге есть файл FIRST.RPT, он выполняется перед выполнением каждого первичного документа и отчётной формы. Туда выносят всё, что должно быть общим для форм: употребительные константы, подпрограммы, значения общих переменных — реквизиты предприятия, Esc-последовательности управления принтером и т.п.

* FIRST.RPT — общие определения
FIRMA='ОАО "Ромашка"'
ESC=[ch 27]
CONDENSED=ESC+[ch 15]   * узкий шрифт принтера

Ограничение уже упоминалось: перед файлами-коэффициентами FIRST.RPT не выполняется. Это разумно: коэффициент вызывается при каждой проводке, и лишние вычисления замедлили бы пересчёт.

Помните, что некоторые обращения в FIRST.RPT (например, функции [user], [ct …], [ged …] или переменные границ отчётного периода) отключают глубокую минимизацию кэша для всех форм — это влияет на производительность. Подробности — в главе «Производительность и отладка».

3.7. Файл PROTO.RPT — прототипы фактов

Лента фактов (глава «Лента фактов») хранит записи из безымянных частей. Чтобы система могла проверять правильность операторов fact, search и им подобных, синтаксис фактов описывают в файле PROTO.RPT — в виде прототипов. Знак % отмечает место части факта, остальные символы — разделители:

**********************************
приход товара, %, %
передача товара, %, %
**********************************

Прототипы можно сделать более выразительными — тогда и операторы читаются как фразы:

************************************************
товара % есть % единиц по цене %
поставщик % передал % единиц товара % на сумму %
************************************************

с ними законны записи вида fact товара T есть Q единиц по цене P и total поставщик Z передал ?? единиц товара ?? на сумму ?S.

Программа автоматически проверяет соответствие прототипа из PROTO.RPT и факта, использованного в файлах форм. Прототипы помогают и при вводе: в режиме редактирования файла-коэффициента или формы нажмите F4, выберите прототип из списка и нажмите Enter — заготовка оператора вставится в текст.

3.8. LIBRARY.RPT и библиотеки *.lib

Подпрограммы и определяемые пользователем функции (операторы call, return (выражение), вызовы [:метка] — см. главу «Операторы») можно собрать в файле LIBRARY.RPT: они станут доступны из любой формы или файла-коэффициента. Возможность появилась в версии ultraH-II от 07.10.04 (на сервере — с версии 4.00).

В отличие от FIRST.RPT, библиотека подходит и для файлов-коэффициентов — поэтому общий расчётный код, нужный и формам, и коэффициентам, следует класть именно в LIBRARY.RPT.

Начиная с версии Сервера 4.21 в рабочем каталоге может быть несколько файлов библиотек *.lib с функциями и подпрограммами — это позволяет разбивать общий код на тематические модули. Файлы *.lib защищаются шифрованием Enigma.

3.9. Вывод результата формы в файл

Результат выполнения отчётной формы автоматически попадает в файл report.txt — его можно открыть в любом текстовом редакторе. Но форма может и сама писать в произвольные файлы: для этого строку вывода завершают знаком > (начать файл заново) или >> (добавить в конец файла), затем пишут ключевое слово file и через пробел(ы) — имя переменной, чьё строковое значение задаёт имя файла:

строка-вывода >file переменная
строка-вывода >>file переменная

Пример из руководства (он работает и в файлах-коэффициентах):

a=[dir 0]+'1.txt'
\>file a
^^^^^^^^^^^b >>file a

Разбор по шагам:

  • [dir 0] — функция, возвращающая имя каталога (см. карточку ниже); имя файла собирается строковым выражением, здесь получается 1.txt в каталоге данных;
  • \>file a — выводится пустая строка с подавленным переводом строки (начальный \): файл усекается до нулевой длины;
  • ^^^^^^^^^^^b >>file a — значение b дописывается в файл; оно попадает в первую строку, потому что при усечении перевода строки не было. Если бы вместо \>file a было написано >file a, первая строка файла была бы пустой, а значение b попало бы во вторую строку.

[dir n] — имя каталога данных или каталога обмена

n=0 — каталог, где находятся данные (на сервере — каталог данных Сервера); n=1 — каталог обмена Клиентов с Сервером. Возвращаемая строка всегда завершается знаком \. В «персональной» (однопользовательской) ФБП функция всегда возвращает .\ — реальный смысл она имеет на сервере.

a=[dir 0]+'1.txt'      * файл 1.txt в каталоге данных
f=[dir 1]+'TXA\'+[stamp 3]+'.TXA'   * файл в подкаталоге TXA каталога обмена
Имя файла без каталога при выводе >file — это не каталог данных Сервера! Всегда составляйте полное имя через [dir 0] или [dir 1], иначе файл окажется не там, где вы ожидаете.

Похожие возможности даёт и оператор printstr … to > file f — он описан в главе «Текст и вывод» (раздел 4.4).

4. Текст и вывод: форматные вставки

Отчётная форма — это прежде всего текст. Строки, которые не являются операторами, попадают в отчёт как есть; чтобы подставить в этот текст значения переменных, служат форматные вставки из знаков ^. В этой главе — как они устроены, как выводить длинные строки, как «склеивать» вывод в одну строку и как тонко управлять представлением чисел.

4.1. Механика ^-полей

В любом месте текста формы (но не внутри операторов) допустима форматная вставка: цепочка знаков ^, затем имя переменной, затем, возможно, ещё знаки ^:

^...^имя_переменной^...^

При выполнении формы вставка заменяется значением переменной, причём знаки ^ задают формат числа:

  • количество ведущих ^ (до имени) — число знаков под целую часть;
  • количество завершающих ^ (после имени) — число знаков под дробную часть.

Пример:

B=234.1; А=B+12.2
^^^^^^А^^^

После вычислений A = 246.3; в отчёт попадёт 246.300 — шесть знаков целой части (значение дополняется ведущими пробелами) и три знака дробной.

Переполнение. Если число не помещается в отведённый формат, вся вставка заполняется знаками ^ — это видимый признак переполнения, а не «мусор». Чтобы такого не случалось, отводите поле с запасом; для слишком длинных строк используйте вырезку [cp s,nn,mm] (см. главу «Функции: строки, даты, математика»).

4.2. Вывод строк и растекатели

Форматные вставки работают и со строковыми значениями: строка просто занимает место вставки. Но что делать, если строка длиннее строки отчёта и должна выводиться «в несколько этажей»? Для этого служат растекатели — цепочки знаков ^ без имени переменной на строках, следующих за форматной вставкой:

S='qwertysdf ghjkzxcvbn mqwer tyuiop'
^^^^^^^^^^^^^^^^^S
^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^

Правила растекания:

  • растекатели располагаются на строках, следующих за форматной вставкой;
  • общее число ^ во вставке и растекателях должно быть не меньше длины выводимой строки;
  • одновременно может «растекаться» только одна форматная вставка.
Если выводимая строка длиннее форматной вставки вместе с растекателями, возникает неуправляемое переполнение: остаток строки растекается по последующим форматным вставкам, искажая выводимую информацию. Всегда проверяйте длину заранее (функция [length s]) или обрезайте строку ([cp …]).

4.3. Обратный слэш: вывод без перевода строки

Знак \ в конце строки формы подавляет перевод строки при выводе (сам \ не выводится). Следующая выводимая строка продолжит текущую — так можно строить вывод в одну строку из цикла, удобно для «широких» форм с заранее неизвестным числом полей.

for i=1 to 10
^^i \
endfor
-----

Результат выполнения:

1 2 3 4 5 6 7 8 9 10
-----

Пустая строка формы просто переводит строку — без неё ----- появились бы сразу после 10.

4.4. Оператор printstr

printstr s — вывод строки без форматирования с ultraH-32 9.04.00

Выводит строковое выражение s в отчёт. В отличие от форматных вставок, printstr не завершает вывод переводом строки (CR LF) и не добавляет лишних пробелов. Чтобы получить перевод строки между двумя printstr, вставьте между ними пустую строку.

printstr 'qwerty'
printstr 'asdf'
* выведет: qwertyasdf

Если те же два оператора разделить пустой строкой, будет выведено две строки: qwerty и asdf.

Начиная с версии ultraH-32 от 27.03.01 вывод printstr можно переадресовать в файл (s — выводимая строка, f — строка с именем файла):

printstr s                  * обычный вывод
printstr s to > file f      * начать новый файл f, записать строку s
printstr s to >> file f     * добавить строку s в конец файла f
printstr s to file f        * то же, что и предыдущий вариант

Сравните с перенаправлением >file / >>file для обычных строк вывода — раздел 3.9 главы «Файлы форм». Те же правила про полное имя файла действуют и здесь: используйте [dir 0] / [dir 1].

printstr особенно удобен при генерации HTML и других текстовых форматов, где лишние пробелы и переводы строк имеют значение — см. главу «Web-приложения».

4.5. Спецкомментарии форматирования: *.on/*.off, *,on/*,off

Особые строки-комментарии позволяют глобально (в пределах формы) управлять тем, как числа превращаются в текст. Они появились в версии ultraH-32 от 9.04.00 (на сервере — с версии 3.14) и действуют и в формах, и в файлах-коэффициентах — до конца формы либо до отменяющего комментария:

СпецкомментарийДействие
*.onразрешает выдавать дробную часть числа
*.offзапрещает выдавать дробную часть числа
*,onразрешает запятые в целой части числа (разделение тысяч)
*,offзапрещает запятые в целой части
*.,dfвозврат к поведению по умолчанию (для форм — к пожеланиям ФБП:Клиента)

По умолчанию действует сочетание «как у *,on + *.off»: запятые есть, дробной части нет.

Полный поучительный пример (функция [sn nn,mm,kk] преобразует число в строку по формату — см. главу «Функции: строки, даты, математика»):

a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a
*.on
*,on
a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a
*.off
*,on
a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a
*.on
*,off
a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a
*.off
*,off
a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a

Результат выполнения формы:

1,200
1,200.345
1,200
1200.345
1200
Спецкомментарии меняют поведение не только функции [sn …], но и форматных вставок — и действуют до конца формы. Забытый *,off в начале формы — типичный источник трудноуловимых расхождений в печати: одна и та же форма выдаёт числа по-разному в зависимости от того, что осталось «включённым». Возвращайте умолчание явно — комментарием *.,df.

5. Переменные, выражения и формулы

Почти вся «начинка» отчётной формы — это формулы: ими вычисляются итоги, проверяются условия, формируются суммы проводок в файлах-коэффициентах. Формула в ФБП строится из чисел, переменных, знаков операций, круглых скобок и вызовов функций — в целом всё похоже на школьную арифметику, но со своими правилами имён, приоритетов и неожиданными возможностями вроде решения систем линейных уравнений.

5.1. Калькулятор: полигон для выражений

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

  • Enter — завершить ввод выражения;
  • F10 — выход с переносом вычисленного значения (если калькулятор вызван при редактировании числа);
  • Esc — выход без переноса.
В калькуляторе можно использовать все функции языка форм: например, прямо в выражении узнать остаток счёта [ea 50] или оборот. А с помощью функции [sed ...] можно оперативно присвоить значение переменной — удобно для проверки формул «на живых» данных.

5.2. Типы данных: числа и строки

Язык ФБП не требует объявлять типы: переменная просто получает значение при присваивании. Значений два вида — числа и строки.

Числа хранятся с фиксированной точкой. Все арифметические операции и сравнения работают с числами обычным образом. Важная тонкость — понятие истинности: выражение считается ложью, только если его абсолютное значение меньше 0.000001; любое другое значение — истина. То есть отрицательные числа в условиях трактуются как «истина», а не как «ложь».

Строки встречаются в выражениях реже, но язык их понимает: все операции сравнения (<, >, =, <> и т.д.) применимы как к числам, так и к строкам. Строковые значения появляются и как признаки ошибок: например, операторы линейной алгебры возвращают строку 'fail' при неудаче (см. 5.8). Создание и обработка строк подробно разобраны в главе «Функции: строки, даты, математика».

Результат сравнения — число 0 (ложь) или 1 (истина), его можно присваивать и использовать в арифметике: X = A > B занесёт в X единицу, если A больше B.

5.3. Имена переменных

Переменные в ФБП намеренно «короткие» — как в записи формул на бумаге. Имя переменной — это одна или две буквы либо буква и цифра. Примеры допустимых имён: Т, А1, x2, ZТ. Русские и латинские буквы равноправны.

Два символа — это жёсткий предел: длинных «говорящих» имён вроде SummaNds в языке нет. Поэтому опытные авторы форм вырабатывают свои соглашения (например, S — сумма, K — количество) и снабжают форму комментариями (строки с *).

Имена массивов — отдельная история: это одиночные латинские буквы (см. главу «Массивы»). Есть и особые предопределённые имена — о них ниже.

5.4. Предопределённые переменные

При запуске формы значения всех переменных не определены — кроме нескольких, которые сервер инициализирует сам. Это сведения об отчётном периоде и (в файлах-коэффициентах) о текущей операции.

ПеременнаяЗначениеГде доступна
MFНомер первого месяца отчётного периодавезде
MLНомер последнего месяца отчётного периодавезде
MCНомер текущего месяца (реальная дата)только файлы-коэффициенты
DCДень текущей операциитолько файлы-коэффициенты
YCНомер отчётного годатолько файлы-коэффициенты
OCНомер операциитолько файлы-коэффициенты
$Сумма текущей операциитолько файлы-коэффициенты

Отдельная группа — переменные реальной даты. Они появились в модификации ultraF от 28.07.97 (DOS) и ultraH от 7.03.99 (Windows) и автоматически инициализируются и в файлах-коэффициентах, и в отчётных формах:

ПеременнаяЗначение
YRРеальный год
MRРеальный месяц
DRРеальный день в месяце
WRРеальный день недели (0 — воскресенье, 1 — понедельник, …)
Начиная с версии ultraH от 11.05.99 (DOS и Windows) запрещены присваивания предопределённым переменным YC, MC, DC, YR, MR, DR, WR. Раньше такие присваивания были источником труднообнаруживаемых нестабильных ошибок. Обратите внимание: MF, ML, OC и $ в список запрещённых не входят.

5.5. Выражения и приоритет операций

Формула может содержать числа, переменные, знаки операций и круглые скобки, а также вызовы функций обращения к данным учёта — например, [ea 50] подставляет в формулу остаток счёта 50 (см. главу «Функции: данные учёта»).

ОперацииЗнакиПриоритет
Отрицание~самый высокий
Умножение, деление, логическое «и», остаток*, /, &, %высокий (одинаковый)
Сложение, вычитание, логическое «или»+, -, |средний (одинаковый)
Сравнения<, >, =, >=, <=, <>самый низкий

Пояснения к неочевидным операциям:

  • & — логическое «и»; | — логическое «или»; ~ — отрицание. Операнды трактуются по правилу истинности из 5.2: ноль (точнее, |x| < 0.000001) — ложь, остальное — истина;
  • % — остаток от целочисленного деления;
  • сравнения дают 0 или 1, и их низкий приоритет означает, что A + 1 > B читается как (A + 1) > B.

Типичное выражение в вычисляющем операторе (подробно об операторах — глава «Операторы»):

* сложить B с начальным дебетовым остатком счёта 01
A1 = B + [BD 01]
Если функция нужна только ради побочного эффекта, а её значение не интересует, вычисляющий оператор можно начать сразу со скобки, без фиктивного присваивания: вместо x = [sed 1,2] допустимо просто [sed 1,2].

5.6. Строковые выражения

Строки в выражениях участвуют прежде всего в сравнениях: к ним применимы все шесть операций сравнения, результат — те же 0 или 1. Это позволяет, например, сравнивать наименования субсчетов или проверять строковые признаки ошибок:

* проверка признака неуспеха от lsolve/invertm
X = [y 1] <> 'fail'

Полный набор строковых функций (выделение подстрок, склейка, преобразования) рассматривается в главе «Функции: строки, даты, математика», а правила вывода строк в текст отчёта — в главе «Текст и вывод».

5.7. Даты в выражениях

Специального типа «дата» в языке форм нет: даты представлены числовыми переменными — год, месяц, день по отдельности. Их дают предопределённые переменные из 5.4: границы отчётного периода MF/ML, сведения о текущей операции (DC, MC, YC — в файлах-коэффициентах) и реальная дата (YR, MR, DR, WR).

Поскольку это обычные числа, с ними работает вся арифметика и все сравнения. Например, в файле-коэффициенте можно разветвить расчёт по месяцу операции:

* в файле-коэффициенте: другой процент для операций декабря
! MC = 12
P = 20
! MC <> 12
P = 10

А в отчётной форме переменные реальной даты удобны для шапки отчёта — переменная WR позволяет учесть даже день недели (0 — воскресенье). Для функций работы с датами (сдвиг даты, разность дат и т.п.) см. главу «Функции: строки, даты, математика».

5.8. Элементы линейной алгебры

В язык встроены средства для работы с квадратными матрицами: одна функция (определитель) и два оператора (решение системы линейных уравнений и обращение матрицы). Это редкость для бухгалтерских систем, но полезно, скажем, для распределения затрат по модели «затраты–выпуск».

Матрица хранится в массиве, разложенная построчно: для матрицы n × n элемент (i, j) соответствует элементу массива с номером (i-1)*n+j. Сами массивы подробно описаны в главе «Массивы»; здесь достаточно знать, что запись [a k, значение] заносит значение в k-й элемент массива a, а [a k] — читает его.

[det a,n] — определитель матрицы

a — латинская буква, имя массива с матрицей n × n; n — размер. Возвращает определитель матрицы.

* определитель матрицы Гильберта 4x4
n=4
for i=1 to n
for j=1 to n
[a (i-1)*n+j, 1/(i+j-1)]
endfor
endfor
x=[det a,n]
^^^^^^^^^x^^^^^^^^^^^^^^

Результат: 0.00000016534392.

lsolve a,b,y,n — решение системы линейных уравнений

Решает систему a · y = b. Параметры: a — массив с матрицей n × n; b — массив с правой частью (вектор); y — массив, куда будет занесено решение (вектор); n — размер. Об ошибках сообщает не прерыванием, а значением первого элемента результата: если массив a (или b) содержал строковое значение, элементу [y 1] присваивается 'string'; при неуспешном решении системы — 'fail'.

[a 1, 20, 23, 17, 90]
[a 5, 33, -4, 12, 0]
[a 9, 77, 34, 11, -2]
[a 13, 10,108, 44, 99]
[b 1, 1, 2, 3, 4]
lsolve a,b,y,4
for i=1 to 4
x=[y i]
^^^^^^^^^^x^^^^^^^^^^
endfor

Результат:

0.0208762740
0.0042390130
0.1106699174
-0.0155156820

invertm a,c,n — обращение матрицы

a — массив с исходной матрицей n × n; c — массив, куда будет занесена обращённая матрица; n — размер. Имена a и c могут совпадать — тогда матрица обращается «на месте». Признаки ошибок — как у lsolve: если a содержал строковое значение, [c 1] получает 'string', при неуспешном обращении — 'fail'.

n=4
for i=1 to n
for j=1 to n
[a (i-1)*n+j, 1/(i+j-1)]
endfor
endfor
*
invertm a,b,n
*
for i=1 to 4
for j=1 to 4
x=[b (i-1)*4+j]
^^^^^^^^^x^^^^^^^^^ \
endfor
endfor

Результат (матрица, обратная к матрице Гильберта 4×4):

16.000000000 -120.000000000 240.000000000 -140.000000000
-120.000000000 1200.000000000 -2700.000000000 1680.000000000
240.000000000 -2700.000000000 6480.000000000 -4200.000000000
-140.000000000 1680.000000000 -4200.000000000 2800.000000000
После lsolve и invertm всегда проверяйте первый элемент результата: значения 'string' и 'fail' означают, что решения нет, и пользоваться остальными элементами массива нельзя. Сравнение со строкой — обычная операция: ! [y 1] <> 'fail'.
В примерах выше встречаются приёмы из следующих глав: цикл for … endfor (глава «Операторы»), заполнение массива [a k, …] (глава «Массивы»), форматные вставки ^ и знак \ в конце строки для слияния вывода в одну строку (глава «Текст и вывод»), строка * — комментарий.

6. Операторы управления и подпрограммы

До сих пор мы писали формы, которые выполнялись «сверху вниз». Этой главы достаточно, чтобы программировать по-настоящему: спрашивать данные у пользователя, выполнять части формы по условию, повторять вычисления в циклах и выносить повторяющийся код в подпрограммы и собственные функции.

6.1. Общие правила записи операторов

  • Оператор начинается с ключевого слова или знака; перед ключевым словом могут идти пробелы — отступы делают форму читаемее.
  • Несколько операторов можно записать в одной строке, разделив их знаком ;:
A=23; B=[EA 50]; C=[ED 68]

Операторы языка форм делятся на группы: вычисляющий оператор и ввод (эта глава), условия и циклы (эта глава), операторы для работы с массивами (глава «Массивы»), оператор выполнения проводки в файле-коэффициенте (глава «Виды операций и проводки»), операторы ленты фактов (глава «Лента фактов») и средства организации подпрограмм (эта глава).

6.2. Вычисляющий оператор

Самый частый оператор языка: вычислить формулу и присвоить результат переменной.

переменная = формула — вычисление и присваивание

Вычисляет формулу (числа, переменные, функции, знаки операций — см. главу «Переменные и выражения») и записывает результат в переменную.

А1=B+[BD 01]

— сложить B с начальным дебетовым остатком счёта 01 и присвоить результат переменной A1.

Если функция вызывается только ради побочного эффекта (например, [sed 1,2] сохраняет значение в дисковую базу), значение можно не присваивать — допускается сокращённая запись, где оператор начинается сразу со скобки [:

[sed 1,2]     * вместо x=[sed 1,2]

6.3. Оператор вопроса ?

Часто форме нужны сведения, которых нет в учётных данных: период, код товара, дата. Оператор вопроса запрашивает их у пользователя при выполнении формы.

?переменная вопрос — ввод значения при выполнении формы

Выводит строку-приглашение и ждёт, пока пользователь введёт значение переменной.

?A ввести переменную A:
Ввод переменных из оператора вопроса в ФБП для DOS и в ФБП для Windows устроен по-разному; в Windows-версиях удобнее функции ввода [is], [im], [ia] — см. главу «Функции: ввод и оформление».

6.4. Условный оператор !

Условный оператор — исторически первый способ ветвления в языке форм: он включает или выключает выполнение последующих строк в зависимости от условия.

! выражение — выполнять дальнейшее, только если выражение истинно

Выражение считается ложью, если его абсолютное значение меньше 0.000001; во всех остальных случаях — истиной. Если условие ложно, все операторы до следующего условного оператора не выполняются. После последнего оператора формы подразумевается неявный !, закрывающий открытое условие.

X=A>B
! X Y=25

Здесь X станет 1 или 0 по результату сравнения A>B; если X истинно, выполнится Y=25.

Результат любого сравнения (<, >, =, >=, <=, <>) — число 0 или 1; сравнения применимы и к числам, и к строкам. Условия комбинируются операциями & (И), | (ИЛИ), ~ (отрицание), % (остаток от деления). Условие можно записать прямо в операторе вместе с действиями:

! A>B X=1 Y=1     * если A>B, то выполнить X=1 и Y=1
Истинность проверяется по модулю: ложно только |x| < 0.000001. Отрицательное число — тоже «истина»!

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

! выражение in выражение..выражение — проверка вхождения в интервал

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

! A in 1000..2000          * 1000 <= A <= 2000
! (X in A..B) | (X in C..D)
Оператор ! нельзя использовать внутри операторов if, for и while — для условий внутри этих блоков применяйте вложенный if.

6.5. Оператор if / elseif / else / endif

Более наглядная замена !: блоки ветвления, которые можно вкладывать друг в друга и в циклы.

if … elseif … else … endif — структурное ветвление

Синтаксис (.... — любые операторы языка, строки текста формы и т.д.):

if выражение
....
elseif выражение
....
else выражение
....
endif

Разделы elseif и else необязательны; разделов elseif может быть сколько угодно.

Внутри оператора if нельзя использовать условный оператор !.

6.6. Оператор цикла # — перебор субсчетов

Самая «бухгалтерская» конструкция языка: цикл, который перебирает субсчета (объекты) указанного счёта и для каждого выполняет тело цикла — так строятся ведомости и списки.

# счёт … # — цикл по субсчетам счёта

Цикл начинается знаком #, далее в той же строке — обозначение счёта. Строки до строки, состоящей из единственного #, — тело цикла. Внутри тела на обозначение выбранного субсчёта ссылаются тем же знаком #.

Название    Цена, ед    Остаток,руб    Остаток,шт
# 10
A=[NA #]; P=[EP #]; ER=[EA #]; EQ=ER/P
^^^^^^^^A    ^^^^^^^^P    ^^^^^^^^^ER    ^^^^^^^^^EQ
#

— простая ведомость по остаткам материалов (счёт 10).

После # может идти не только обозначение счёта, но и *-обозначение — косвенная ссылка на счёт через переменную:

A=`70`
# *A
B=[n1 #]
^^^^^^^^^^B
#

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

#68 sort [ea #]; n=[n1 #]; m=[ea #]
^^^^^^^^n    ^^^^^^^^^^^^m^^
#

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

#70 sort [n2 #]; n=[na #]
^^^^^^^^^^^^^^^^^^^^^^^n
#
Подробно о функциях [na], [ea] и других обращениях к данным счетов — в главе «Функции: данные учёта»; об @- и *-обращениях — в главе «@-обращения и контекст».

6.7. Оператор for / endfor

Классический цикл со счётчиком: повторить тело заданное число раз, наращивая или уменьшая переменную.

for переменная=выражение to|downto выражение … endfor — цикл со счётчиком

Переменная пробегает значения от первого выражения до второго; to — по возрастанию, downto — по убыванию.

for i=10 downto 1
^^i
endfor
for i=1 to [get %,x]
....
endfor
Выражение после to (или downto) вычисляется на каждом витке цикла. Вынос его в переменную ускоряет цикл, но меняет смысл, если функция в выражении возвращает разные значения: в первом варианте новое значение [get %,x] может изменить общее число витков.
n=[get %,x]     * граница зафиксирована
for i=1 to n
....
endfor

Внутри for нельзя использовать условный оператор ! — применяйте if.

6.8. Оператор while / endwhile

Цикл «пока условие истинно» — удобен, когда число повторений заранее неизвестно.

while выражение … endwhile — цикл с условием

Повторяет тело, пока выражение истинно (по тому же правилу: ложь — это абсолютное значение меньше 0.000001).

i=1
while i<10
^^i \
i=i+1
endwhile

Результат: 1 2 3 4 5 6 7 8 9 (знак \ в конце строки с форматной вставкой сливает вывод в одну строку — см. главу «Текст и вывод»).

Внутри while нельзя использовать условный оператор !.

6.9. Операторы stop и error

stop — прекратить выполнение формы

Прекращает выполнение формы или файла-коэффициента. Обязателен в конце основной части формы перед разделом подпрограмм (см. 6.12).

error — прервать файл-коэффициент с сообщением

Прекращает выполнение файла-коэффициента и выдаёт все оставшиеся символы строки, в которой находится оператор, как сообщение программы в красной полосе внизу экрана. При этом происходит переход в режим ввода и редактирования операций, а селектор указывает на операцию, в которой сработал error — так файл-коэффициент сообщает бухгалтеру об ошибке в данных.

if Z=0
error Не задано количество!
endif

6.10. Оператор goto и метки

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

goto метка / :метка — безусловный переход

Передаёт управление строке, в которой описана метка. Метка — слово длиной до 10 символов, расположенное после первого символа : в строке.

:НАЧАЛО
бесконечность
goto НАЧАЛО

— форма, создающая бесконечно растущий файл (пример того, как делать не надо).

6.11. Комментарии в формах

Комментарии начинаются символом *: все символы после * программой игнорируются. Звёздочку ставят и в начале отдельной строки-комментария:

* это комментарий на всю строку
A=2    * а это пояснение к оператору
Особые комментарии *.on, *.off, *,on, *,off, *.,df не просто игнорируются, а управляют форматом вывода чисел — см. главу «Функции: строки, даты, математика» (функция [sn]).

6.12. Оператор printstr

Выводит строку «как есть»: без автоматического перевода строки и без лишних пробелов, которые добавляют форматные вставки ^.... Незаменим при выводе HTML-разметки и точно сформированного текста.

printstr s — вывод строки без перевода строки ultraH-32 от 9.04.00

s — строковое выражение. Вывод не завершается переводом строки (CR LF). Чтобы получить перевод строки между двумя printstr, вставьте между ними пустую строку.

printstr 'qwerty'
printstr 'asdf'

выведет qwertyasdf; те же операторы, разделённые пустой строкой, выведут две строки.

Расширение (ultraH-32 от 27.03.01) — переадресация вывода в файл (s — выводимая строка, f — строка с именем файла):

printstr s                  * обычный вывод
printstr s to > file f      * начать новый файл f, записать строку s
printstr s to >> file f     * добавить строку s в конец файла f
printstr s to file f        * то же, что и предыдущий вариант

6.13. Подпрограммы: call и return

Если один и тот же фрагмент нужен в нескольких местах формы, его описывают один раз в конце формы и вызывают оператором call.

call метка / :метка … return — вызов подпрограммы

Подпрограмма начинается с метки :L и завершается оператором return. Вызов передаёт управление на метку; return возвращает выполнение на следующий за call оператор.

...
call L     * вызов подпрограммы
...
stop
*****
:L
это тело подпрограммы
return
Перед телами подпрограмм в конце формы обязателен stop: иначе выполнение основной части «провалится» в подпрограмму и та выполнится лишний раз.

Разделы, совместно используемые несколькими формами, описывают в файле общих определений first.rpt, а подпрограммы в файле LIBRARY.RPT доступны из любой формы или файла-коэффициента — см. главу «Файлы форм и общие файлы».

6.14. Параметры подпрограмм и оператор local

Подпрограмме можно передать значения: в call указывают фактические параметры, а после метки — формальные.

  • В CALL после имени метки — фактические параметры: выражение или выражения через запятую в круглых скобках.
  • После метки — формальные параметры: переменная или переменные через запятую в круглых скобках.
  • При входе в подпрограмму формальные параметры получают значения фактических.
  • Формальные параметры — локальные переменные подпрограммы; их экземпляры автоматически уничтожаются при выполнении RETURN.
  • Параметры передаются «по значению»: изменение формального параметра внутри подпрограммы не меняет фактический параметр в вызывающей программе.

local переменная {, переменная} — локальные переменные подпрограммы

Создаёт новые экземпляры указанных переменных, которые автоматически уничтожаются при выполнении оператора return. Одноимённые переменные вызывающей программы не затрагиваются.

A=5
call P
^^A
stop
:P
local A,B
A=1; B=2
^^A ^^B
return

Результат выполнения:

1 2
5

Переменная A основной части сохранила значение 5, несмотря на одноимённую локальную переменную в подпрограмме.

Пример подпрограммы с параметрами:

call SUM(1,2)
stop
:SUM(a,b)
local c; c = a+b
^^^^^c
return

Результат выполнения: 3.

6.15. Определяемые пользователем функции

Подпрограмма может не только что-то делать, но и возвращать значение — тогда её можно вызывать прямо в формулах, как встроенную функцию.

[:метка параметры] и return (выражение) — своя функция

Если в операторе RETURN указать возвращаемое значение — выражение в круглых скобках, — то к подпрограмме можно обращаться как к функции:

[:метка]
[:метка параметры]

параметры — выражение или выражения, разделённые запятыми.

x=[:max 3,5]
^^^^^x
stop
:max(x,y)
if x > y return (x); else return (y); endif

Результат выполнения: 5.

Допускаются рекурсивные вызовы функций. Пример — вычисление факториала N! = N·(N−1)·…·1:

x=[:factorial 5]
^^^^^^^x
stop
:factorial(n)
if n=0 return (1); else return (n*[:factorial n-1]); endif

Результат: 120.

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

7. Массивы

Массивы нужны там, где обычных переменных не хватает: накопить в цикле сотни значений, отсортировать их, обратиться к элементу по вычисленному номеру. В языке ФБП массивы устроены предельно просто: они не объявляются, именуются одной латинской буквой и могут хранить как числа, так и строки.

7.1. Обращение к элементам: чтение и присваивание

Имя массива — одна латинская буква, записанная после открывающей квадратной скобки; далее через пробел идёт выражение-индекс. Та же запись служит и функцией чтения, и оператором присваивания — в зависимости от числа аргументов:

ЗаписьСмысл
[a i]функция: значение i-го элемента массива a
[a j,v]оператор: присвоить j-му элементу массива a значение v
x = [a i]
[a j,v]

Есть и групповое присваивание: если после индекса перечислить несколько значений через запятую, они попадут в элементы подряд, начиная с указанного:

[m 1,'Jan','Feb','Mar','Apr','May','Jun']

Здесь [m 1] получит значение 'Jan', [m 2] — 'Feb' и так далее.

7.2. Индексы, имена и ограничения

  • Индекс элемента — от 1 до 1000000. Значение индекса всегда округляется до ближайшего целого.
  • Всего можно создать 26 массивов — по числу латинских букв.
  • Малые и большие буквы в именах массивов не различаются: [A 1] и [a 1] — один и тот же элемент.
  • Массив и одноимённая переменная — совершенно разные объекты: можно одновременно иметь массив z и переменную z, они никак не влияют друг на друга.
  • При старте формы или файла-коэффициента все элементы всех 26 массивов получают нулевые значения — предварительная инициализация не нужна.
  • В элементе с индексом 0 хранится индекс последнего элемента, которому было присвоено значение, — то есть текущая длина массива: t = [m 0].
Сохранение массивов после завершения формы не гарантируется. Но сразу после выполнения формы значения её элементов ещё можно посмотреть калькулятором — это удобно при отладке.
В формах-историях массивы запрещены в разделах prolog и everydebet (из-за побочных эффектов при выполнении проводок оператором с кодом 177), а в разделе epilog использовать их можно — например, чтобы отсортировать накопленные значения перед печатью.

array a,b,c — обнуление массивов

Обнуляет перечисленные массивы, освобождая занимаемую ими память. При старте формы и файла-коэффициента этот оператор выполняется автоматически для всех массивов, так что вручную он нужен лишь для повторной очистки в середине работы:

array a,b,c

7.3. Сортировка: оператор sort array

Сортировать большие списки «вручную» не придётся — есть встроенный оператор в двух вариантах.

sort array a[,b] — сортировка массива

Первый вариант — sort array a: сортирует массив a по возрастанию значений элементов на месте (сам массив изменяется).

Второй вариант — sort array a,b: массив a не изменяется; результат создаётся в массиве b в виде новых номеров элементов. Если элемент [a 1] после сортировки должен оказаться пятым, то [b 1] будет равно 5.

sort array a      * a отсортирован по возрастанию
sort array a,b    * b — таблица новых номеров, a прежний
Второй вариант — стандартный способ «сортировки по ключу»: заполните один массив ключами (например, суммами), а второй — сопутствующими данными (наименованиями). После sort array k,b обходите элементы в порядке [b 1], [b 2], … — и данные будут упорядочены по возрастанию ключа, при этом исходный массив останется нетронутым.

7.4. Типичные приёмы работы с массивами

Накопление в цикле с запоминанием длины. Значение [a 0] всегда подсказывает, сколько элементов уже заполнено, — это удобная «длина массива»:

for i=1 to 10
[a i, i*i]
endfor
n=[a 0]          * n равно 10 — индекс последнего заполненного элемента

Сбор данных в форме-истории и сортировка в конце. В разделе everydebet массивы недоступны, поэтому значения копят через экстрапараметры, а в epilog переносят в массив и сортируют:

prolog
n=0
every debet
n=n+1
[set X, n, ss]
epilog
for i=1 to n
[a i,[get X, i]]
endfor
sort array a
for i=1 to n
y=[a i]
^^^^^^^^^^^y
endfor

Массив как «холст». Массивы — основа графических операторов (makepng, drawline и др.): точке изображения с координатами (x, y) соответствует элемент с индексом (y-1)*w+x. Это подробно разобрано в главе «Графика». Там же описаны и операции линейной алгебры ([det a,n], lsolve, invertm), где матрица n×n хранится в массиве по тому же правилу: элемент (i, j) — это индекс (i-1)*n+j.

Массивы появились в версии ultraG (вместе с операторами array и sort array); послабление для раздела epilog форм-историй — с версии ultraH от 18.01.99.

8. Лента фактов и ta-факты

Остатки и обороты счетов отвечают на вопрос «сколько?», но не «что именно произошло?». Механизм фактов позволяет запоминать сами события: «пришёл товар А12 в количестве 5», «поставщик Z передал товар на сумму 100». Формы могут потом искать такие события, считать их итоги, сортировать и удалять. Механизм появился в версии ultraF.

8.1. Что такое факт и как хранится лента фактов

Факт — запись из одной или нескольких частей; каждая часть может быть числовой или строковой. Части не имеют имён и закреплённых типов — говорят просто о первой, второй и т. д. частях. Какой смысл вкладывать в каждую часть, решаете вы сами: это может быть событие, отношение между объектами, любое наблюдение.

Организация хранения:

  • Все факты образуют общий список facts — «ленту фактов». Порядок фактов в ленте совпадает с порядком следования операций, породивших их.
  • Каждый факт автоматически снабжается отметкой о дате операции (значения предопределённых переменных YC, MC, DC).
  • При удалении операций соответствующие факты вычёркиваются из списка автоматически.
  • Факты размещаются в оперативной памяти и воссоздаются заново при каждом старте программы.
Переход на новый отчётный год. Постройте обороты счетов (Alt+F3) и нажмите F2 — на диске создаётся файл facts.e со всеми фактами года. Перенесите его в новый рабочий каталог и переименуйте в facts.b: при старте программы эти факты будут «подхвачены». Файл facts.b — обычный текстовый файл.

8.2. Операторы ленты фактов: обзор

ОператорДействие
factвнести новый факт в ленту фактов
searchнайти подходящий по образцу факт
rewind factsперемотать ленту фактов в начало
newsдать последнюю «новость» о факте
totalсуммирование подходящих частей фактов
eraseвычеркивание фактов из ленты
neraseвычеркивание последнего подходящего факта
selectвыбор подходящих фактов в буфер
rewind bufferперемотать буфер в начало
sort bufferотсортировать факты в буфере
rangeзадать интервал дат для отбора фактов

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

8.3. Оператор fact

fact e1, e2, ... eN — внести факт в ленту

Записывает новый факт: первая часть получит значение e1, вторая — e2 и т. д. Части — формулы языка форм или строки (кавычки для строк необязательны). Синтаксис записи управляемый — его можно настроить прототипами в файле proto.rpt (см. 8.11).

Важно: оператор можно использовать только в файлах-коэффициентах — факт всегда привязан к операции и её дате.

fact приход товара, K, Q
fact передача товара, K, Q

Так, файл-коэффициент прихода товара заносит факт «приход товара, код, количество», а файл-коэффициент передачи — факт о передаче. Дальше отчётные формы работают уже с этой лентой.

rewind facts — перемотать ленту в начало

Устанавливает указатель current на первый факт ленты. Перед началом поиска с самого начала ленты перемотка обязательна:

rewind facts

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

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

Факт считается подходящим, если значения всех частей-формул совпали с соответствующими по номеру частями факта. При успехе переменным из частей ?переменная присваиваются значения частей факта, а указатель current смещается на следующий факт — поэтому повторный search продолжит поиск дальше, и цикл «перемотка → поиск в цикле» перебирает все подходящие факты.

Тонкости образца:

  • Если образец содержит только ?переменная — подходящим является каждый факт (так печатают всю ленту).
  • Если частей в образце больше, чем частей в подходящем факте, переменным из избыточных частей присваивается строковое значение NO ELEM.
  • Признак результата — функция [success] (см. 8.13): 1 при удаче, 0 при неудаче.
  • Дату операции найденного факта дают функции [fact_y], [fact_m], [fact_d].

Форма list0.rpt — печать всей ленты фактов без отметок о датах:

* Все элементы списка без отметок о датах
rewind facts
:L
search ?X, ?Y, ?Z
! [success] = 1
^^^^^^^^^^^^^^^^^^^^^X ^^^^^^^^^Y ^^^^^^^^^^Z
goto L
!
* конец формы

Форма list1.rpt — поиск по образцу с константой и с датами (только передачи товара A11):

* Только передачи товара A11 с датами передач
Y='A11'
rewind facts
:L
search передача товара, Y, ?Z
! [success] = 1
M=[fact_m]; D=[fact_d]
^D ^M передача товара ^^^^^^^^^Y ^^^^^^^^^^Z
goto L
!
* конец формы
Подводный камень: fact и search в одном файле-коэффициенте. Если нужно тут же найти только что внесённый факт, перед вызовом search (или другого оператора поиска) обязательно сделайте rewind facts — иначе поиск продолжится со старой позиции указателя и факт может быть не найден:
fact приход товара 'A12'
...
b1=0
rewind facts
search приход товара ?b1
Эта тонкость относится ко всем операторам поиска, включая nerase.

8.5. Оператор news: последняя «новость»

news p1, p2, ... pN — найти последний подходящий факт

Части образца интерпретируются так же, как для search, но поиск всегда начинается от конца списка и движется к началу — до первого подходящего факта. Это и есть «новость»: самое свежее событие нужного вида.

  • rewind facts на поведение news не влияет.
  • Указатель current не изменяется.
  • Если интервал дат задан оператором range, то news игнорирует меньшую границу и продолжает поиск до начала ленты фактов.
  • [success] работает как индикатор результата.

Форма list2.rpt — когда был последний приход товара A12:

* Поиск последнего подходящего факта - "новости"
news приход товара, 'A12', ?Z
! [success] = 1
M=[fact_m]; D=[fact_d]
^D ^M приход товара A12 ^^^^^^^^^^^Z
!
* конец формы

8.6. Оператор total: суммирование по фактам

total p1, p2, ... pN — просуммировать части фактов

Части образца — как у search, но в переменных из частей ?переменная накапливаются суммы соответствующих частей всех подходящих фактов (перед вызовом такую переменную следует обнулить).

  • Поиск идёт от начала списка к концу; rewind facts не влияет, а range — влияет (суммирование внутри интервала дат).
  • Указатель current не изменяется.
  • [success] — 1, если найден хотя бы один подходящий факт; [found] — количество найденных фактов.
* Сколько всего было передано товара с кодом A11 ?
S=0
total передача товара, 'A11', ?S
Всего отгружено A11: ^^^^^^^^^^S
* конец формы
Если при суммировании встретилось сложение строки с числом, соответствующей переменной присваивается строковое значение ОШИБКА TOTAL - сложение строки с числом. Следите, чтобы суммируемые части фактов были числовыми.

8.7. Вычеркивание фактов: erase и nerase

erase p1, p2, ... pN — вычеркнуть все подходящие факты

Вычёркивает из списка facts все подходящие по образцу факты. Доступен только в файлах-коэффициентах. При удалении операции, вызвавшей erase, все вычеркнутые ею факты восстанавливаются — как и положено при виртуальном пересчёте.

  • rewind facts не влияет; указатель current не изменяется.
  • [success] — 1, если вычеркнут хотя бы один факт; [found] — количество вычеркнутых.

nerase p1, p2, ... pN — вычеркнуть последний подходящий факт ultraF от 4.04.97

Вычёркивает из списка последний подходящий факт: сначала действует как news, затем удаляет найденный факт. [success] — индикатор успеха.

Замена erase на nerase там, где оба оператора эквивалентны по действию (например, когда вычёркивается заведомо один факт), даёт очень значительный выигрыш во времени пересчёта. Не забывайте про rewind facts перед поиском только что внесённого факта:
fact приход товара 'A12'
...
b1=0
rewind facts
nerase приход товара ?b1

8.8. Выборка и сортировка: select, sort buffer, rewind buffer

Лента фактов упорядочена по датам операций, а select позволяет просматривать подходящие факты в ином порядке — например, по возрастанию какой-то части факта или по алфавиту. Для этого найденные факты помещаются во временный список buffer, который можно отсортировать и обойти.

select p1, p2, ... pN — выбрать факты в буфер

Ищет все подходящие факты в facts и помещает их во временный список buffer. Поиск в прямом направлении; rewind facts не влияет; указатель current не изменяется.

  • [success] — 1 при наличии находок;
  • [found] — количество найденных фактов;
  • [selected] — количество фактов, фактически помещённых в buffer. Если ёмкость буфера достаточна, [selected] равно [found]; иначе [selected] = 10000 (ёмкость списка).

sort buffer e1 — сортировка буфера

Сортирует список buffer по значению части факта, номер которой равен модулю e1 (e1 — формула с положительным или отрицательным числовым значением). Если e1 отрицательно — порядок по убыванию (обратный алфавитному для строк).

sort buffer 3    * по третьей части факта, по возрастанию

rewind buffer — перевести search на буфер

Устанавливает указатель current на первый элемент buffer. После rewind facts оператор search ищет в списке facts, после rewind buffer — в buffer.

rewind buffer не влияет на news, total, erase и select — они всегда ищут только в списке facts. В буфере работает только search.

Форма list5.rpt — все передачи товара, упорядоченные по количествам:

* Перечень всех передач товара с упорядочением
* по количествам
select передача товара, ??, ??
sort buffer 3
rewind buffer
:L
search передача товара, ?Y, ?Z
! [success] = 1
M=[fact_m]; D=[fact_d]
^M ^D передача товара ^^^^^^^^Y ^^^^^^^^^Z
goto L
!
* конец формы

8.9. Интервал дат: оператор range

rewind facts range YYYYMM.DD,YYYYMM.DD — поиск в интервале дат

Задаёт интервал дат, внутри которого ищут search, select, news и total. Первое число — начало периода, второе — конец; YYYY — год (может отсутствовать), MM — месяц, DD — день.

rewind facts range 1.06,1.06

Правила интерпретации границ:

  • Границы трактуются просто как числа: DD=0 или DD=1 означают начало месяца MM; DD=31 или больше — конец месяца независимо от его реальной длины.
  • news игнорирует меньшую границу и ищет от конца интервала до начала ленты.
  • Обычный rewind facts границы интервала не отменяет.
  • Удобно использовать предопределённые переменные MF, ML (первый и последний месяц отчётного периода) и DF, DL (первый и последний день). Границы отчётного периода устанавливаются календарём — в однопользовательской версии, или каждым клиентом для себя — в сетевой.
Применение range значительно уменьшает время построения форм с фактами: с версии ultraH от 23.01.99 лента фактов индексируется по датам, и rewind facts range выполняется за малое постоянное время вместо полного просмотра ленты.

8.10. Шаблоны поиска: {}-способ и `-способ

Образцы для search, news, erase, nerase, select и total не обязаны заново перечислять все части факта. Есть два способа формировать шаблон гибче.

{}-способ. Вместо полной записи прототипа (выражение / ?переменная / ?? для каждого поля) указывается несколько пар выражений в фигурных скобках; выражения внутри пары разделяются запятой:

  • первое выражение пары — числовое: номер поля факта;
  • второе — желаемое значение этого поля.

Поля, не «охваченные» парами, на поиск не влияют (эквивалентны ??). Например, вместо

search Товар ??, X, ??, Y

можно записать:

search Товар {3,X} {5,Y}

Номера полей могут быть сколь угодно сложными выражениями — это позволяет динамически формировать шаблоны:

? X по какому полю искать
? Y значение поля
news Товар {X,Y}

Прототип факта можно не указывать вообще — news {X,Y} ищет любой факт, у которого в поле с номером X значение Y. Первое слово прототипа — это просто поле факта с номером 1, поэтому записи эквивалентны:

news {1,'Товар'} {X,Y}
news Товар {X,Y}
{}-способ особенно удобен в сочетании с функцией [fact_f i] при работе с фактами с большим количеством полей. Для оператора total он напрямую не подходит для суммирования (суммы накапливаются только в ?переменных), но его можно использовать для получения [found] — то есть просто для подсчёта подходящих фактов.

`-способ (обратный апостроф, код 96). Наряду с традиционными указаниями полей (??, ?переменная, выражение) в шаблоне можно использовать конструкцию

`выражение

Если такое выражение выдаёт строку или число, поведение будет таким, как если бы эта строка или число находились в тексте шаблона вместо выражения — шаблон формируется косвенно, в момент поиска. Например, если на вопросы формы ничего не отвечать (три раза Enter), будет выполнен поиск search q ?A, ?B, ?C; а если на вопрос «Имя?» ответить «Вася», это будет понято как search q 'Вася', ?B, ?C.

В отличие от {}-способа, `-способ подходит для total: суммирование идёт в ?переменной, и неважно, указана она прямо или через `.

8.11. Прототипы фактов: файл proto.rpt

Синтаксисом записи фактов можно управлять, описав в файле proto.rpt прототипы фактов. В прототипе знак % обозначает место части факта, а всё остальное (например, запятые) — разделители между частями:

**********************************
приход товара, %, %
передача товара, %, %
**********************************

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

************************************************
товара % есть % единиц по цене %
поставщик % передал % единиц товара % на сумму %
************************************************

Тогда в файле-коэффициенте пишут fact товара T есть Q единиц по цене P, а в форме — total поставщик Z передал ?? единиц товара ?? на сумму ?S. Программа автоматически проверяет соответствие прототипов из proto.rpt и фактов, использованных в файлах форм.

Быстрый ввод записи факта: в режиме редактирования файла-коэффициента или формы нажмите F4, выберите прототип из списка и нажмите Enter — запись факта будет подставлена по образцу.

8.12. ta-факты: детальная информация о проводках

ФБП:Сервер может самостоятельно запоминать через механизм фактов детальную информацию о каждой выполненной проводке. Это даёт возможность видеть проводки операций, анализировать счета, получать оборотные ведомости за любой период — без написания собственных fact-записей.

Пример: при обработке операции с ключом CAAA-170 выполняется проводка «дебет X, кредит Y, сумма 100», после которой остаток X становится 1100, остаток Y — 900. Сервер добавит два факта:

fact ta 'X', 100, 0, 'Y', 1100, 'CAAA-170'
fact ta 'Y', 0, 100, 'X', 900, 'CAAA-170'

Структура прототипа ta (на примере первой записи):

ПолеЗначениеСмысл
1'X'счёт
2100на какую сумму дебетуется счёт X
30на какую сумму кредитуется счёт X
4'Y'корреспондирующий (кредитуемый) счёт
51100остаток счёта X после проводки
6'CAAA-170'штамп операции

Для второй записи роли зеркальны: счёт Y, кредитование 100, корреспондент X, остаток 900.

На ta-фактах построены встроенные в Сервер формы:

__SYS000 * Список проводок по операции
__SYS001 * Обороты счетов
__SYS002 * Обороты субсчетов
__SYS003 * Анализ счета
__SYS004 * Анализ корреспонденции
__SYS005 * Корреспонденции счета синтетически
__SYS006 * Корреспонденции счета детально

Управление режимом:

  • Сервер может работать как с запоминанием ta-фактов, так и без него; запоминать их можно не во всех месяцах — диапазон месяцев указывается в командной строке запуска. Пример (запоминать с марта по май):
FNT -c20000 c:\DATA c:\_BOX_ color dual 3 5
  • Однопользовательские версии также могут работать с ta-фактами (включение — в разделе «Отчётный период»).
  • При включённых ta-фактах возможен просмотр проводок в операциях по F6.
  • В режиме с ta-фактами потребность Сервера в оперативной памяти существенно увеличивается.
  • Если ta-факты отключены, для форм __SYS... границы выбранного периода выравниваются на начало и конец указанных месяцев.
Извлечь значения параметров с помощью ta-фактов нельзя — для параметров пользуйтесь функциями [bp]/[ep] и экстрапараметрами (глава «Экстрапараметры и дисковая база»).

Через ta-факты можно строить собственные формы. Например, обороты между счетами X и Y за период в днях (аналог функции [op X,Y]):

**** пример формы ****
S=0
if DF=0 DF=1; endif
if DL=0 DL=31; endif
rewind facts range MF+DF/100, ML+DL/100
total ta 'X',?S, ??, 'Y', ??, ??
^^^^^^^^S^^
**** конец формы ****

А так считаются обороты по дебету счёта X (аналог [od X]):

**** пример формы ****
S=0
if DF=0 DF=1; endif
if DL=0 DL=31; endif
rewind facts range MF+DF/100, ML+DL/100
total ta 'X',?S, ??, ??, ??, ??
^^^^^^^^S^^
**** конец формы ****

[ta m1,m2] — проверка режима запоминания ta-фактов

m1, m2 — номера первого и последнего месяцев диапазона. Возвращает 1, если ta-факты запоминаются для всех месяцев диапазона, и 0, если хотя бы для одного — нет. Полезна, чтобы форма могла проверить, имеет ли смысл обращаться к ta-фактам.

8.13. Функции ленты фактов

Эти функции используются и в файлах-коэффициентах, и в отчётных формах — они сообщают о результатах операторов поиска.

[success] — признак успешного поиска

Равна 1, если поиск факта по шаблону в ленте фактов (или в буфере) операторами search, news, erase, nerase был успешным; 0, если ни один подходящий факт не найден. Для total — 1, если найден хотя бы один подходящий факт.

news приход товара, 'A12', ?Z
! [success] = 1
Дата последнего прихода: ^[fact_d].^[fact_m]

[found] — количество найденных фактов

Количество фактов, найденных по шаблону операторами select, total, erase (для erase — число вычеркнутых).

[selected] — количество фактов в буфере

Количество фактов, помещённых во временный список buffer оператором select. Если ёмкость buffer достаточна, [selected] = [found], иначе [selected] = 10000 (ёмкость списка).

[fact_y] [fact_m] [fact_d] — дата найденного факта

Если поиск факта (search, news, erase, nerase) успешен, возвращают дату найденного факта: год, месяц и день соответственно (числовые значения).

M=[fact_m]; D=[fact_d]
^D ^M передача товара ^^^^^^^^^Y

[fact_f i] — поле найденного факта по номеру

Выдаёт поле последнего найденного факта с номером i (числовое выражение). При i=1 — первое слово прототипа факта; при i=0 — количество полей в факте.

Применять [fact_f] можно только если [success] = 1 — иначе значение не определено.

Функция позволяет обрабатывать факт целиком, не расписывая шаблон по полям. Вместо:

news Товар ?X, ?Y, ?Z, ?T
if [success]
^^^^^^^^^^^^^^^X ^^^^^^^^^^^^^^^Y ^^^^^^^^^^^^^^^Z ^^^^^^^^^^^^^^^T
endif

можно записать:

news Товар ??,??,??,??
if [success]
for i=1 to [fact_f 0]
x=[fact_f i]
^^^^^^^^^^^^^^^x \
endfor
endif

Это особенно удобно при большом количестве полей и при записи шаблона без указаний ?переменная (форма ??) — в том числе в сочетании с {}-способом (см. 8.10).

8.14. Производительность при работе с фактами

  • Опция «Быстрые факты» (с ultraH от 7.01.99): лента фактов индексируется, и search, news и другие поиски выполняются за малое постоянное время; платой служит замедление оператора fact примерно в два раза и рост расхода памяти. Внешняя логика операторов не меняется.
  • Индексация по датам (с ultraH от 23.01.99): rewind facts range выполняется за малое постоянное время — всегда задавайте интервал дат, если он известен.
  • nerase вместо erase там, где вычёркивается один факт, — очень значительный выигрыш во времени пересчёта.
Родственный механизм — экстрапараметры счетов ([set], [plus], [get]) — рассматривается в главе «Экстрапараметры и дисковая база»; об общем устройстве форм и файлов-коэффициентов см. главу «Файлы форм и общие файлы».

9. Функции: данные учёта

Эта глава — первая часть справочника функций языка форм. Здесь собраны функции, которые читают данные бухгалтерского учёта: остатки и обороты счетов, суммы проводок, параметры субсчетов, наименования и обозначения. Именно они — «рабочая лошадка» почти любой отчётной формы: чтобы построить отчёт, вы запрашиваете у системы числа через эти функции и выводите их форматными вставками (см. главу «Текст и вывод»).

9.1. Как вызываются функции

Обращение к функции заключается в квадратные скобки: внутри сначала идёт имя функции (обычно двухбуквенное), затем через запятые — параметры: обозначения счетов, номера месяцев и т.п. Параметры-месяцы могут быть не только числами, но и формулами и переменными:

[EA 51]        - остаток счета 51 на конец отчетного периода
[OK 70]        - оборот счета 70 по кредиту за отчетный период
[OK 70,M,L]    - оборот счета 70 по кредиту с месяца M по месяц L
                 (M и L - имена переменных)

Три понятия, без которых нельзя читать дальше:

  • отчётный период — интервал месяцев, для которого строится форма; если в функции месяц не указан, берётся именно он;
  • файл-коэффициент — программа, выполняющаяся внутри регистрируемой операции; ряд функций (AD/AK/MD/MK/AP/MP) ведёт себя в нём иначе, чем в отчётной форме;
  • развёрнутое сальдо — для счёта с субсчетами это сумма односторонних (только дебетовых или только кредитовых) остатков его субсчетов.

9.2. Остатки по счетам (сальдо)

Шесть функций сальдо имеют одинаковый синтаксис: [XX xx] или [XX xx, mm], где xx — обозначение счёта, mm — номер месяца (число или формула). Без mm берётся отчётный период. Мнемоника имён: B — beginning (начало), E — end (конец); вторая буква: A — остаток «со знаком», D — по дебету, K — по кредиту.

ФункцияМоментЧто возвращает
[ba xx]началоостаток со знаком
[ea xx]конецостаток со знаком
[bd xx]началоостаток по дебету (развёрнутое сальдо по дебету)
[ed xx]конецостаток по дебету (развёрнутое сальдо по дебету)
[bk xx]началоостаток по кредиту (развёрнутое сальдо по кредиту)
[ek xx]конецостаток по кредиту (развёрнутое сальдо по кредиту)

[ba X] — остаток на начало периода со знаком

Синтаксис: [ba xx] или [ba xx, mm]. Параметры: xx — обозначение счёта; mm — номер месяца (необязателен). Результат — число: сальдо счёта на начало отчётного периода (или месяца mm); дебетовый остаток положителен, кредитовый — отрицателен.

Было на счете 50 на начало: ^^^^^^^^^^[ba 50]

[ea X] — остаток на конец периода со знаком

Синтаксис: [ea xx] или [ea xx, mm]. Самая употребительная функция языка: сальдо счёта xx на конец отчётного периода (или месяца mm).

Остаток на счете 51: ^^^^^^^^^^^^^[ea 51]

[bd X] — дебетовый остаток на начало

Синтаксис: [bd xx] или [bd xx, mm]. Остаток по дебету на начало периода/месяца; для счёта с субсчетами — сумма дебетовых остатков субсчетов (развёрнутое сальдо по дебету).

[ed X] — дебетовый остаток на конец

Синтаксис: [ed xx] или [ed xx, mm]. Остаток по дебету на конец периода/месяца; развёрнутое сальдо по дебету для счетов с субсчетами. Удобна для активно- пассивных счетов (например, расчётов), где нужны «чужие долги» без «наших».

[bk X] — кредитовый остаток на начало

Синтаксис: [bk xx] или [bk xx, mm]. Остаток по кредиту на начало периода/месяца; развёрнутое сальдо по кредиту.

[ek X] — кредитовый остаток на конец

Синтаксис: [ek xx] или [ek xx, mm]. Остаток по кредиту на конец периода/месяца; развёрнутое сальдо по кредиту.

* задолженность перед поставщиками (счет 60)
Итого мы должны: ^^^^^^^^^^^^^^^[ek 60]
Если счёт активный или пассивный «по природе», разницы между [ea] и парой [ed]/[ek] вы не увидите. Она проявляется на активно-пассивных счетах с субсчетами: [ea] «сворачивает» дебет и кредит, а [ed] и [ek] показывают их раздельно.

9.3. Обороты по счетам

Обороты отвечают на вопрос «сколько прошло по счёту», а не «сколько на нём осталось». У функций AD/AK/MD/MK — двойная семантика: в отчётной форме они смотрят на прошлое относительно конца отчётного периода, а в файле-коэффициенте — относительно текущей проводки регистрируемой операции.

[od X] — обороты по дебету

Синтаксис: [od xx] или [od xx, m1, m2]. Результат — обороты счёта xx по дебету за отчётный период или за интервал месяцев m1..m2 (номера могут быть числами или формулами).

* поступления в кассу за год
Поступило: ^^^^^^^^^^^^^^^[od 50]

[ok X] — обороты по кредиту

Синтаксис: [ok xx] или [ok xx, m1, m2]. Обороты по кредиту за отчётный период или за интервал месяцев.

[OK 70,M,L]   * оборот счета 70 по кредиту с месяца M по месяц L

[ad X] — нарастающий оборот по дебету от начала года

Синтаксис: [ad xx]. В отчётной форме — обороты по дебету от начала года до конца отчётного периода. В файле-коэффициенте — от начала года до текущей проводки (не включая её). Именно «коэффициентный» смысл позволяет, например, начислять проценты на накопленную сумму.

[ak X] — нарастающий оборот по кредиту от начала года

Синтаксис: [ak xx]. То же, что [ad], но по кредиту счёта.

[md X] — оборот по дебету за месяц

Синтаксис: [md xx]. В отчётной форме — обороты по дебету за последний месяц отчётного периода. В файле-коэффициенте — от начала текущего месяца до текущей проводки.

[mk X] — оборот по кредиту за месяц

Синтаксис: [mk xx]. То же, что [md], но по кредиту счёта.

Функции [ad] [ak] [md] [mk] (и [ap] [mp] ниже) имеют двойную семантику — форма и файл-коэффициент считают разные интервалы. Нельзя бездумно переносить фрагмент кода из формы в файл-коэффициент и обратно: логику накопления придётся пересматривать. Текущий режим выполнения можно проверить функцией [re] (см. главу 11).

9.4. Суммы проводок между счетами

Эти функции отвечают на вопрос «сколько прошло между двумя конкретными счетами» — например, сколько оплатил данный покупатель через данный расчётный счёт.

[op X, Y] — сумма проводок за период

Синтаксис: [op xx, yy] или [op xx, yy, m1, m2]. Результат — сумма проводок между счетами xx (дебет) и yy (кредит) за отчётный период или за интервал месяцев m1..m2.

* сколько оплачено поставщику 60-01 с р/счета 51
Оплачено: ^^^^^^^^^^^^^^[op 51, 60-01]

[ap X, Y] — сумма проводок от начала года

Синтаксис: [ap xx, yy]. В отчётной форме — сумма проводок между счетами от начала года до конца отчётного периода; в файле-коэффициенте — от начала года до текущей проводки.

[mp X, Y] — сумма проводок за месяц

Синтаксис: [mp xx, yy]. В отчётной форме — сумма проводок между счетами за последний месяц отчётного периода; в файле-коэффициенте — от начала текущего месяца до текущей проводки.

9.5. Параметры субсчетов

Субсчета могут иметь параметры — значения, задаваемые при редактировании счетов и начальных условий (например, адрес склада, договорная цена). Параметры нумеруются с 1. Это не то же самое, что экстрапараметры [set]/[get] — о них глава «Экстрапараметры и дисковая база».

[bp X] — параметр на начало периода

Синтаксис: [bp xx], [bp xx, nn], [bp xx, nn, mm]. Результат — значение nn-го параметра субсчета xx на начало отчётного периода (или месяца mm). Без nn берётся первый параметр.

[BP xx]        - значение 1-го параметра субсчета xx на начало отчетного периода
[BP xx, nn]    - значение nn-го параметра на начало отчетного периода
[BP xx, nn, mm]- значение nn-го параметра на начало месяца mm

[ep X] — параметр на конец периода

Синтаксис: [ep xx], [ep xx, nn], [ep xx, nn, mm]. Значение nn-го параметра субсчета на конец отчётного периода (или месяца mm); в файле-коэффициенте — значение непосредственно перед текущей проводкой.

9.6. Обозначения и наименования

Отчёт, состоящий из одних чисел, нечитаем. Эти функции возвращают строки — обозначения и наименования счетов, чтобы подписать строки и колонки отчёта.

[n1 X] — обозначение счёта

Синтаксис: [n1 xx]. Результат — строка с обозначением счёта/субсчета (например, 50 или 60-01).

[n2 X] — наименование счёта

Синтаксис: [n2 xx]. Результат — строка с наименованием счёта/субсчета (например, Касса).

Счет ^^^^^^^^^[n1 50] — ^^^^^^^^^^^^^^^^^^^^[n2 50]

[na X] — обозначение и наименование

Синтаксис: [na xx]. Строка, содержащая и обозначение, и наименование счёта — удобна для подписей «в один приём».

[pa X] — обозначение счёта-«родителя»

Синтаксис: [pa xx]. Строка с обозначением счёта, субсчетом которого является указанный. Так из обозначения субсчета 60-01 можно получить 60.

[np X] — наименование параметра

Синтаксис: [np xx] или [np xx, nn]. Строка с наименованием nn-го параметра субсчетов счёта xx; по умолчанию nn = 1. Позволяет подписывать колонки значений [bp]/[ep] их наименованиями.

9.7. Сведения о счетах

[aa X] — тип счёта

Синтаксис: [aa xx]. Результат — число:

ЗначениеТип счёта
1активный
-1пассивный
0активно-пассивный
2внебалансовый

[as X] — количество субсчетов

Синтаксис: [as xx]. Результат — число субсчетов указанного счёта. В паре с [sa] (обозначение субсчета по номеру, см. главу 11) позволяет организовать перебор всех субсчетов счёта.

9.8. Доступ к журналу операций и ta-фактам

Иногда из формы нужно добраться не до итоговых цифр, а до самих операций — например, показать в отчёте документы, относящиеся к событию, или журнал операций в нестандартном виде. Для этого служат функции [ta] и [jf].

[ta m1,m2] — запоминаются ли ta-факты

Синтаксис: [ta m1,m2], где m1, m2 — номера первого и последнего месяцев диапазона. Возвращает 1, если ta-факты запоминаются для всех месяцев диапазона, и 0, если хотя бы для одного месяца они не запоминаются. В однопользовательских версиях запоминание ta-фактов можно включать для нужного месяца (это используется при настройке сетевой базы знаний). О самой ленте фактов — глава «Лента фактов».

[jf S] — «дай операцию или список операций» ФБП/ultraH-32 и ФБП:Сервер

Синтаксис: [jf s], где s — строковое выражение, интерпретируемое так же, как директива J ФБП:Сервера (см. главу «Язык запросов к серверу»).

[jf 'o key=GAAA-001']
[jf 'o key=G7']
[jf 'all mc=1,12']

У функции есть результат и побочный эффект:

  • результат — первая строка ответа на соответствующую директиву J;
  • побочный эффект — в файл extrd.dat помещаются все строки ответа и их количество: [ged 'jf.N'] — количество строк, [ged 'jf.1'] — первая строка (совпадает с результатом [jf]), [ged 'jf.2'] — вторая и т.д. (о дисковой базе extrd.dat и функции [ged] — глава 13).

Типовые применения: показ в отчёте операций, относящихся к событию; вывод журнала операций в нестандартном виде — с разметкой тегами HTML при работе с Internet-клиентом (включая тег fwp) или в оформлении директивой N: для Windows-клиента.

Функции этой главы только читают данные учёта — они не изменяют ни баланс, ни журнал операций (побочный эффект [jf] касается лишь служебного файла extrd.dat). Поэтому отчётные формы можно запускать многократно и экспериментировать без риска для учёта.

10. Функции: ввод данных и оформление чисел

Отчётная форма — это не только печать цифр: часто перед построением отчёта нужно спросить пользователя «за какой месяц?», «по какому контрагенту?», «продолжать?». Для таких диалогов служат функции ввода — они показывают окно или меню и возвращают ответ пользователя как обычное значение, которое можно присвоить переменной. Вторая половина главы — функция [sn] и спецкомментарии, управляющие тем, как числа превращаются в строки (разделители тысяч, дробная часть).

10.1. Функции ввода: диалоги с пользователем

Все пять функций ввода вызываются в правой части присваивания и возвращают результат — число или строку. По сравнению с оператором вопроса (см. главу «Операторы») они компактнее: вопрос, значение по умолчанию и приём ответа умещаются в одну строку.

[iy S] — вопрос «Да/Нет»

Синтаксис: [iy s], где s — строка вопроса. Выводит окно с вопросом и меню из двух пунктов «Да»/«Нет»; возвращает 1 при ответе «Да» и 0 при ответе «Нет». Назначение — изменение хода выполнения формы.

A=[IY 'Продолжить дальше?']

[ir S, n, k] — ввод числа

Синтаксис: [ir s, nn, mm], где s — строка вопроса, nn — значение по умолчанию (число или формула), mm — количество знаков после десятичной точки. Возвращает введённое число.

A=[IR 'Введите номер поручения:',X,0]

[is S, S0, n] — ввод строки

Синтаксис: [is s, sd, mm], где s — строка вопроса, sd — значение по умолчанию, mm — предельная длина вводимой строки. Возвращает введённую строку.

A=[IS 'Введите название фирмы:',A,20]

[im H, S1, S2, ...] — меню из произвольных пунктов

Синтаксис: [im h, s1, s2, ...], где h — заголовок меню, а s1, s2, ... — пункты. Возвращает номер выбранного пункта (нумерация с 1 по порядку следования пунктов).

A=[IM 'Тип счета:','активный','пассивный','внебалансовый']

[ia X] — выбор субсчета из меню

Синтаксис: [ia xx]. Показывает меню из обозначений и наименований субсчетов счёта xx и возвращает строку — обозначение выбранного субсчета. Идеальна для форм вида «отчёт по произвольному контрагенту».

A=[IA 70]
B=[IA План]
Типовой приём: сначала A=[IA 60] — спросить контрагента, затем использовать A в функциях данных учёта: [ea A], [op 51,A]. Так одна и та же форма обслуживает всех контрагентов без правки текста.

10.2. Функция [sn]: число в строку по формату

Форматные вставки (^^^^) оформляют числа «по своему усмотрению», а [sn] позволяет задать формат явно: длину строки и количество знаков после точки. Это нужно, когда число вставляется внутрь другой строки или требуется точное выравнивание.

[sn n, len, k] — форматированное число строкой

Синтаксис: [sn nn, mm, kk], где nn — число, mm — длина строки результата, kk — количество знаков после десятичной точки. Результат — строка.

a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a

На точный вид результата влияют спецкомментарии — см. следующий подраздел.

10.3. Спецкомментарии, управляющие [sn] и форматными вставками

Пять специальных комментариев (появились в версии ultraH-32 от 9.04.00) переключают два независимых свойства вывода чисел: показывать ли дробную часть и разделять ли тысячи запятыми. Они действуют и в отчётных формах, и в файлах-коэффициентах — от места появления до конца формы либо до отменяющего комментария.

КомментарийДействие
*.onразрешает выдавать дробную часть числа
*.offзапрещает выдавать дробную часть числа
*,onразрешает запятые в целой части числа (разделение тысяч)
*,offзапрещает запятые в целой части
*.,dfвозврат к поведению по умолчанию (для форм — к пожеланиям ФБП:Клиента)

По умолчанию действует сочетание «запятые есть, дробной части нет» (*,on + *.off). Полный пример из руководства:

a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a
*.on
*,on
a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a
*.off
*,on
a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a
*.on
*,off
a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a
*.off
*,off
a=[sn 1200.345,17,3]
^^^^^^^^^^^^^^^^a

Результат выполнения формы:

1,200
1,200.345
1,200
1200.345
1200
Спецкомментарии влияют не только на [sn], но и на форматные вставки — и действуют до конца формы. Забытый где-то в середине формы *,off меняет вид всех последующих чисел — это источник трудноуловимых расхождений при печати. Возвращайте поведение по умолчанию комментарием *.,df.
Сопутствующие функции этой главы: обратное к [sn] преобразование «строка → число» выполняет [vl], а [intsn] выдаёт целую часть числа строкой без разделителей — обе описаны в главе «Функции: строки, даты, математика, файлы, система».

11. Функции: строки, даты, математика, файлы, система

Заключительная часть справочника функций: всё, что не относится напрямую к данным учёта — работа со строками и числами, даты и время, математика, сведения о системе и пользователях, вызов внешних программ и файловые сервисы.

11.1. Строки

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

[cp S, n, k] — вырезка из строки

Синтаксис: [cp s, nn, mm], где s — исходная строка, nn — номер первого символа вырезки, mm — количество символов. Главное назначение — предотвращение переполнений форматных вставок слишком длинными строками: перед выводом строку обрезают до ширины колонки.

[length S] — длина строки

Синтаксис: [length строка]. Результат — число символов: [length 'qwerty'] = 6.

Граничный случай: [length ''] равно 1, а не 0. Учитывайте это в проверках вида «строка пуста?».

[strip S] — обрезка пробелов

Синтаксис: [strip строка]. Удаляет ведущие и завершающие пробелы: [strip ' абвгде '] → абвгде. Полезна перед сравнением строк, введённых пользователем.

[ps S1, S2] — позиция подстроки

Синтаксис: [ps 'фрагмент','строка в которой его ищем']. Возвращает позицию первого вхождения (нумерация символов с 1); если фрагмент не найден — 0.

n2=[ps 'klw','ghiklwstr']
^^^n2

Результат: 4.

[vl S] — строка → число

Синтаксис: [vl s]. Преобразует строку в число, если это возможно: [vl '1234'] → число 1234. Если преобразование невозможно, возвращается исходная строка: [vl '1234f'] → '1234f' — ошибки не возникает. (В ранних версиях было доступно только обратное преобразование через [sn].)

Поскольку [vl] при неудаче возвращает строку, а не 0 и не ошибку, тип результата стоит контролировать функцией [type], иначе дальнейшая арифметика над «числом» может оказаться арифметикой над строкой.

[type V] — тип переменной

Синтаксис: [type переменная]. Возвращает 1, если переменная — строка, и 0, если число.

[intsn n] — целая часть строкой без форматирования

Синтаксис: [intsn число]. Результат — строка с целой частью числа, без пробелов и запятых (в отличие от [sn], на которую влияют спецкомментарии). Диапазон числа: от -2147483648 до +2147483648.

a=[intsn yc]
^^^^a

Результат: 1999 (а не 1,999) — даже при включённой опции «числа с запятыми». Функция незаменима при сборке служебных индексов вида [ged 'tf.'+[intsn i]].

[ch n] — символ по коду

Синтаксис: [ch nn]. Возвращает строку из одного символа с кодом nn. Употребительна для управления принтером ESC-последовательностями (27 — код ESC):

F1=[CH 27]+'M'
^^^^F1

[sa X, n] — обозначение субсчета по номеру

Синтаксис: [sa xx, nn], где xx — счёт, имеющий субсчета, nn — номер субсчета. Возвращает строку — обозначение nn-го субсчета. Главное назначение — организация перебора субсчетов в цикле в паре с [as xx] (количество субсчетов, см. главу 9).

[hc S, i] — код символа строки

Синтаксис: [hc s,i]. Возвращает число — код i-го символа строки s (обратная к [ch]). Применения — перекодировка строк, подсчёт контрольных сумм.

11.2. Числа: целая и дробная части, округление, число прописью

[tr n] — целая часть

Синтаксис: [tr nn]. Возвращает целую часть числа. Приём округления до целого: [tr 12.56+0.5] → 13.

[ce n] — дробная часть «в сотых»

Синтаксис: [ce nn]. Выделяет дробную часть числа, но выраженную как число сотых: [ce 12.3456] = 34.56, а не 0.3456. Приём округления дробной части:

[tr ([ce 12.3456]+0.5)]   = 35

[ro n] — округление

Синтаксис: [ro nn]. Округляет число. Само руководство предупреждает: на практике для округления надёжнее использовать сочетание [tr] и [ce], чем [ro].

[wn n] — число прописью

Синтаксис: [wn nn] или [wn X, руб.]. Возвращает строку — числительное, изложенное словами; второй необязательный аргумент — обозначение единицы измерения. Незаменима в платёжных документах («Сумма прописью»).

11.3. Даты и время

[dt m, d] — дата строкой

Синтаксис: [dt mm, nn], где mm — месяц, nn — день (числа или формулы). Возвращает строку с датой — для подписей в отчётах.

[da d, m, y] — номер дня от «начала эры»

Синтаксис: [da dd, mm, yy] или [da]. Возвращает количество дней, прошедших от 1 ноября 1990 года («начало эры Хакерс Дизайн») до указанной даты — или до текущей операции. Запись [da] эквивалентна [da DC, MC, YC] (предопределённые переменные дня, месяца и года текущей операции). Разность двух таких номеров — точное число дней между датами, что используется, например, для автоматического начисления процентов по кредитам.

[antida n] — дата по номеру дня

Синтаксис: [antida yy]. Обратная к [da]: возвращает в упакованном виде дату, соответствующую номеру дня. Формат упаковки виден из примера: [da 3,28,1996] = 1975, а [antida 1975] = 199603.28 — то есть ггггмм.дд.

[dw d, m, y] — день недели

Синтаксис: [dw dd, mm, yy] или [dw]. Возвращает день недели: 0 — воскресенье, 1 — понедельник и т.д. Запись [dw] эквивалентна [dw DC, MC, YC].

[tm] — текущее время

Синтаксис: [tm]. Возвращает строку вида часы:минуты. Применения: «штамп» времени на формах; текущее время можно узнать даже из калькулятора, введя [TM].

11.4. Режим выполнения и контекст

Одна и та же программа может выполняться как отчётная форма, файл-коэффициент или первичный документ — и функции этой группы позволяют узнать, «где мы», кто пользователь и что передано форме извне.

[re] — номер режима выполнения

Синтаксис: [re]. Возвращает: 0 — внутри файла-коэффициента, 1 — внутри отчётной формы, 2 — внутри первичного документа. Назначение — предотвращать работу форм в неподходящем режиме:

![re]>0
Это не отчетная форма, а файл коэффициент
stop

[co] — комментарий текущей операции

Синтаксис: [co]. Возвращает строку — комментарий из текущей операции. Используется только внутри форм первичных документов, активируемых псевдопроводкой «создать документ» (см. главу «Виды операций и проводки»). В других режимах бессмысленна — контролируйте режим через [re].

[ex X] — существование субсчета

Синтаксис: [ex обозначение субсчета]. Возвращает 1, если субсчет с таким обозначением имеется, и 0, если отсутствует. Позволяет форме аккуратно обходиться со счетами, которые ещё не заведены.

[stamp n] — штамп операции

Синтаксис: [stamp n], где n: 1 — имя пользователя, зарегистрировавшего операцию; 2 — время регистрации операции; 3 — уникальный код операции. Используется только в файлах-коэффициентах.

[user] — имя пользователя

Синтаксис: [user]. В файле-коэффициенте — имя пользователя, регистрирующего операцию; в отчётной форме — имя пользователя, для которого сервер выполняет форму. В однопользовательской версии всегда supervisor.

[f1], [f2] — аргументы формы

Синтаксис: [f1] — значение первого аргумента формы, [f2] — второго. Значения для различных кнопок (и сами кнопки) задаются пользователем в клиентской части: меню «Опции», раздел «Аргументы и фильтры». В однопользовательской версии всегда 0.

[ct S] — контекст предыдущего @-обращения

Синтаксис: [ct S]. В сетевой версии, в формах, отвечающих за @-обращения, выдаёт первое слово строки, выбранной в предыдущем @-обращении к счёту S в текущем листе дерева; если предыдущих @-обращений не было — пустую строку. Назначение — облегчить выбор, используя результаты предыдущего. В однопользовательской версии всегда ' '. Об @-обращениях — глава «@-обращения и контекст».

11.5. Математика с 3.22

Группа математических функций: тригонометрия, корень, модуль и перевод между градусами и радианами. Аргументы тригонометрических функций — в радианах.

ФункцияРезультат
[abs x]модуль (абсолютное значение)
[fsin x]синус
[fcos x]косинус
[arctan x]арктангенс
[sqrt x]квадратный корень
[rad x]переход от градусов к радианам
[dgr x]переход от радиан к градусам

[abs x] — модуль числа

Синтаксис: [abs x]. Возвращает абсолютное значение числа.

[fsin x], [fcos x], [arctan x] — тригонометрия

Синтаксис: [fsin x] — синус, [fcos x] — косинус, [arctan x] — арктангенс. Углы задаются и возвращаются в радианах; для перевода используйте [rad] и [dgr].

[sqrt x] — квадратный корень

Синтаксис: [sqrt x]. Возвращает квадратный корень из числа.

[rad x], [dgr x] — градусы и радианы

Синтаксис: [rad x] — переводит градусы в радианы; [dgr x] — радианы в градусы.

* синус 30 градусов
x=[fsin [rad 30]]

[det a, n] — определитель матрицы с 3.22

Синтаксис: [det a,n], где a — имя массива, содержащего квадратную матрицу n×n, n — размер. Матрица хранится в массиве построчно: элемент (i,j) соответствует элементу массива с номером (i-1)*n+j (о массивах — глава «Массивы»). Пример — определитель матрицы Гильберта 4×4:

n=4
for i=1 to n
for j=1 to n
[a (i-1)*n+j, 1/(i+j-1)]
endfor
endfor
x=[det a,n]
^^^^^^^^^x^^^^^^^^^^^^^^

Результат: 0.00000016534392. Родственные средства линейной алгебры — операторы lsolve (решение системы линейных уравнений) и invertm (обращение матрицы) — описаны в главе «Операторы».

11.6. Кодировки и внешний мир

[win S], [dos S] — перекодировка строк с 3.22

Синтаксис: [win s] — преобразование строки из кодировки CP866 в Win1251; [dos s] — обратно, из Win1251 в CP866. Нужны при обмене текстами с Windows-программами и при подготовке HTML: тексты форм пишутся в CP866 (см. главу «Файлы форм»), а браузер ожидает Win1251.

[ru S1, S2, n] — вызов внешней программы

Синтаксис: [ru s1, s2, n], где s1 — имя вызываемой программы, s2 — строка аргументов, n — что делать с экраном: 0 — очистить и затем восстановить; 1 — оставить как есть (в расчёте на то, что программа ничего не выводит). Возвращает первую строку файла ru.txt или строку NO RU.TXT, если файл отсутствует: обратную связь внешняя программа передаёт, записывая результат в ru.txt. Применение — обмен с внешними базами данных.

[ru] неэффективна при многократных вызовах: каждый вызов — это запуск программы и чтение ru.txt. Для интенсивного обмена в 16-разрядных версиях предусмотрена dllcall.

[hz f, t] — звуковой сигнал

Синтаксис: [hz f, t], где f — частота в герцах, t — длительность в миллисекундах. Если t = 0, звук не выключается и звучит до вызова [hz 0,0].

[dllcall L, i, D] — вызов процедур из DLL

Синтаксис: R = [dllcall library, index, data], где library — строка с именем библиотеки, index — индекс вызываемой процедуры, data — передаваемый аргумент (строка; сама переменная не изменяется). Возвращает строку. Функция введена потому, что [ru] неэффективна из-за затрат на многократный запуск программы и чтение ru.txt.

Требования к вызываемой процедуре: она должна быть описана как void P(lpString); (в C) или procedure P(s: pChar); (в Borland Pascal). Передаётся указатель на строку, завершающуюся нулевым байтом; процедура возвращает результат в эту же строку — он становится значением dllcall.

Пример вызова (библиотека MONTH.dll, процедура с индексом 1 выдаёт английское название месяца по его номеру в N):

R = [dllcall 'MONTH', 5, [sn N,1,0]]

После выполнения переменная R получает значение May. Исходный текст такой библиотеки на Borland Pascal:

library MONTH;
uses strings;
procedure P(s :pChar); export;
const M :array [1..12] of string[15] =
('January','February','March','April','May',
'Jun','July','August','September','October',
'November','December');
var St :string; x,c :integer;
begin St:=StrPas(s); Val(St, x, c);
if c<>0 then St:='ERROR 1'
else if (x<1) or (x>12) then St:='ERROR 2'
else St:=M[x];
StrPcopy(s, St);
end;
exports P index 1;
begin
end.
dllcall поддерживается только в 16-разрядных версиях. В приведённом фрагменте условие проверки диапазона восстановлено по смыслу — в печатном руководстве знак сравнения при переносе был утрачен.

11.7. Файлы и система

[fe S] — существование и размер файла

Синтаксис: [fe s], где s — имя файла. Возвращает -1, если файл не существует, иначе — размер файла в байтах.

[crc S] — контрольная сумма файла

Синтаксис: [crc s]. Вычисляет CRC (Cyclic Redundancy Check) файла по тому же 32-битному алгоритму, что и pkzip (PkWare). Применение — проверка целостности файлов (*.rpt, *.b, fnt.exe, cas.rul). Результат — восьмисимвольная строка с CRC в шестнадцатеричном представлении; если файл отсутствует — односимвольная строка '0'. Отличить «нет файла» от настоящего CRC 00000000 можно по длине строки результата.

[unicfile] — уникальное имя файла с 3.22

Синтаксис: [unicfile]. Возвращает строку — уникальное имя файла. Используется, когда форма создаёт временные файлы (например, картинки для Web-приложения) и имена не должны сталкиваться.

[dir n] — рабочие каталоги

Синтаксис: [dir n], где n = 0 или 1. В «персональной» ФБП всегда возвращает строку .\. Введена для совместимости с ФБП:Сервером, где n=0 — имя каталога данных, n=1 — имя каталога обмена Клиент↔Сервер. Возвращаемые имена каталогов всегда завершаются знаком \. См. также тему «вывод данных в файл» в главе «Текст и вывод».

[ver] — версия программы

Синтаксис: [ver]. Финансы без проблем/ultraH → [ver] = 3.02; Финансы без проблем/ultraF → 3.01. Более ранние версии функция ver не определяет.

[sf n, k] — специальная функция с 3.14

Особая функция с зарезервированными кодами. Известные значения:

ВызовРезультат
[sf 0,77]условный номер BIOS
[sf 0,98]время первого старта NULLCG.CGI в виде цепочки цифр
[sf 0,99]строка с текущим временем в формате log-файлов Web-серверов
[sf 0,9]длинная строка, новая при каждом вызове; применяется для борьбы с кэшами браузеров

Варианты 0,98, 0,99 и 0,9 предназначены для отладки Web-приложений, работающих с NULLCG.CGI — см. главу «Web-приложения».

[pw S1, S2] — проверка пароля

Синтаксис: [pw s1,s2], где s1 — имя пользователя, s2 — пароль. Результат: 0, если пользователя s1 с паролем s2 нет в файле _rights_.fbp, иначе — номер пользователя в этом файле.

[uf S] — список пользователей

Синтаксис: [uf s], где s — ' ', '+' или ':'. Действует аналогично директиве U ФБП:Сервера. Результат — количество строк, помещённых в extrd.dat; [ged 'uf.N'] — то же количество, [ged 'uf.' + [intsn i]] — i-я строка ответа.

[tf U] — дерево видов операций

Синтаксис: [tf u], где u — имя пользователя или 'ALL'. Аналог директивы T ФБП:Сервера от имени пользователя u. Возвращает количество строк дерева видов операций, помещённых в extrd.dat (дублируется в [ged 'tf.N']):

  • [ged 'tf.'+[intsn i]] — i-я строка дерева в формате ответов сервера на директиву T;
  • [ged 'tfb.'+[intsn i]] — баллон i-й строки дерева или 0, если баллон не был определён.

Доступные пользователю виды операций и баллоны разбираются в главе «Виды операций и проводки».

В ultraH-32 у функций этой группы есть упрощения: [pw] всегда возвращает 1; [uf] поддерживается только синтаксически и никаких действий не выполняет; [tf] любое имя пользователя считает эквивалентом 'ALL'. В однопользовательских версиях [dir] всегда даёт .\, [user] — supervisor, [f1]/[f2] — 0, [ct] — пустую строку.
Сюда же по духу относятся функции дисковой базы [sed]/[ged] и экстрапараметры [set]/[plus]/[get] — они описаны в главе «Экстрапараметры и дисковая база», а функции ленты фактов ([success], [found], [fact_f] и др.) — в главе «Лента фактов». Собственные функции с возвратом значения (return (выражение), вызов [:метка]) — в главе «Операторы».

11.8. Табличный вывод V01.02

До появления функции [table] таблицу псевдографикой приходилось рисовать вручную: рассчитывать позиции колонок, добивать значения пробелами до нужной ширины, самостоятельно следить за рамками. Функция берёт это на себя: колонки описываются один раз, а строки данных выводятся в обычном цикле — их число заранее задавать не нужно.

Порядок построения таблицы всегда один и тот же:

  1. open — открыть таблицу и выбрать стиль рамки;
  2. column — объявить каждую колонку: заголовок, ширина, выравнивание, точность для чисел, перенос длинного текста (wrap);
  3. header — вывести верхнюю рамку и строку заголовков;
  4. rowsep — необязательно: включить линии-разделители между записями;
  5. row — вывести записи (обычно в цикле);
  6. close — вывести нижнюю рамку и закрыть таблицу.
Функция появилась в ФБП:Сервере V01.02; серверы более ранних версий форму с [table] не скомпилируют (неизвестная функция).

[table …] — таблица псевдографикой с произвольным числом строк V01.02

Функция формирует таблицу последовательно: описание и колонки, заголовок, строки данных, закрытие. Число строк заранее задавать не нужно — row можно вызывать в обычном цикле формы. Каждая подкоманда возвращает число 0, но получать это значение не обязательно. Запись rc=[table 'header','T'] равнозначна отдельному вызову [table 'header','T']: rc здесь — произвольная переменная, а не имя таблицы и не обязательный элемент синтаксиса.

ПодкомандаНазначение
[table 'open','T']открыть таблицу T с одинарной рамкой
[table 'open','T','double']двойная рамка; также допустимы single и none
[table 'column','T','Код',12]добавить колонку с заголовком «Код» и шириной 12; выравнивание по умолчанию — left
[table 'column','T','Остаток',15,2]числовая колонка: два знака после запятой, выравнивание вправо
[table 'header','T']вывести верхнюю границу, заголовки из column и разделитель; заголовки по умолчанию по центру
[table 'header','T','left']выравнивание заголовков влево; также допустимы center и right
[table 'rowsep','T']включить линии-разделители между записями; по умолчанию их нет
[table 'row','T',K,N]вывести одну запись; при wrap она может занимать несколько строк
[table 'close','T']вывести нижнюю границу и закрыть таблицу

Где задаются заголовки. Третий аргумент каждой команды column — это название колонки. Команда header выводит верхнюю рамку, строку с названиями всех объявленных колонок и разделитель под ними. Без дополнительного параметра заголовки выравниваются по центру. Для другого положения используются [table 'header','T','left'], center или right. Выравнивание заголовков не меняет выравнивание данных. Строки данных добавляются командой row.

Выравнивание строковых значений: left, right или center; если параметр не указан, используется left. Ширина колонки включает всё пространство между вертикальными линиями. Дополнительные пробелы слева и справа от значения не добавляются: текст первой ячейки начинается сразу после рамки, поэтому его можно передать через #1 в N:. Длинные значения обрезаются по ширине. При стиле none рамок нет, а поля разделяются одним пробелом.

Пример с двумя колонками — «Код» и «Наименование»:

[table 'open','T']
[table 'column','T','Код',6]
[table 'column','T','Наименование',20,'left']
[table 'header','T']
[table 'row','T','A-001','Товар первый']
[table 'row','T','A-002','Товар второй']
[table 'close','T']

Результат:

┌──────┬────────────────────┐
│ Код  │    Наименование    │
├──────┼────────────────────┤
│A-001 │Товар первый        │
│A-002 │Товар второй        │
└──────┴────────────────────┘

Здесь верхняя линия, строка с заголовками и разделитель выведены одной командой header. Нижняя линия выводится командой close.

Если данные формируются в цикле, row вызывается внутри него:

for i=1 to n
[table 'row','T',K,N]
endfor

Числовые колонки. Число можно передавать в row непосредственно, без [sn] и [intsn]. Число знаков после запятой указывается после ширины колонки; числовое значение автоматически форматируется и выравнивается вправо. Отрицательные числа выводятся со знаком -.

[table 'open','N']
[table 'column','N','Остаток',10,2]
[table 'header','N']
[table 'row','N',1250.5]
[table 'row','N',-1250.5]
[table 'close','N']

Результат:

┌──────────┐
│ Остаток  │
├──────────┤
│   1250.50│
│  -1250.50│
└──────────┘

Вместо сокращения [table 'column','N','Остаток',10,2] можно записать полную форму [table 'column','N','Остаток',10,'right',2]. Допустимая точность — от 0 до 22 знаков. Если точность не задана, число выводится без дробной части. Для вывода числа как строки с другим выравниванием его можно по-прежнему преобразовать через [sn] или [intsn].

Перенос длинного текста — wrap. Без этого параметра длинный текст обрезается по ширине колонки. Если указать wrap, сервер переносит его между словами; слово длиннее колонки разрывается по символам. Код и остальные значения остаются на первой строке записи, а ячейки продолжения — пустыми. Числа не переносятся. Допустимы обе формы:

[table 'column','T','Наименование',12,'left','wrap']
[table 'column','T','Наименование',12,'wrap']  * то же самое, left по умолчанию

Разделители между записями — rowsep. Эта необязательная подкоманда включает горизонтальную линию перед каждой записью, кроме первой. Внутри многострочной записи линий нет. Включать её нужно до первой строки данных:

[table 'open','T']
[table 'column','T','Код',6]
[table 'column','T','Наименование',12,'wrap']
[table 'header','T']
[table 'rowsep','T']
[table 'row','T','A-001','Очень длинное название товара']
[table 'row','T','A-002','ABCDEFGHIJKLMNO']
[table 'close','T']

Результат:

┌──────┬────────────┐
│ Код  │Наименование│
├──────┼────────────┤
│A-001 │Очень       │
│      │длинное     │
│      │название    │
│      │товара      │
├──────┼────────────┤
│A-002 │ABCDEFGHIJKL│
│      │MNO         │
└──────┴────────────┘

Один вызов row — одна запись, даже если из-за wrap она занимает несколько строк. Разделитель ставится только между такими записями. Без rowsep их нет; после последней записи выводится обычная нижняя рамка close. Для стиля none разделители не рисуются.

Число значений в row должно совпадать с числом колонок. Общая ширина таблицы вместе с рамками не может превышать 255 символов CP866. До завершения формы каждую таблицу нужно закрыть через close; нарушение последовательности вызовов приводит к ошибке E-27.

Для переходов по строкам таблицы используются обычные управляющие строки N: — см. пример в главе 15.

12. Обращения к счетам: @-обращения и контекст

Когда бухгалтер вводит операцию, дерево видов операций может потребовать уточнить, какой именно субсчёт участвует в проводке — например, какое подотчётное лицо по счёту 71 или какой товар по счёту 41. Такое требование записывается в листе дерева @-обращением — знаком @ перед обозначением счёта (@71, @41). Программа покажет список субсчетов, пользователь выберет нужный, и его обозначение будет подставлено в проводку. @-обращения появились в ФБП очень рано — в версии 2.2 (апрель 1992) — и именно они позволяют держать дерево видов операций компактным: вместо отдельной ветви на каждый субсчёт достаточно одной ветви с @-обращением.

Эта глава объясняет, как устроен показ списков субсчетов (и как его программировать самому), как управлять поведением форм @-обращений словами pragma и multiline, как формы узнают об «окружении» регистрируемой операции через функцию [ct] и контексты, и как работают *-подстановки в клиент-серверной версии.

12.1. Как выполняется @-обращение

В сетевой версии, обнаружив в листе дерева @-обращение (скажем, @71), ФБП:Клиент посылает Серверу директиву R 71, рассчитывая, что в каталоге данных есть форма 71.RPT, которая построит список субсчетов — обычно циклом по субсчетам счёта. Если такой формы нет, то начиная с версии Сервера 2.04 список субсчетов выводится внутренними средствами Сервера — но при этом показ параметров отсутствует.

Вот типичная форма, отвечающая за @-обращение к счёту 71 (подотчётные лица):

* Список подотчетных лиц (форма 71.RPT)
#71 цикл по субсчетам
N=[n1 #] извлекаем обозначение текущего субсчета
M=[n2 #] извлекаем наименование текущего субсчета
^^^^^^^^^N ^^^^^^^^^^^^^^^^^^^M
^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^
#
* конец формы

Здесь работают знакомые средства: цикл #71 … # (см. главу «Операторы»), функции [n1 #] и [n2 #] — обозначение и наименование текущего элемента цикла (см. главу «Функции: данные учёта»). Пустые строки-«рамки» из ^ внизу оставляют место, чтобы пользователь мог выбрать строку мышью или клавишами.

Писать собственную форму для @-обращения стоит тогда, когда нужно показать не только обозначения и наименования, но и параметры/остатки субсчетов, отфильтровать список (см. 12.6) или «ответить» на меню формы из дерева словом pragma (см. 12.3).

12.2. @-обращения в формулах и файлах-коэффициентах

@-обращение — это не только «показать список»: выбранным субсчётом можно пользоваться в формулах листа дерева. Например, в листе переоценки материалов остаток выбранного субсчёта счёта 10 получают прямо в формуле проводки:

@10 88 [EA @10] * (К-1)

В проводках, выполняемых оператором проводки в файле-коэффициенте, тоже могут участвовать @-обращения к счетам с субсчетами (наряду со счетами без субсчетов, собственно субсчетами, *-обращениями к переменным и текущим субсчётом # цикла). Но здесь действует важное правило:

Все требуемые @-обращения должны быть описаны в листе дерева. В файле-коэффициенте при повторном @-обращении подставляются те же самые субсчета, которые были выбраны в листе дерева, — дополнительного выбора по @-обращению в файле-коэффициенте не происходит. Помните и про общее ограничение: фраза операции «складывается» не более чем из 12 «кирпичиков» (ветвей дерева и @-обращений) — оно действует и для проводок файла-коэффициента.

12.3. Ключевое слово pragma: ответ на меню формы из дерева

Форма @-обращения может начинаться с меню (функция [im…]), и от выбранного пункта зависит дальнейший ход её выполнения. Ключевое слово pragma позволяет ответить на это меню прямо из дерева видов операций: для одного вида операции форма покажет полный список, для другого — только часть, хотя используется одна и та же форма.

Пусть форма 41.RPT начинается с меню:

* начало формы 41.RPT
A=[im 'Меню показа','весь список товаров','только имеющиеся на складе']
! A=1 goto ...
! A=2 goto ...
....
* конец формы 41.RPT

Требуется: для операции «Товары-прием на склад:» выбирать пункт 1 («весь список»), а для «Товары-резервирование:» — пункт 2. Для этого в соответствующих листьях дерева после проводок ставится псевдокомментарий с ключевым словом pragma и номером нужного пункта меню.

Между проводками и ключевым словом pragma в листе дерева должен стоять коэффициент (в круглых скобках) или файл-коэффициент (без скобок). Если сумма проводки вас не интересует — укажите коэффициент (0).

12.4. Ключевое слово multiline: куда вводить многострочные ответы

Если при вводе операции на вопросы (ветви-вопросы дерева или поле ввода суммы) дан неопределённый ответ — ничего не ввели, просто нажали Enter — то клиент показывает столбцы для многострочного ввода ответов. По умолчанию эти столбцы накладываются на результат выполнения формы, соответствующей первому @-обращению в листе дерева.

Слово multiline, указанное в нужном месте листа дерева, переадресует столбцы ввода ответов к другому @-обращению — например, если именно его форма (41.RPT) выводит удобный для многострочной пометки список. multiline можно сочетать со словом pragma в том же листе.

12.5. Функция [ct] и контексты операции

Контекст формируется во время ввода хозяйственной операции: с его помощью в отчётные формы, отвечающие за списки объектов учёта, передаётся «окружение», в котором регистрируется операция. Функция [ct] возвращает значение контекста в отчётной форме.

[ct S] — контекст предыдущего @-обращения или операции

S — строка, имя контекста. В формах, отвечающих за @-обращения, функция выдаёт первое слово строки, полученной в предыдущем @-обращении к счёту S в текущем листе дерева. Если предыдущих обращений не было — пустую строку. Назначение — облегчить выбор в @-обращении, используя результаты предыдущих. В однопользовательской версии [ct] всегда выдаёт пустую строку.

* в форме 41.rpt — результаты выбора @62 и @S
A62=[ct '62']
AS=[ct 'S']

Существует несколько видов контекстов операции:

Вид контекстаИмя контекстаЗначение
Выбранные субсчетаобозначение родительского счётаобозначение выбранного субсчёта
Ответы на вопросы операциипервые 9 символов вопросаответ, ограничен 10 символами
MODOKEYслово MODOKEY (только заглавные латинские буквы)дата регистрации и уникальный ключ операции
Сумма'Сумма' (для англоязычного Сервера — 'Sum')введённая сумма операции, ограничена 10 символами

Пример с ответами на вопросы: пусть в дереве есть ветви платежка и сумма, в баллоне к первой указано ·R FORM1, в баллоне ко второй — ·R FORM2. Тогда передать в форму FORM2 результат выбора из FORM1 можно вызовом [ct 'платежка']. (В формах FORM1 и FORM2 первая строка — заголовок, а текст формы должен быть «прижат» к левой стороне экрана.)

Контекст MODOKEY: дата и ключ регистрируемой операции

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

CT=[ct 'MODOKEY']                  - получаем контекст
MO=[vl [cp CT,1,2]]                - месяц
DO=[vl [cp CT,3,2]]                - день
K=[ch 64+MO]+[cp CT,5,3]+'-'+[cp CT,8,3]  - код операции

Типичная задача, которую решает MODOKEY, — данные вносятся «в прошлое». Пусть цена товара зависит от курса доллара, и форма 41.rpt должна показывать курс на день регистрации операции, а не последний введённый. Фрагмент news курс доллара ?d дал бы курс на текущую дату — это неправильно. Решение — взять дату из контекста и ограничить поиск фактов (лента фактов подробно рассмотрена в главе «Лента фактов»):

* фрагмент формы 41.rpt — курс доллара на дату операции
CT=[ct 'MODOKEY']
MO=[vl [cp CT,1,2]]
DO=[vl [cp CT,3,2]]
if [type MO] MO=MC; endif
if [type DO] DO=DC; endif
rewind facts range 1.01,MO+DO/100
news курс доллара ?d
Курс доллара ^^^^d^^^ ^^^^DO

Здесь курс хранится фактами вида fact курс доллара d (в файле-коэффициенте вида операций «Новый курс доллара»). Если форма выполняется вне ввода операции, контекста нет — [vl …] вернёт строку, и проверки [type …] подставят текущие MC и DC. При регистрации операции, скажем, 12 марта форма покажет курс, действовавший именно на 12 марта.

Значения контекстов влияют на кэширование результатов форм Сервером — обязательно прочитайте замечания в разделе 13.6: в частности, [ct 'MODOKEY'] распознаётся только со строковой константой в аргументе.

12.6. Аргументы и фильтры форм @-обращений

Для каждой формы (*.rpt) в клиентах можно определить свой набор клавиш и связать поведение формы с их состоянием — значениями аргументов [f1] и [f2] (см. главу «Функции: строки, даты, математика»). Это удобно для фильтрации длинных списков: одна и та же форма показывает разные подмножества субсчетов в зависимости от нажатой клавиши.

Пример: на счёте 41 учитываются товары, а признаком группы служит пятый символ обозначения субсчёта:

Счета и начальные условия:
41 [А] Товары
41-01000 Пиво Жигулевское
41-01001 Пиво Бадаевское
41-01002 Пиво Клинское
41-02000 Колбаса вареная
41-02001 Сосиски
41-02002 Сардельки

Пятый символ 1 — группа «Напитки», 2 — «Колбаса». В клиенте (клавиша Tab, окно «Аргументы формы 41.rpt») для клавиши «Напитки» задаётся [f1]=1, для «Колбасы» — [f1]=2. Форма отбирает субсчета по аргументу:

*** пример формы 41.RPT ***
Список товаров:
#41 - перебрать в цикле все субсчета счета 41;
T3=[cp[n1 #],5,1] - "вырезка" из обозначения субсчетов 5-го символа;
if [f1]=T3 - проверка аргумента [f1] и "вырезанного" символа;
T1=[n1 #] - обозначения субсчетов;
T2=[cp[n2 #],1,16] - наименования субсчетов, ограниченное 16-ю символами;
^^^^^^^T1 ^^^^^^^^^^^^^^T2
endif
#
*** конец формы 41.RPT ***

Особенности клиентов:

  • если в столбце «Умолчание» указать «Эта клавиша», то при показе формы именно выбранная клавиша будет сразу подсвечена и список субсчетов будет ей соответствовать;
  • режим Фильтров даёт более тонкий отбор, чем один аргумент: в DOS-клиенте в него входят клавишей Enter из режима «Аргументы формы…», в Windows-клиенте — кнопкой Фильтры. Например, из группы «Колбасы» можно отобрать субсчета, начинающиеся с отрывка «Сард», и определить для них клавишу «Сардельки».
Аргументы [f1]/[f2] — строки по 12 байт (дополняются пробелами); в однопользовательской версии они всегда равны 0.

12.7. *-подстановки и файл-ключ FREE-AT

В однопользовательских версиях в проводках допускалась *-подстановка счёта (имя счёта подставляется из переменной или ответа на вопрос) — в том числе для счёта с субсчетами, перед которым указывался символ @. В клиент-серверной версии с этим связано ограничение:

Если на базу, заполненную однопользовательскими версиями, установлен ФБП:Сервер, то уже имеющиеся операции будут подхвачены Сервером, но вводить новые операции с @-подстановкой в качестве дебетуемого/кредитуемого счёта с последующим использованием *-подстановки — запрещено. Выход — использовать в ветвях-вопросах уточнение ·R с подсоединением формы (см. главу «Виды операций и проводки»).

Две типовые формы, заменяющие *-подстановку. Первая выводит сразу весь план счетов вместе с субсчетами (цикл #План, функции [as #] — число субсчетов текущего счёта, [sa #,i] — обозначение i-го субсчёта):

Счета и субсчета:
#План - перебрать в цикле весь план счетов;
b1=[n1 #] - обозначение счета;
b2=[cp[n2 #],1,25] - наименование счета, ограниченное 25-ю символами;
^^^^^^^b1 ^^^^^^^^^^^^^^^^^^^^^^^b2
...................................
if [as #]>0 - если счет имеет субсчета, то:
for i=1 to [as #] - для всех субсчетов
s1=[sa #,i] - обозначение субсчета;
s2=[cp[n2 *s1],1,25] - наименование субсчета, ограниченное 25-ю символами;
^^^^^^^s1 ^^^^^^^^^^^^^^^^^^^^^^^s2
endfor
...................................
endif
# - конец цикла по плану счетов.

Вторая форма уточняет выбор последовательно — сначала счёт, потом субсчёт ([ia …] — меню выбора из плана счетов или списка субсчетов):

Счета и субсчета:
a=[ia План] - показать меню из всего плана счетов;
if [as *a]=0 - если счет не имеет субсчетов, то
b1=[n1 *a] - обозначение счета;
b2=[cp[n2 *a],1,25] - наименование счета, ограниченное 25-ю символами;
^^^^^^^b1 ^^^^^^^^^^^^^^^^^^^^^^^^^b2
else - если счет имеет субсчета, то
a=[ia *a] - вывод меню из списка субсчетов;
s1=[n1 *a] - обозначение субсчета;
s2=[cp[n2 *a],1,25] - наименование субсчета, ограниченное 25-ю символами;
^^^^^^^s1 ^^^^^^^^^^^^^^^^^^^^^^^^^s2
endif

Файл-ключ FREE-AT — выбор любого субсчёта в @-обращении Сервер 3.14

В каталоге данных можно завести файл-ключ FREE-AT (пустой файл с таким именем). В его присутствии Сервер не проверяет, имеет ли субсчёт, введённый в @-обращении, предком именно тот счёт, который указан в листе дерева: из формы, разлистывающей список субсчетов, может быть введён любой субсчёт. В присутствии FREE-AT на мнемосхеме Сервера в правом нижнем углу отображаются знаки @!. Это средство в значительной степени компенсирует запрет на * в @ в клиент-серверной версии.

Пример: деньги в кассу поступают от покупателей (61), подотчётных лиц (71) и разных дебиторов (76). Без послабления нужны три ветви:

50 @61
50 @71
50 @76

С ключом FREE-AT достаточно одной ветви 50 @X и формы X.RPT, которая сначала спрашивает счёт, а затем разлистывает его субсчета (цикл #*A — по субсчетам счёта, чьё имя в переменной A):

Заголовок
A=[im 'какой счет','61','71','76']
if A=1 A='61'; elseif A=2 A='71'; else A='76'; end
*
#*A
n=[na #]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^n
#

Условия: в плане счетов должны быть и 61, и 71, и 76, и X — все с субсчетами (для фиктивного счёта X достаточно одного фиктивного субсчёта). Выбранный субсчёт подставляется в фразу операции, а FREE-AT отключает проверку родителя. Дерево при этом сокращается втрое.

Подводный камень — клавиша F7. Клиенты (и DOS, и Windows) на нажатие F7 внутри раскрытого списка попытаются создать субсчёт с родителем X — всегда, независимо от того, какой список сейчас показан: в качестве имени счёта-родителя выбирается имя формы. Обход: форма X.RPT запоминает пользователя и счёт-родитель в дисковой базе ([sed …], см. главу «Свои данные»), а отдельная форма MAKESUBA.RPT, ассоциированная с пользовательской клавишей, создаёт субсчёт нужному счёту:
* доработанная X.RPT — запоминает счёт-родитель
Заголовок
A=[im 'какой счет','61','71','76']
if A=1 A='61'; elseif A=2 A='71'; else A='76'; endif
[sed [user] + ' parent account',A]
*
#*A
n=[na #]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^n
#
* форма MAKESUBA.RPT — создание субсчета нужного счета
p=[ged [user] + ' parent account']
if p = 0
Форма запущена вне ввода операции
stop
endif
a=[is 'Обозначение субсчета', ' ', 9]
n=[is 'Наименование субсчета', ' ', 20]
Подтвердите создание субсчета нажатием на строку ниже
N:ENTER:a ·#1·#2·#3 #4 #5 #6·
^^^^^^^^^p ^^^^^^^^^^a ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^n
[sed [user] + ' parent account', 0]

Строка [sed [user] + ' parent account',A] запоминает, кто запустил X.RPT, и обозначение счёта-родителя; [ged …] читает значение (и возвращает 0, если ничего не записано); запись 0 тем же ключом очищает его. В директиве N:ENTER:a ·#1·#2·#3 #4 #5 #6· знак · (код 250) — разделитель, а #1, #2… — подстановки параметров фразы операции (подробно о директивах N: — в главе «Директивы N:»). Теперь для создания субсчёта нажимают не F7, а пользовательскую клавишу: MAKESUBA.RPT знает, кто запустил X.RPT и какой счёт-родитель нужен.

13. Свои данные: экстрапараметры и дисковая база

Кроме остатков, оборотов и обычных параметров счетов программа на языке ФБП может хранить собственные данные двумя способами:

  • экстрапараметры — дополнительные именованные значения, привязанные к счетам и субсчетам; они живут в оперативной памяти и пересчитываются вместе с балансом (функции [set], [plus], [get]);
  • дисковая база extrd.dat — произвольные пары «ключ → значение», которые сразу записываются на диск и сохраняются между сеансами (функции [sed], [ged]).

Экстрапараметры подходят для величин, которые должны автоматически пересчитываться при изменении прошлого (как и весь учёт ФБП), а дисковая база — для справочной информации, не зависящей от операций: реквизитов контрагентов, настроек и т.п.

13.1. Экстрапараметры: что это и чем они отличаются от параметров

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

Экстрапараметры размещаются в ОЗУ и воссоздаются заново при каждом старте программы — как и лента фактов. При переходе на новый отчётный год (однопользовательская версия) постройте обороты счетов (Alt+F3) и нажмите F2: на диске создаётся файл extra.e со всеми экстрапараметрами года. Его переносят в новый рабочий каталог и переименовывают в extra.b — при старте экстрапараметры будут «подхвачены». Файл extra.b — текстовый.

13.2. Функции [set] и [plus]: запись экстрапараметров

[set A, S, V] — установить экстрапараметр с ultraF

A — обозначение счета; S — имя экстрапараметра (число или строка); V — значение (число или строка). Устанавливает экстрапараметр S счёта A в значение V. Всегда возвращает 0 — функцию вызывают ради побочного эффекта.

[set X, n, ss]          * запомнить сумму ss под номером n

[plus A, S, V] — увеличить экстрапараметр с ultraF

Увеличивает экстрапараметр S счёта A на V: для чисел это арифметическое сложение, для строк — конкатенация (в зависимости от типов). Всегда возвращает 0.

[plus 50, 'Итого', SS]  * накопить сумму проводки

Обе функции могут использоваться только в листьях дерева и файлах-коэффициентах — с одной оговоркой для отчётных форм (см. 13.3). Поскольку результат всегда 0, удобна сокращённая запись вызова ради побочного эффекта — оператор сразу со скобки, без фиктивного присваивания: [set X, n, ss] вместо y = [set X, n, ss].

13.3. [set]/[plus] в отчётных формах: фиктивный счёт % и оператор array %

В отчётных формах [set] и [plus] должны ссылаться на фиктивный счёт %, который следует определить в списке счетов (в сетевой версии эта возможность — с Сервера 2.04). Обратное тоже верно: в файлах-коэффициентах и листьях дерева [set]/[plus] на счёт % ссылаться не должны. Применение — промежуточное накопление структурированной информации в отчётных формах: например, в разделе every debet формы-истории можно собирать суммы проводок, а в epilog — переносить их в массив и сортировать:

prolog
n=0
every debet
n=n+1
[set X, n, ss]
epilog
for i=1 to n
[a i,[get X, i]];
endfor
sort array a
for i=1 to n
y=[a i]
^^^^^^^^^^^y
endfor

array % — обнуление экстрапараметров счёта %

Обнуляет все экстрапараметры счёта %, накопленные в форме вызовами [set %…]. Наряду с обнулением освобождается занятая ими память.

array %

13.4. Функция [get]: чтение экстрапараметров

[get A, S], [get A, S, M], [get A, S, M, D] — значение экстрапараметра с ultraF

Возвращает значение экстрапараметра S счёта A. Момент, на который берётся значение, зависит от места вызова и аргументов:

  • в листьях дерева и файлах-коэффициентах — значение к моменту выполнения текущей проводки; в отчётных формах — на конец отчётного периода;
  • с третьим аргументом M — значение на конец месяца M;
  • с аргументами M и D — значение в месяце M на конец дня D.
n=[get %,x]            * значение, накопленное формой
for i=1 to n
...
Выражение после to в операторе for вычисляется на каждом витке цикла, поэтому [get …] выгодно вынести в переменную, как в примере выше.

13.5. Дисковая база extrd.dat: [sed] и [ged]

Экстрапараметры пересоздаются при каждом старте программы, поэтому для данных, которые должны храниться постоянно (и не зависеть от пересчёта баланса), есть другой механизм — встроенное обращение к внешней дисковой базе данных, файлу extrd.dat (он создаётся автоматически в каталоге данных). Данные могут быть числовыми и строковыми.

[sed i, v] — сохранить значение в дисковой базе Сервер 2.11, ultraF 4.04.97

i — индекс (числовой или строковый), v — значение (числовое или строковое). Сохраняет v в extrd.dat, ассоциируя его с i. Всегда возвращает 0. Данные сразу выгружаются на диск и не занимают оперативную память. Запись значения 0 по существу очищает ключ.

[sed [user] + ' parent account',A]   * запомнить, кто и что выбрал
[sed 'ИНН-' + [n1 #], inn]

[ged i] — извлечь значение из дисковой базы Сервер 2.11, ultraF 4.04.97

Извлекает значение, ассоциированное с индексом i. Если с индексом ещё ничего не ассоциировано — возвращает 0.

p=[ged [user] + ' parent account']
if p = 0
Форма запущена вне ввода операции
stop
endif

Обе функции можно использовать в листьях дерева, файлах-коэффициентах, отчётных формах и первичных документах. Типичная область применения — запоминание справочной информации (реквизиты контрагентов: ИНН, банк) для автоматического заполнения платёжек, счетов-фактур и др.: одна форма записывает реквизиты через [sed …], другая извлекает их через [ged …]. Файл extrd.dat — текстовый, его можно посмотреть любым просмотрщиком.

Косвенные индексы вида 'jf.N', 'jf.1', 'tf.N', 'uf.N', 'ld.'+[intsn i], 'lt.N', 'lb.N' и т.п. — это тот же механизм extrd.dat, заполняемый соответствующими функциями ([jf …], [tf …], [uf …]) и операторами сканирования (loaddir, loadtxt, loadbin). См. главы «Функции: данные учёта» и «Операторы».

13.6. Замечания о кэшировании

ФБП:Сервер (начиная с версий 2.31–2.33) умеет кэшировать результаты выполнения форм: ответ на директиву R копируется в каталог cache (файлы 10000001 и т.д.), а ключом служит составной индекс — имя формы, пожелания клиента, имя пользователя, ответы на вопросы, контекст. Управление кэшем — файл cache\config.txt, в первой строке которого стоит одно из слов NO, ALL-, ALL+, THESE-, THESE+, EXCEPT-, EXCEPT+ (знак + означает фоновую актуализацию; далее идут строки с именами форм). Если закэшированный ответ устарел, в его конец добавляется символ с кодом 7 — клиенты показывают предложение «Обновить».

Формы с [sed …] инвалидируют кэш зависимых от [ged …] форм. Поэтому дисковая база и кэш уживаются, но помните: выполнение формы, пишущей в extrd.dat, делает устаревшими закэшированные результаты форм, читающих те же данные.

С версии Сервера 2.32 действует минимизация запросов: если в форме нет [ct …], информация о контекстах исключается из ключа кэша. При этом функции и переменные окружения распознаются анализатором только как строковые константы:

x=[ct 'MODOKEY']      - правильно
a='MODOKEY'
x=[ct a]              - НЕ распознается, кэширование будет ошибочным!

То же относится к [ct 'Sum'], [user], [f1]/[f2] и переменным MF, ML, DF, DL. Использование любой из этих функций или переменных в FIRST.RPT считается использованием в каждой форме — глубокая минимизация становится невозможной. Какие формы попали в кэш и почему, показывает директива f+ (отметки N/Y и буквы g, s, c, u, f — использование [ged], [sed], [ct], [user], [f1]/[f2]; скобками помечаются MF/DF/DL/ML).

Версия Сервера 2.33: файл-ключ fastged в каталоге данных включает зеркало extrd.dat в ОЗУ — это ускоряет [ged …] в 10–20 раз. Если формы много читают из дисковой базы, ключ окупается.
Не включайте кэширование форм-историй вместе с фоновой актуализацией (ALL+/THESE+).

14. Виды операций и проводки: программируем учёт

До сих пор мы говорили о языке отчётных форм — о программах, которые читают данные учёта. Эта глава — о том, как данные в учёт попадают: о дереве видов операций. Дерево — это тоже программа на языке ФБП, только специального назначения: она определяет, какие вопросы задаст программа при вводе хозяйственной операции и какие бухгалтерские проводки выполнит по ответам. Именно здесь «программируется учёт» — схемы проводок, аналитика, расчёт сумм. Именно поэтому изменение дерева приводит к виртуальному пересчёту всего журнала операций (см. главу 1): операции хранят только ответы, а проводки вычисляются по правилам дерева каждый раз заново.

14.1. Дерево видов операций: фразы, ветви и листья

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

Например, дерево из ветвей «Расчётный счёт» → «приход» → «сдана касса» и «Расчётный счёт» → «расход» → «передано в кассу» порождает две фразы-вида операций. При вводе операции бухгалтер «выкладывает» такую фразу, выбирая пункты меню, вводит сумму — и программа выполняет проводки из листа, соответствующего выбранной фразе.

Работа с деревом (однопользовательские версии): режим «Виды операций и проводки» открывается из меню Правила или по Alt+F6; новая ветвь создаётся клавишей F7; переход от последней ветви фразы к редактированию листа с проводками — клавишей F6. Ввод операции выполняется в журнале операций (Alt+F2, затем F7). В сетевой версии дерево хранится на сервере, а клиент получает его запросом T (см. главу «Язык запросов к серверу»).

Если вы изменили ветвь или лист дерева, а в журнале уже есть операции, нужно «перебить» несоответствующие операции: установите отчётный период с 1 января по 31 декабря и постройте «Обороты счетов» (Alt+F3). Программа сама проверит весь журнал и предложит перебить операции, не соответствующие новому описанию. Повторяйте построение оборотов, пока форма не будет построена без таких предложений — состояние всех счетов будет пересчитано автоматически.

14.2. Формат проводки: дебет, кредит и откуда берётся сумма

Лист дерева — это список проводок. Каждая проводка записывается одной строкой простого и строгого формата:

дебет кредит коэффициент/формула/файл-коэффициент

Здесь дебет и кредит — обозначения счетов (или заменяющие их конструкции: @-обращение, *-обращение, # в цикле). Третий элемент определяет сумму проводки, и тут есть три принципиально разных способа:

Третий элемент проводкиКак вычисляется сумма проводки
числовой коэффициент (например, 0.18) Сумма операции умножается на коэффициент. Если коэффициент не указан, он равен 1.
формула (выражение языка форм) Сумма проводки равна значению выражения — никакого умножения на сумму операции не происходит.
файл-коэффициент (имя файла с алгоритмом на языке форм) Сумма проводки равна значению, которое алгоритм вернёт через переменную $ (см. 14.8).
Переменная $ в описаниях проводок и файлах-коэффициентах определена всегда и равна сумме операции, введённой бухгалтером. Формула $*I/100 в листе дерева означает: «взять сумму операции, умножить на значение переменной I (например, ответ на ветвь-вопрос о ставке налога) и разделить на 100».

Пример листа дерева для фразы «Реализация»: первая проводка с числовым коэффициентом (на сумму операции, умноженную на 1), вторая — с формулой (НДС по ставке из переменной):

62 46
46 68-2 $*I/100

Максимальное количество проводок в одном листе дерева — 15. Если этого недостаточно, используйте оператор выполнения проводок в файле-коэффициенте (см. 14.8) — там ограничения на число проводок нет.

14.3. @-обращения: выбор субсчёта при вводе операции

Аналитический учёт в ФБП ведётся по субсчетам. Чтобы не писать отдельную ветвь дерева на каждого контрагента или сотрудника, в описании проводки можно сослаться на счёт, имеющий субсчета, с префиксом @:

@76 51

При вводе операции программа покажет меню из обозначений и названий субсчетов счёта 76; выбранный субсчёт будет ассоциирован с операцией и подставлен в проводку. Такой приём избавляет от однородных ветвей дерева: одна ветвь «Оплата от покупателя» с @76 заменяет десятки ветвей по каждому покупателю.

В одном листе допускается несколько @-обращений, причём к разным счетам:

@70 @69-1
@70 68-3

Если один и тот же счёт упоминается в листе с @-обращением несколько раз (здесь — @70), список субсчетов появится при регистрации операции только один раз: во все остальные проводки подставится уже выбранный субсчёт.

Компилятор дерева строго следит за корректностью: @-обращение к несуществующему счёту или к счёту без субсчетов — ошибка («@-обращение к несуществующему счету», «@-обращение к счету без субсчетов»). И наоборот: счёт, состоящий из субсчетов, нельзя указывать в проводке без @ («Неизвестный (или состоящий из субсчетов) дебетуемый счет»).

14.4. Ветви-вопросы: данные, которых нет в счетах

Не всё, что нужно для проводки, хранится на счетах: ставка налога, количество, номер документа — такие данные проще спросить при вводе операции. Для этого служит ветвь-вопрос:

? переменная вопрос — ветвь-вопрос

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

?I ндс,%
?N количество

Например, ветвь ?I ндс,% спросит при вводе операции ставку НДС, а проводка 46 68-2 $*I/100 использует ответ в формуле. Помните: переменная $ (сумма операции) определена всегда, остальные переменные получают значения из ответов на ветви-вопросы.

В Windows-клиенте ответы на вопросы операции ограничены 11 символами. В сетевой версии ответы попадают в «контекст» операции и доступны формам списков выбора через функцию [ct ...]: имя контекста — первые 9 символов вопроса, значение ограничено 10 символами (см. главу «@-обращения и контекст»).

14.5. Уточнения ветвей-вопросов: ·C, ·N, ·S, ·R, ·X

По умолчанию ветвь-вопрос показывает поле редактирования, куда ответ вводят с клавиатуры. Это поведение можно изменить: вместо свободного ввода предложить меню, потребовать число в диапазоне, преобразовать ответ в строку или показать целую форму-список. Уточнения записываются в тексте баллонной помощи ветви-вопроса: установите селектор на ветвь, нажмите Alt+F1, наберите текст, затем F2 (запись) и Esc. Разделитель · — средняя точка, символ с кодом 250 (вводится удержанием Alt и набором 250 на цифровом блоке клавиатуры).

·C·заголовок·пункт·пункт·… — меню вместо поля ввода

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

·C·%·20·10·
·C·тип расхода·затрата·из прибыли·

·N·умолчание·мин·макс· — настаивать на вводе числа

Программа потребует ввести число в заданном диапазоне; значение по умолчанию подставляется в поле ввода. Вариант ·N (без параметров) просто следит, чтобы ответ интерпретировался как число — это защищает от нежелательных знаков: программа «настоит» на вводе 16.67, а не 16,67 (десятичный разделитель — точка). Конструкция ·N·0· задаёт ввод числа с умолчанием 0.

·N·10·0·20·     * число от 0 до 20, по умолчанию 10

·S — принудительно преобразовать ответ в строку

Сервер будет считать ответ строкой, даже если он имеет вид числа: введённое 0023456 останется строкой '0023456' с ведущими нулями. Начиная с версии ФБП:Сервера 3.18 уточнение ·S может следовать за любым другим уточнением в следующей строке.

·R L
·S

·R форма·ответ·… и ·X форма·ответ·… — форма как список выбора

Вместо поля ввода будет показана отчётная форма; когда пользователь выбирает в ней строку, клиент выделяет первое слово этой строки (до первого пробела) и использует его как ответ на вопрос. После имени формы можно передать ответы на вопросы самой формы. Разница: ·R даёт «готовый» ответ, а ·X позволяет отредактировать полученный из формы ответ.

·R 70       ( ·X 70 )
·R 10 ·1·   ( ·X 10 ·1· )
Уточнение разбирается по первому знаку после ·, и ·S приоритетнее ·N, если употреблены одновременно. Запись ·X AAAA ·SU-NDS· будет воспринята просто как ·S — и числа станут считаться строками.
В однопользовательских версиях редактирование баллонной помощи не влечёт немедленного пересчёта вычислительного состояния — оно пересчитывается только после рестарта программы.

14.6. *-обращения: счёт из значения переменной

В описаниях проводок наряду с обозначениями счетов и @-обращениями можно использовать обращение вида:

* переменная — счёт, заданный переменной

Смысл: взять счёт, обозначение которого совпадает со значением переменной. Обычно переменная получает значение в ветви-вопросе. Работает в ФБП для Windows и в сетевой версии.

* ветви-вопросы дерева:
?D дебет
?K кредит
* лист дерева:
*D *K

Как это работает: при регистрации операции на вопрос «дебет» ответим @76, а на вопрос «кредит» — 51. Программа предложит список субсчетов счёта 76; выберем, скажем, 76-01 Первый. Тогда переменная D получит значение 76-01, переменная K — значение 51, и проводка *D *K эквивалентна записи 76-01 51.

Об особенностях использования *-обращений в сетевой версии см. «Руководство пользователя по сетевой версии».

14.7. Ветви-циклы: одна операция — проводки по всем субсчетам

Часто нужно проделать однотипные проводки для всех субсчетов счёта: начислить амортизацию на все объекты счёта 01, переоценить материальные запасы при изменении курса, начислить зарплату всем сотрудникам. Для этого служит ветвь-цикл:

# счёт комментарий — ветвь-цикл

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

* ветвь дерева:
# 70 начисление зарплаты всем сотрудникам
* лист дерева (выполняется для каждого субсчёта счёта 70):
# 68-3 $*0.12

Обратите внимание на масштаб: в листе может быть до 15 проводок, значит для каждого субсчёта будет выполнена вся группа проводок. В журнале — одна операция, а проводки сделаны для всех субсчетов!

continue if выражение — пропустить субсчёт в цикле

Псевдопроводка в листе дерева: если выражение принимает значение 1, оставшиеся проводки тела цикла для текущего субсчёта пропускаются, и цикл продолжается со следующего субсчёта. Типичное применение: сотрудник уволен — начислений ему не делаем.

continue if [ep #]=0   * пропустить, если параметр субсчёта нулевой

break if выражение — досрочно завершить цикл

Если выражение принимает значение 1, оставшиеся проводки и псевдопроводки тела цикла пропускаются и цикл завершается досрочно.

break if $<0           * прекратить перебор при отрицательной сумме
Если важна совместимость с сетевой клиент-серверной версией, избегайте ситуаций, когда break if или continue if находятся в листе дерева между @-обращениями к разным счетам. В однопользовательских версиях такая конструкция может предотвратить нежелательный выбор субсчёта, идущий после break if (continue if); в сетевой версии клиентская часть ничего не знает об этих операторах, поэтому нежелательный выбор субсчёта будет предложен в любом случае.

14.8. Файлы-коэффициенты и оператор выполнения проводки

Когда сумму проводки нельзя выразить коэффициентом или короткой формулой — например, подоходный налог со шкалой и вычетами — алгоритм расчёта выносят в файл-коэффициент: обычный файл *.rpt с программой на языке форм. В проводке после дебета и кредита указывается имя этого файла. Создать файл-коэффициент можно прямо из дерева: допишите в строке проводки после пробела имя файла и нажмите F4.

Соглашение о параметрах файла-коэффициента:

ПеременнаяСмысл
$На входе — сумма операции; в эту же переменную алгоритм возвращает вычисленную сумму проводки (то есть $ — и передаваемый, и возвращаемый параметр).
$DОбозначение дебетуемого счёта.
$KОбозначение кредитуемого счёта.

Вот настоящий файл-коэффициент tax.rpt — расчёт подоходного налога (в листе дерева проводка выглядит как @70 68-3 tax):

* Подоходный налог с физических лиц
*
D=[AK $D] Доход, начисленный сотруднику с начала года;
U1=[AP $D,68-3] Уже удержанный с начала года под/налог 9%,17% (город);
U2=[AP $D,68-31] Уже удержанный с начала года под/налог 3% (федер);
G=D*0.01 вычеты в Пенсионный фонд 1%
M=[ed MIN] Подлежащие вычету минимумы;
P=[ep @70] Сумма льгот, накопленная к моменту начисления;
if D in 0..10000 D=D-P-2*M; L=M*2
elseif D in 10000..30000 D=D-P-M; L=M
else D=D-P; L=0
endif
*
D=D-G учтем вычеты 1% в Пенсионный Фонд
...
J1=[tr T1-U2+0.5] доначисленный в этом месяце подоходный налог.
J2=[tr T2-U1+0.5]
$=J2
**** конец файла-коэффициента TAX ****

Здесь видны приёмы языка форм: комментарии (* в начале строки или текст после выражения), обращения к данным учёта в квадратных скобках ([AK $D] — накопленный оборот, [AP $D,68-3] — оборот между счетами, [ed MIN] и [ep @70] — экстрапараметр и параметр счёта, подробнее — в главах 9 и 13), условный оператор с проверкой диапазона in, несколько операторов в строке через ;. Результат возвращается присваиванием $=....

Файл общих определений first.rpt выполняется перед отчётными формами и первичными документами, но не выполняется перед файлами-коэффициентами — все нужные определения файла-коэффициента должны быть в нём самом (или в library.rpt, доступном и отсюда). Подробности — в главе «Файлы форм и общие файлы».

▒ дебет кредит (выражение) — оператор выполнения проводки в файле-коэффициенте

Внутри файла-коэффициента можно выполнять проводки напрямую — строкой, начинающейся с знака проводки (символ с кодом 177; в ФБП для Windows от 5.03.99 он вводится клавишей F6, ввод через Alt+177 не гарантируется). Синтаксис — как в листьях дерева: сначала дебетуемый счёт, затем кредитуемый, далее выражение, обязательно в скобках.

▒ 50 51 ($)
▒ 68-2 46 ($*0.18)

В проводках файла-коэффициента могут участвовать счета без субсчетов, собственно субсчета, @-обращения, *-обращения к переменным и текущий субсчёт # цикла. Ограничений на количество проводок в файле-коэффициенте нет — этим и обходят лимит в 15 проводок листа. Но два ограничения есть:

  • из файла-коэффициента нельзя вызвать другой файл-коэффициент;
  • все требуемые @-обращения должны быть описаны в листе дерева: при повторном @-обращении в файле-коэффициенте подставляются те же субсчета, что были выбраны в листе, — дополнительного выбора не происходит. Это связано с тем, что фраза операции «складывается» максимум из 12 «кирпичиков» (ветвей дерева и @-обращений), и ограничение действует также для проводок в файле-коэффициенте.
Проводки, фактически сделанные по операции, можно посмотреть клавишей F6 (в сетевой версии — при включённом запоминании ta-фактов).

14.9. Псевдопроводки: параметры счетов, документы, горячие клавиши

Псевдопроводки имеют тот же синтаксис, что и обычные проводки, но не имеют смысла «дебет-кредит». Они применяются в листьях дерева для трёх задач: присваивать новые значения параметрам субсчетов, создавать первичные документы и определять горячие клавиши.

14.9.1. Параметры счетов и псевдопроводка «=n»

У каждого счёта есть 9 параметров, связанных со всеми его субсчетами: параметр может выражать цену единицы материала, норму амортизации основного средства, процент налога, уплаченного при покупке объекта, и т.д. В формах значения параметров читаются функциями [bp ...] и [ep ...] (см. главу «Функции: данные учёта»). А изменять значения параметров можно динамически — псевдопроводками в листьях дерева:

субсчёт =n — присвоить значение параметру

В качестве дебетуемого указывается субсчёт, параметр которого нужно изменить, а в качестве кредитуемого — обозначение =n, где n — цифра, номер параметра. Присваиваемое значение определяется, как обычно: по сумме операции, формуле или файлу-коэффициенту.

* записать сумму операции в параметр 1 выбранного субсчёта счёта 41:
@41 =1
* увеличить параметр 3 на 5% от суммы операции:
@41 =3 [ep @41,3]+$*0.05

Для складских задач («а сколько этого товара есть сейчас?») при вводе операции полезен расчётный параметр: в плане счетов и начальных условий задаётся наименование расчётного параметра и формула его расчёта — например, для счёта 41 «Товары» формула остатка, делённого на цену из первого параметра, — и при вводе операции программа сразу показывает вычисленное количество. Тонкость: ссылаться на расчётный параметр в файлах форм нельзя.

14.9.2. Псевдопроводка «создать документ»

С операциями могут быть связаны первичные документы — накладные, ордера, счета — автоматически создаваемые при вводе операции. Формы документов пишутся на том же языке, что и отчётные формы и файлы-коэффициенты:

создать документ имя-формы [аргумент] — псевдопроводка первичного документа

Вместо имён счетов указываются ключевые слова создать документ (или create document), далее — имя файла, где содержится форма документа. После имени формы может идти аргумент — обозначение счёта или заменяющая его конструкция (#, @, *); в форме документа на переданный аргумент ссылются как $P.

создать документ nakl @41
create document order

Как это устроено в сетевой версии. Для хранения документов в каталоге данных должен быть подкаталог TXA, а на клиентских местах включена опция «Хранить первичные» (меню Опции). При старте сервер создаёт в TXA 12 подкаталогов — по одному на месяц (A — январь … L — декабрь), и каждый документ (*.txa) хранится в подкаталоге своего месяца. Рядом сервер ведёт файл дескриптора *.des: первая строка — дата и время операции; если при пересчёте баланса штамп операции не совпадает с дескриптором, документ создаётся заново, а предыдущая версия дописывается в дескриптор — так накапливается вся история модификации документа. Документ, созданный операцией «в прошлом» или перебивкой, можно увидеть клавишей F4 (начиная с версии сервера 3.02, с задержкой, зависящей от скорости пересчёта).

В режиме ввода и просмотра операций первичные документы можно создавать, просматривать и удалять; в журнале слева от записи операции появляется символ, означающий, что для неё есть документ на диске.

В исходных текстах первичных документов нельзя использовать оператор вопроса и функции IM, IS, IR, IA, IY — сервер их игнорирует. Причина: для работы первичного документа вводится операция, и диалог внутри документа создал бы неопределённость — сервер был бы вынужден либо продолжать ввод других операций, игнорируя неоконченные вычисления, либо простаивать в ожидании диалога. В остальном статус формы обычный: функция [re] выдаёт в ней значение 2, доступны $P и определения из first.rpt.
Форму документа можно запустить и из общего меню форм, но тогда почти все данные придётся ввести вручную, а связь между операцией и документом не возникнет. Если в каталоге обмена с сервером есть подкаталог TXA, документ сначала ищется в нём; создать документ там можно функцией [dir 1] и операторами вывода в файл >file / >>file (см. главу «Текст и вывод»):
F=[dir 1]+'TXA\'+[stamp 3]+'.TXA'
ЗАГОЛОВОК ДОКУМЕНТА >file F
строки документа >>file F

14.9.3. Псевдопроводка «горячая клавиша»

Для самых частых видов операций стоит организовать ускоренный ввод:

горячая клавиша название — кнопка быстрого ввода

В лист дерева к группе проводок добавляется псевдопроводка с ключевыми словами горячая клавиша (или hot key) и названием клавиши. В режиме ввода операций на экране появится клавиша-кнопка с этим названием; щелчок по ней автоматически «выкладывает» всю фразу или её часть до первой ветви-вопроса.

горячая клавиша Сдана касса

В сетевой ФБП для каждого пользователя можно организовать свой набор горячих клавиш — через переопределение системных запросов в форме USERS (см. главу «Директивы N:»). В Windows-клиенте горячих клавиш может быть до восьми.

14.10. Ограничения, редактирование дерева и ошибки компилятора

Соберём в одном месте числовые ограничения дерева видов операций — их полезно помнить при проектировании:

ОграничениеЗначение
Ветвей первого уровня деревадо 15
Ветвей следующего уровня у каждой ветвидо 15
Длина фразы операции (ветви дерева и @-обращения)до 12 «кирпичиков»
Проводок в одном листе деревадо 15 (обходится файлом-коэффициентом)
Параметров у каждого счёта9
Длина ответа на вопрос операции (Windows-клиент)11 символов
Горячих клавиш (Windows-клиент)до 8

Редактирование дерева. Поиск отрывка по образцу — Alt+F7. Дерево можно резать и клеить: установите селектор на ветвь, нажмите Alt+F10 и выберите «Отрезать» — фрагмент ветви со всеми проводками будет временно сохранён; затем установите селектор на новое место, снова нажмите Alt+F10 и выберите «Приклеить». Пункты меню «Записать в файл *.rul» и «Приклеить из файла *.rul» сохраняют фрагменты дерева в файлах с расширением rul (удобно для переноса между настройками), а пункт «Печатать» распечатывает фрагменты дерева.

Компилятор дерева обнаруживает большинство формальных ошибок «на первой секунде» попытки построить баланс. Вот его сообщения и их смысл:

СообщениеПричина и что делать
ПУСТАЯ СТРОКА В ОПИСАНИИ ПРОВОДКИВ листе дерева есть пустая строка — уберите её.
НЕПРАВИЛЬНО ОПИСАНА ПРОВОДКАНарушен формат «дебет кредит коэффициент».
НЕПРАВИЛЬНЫЙ КОЭФФИЦИЕНТ В ОПИСАНИИ ПРОВОДКИОшибка в числовом выражении коэффициента.
@-ОБРАЩЕНИЕ К НЕСУЩЕСТВУЮЩЕМУ СЧЕТУСчёт не описан в списке счетов — введите его вместе с субсчетами.
@-ОБРАЩЕНИЕ К СЧЕТУ БЕЗ СУБСЧЕТОВ@ может ссылаться только на счёт, имеющий субсчета.
НЕИЗВЕСТНЫЙ (ИЛИ СОСТОЯЩИЙ ИЗ СУБСЧЕТОВ) ДЕБЕТУЕМЫЙ/КРЕДИТУЕМЫЙ СЧЕТСчёт не описан либо состоит из субсчетов, а @-обращение не применено.
НЕТ ОПИСАНИЙ ПРОВОДОК ДЛЯ ЭТОЙ ОПЕРАЦИИДля вида операции нет листа с проводками — добавьте лист в дерево.
НЕПРАВИЛЬНО УКАЗАН ВИД ОПЕРАЦИИВетвь дерева изменилась после ввода операции — при редактировании операции укажите вид заново.
В ОПИСАНИИ ПРОВОДКИ НЕТ ССЫЛКИ НА ФАЙЛ-КОЭФФИЦИЕНТВозникает при редактировании дерева по F4, если в проводке нет коэффициента или задан числовой коэффициент.

14.11. Сквозной пример: от плана счетов до введённой операции

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

Шаг 1. Счета. В «Счетах и начальных условиях» заводим два счёта без субсчетов:

50 Касса
51 Расчетный счет

Шаг 2. Ветви дерева. Переходим в режим «Виды операций и проводки» (меню Правила или Alt+F6), ставим селектор на строку «Дерево видов операций» и нажимаем F7 — пишем название ветви первого уровня Расчетный счет. Ставим селектор на неё и тем же F7 добавляем ветви второго уровня, повторяя действия для всего нужного: получаем ветви приход и расход, а внутри каждой — завершающие ветви сдана касса и передано в кассу. Дерево порождает две фразы:

Расчетный счет - приход - сдана касса
Расчетный счет - расход - передано в кассу

Шаг 3. Листы с проводками. Ставим селектор на последнюю ветвь первой фразы (сдана касса) и нажимаем F6 — открывается лист дерева. Деньги пришли на расчётный счёт из кассы, поэтому записываем проводку без коэффициента (по умолчанию он равен 1 — проводка на всю сумму операции):

51 50

Аналогично для фразы «расход - передано в кассу» лист будет зеркальным:

50 51

Шаг 4. Ввод операции. Переходим в журнал операций (меню Операции или Alt+F2), нажимаем F7 и Enter. После ввода даты и выбора положения операции в течение дня появляется меню из ветвей первого уровня — у нас это единственная ветвь «Расчетный счет»; далее — меню второго уровня («приход» / «расход») и третьего. Выкладываем фразу «Расчетный счет - приход - сдана касса», вводим сумму — и по F6 смотрим сделанные программой проводки: дебет 51, кредит 50, сумма операции.

Шаг 5 (развиваем пример). Аналитика и вопросы. Добавим аналитику по контрагентам и ставку НДС. Заводим счёт 76 с субсчетами-покупателями и строим вторую ветвь первого уровня:

Реализация
   ?I ндс,%            * ветвь-вопрос: ставка налога
   оплачено покупателем

В баллонной помощи ветви ?I ндс,% (селектор на ветвь, Alt+F1) пишем уточнение-меню, чтобы ставку выбирали, а не вводили:

·C·%·20·10·

Лист для фразы «Реализация - ?I ндс,% - оплачено покупателем»:

51 @76
@76 68-2 $*I/100
горячая клавиша Оплата от покупателя

При вводе такой операции программа покажет меню ставок НДС (20 или 10), затем список субсчетов счёта 76 (один раз, хотя @76 встречается дважды), выполнит две проводки — оплату на всю сумму и выделенный налог по формуле $*I/100, — а кнопка «Оплата от покупателя» позволит в дальнейшем выкладывать фразу одним щелчком.

Захотите изменить проводки в уже работающей настройке — смело меняйте дерево: операции хранят только выбранные фразы и ответы, а проводки вычисляются по новым правилам при пересчёте. Постройте «Обороты счетов» (Alt+F3) за период с 1 января по 31 декабря — программа предложит перебить операции, не соответствующие новому описанию, и состояние всех счетов будет пересчитано автоматически.

15. Директивы N:: навигация и запросы из форм

Обычная отчётная форма — это «слепок» данных: она показывает информацию, но не умеет реагировать на действия пользователя. Директивы N: превращают форму в интерактивный экран: из неё можно по нажатию клавиши запускать другие формы, переходить к операции в журнале, регистрировать операции и заводить субсчета — не выходя из отчёта. Формы со взаимными ссылками образуют настоящие «приложения»: список накладных, из которого по Enter открывается сама накладная, и т.п.

Механика такова: строки N:... в тексте формы не выводятся в отчёт, а запоминаются Клиентом. Когда пользователь нажимает привязанную клавишу, Клиент конструирует из шаблона строку запроса и посылает её Серверу — так, будто запрос введён вручную в строке запроса (см. главу «Язык запросов к серверу»).

15.1. Базовая форма: N:запрос

В самом простом виде после N: сразу следует строка запроса к Серверу:

N:R test2

Такая директива привязывает запрос R test2 (выполнить форму test2.rpt) к клавише «Отправить запрос», которая появляется в нижнем правом углу окна отчётной формы. Запрос выполняется двойным щелчком мыши по этой клавише.

Директивы N: обрабатывает именно Клиент, а не Сервер: Сервер про них ничего не знает. Поэтому все приёмы этой главы работают только при просмотре формы в ФБП:Клиенте.

15.2. Переименование клавиши: N:NAME:

Надпись на клавише «Отправить запрос» можно заменить своей — так пользователь поймёт, что произойдёт при нажатии:

N:NAME:Запустить форму
N:R test2

Директива N:NAME:текст задаёт подпись клавиши; она относится к запросам, описанным следующими строками N:.

15.3. Привязка к клавишам: N:Fi: и N:ENTER:

Запрос можно привязать не к экранной клавише, а к функциональным клавишам F1…F12 и к клавише Enter. Синтаксис:

N:F5:R price ·2·
N:F6:R price ·1·
N:ENTER:R price ·3·

Запрос выполняется сразу после нажатия соответствующей клавиши. В этом примере все три клавиши запускают одну форму price.rpt, но с разными ответами на её вопрос (·2·, ·1·, ·3· — знак ·, код 250, обрамляет ответы; см. главу «Язык запросов к серверу»). В самой форме price.rpt переданные значения разбираются, например, функцией ввода [im ...].

Чтобы пользователь видел назначение клавиш, выведите подсказку в нижней строке экрана: строка текста формы, предварённая символом с кодом 254, попадает именно туда.

15.4. Область действия директив N:

Директива N: действует не на всю форму, а на строки отчёта, следующие после неё — до ближайшей следующей директивы на ту же клавишу. Это позволяет в разных частях одной формы назначить одной клавише разные действия:

N:F9:r test1
...
N:F9:r test2
...
N:F9:

Директива без строки запроса (N:F9: в конце примера) помечает конец действия предыдущей директивы: ниже этой строки клавиша F9 уже ничего не делает.

15.5. Динамические ссылки на данные строки: #i

Главная сила директив N: — возможность строить запрос из содержимого строки, на которой стоит селектор. Ссылки на слова выделенной строки записываются как #i, где i — от 1 до 9, порядковый номер слова в строке. #1 — это слово, начинающееся из крайнего левого положения строки (символы псевдографики при подсчёте не учитываются).

Например, форма разлистывает покупателей и счета, а по Enter запускает форму F16.rpt, передавая ей код покупателя (первое слово строки) и номер счёта (третье слово):

N:ENTER:R F16 ·1·#1·#3·

Чтобы форма F16.rpt подхватила эти значения, в её начале используется функция [ia ...] — переданные ответы она получает автоматически.

Если в форму вывести штамп операции, можно организовать переход в журнал операций с установкой селектора на нужную операцию:

N:ENTER:J KEY=#1

Ссылки из строк таблицы [table] V01.02

При выводе таблицы функцией [table] управляющая строка размещается непосредственно перед вызовом row. Она действует на сформированную строку данных; псевдографика при выделении слов #1…#9 не учитывается:

for i=1 to n
N:ENTER:R CARD ·#1·
[table 'row','T',K,N,[sn S,15,2]]
N:ENTER:
endfor

Если код карточки находится в первой колонке, #1 передаст его в форму CARD. Пустая строка N:ENTER: завершает область действия привязки, чтобы она не распространялась на итоги и другие строки после таблицы. Если для колонки включён wrap, код и остальные параметры находятся на первой строке записи; в строках продолжения длинного текста эти ячейки пустые.

15.6. Специальные команды: ##, #CLOSE, #0, #NULL

Кроме ссылок #i, в строках запроса есть служебные конструкции:

КонструкцияНазначение
##Литеральный знак # в запросе. Если в тексте запроса нужен сам символ #, его пишут дважды.
#CLOSEЗакрыть окно формы после выполнения запроса.
#NULL #iПроверка присутствия данных: если i-го слова в строке нет, Клиент прерывает выполнение следующих запросов.
#0Особая ссылка: команда с #0 не исполняется, если в начале строки есть хотя бы один пробел (без учёта псевдографики).

Автозакрытие удобно для форм-«переходников»: форма закрывается сразу после того, как отработал её запрос:

N:ENTER:J KEY=#1
N:ENTER:#CLOSE

При переходе в список операций окно формы закроется само. То же для экранной клавиши:

N:#CLOSE
N:NAME:Запустить форму
N:R test2

Проверки #NULL можно комбинировать:

  • несколько команд #NULL подряд — проверка по условию И (все указанные слова должны присутствовать);
  • несколько ссылок в одной команде — по условию ИЛИ.

15.7. Автозапуск: N:AUTO:

Директива N:AUTO: выполняет запросы один раз, автоматически, при первой загрузке отчётной формы — без нажатия каких-либо клавиш:

N:AUTO:#CLOSE

Это удобно для автоматической регистрации операций (директива O), заведения нового субсчёта (директива A) и т.п. В сочетании с #CLOSE получаются «невидимые» формы — например, форма обнуления экстрапараметров: она выполняется и закрывается, не показываясь пользователю.

Ограничения N:AUTO:: у неё нет области действия, в ней нельзя использовать ссылки на данные строки (#i) и команду #NULL.

15.8. Пример: взаимодействующие формы LIST и NAKL

Классическая пара «список → документ». Форма LIST разлистывает список накладных, собранный фактами; по Enter на выбранной строке открывается форма NAKL с самой накладной:

N:ENTER:R NAKL ·#1·
p0=[user]
Список чеков пользователя ^^^^^^^^^^^^^^^^^^^^^^p0
i=1
:POVTOR
search оператор p0 накл ?nk сумма ??
if [success]=1
i=i+1
¦^^^^^^^^^nk¦
goto POVTOR
endif

По Enter выполняется форма NAKL, которая получает номер накладной (#1 — первое слово выделенной строки) и ищет её в ленте фактов:

* Накладная
?nk number
^^^^^^^^^^^^^^nk
t=[type nk]
if t=0 nk=[intsn nk]; endif
rewind facts
search оператор ?? накл [strip nk] сумма ?sm
if [success]=1
Н А К Л А Д Н А Я
номер: ^^^^^^^^^^nk сумма ^^^^^^^^^^^sm^^
endif

Обратите внимание на идиомы: [type nk] — проверка типа значения (число/строка); [intsn nk] — преобразование; [strip nk] — удаление лишних пробелов; rewind facts + search — цикл по ленте фактов; [success] — признак успешности поиска (см. главы «Лента фактов» и «Функции: строки, даты, математика»).

15.9. Форма USERS и переопределение системных запросов

Директивы N: можно использовать в особой форме USERS (users.rpt). Эта форма выполняется программой-клиентом при старте, и все запросы из неё клиент отправляет на Сервер автоматически. Так пользователю можно сразу «настроить рабочее место».

К системным запросам относятся, в частности:

  • T — получить дерево видов операций для регистрации операций;
  • F — получить список отчётных форм на Сервере;
  • R __SYS001 и другие __SYS… — встроенные системные формы (оборотная ведомость и др.), которые при первом старте Сервера компилируются автоматически и видны как «формы на сервере».

В форме USERS эти запросы можно переназначить — например, заменить встроенные системные формы собственными вариантами, а список форм — своей формой:

* Пример отчетной формы USERS:
if [user]='anyuser'
__SYS001 = __SYS01S
__SYS002 = __SYS02S
__SYS005 = __SYS05S
__SYS006 = __SYS06S
F='R FORMS'
^^^^^^^^^^^^F
endif

Здесь для пользователя anyuser вместо встроенных __SYS001, __SYS002 и т.д. будут выполняться формы __SYS01S, __SYS02S и т.д., а вместо стандартного ответа на запрос F — форма FORMS.

Похожим приёмом делают локальные горячие клавиши «по пользователю»: получите директивой T дерево видов операций, сохраните ответ в файл TREE.RPT, разметьте его условиями if..endif по именам пользователей ([user]) и создайте соответствующий USERS.RPT. Тогда у каждого пользователя будет свой набор горячих клавиш: у SVETA — одна, у TANYA — другая.

15.10. Передача контекста между формами: [ct]

При регистрации операции формы, обслуживающие ветви дерева (списки объектов учёта, уточнения ·R/·X — см. главу «Виды операций и проводки»), выполняются в контексте вводимой операции. Функция [ct s] возвращает в форме значение контекста с именем s. Это позволяет, например, форме второго вопроса использовать ответ, выбранный в форме первого вопроса.

Виды контекстов операции:

КонтекстИмяЗначение
Выбранный субсчётобозначение родительского счётаобозначение выбранного субсчёта
Ответ на вопрос операциипервые 9 символов вопросаответ, ограничен 10 символами
MODOKEYтолько заглавными латинскими буквамидата регистрации и уникальный ключ операции (ключ присутствует, если операция перебивается)
СуммаСумма (для англоязычного Сервера — Sum)введённая сумма операции, ограничена 10 символами

Пример: в дереве есть ветви-вопросы платежка и сумма; в баллонной помощи первой указано ·R FORM1, второй — ·R FORM2. Чтобы FORM2 знала, что выбрали в FORM1, в ней пишут:

[ct 'платежка']
В формах, обслуживающих ветви-вопросы, первая строка — заголовок, а текст формы должен быть «прижат» к левой стороне экрана.

Разбор контекста MODOKEY (позиции 1–2 — месяц, 3–4 — день, 5–7 и 8–10 — части кода операции вида CAAA-002):

CT=[ct 'MODOKEY']                    - получаем контекст;
MO=[vl [cp CT,1,2]]                  - месяц;
DO=[vl [cp CT,3,2]]                  - день;
K=[ch 64+MO]+[cp CT,5,3]+'-'+[cp CT,8,3]  - код операции;

Характерная задача: форма списка товаров должна показывать курс доллара на дату операции, а не последний введённый (операции часто вносятся «в прошлое»). Фрагмент news курс доллара ?d дал бы курс на текущую дату — это неправильно. Решение — извлечь дату из MODOKEY и откатить ленту фактов:

* начало формы 41.rpt
CT=[ct 'MODOKEY']
MO=[vl [cp CT,1,2]]
DO=[vl [cp CT,3,2]]
if [type MO] MO=MC; endif
if [type DO] DO=DC; endif
rewind facts range 1.01,MO+DO/100
news курс доллара ?d
Курс доллара ^^^^d^^^ ^^^^DO

Здесь if [type MO] MO=MC; endif — защита на случай пустого контекста (операция регистрируется в реальной дате, не перебивается): подставляются текущие месяц и день (MC, DC). Запись MO+DO/100 складывает месяц и день в дату вида 3.12 (12 марта).

15.11. Типичные ошибки и подводные камни

1. Несколько запусков форм на одну клавишу. Конструкция

N:ENTER:R forma1
N:ENTER:R forma2
N:ENTER:R forma3

не построит все три формы — построена будет только последняя.

2. Пропали строки формы-источника. Если форма form1 запускает form2 (например, N:Enter:r form2) и текст form1 содержит только расчётную часть или вызовы функций [ir...], [is ...], [ia ...], то после строки N:Enter:r form2 следует оставить хотя бы две пустые строки.

3. В N:AUTO: нельзя ссылки #i. Автозапросы не имеют области действия и не могут ссылаться на данные строк отчёта — использовать #i и #NULL в них нельзя.

4. Как правильно сформировать строку запроса (например, для регистрации операции директивой O): откройте два окна — ввод операции и список запросов. Введите операцию вручную — во втором окне сконструируется готовая строка запроса к Серверу, которую можно скопировать в директиву N:.

16. Язык запросов к серверу

Вся работа сетевой версии ФБП — это постоянный диалог Клиентов и Сервера, а основа диалога — запросы к Серверу. Часть запросов Клиент формирует автоматически (когда вы открываете журнал, вводите операцию, запускаете форму), но любой запрос можно ввести и вручную — в строке запроса Клиента. Знание языка запросов нужно и программисту форм: директивы N: (см. главу «Директивы N:») конструируют именно такие запросы, а функция [jf s] интерпретирует строку как директиву J.

Запрос состоит из одной или нескольких директив. Директива — строка, первая литера которой — латинская буква, однозначно определяющая вид директивы (можно использовать и строчные, и прописные буквы). Если запрос состоит из нескольких строк, Сервер выполняет их как одну транзакцию.

16.1. Как происходит передача запросов и приём ответов

Обмен идёт не по сетевым протоколам, а через файлы в общем каталоге обмена (_BOX_). После начального диалога Клиент создаёт в каталоге обмена канал связи — файл с обычно восьмизначным случайным именем из букв и/или цифр. Протокол на примере канала ZXC456A3:

  1. Клиент создаёт запрос в файле ZXC456A3.REQ, затем переименовывает его в ZXC456A3.IN.
  2. Сервер постоянно просматривает каталог обмена; обнаружив ZXC456A3.IN, выполняет запрос, удаляет .IN, а ответ пишет в ZXC456A3.ANS.
  3. По завершении формирования ответа файл переименовывается в ZXC456A3.OUT.
  4. При следующем запросе .OUT удаляется Клиентом; если этого не произойдёт, Сервер начнёт оставлять ответы в .ANS.

Начальный диалог устроен так: Клиент посылает директиву I (проверка имени пользователя и пароля, синхронизация времени, выяснение, с какими данными работает Сервер), затем директиву T — получение доступных ветвей и листьев дерева видов операций. Сервер сообщает тексты ветвей, сведения об @-обращениях и признаки, требуется ли ввод суммы, — но скрывает от Клиента сведения о проводках, предусмотренных в листьях дерева.

Начиная с ФБП:Сервера 3.23 в каталоге обмена можно создавать подкаталоги — у каждого подкаталога свой файл upti для автообновления отчётов. Для безопасности каждому пользователю средствами ОС следует давать право сетевого доступа только к каталогу обмена или его подкаталогу.

16.2. Пожелания клиента (невидимая часть запроса)

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

Невидимая часть — 32 байта: восемь байтов опций ABCDEFGH, затем аргумент [f1] (12 байт) и аргумент [f2] (12 байт) — строки, дополненные игнорируемыми пробелами:

БайтНазначениеЗначение
Aопция «Строк на операцию»например 6
B«Числа с запятыми» + «Числа с копейками»0 — обе выкл; 1 — запятые вкл, копейки выкл; 2 — запятые выкл, копейки вкл; 3 — обе вкл
C«Хранить первичные»1 / 0
Dначало отчётного периода, месяц (MF)латинские буквы 'A'–'L' (A = январь)
Eзарезервировано2
Fконец отчётного периода, месяц (ML)латинские буквы 'A'–'L'
Gначало отчётного периода, день (DF)латинские буквы от 'A', где A есть 1
Hконец отчётного периода, день (DL)латинские буквы от 'A', где A есть 1

Так, например, поведение функции [sn ...] в формах зависит от пожелания «Числа с копейками», а отчётный период попадает в форму как переменные MF, DF, ML, DL.

16.3. Форматы ответов: обычный текст, E- (ошибка), Q- (уточнение)

Ответ Сервера — одно- или многострочный текст. Два особых случая помечаются префиксами в начале строки:

ПрефиксСмыслПример
E-Ошибка: далее код и смысл ошибкиE-06: неправильно указан вид операции
Q-Требуется уточнение: далее код и смысл уточнениявозникает, когда форма содержит оператор вопроса или функции ввода, а ответов в директиве нет

Встречающиеся коды ошибок: E-06 — неправильно указан вид операции; E-14 — нет счета-предка; E-18 — ошибочный ключ.

16.4. Получение помощи: ?

?
? R

Директива ? выдаёт перечень всех допустимых директив; ? R — справку по конкретной директиве (требуется подкаталог HELP в каталоге данных).

С версии 3.14 в директивах вместо знаков можно указывать их коды — для преодоления затруднений при выводе некоторых знаков из программ. Синтаксис: //xxx//, где xxx — обязательно три цифры, образующие число от 0 до 255. Директивы R AX и R //065//X эквивалентны (65 — код латинской A).

В описаниях синтаксиса ниже: [ и ] — необязательный элемент; < и > — элемент может повторяться многократно; знак · (средняя точка, код 250; вводится Alt+250 на цифровой клавиатуре) — разделитель и ограничитель строковых значений; знак с кодом 254 в компонентах вида операции обозначает субсчёт, входящий в вид операции.

16.5. Полный перечень директив

БукваДействие
Aсоздать субсчёт (Account)
Bпрервать выполнение формы (Break)
Cкомпилировать форму (Compile)
Dудалить операцию (Delete)
Eвычислить выражение (Evaluate)
Fполучить список форм (Forms)
Gкомпилировать и выполнить форму (Go)
Hпоказать состояние cache
Iполучить синхронизацию (Information)
Jполучить список операций (Journal)
Kудалить субсчёт (Kill)
Lполучить документ (Load)
Mмонополизировать доступ к Серверу (Monopoly)
Oдобавить операцию (Operation)
Pпринудительный пересчёт состояния (Past)
Qзаписать файл ACNT.E (Quit)
Rвыполнить форму (Run)
Sполучить статистику сервера (Statistics)
Tполучить ветви дерева (Tree)
Uполучить права (User)
Vполучить версию сервера (Version)
Wуправление подключением к данным (Wait)
%использовать Профилер
?получить помощь

16.6. Описание директив

R — выполнить форму (Run)

Выполняет форму (файл *.rpt). Ответ — текст формы, или сообщение об ошибке, или запрос уточнения (Q-), если в форме есть оператор вопроса или функции ввода данных, а ответов в директиве нет.

Синтаксис: R форма [·ответ· <·ответ·>]. форма — наименование формы (ограничения те же, что у имён файлов в конкретной ОС); ответ — строковые значения, обрамлённые знаками · (код 250). Ответы передаются заранее оператору вопроса ? и функциям ввода [im...], [ir...], [iy...], [is...], [ia...]. Директива R используется при описании «Определений клавиш» в DOS- и Windows-клиентах.

R BALANCE
R 10 ·3·

Вторая директива выполняет 10.RPT; при встрече оператора вопроса или функции ввода та сразу получит ответ 3.

O — добавить операцию (Operation)

Добавляет операцию к соответствующему файлу *.f3p и «подсбивает» баланс. Сервер присваивает операции штамп: он начинается знаком · и состоит из имени пользователя, времени добавления и восьмизначного уникального кода; элементы разделяются знаком ·. Штамп размещается в последних позициях комментария операции, дополняя его длину до 56.

Синтаксис: O [PD=p] [DC=d] [MC=m] [s] ·компонент·<компонент>·[··комментарий·]

  • s — сумма операции; по умолчанию 0;
  • MC=m — месяц операции (1..12); по умолчанию месяц реальной даты;
  • DC=d — день операции; по умолчанию день реальной даты;
  • PD=p — позиция в пределах дня: 0 — в общем порядке, -1 — в начале дня, +1 — в конце дня;
  • компонент — часть вида операции (путь по дереву); если начинается знаком с кодом 254 — это обозначение субсчёта, входящего в вид операции;
  • комментарий — в однопользовательских версиях до 56 литер; в сетевой предельная длина уменьшена на длину штампа.

Если вид операции предусматривал создание первичного документа, ответом-подтверждением будет текст этого документа; иначе — восьмизначный уникальный код. При неудаче ответ — строка вида E-....

O 1000000 ·Касса·приход ниоткуда···непонятно за что·

Добавляет операцию в файл 199908.F3P (если сегодня 29 августа 1999 г., пользователь SUPERVISOR); к комментарию приписывается штамп вида:

·SUPERVISOR·2908140556·HAAA-678

Та же операция 1 января в начале дня:

O PD=-1 DC=1 MC=1 1000000·Касса·приход ниоткуда···непонятно за что ·

Редактирование операции — слово KEY с тем же уникальным кодом:

O KEY=HAAA-678 1000000 ·Касса·приход ниоткуда···непонятно за что·

Пример ошибки (ветви в дереве нет):

O ·нет такого вида операции·
E-06: неправильно указан вид операции

Структура уникального кода: буквамесяца трбуквыкода - трицифрыкода. Буква месяца: A — январь, …, L — декабрь. Остальные элементы дают 26·26·26·10·10·10 = 17 576 000 комбинаций; уникальность — только в пределах года.

D — удалить операцию (Delete)

Удаляет операцию по уникальному ключу и «подсбивает» баланс.

Синтаксис: D уникальный_код или D { уникальный_код уникальный_код } — удаление операций с ключами из указанного интервала; кодов может быть много.

Если ключ (или хотя бы один ключ интервала) не связан с операцией — ответ E-18 ошибочный ключ... При успехе — ответ удалено.

D HAAA-678

Особые правила для групповых операций: удаление операций { и } запрещено — Сервер сам удалит их, когда в группе будет удалена последняя операция; при удалении операции из группы сумма операции { устанавливается равной количеству оставшихся в группе операций. Перед удалением групповой операции нужно включить режим «операции свёрнуто», затем F8.

J — получить список операций (Journal)

Выдаёт список операций из файлов *.f3p.

Синтаксис: J [MC=m1[,m2]] [d1[,d2]] [USER=u[,u]] [ALL] [{]

  • m1, m2 — месяц или интервал месяцев; по умолчанию — месяц реальной даты;
  • d1, d2 — день или интервал дней. Если заданы m1, m2 и m2>m1, интервал дней трактуется как «со дня d1 месяца m1 по день d2 месяца m2»; если d1 не указан, но есть m1 — все дни месяца m1;
  • USER=u — имена пользователей, чьи операции включать; без USER — только операции текущего пользователя; с ALL — операции всех пользователей;
  • { — включать операции «свёрнуто»: операции видов { и } выступают скобками группы, группа показывается как одна операция;
  • элементы директивы могут следовать в произвольном порядке.
J                                " операции текущего пользователя за сегодня
J MC=1 USER=SVETA {              " свернуто, SVETA, за январь
J MC=1,8 ALL                     " все пользователи, январь..август
J 5,15                           " текущий пользователь, с 5-го по 15-е текущего месяца
J KEY=CAAA-170                   " операция с уникальным кодом CAAA-170

Опции (с версии Сервера 3.08):

  • SUM= — интервал сумм операций (сравнение с точностью ±0.000001);
  • WORD= — только операции, содержащие указанный отрывок текста (в виде операции или комментарии); строка с пробелами обрамляется знаками ·;
  • PATH= — путь в дереве видов операций; всегда идёт в конце директивы.
J MC=1,12 SUM=1000        " операции за год, сумма = 1000
J SUM=2000,3000           " сегодняшние, суммы в интервале 2000..3000
J MC=6 WORD=60-1          " июньские, с текстом "60-1"
J MC=6 WORD=·а б·         " строка с пробелами обрамляется знаками ·
J PATH=Касса·расход       " виды, начинающиеся с указанных ветвей
J PATH=к·р                " то же короче, если однозначно
J MC=8 SUM=120,130 WORD=Вася PATH=Расч

Совместно использованные опции объединяются логическим «И».

Связанная функция форм [jf s] интерпретирует строку s как директиву J: результат — первая строка ответа, а побочный эффект — все строки ответа и их количество помещаются в extrd.dat (доступ через [ged 'jf.N'], [ged 'jf.1'] и т.д. — см. главу «Экстрапараметры и дисковая база»).

[jf 'o key=GAAA-001']
[jf 'o key=G7']
[jf 'all mc=1,12']

A — создать субсчёт (Account)

Создаёт субсчёт с указанными счётом-предком, обозначением и наименованием. Баланс не разрушается, «подсбивка» не происходит. Начальное сальдо и начальные значения всех параметров, кроме 9-го, инициализируются нулями; в девятый параметр заносится 1 — «признак новизны» субсчёта (проверка в формах: [bp ...,9]).

Синтаксис: A ·предок·обозначение·наименование·

A ·10·10-0001M·Особый материал·
субчет 10-0001M создан          " ответ при успехе
E-14: нет счета-предка          " ответ, если счета 10 нет

Приёмы ввода нового субсчёта: если за выводом списка стоит форма (например 41.rpt) или ввод идёт через ветвь-вопрос с уточнением вида ·X 41, нужна проверка на новизну по 9-му параметру — без неё новый субсчёт не отразится в списке на экране (хотя запишется в acnt.a3p) и станет виден лишь при регистрации следующей операции.

Предостережения по именам счетов: не рекомендуется заводить счета типа a и A (им можно противопоставить только одну форму A.RPT); не следует заводить счета, начинающиеся с цифры (при описании прав в _rights_.fbp идентифицируются только формы, начинающиеся с буквы).

Уточнение E — редактирование существующего счёта (с Сервера 4.11): можно менять наименование ([n2...]), тип счёта ([aa...]), начальный остаток ([ba...]), наименования параметров ([np...]), начальные остатки параметров ([bp...]):

A E ·счет· BA=число
A E ·счет· N2=·строка·
A E ·счет· AA=число
A E ·счет· NP1=·строка·   ...   A E ·счет· NP9=·строка·
A E ·счет· BP1=число      ...   A E ·счет· BP9=число

Обозначение счёта и строка после N2 обрамляются знаками · (код 250).

K — удалить субсчёт (Kill)

Удаляет субсчёт с указанным обозначением; после удаления Сервер перекомпилирует дерево видов операций и делает «пересбивку баланса».

Синтаксис: K обозначение

История поддержки: с версии Сервера 2.0 не поддерживалась; в Сервере 3.27 вновь разрешена, но только при наличии в каталоге данных ключевого файла freeacnt и только пользователю с правом U (пример: K Y1).

Опасности: удаление может вызвать ошибки компиляции и «подсбивки» (ссылки на субсчёт в дереве и операциях); обозначение удалённого счёта могло остаться в выражениях [get a, i] (результат непредсказуем), в полях фактов (такой факт несёт заведомо ложную информацию), в extrd.dat. Директива K не освобождает память, занятую счётом, — лишь лишает его ссылки: удалённые счета продолжают дебетоваться/кредитоваться «как ни в чём не бывало», а авария произойдёт только после перезагрузки Сервера — компиляция дерева завершится ошибкой.

Пользуйтесь директивой K только в крайней необходимости и доверяйте её одному лицу. «Мозолящие глаза» счета лучше не удалять, а делать невидимыми.

E — вычислить выражение (Evaluate)

Вычисляет выражение на языке форм; ответ — результат вычисления.

Синтаксис: E выражение

E 2+2                    " ответ: 4
E [ea 50] + [ea 51]      " сумма остатков в кассе и на расчетном счете

F — получить список форм (Forms)

Выдаёт список форм (*.rpt) каталога данных — как по Alt-F4 в однопользовательских версиях: упорядочен по алфавиту и разделён на группы: обычные формы, первичные документы, формы-истории, общие определения.

Синтаксис: F или F+. Вариант F+ показывает отметки об использовании в формах функций:

A Ngscuf > *Это форма A
B Y..... > *Это форма B

Расшифровка для формы A: кэш не включён (N); используются [ged...] (g), [sed...] (s), [ct...] (c), [user] (u), [f1] и/или [f2] (f); переменные MF (пробел) и ML (>). Форма B кэшируется (Y) и зависит от MF, DF, DL и ML.

V — получить версию сервера (Version)

Сообщает в однострочном ответе версию и модификацию Сервера.

V
Финансы без проблем: Сервер 2.9h от 20.04.99. Для Windows 95, NT

Связанная функция форм [ver] выдаёт число, соответствующее версии системы: для ultraf и соответствующих версий Сервера — 3.01; для ultraH и Сервера 2.9H — 3.02.

I — получить синхронизацию (Information)

Информация о метке данных на Сервере, текущем годе, дате и времени. Синтаксис — единственная буква I. Многострочный ответ из 11 строк:

СтрокаНазначениеПример
1Согласие/отказ предоставить информацию (YES/NO)YES
2Метка данных (содержимое файла FIN.LBL)ТОО 'Юго-Запад'
3Текущий год2012
4Текущий месяц8
5Текущее число29
6Время, часы16
7Время, минуты48
8Время, секунды17
9Признак права пользоваться деревом (T или пусто)T
10Признак права вмешательства в прошлое (P или пусто)P
11Резерв-

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

T — получить ветви дерева (Tree)

Выдаёт многострочным ответом информацию о доступных пользователю ветвях и листьях дерева (схематичное изображение доступных ветвей; сведения о проводках в листьях от Клиента скрыты). Синтаксис — единственная буква T. Ответ на T можно сохранить и переопределить через форму USERS — см. главу «Директивы N:».

S — получить статистику сервера (Statistics)

Многострочный ответ с накопленной статистикой: в разрезе пользователь/директива — количество обработанных директив, затраченное время, средние и худшие времена реакции и т.д. Синтаксис — единственная буква S. Эквиваленты в меню клиентов: «Статистика сервера», «Статистика клиента».

С версии 3.20 в ответе появился раздел «Потенциально опасные конструкции в *.rpt»: перечисляются формы, где встречаются форматные вставки с переадресацией вывода в файл, printstr с переадресацией в файл, операторы deletefile и renamefile.

Q — записать файл ACNT.E (Quit)

Записывает конечное состояние счетов в виде файла ACNT.E — требуется только при переходе к новому году (как в однопользовательских версиях). Синтаксис — единственная буква Q. Исполняется, только если пользователь имеет право U.

При записи состояния следует отключать ta-факты. Уникальные коды операций уникальны только в пределах года; если ta-факты останутся включены, возможно наложение записей о ta-фактах на записи нового года и искажение начальных остатков (наблюдается, например, в анализе счёта/субсчёта).

L — получить документ (Load)

Выдаёт многострочным ответом сохранённый при вводе операции первичный документ.

Синтаксис: L уникальный_код_операции

L IAAB-045

Если директива относится к операции }, Сервер выдаёт не только документ этой операции, а документы всех операций группы от { до }, последовательно соединённые в одно целое.

U — получить права (User)

Информация о правах пользователей, их именах и паролях (файл _rights_.fbp в каталоге данных; встроенных средств редактирования нет — правится любым текстовым редактором).

U
U+
U:
  • U — умалчивает о паролях;
  • U+ — сообщает пароли (только если текущий пользователь имеет право U);
  • если право U есть — выводится содержимое _rights_.fbp целиком; иначе Сервер подробно объясняет на естественном языке права текущего пользователя;
  • U: — выводит имя пользователя, а в последующих строках — общее количество операций в месяц; требует права U; может использоваться при написании других клиентских частей.

С версии 3.27 ключевой файл freeacnt разрешает пользователю с правом U создание счетов первого уровня, создание субсчетов к счёту первого уровня без субсчетов и удаление директивой K.

M — монополизировать доступ (Monopoly)

Монополизация доступа Клиента к Серверу; требуется право U.

M ON    " монополизировать доступ
M OFF   " отказаться от монополии

W — управление подключением к данным (Wait)

Требуется право U.

W ON     " «отключить» Сервер от данных и правил: обслуживание прекращается,
         " можно безопасно править правила и данные однопользовательской версией
W OFF    " снова «подключить» Сервер к правилам и данным
W FORM   " перекомпилировать формы
W DOWN   " завершить работу Сервера

Особенность W FORM (с версии 3.05): перекомпилируются не все формы, а только изменённые после старта Сервера. Первичные документы и файлы first.rpt и proto.rpt этой директивой не перекомпилируются — после их изменения нужна перезагрузка Сервера (W ON, W OFF).

C — компилировать форму (Compile)

Компилирует форму, строки которой лежат в последующих строках файла запроса. При ошибках Сервер возвращает сообщение с номером строки и позицией в строке; если сообщений не вернулось — компиляция успешна. Формируется автоматически Клиентом при редактировании документа и проверке по F8.

G — компилировать и выполнить форму (Go)

Компиляция (как C) + выполнение (как R). Для точной диагностики указываются номер строки и позиция ошибки. Формируется автоматически при выполнении форм по Enter — так форма выполняется без перезагрузки Сервера.

H — показать состояние cache

Полезна администраторам (право U). Смысл: «покажи составной индекс кэш». Пример ответа:

Кэшированные вызовы:
10000003 (1) ( 53) SUPERVISOR··610A0L@@·R F0
10000004 (1) ( 50) SUPERVISOR··610A0L@@·R F2
10000005 (1) ( 0) SUPERVISOR··610A0L@@·R F1
10000006 (1) ( 0) SUPERVISOR··610A0L@@·R F1 ·23·
4 (вызовы) 329 (байты) 584 (тики)

В каждой строке: имя файла в каталоге cache; признак актуальности (1 — актуален, 0 — нет, 2 — в процессе актуализации); количество тиков при последнем выполнении запроса (в секунде примерно 18 тиков); сама строка запроса с невидимыми частями.

Разновидность H- очищает индекс кэш — запоминание ответов начинается заново.

B — прервать выполнение формы (Break)

Прерывает выполнение зациклившейся или долго работающей формы; особенно полезна при отладке новых форм из Клиента. Синтаксис — единственная буква B.

P — принудительный пересчёт состояния (Past)

«Сообщение о том, что в прошлом было изменение». Не вносит изменений в файлы операций (*.f3p), а информирует Сервер о необходимости начать пересчёт.

P [MC=m] [DC=d]
P [KEY=k]
P key=GDFG-987
P MC=1

Гибкое регулирование права P (с Сервера 3.07): ранее право P означало вмешательство в любой месяц прошлого; теперь его можно ограничить:

  1. Относительно текущей даты (допустим, сейчас август): P -1 — изменения в любом дне августа; P -2 — в любом дне августа и июля;
  2. Абсолютно, буквами месяцев (A — январь, B — февраль и т.д.): P AH — изменения в январе и августе. Между буквами не должно быть пробелов; порядок букв произвольный.

С Сервера 3.27: P ! — вызывает обновление файла(ов) upti и не влечёт пересчёта состояния; только автообновления в ФБП:Клиентах.

% — использовать Профилер с 3.03

Встроенный Профилер показывает распределение времени: между фазами пересчёта состояния; между файлами-коэффициентами; между командами виртуальной машины; между строками исходного текста файла-коэффициента или формы. Подробно Профилер разобран в главе «Производительность и отладка»; здесь — краткая сводка.

  • % — информация из глобальных счётчиков + обобщённая, отсортированная по локальным; ответ начинается строкой Финансы без проблем:Профилер сообщает: и включает таблицы расходов времени на пересчёт состояния, на выполнение файлов-коэффициентов и команд;
  • % имя — профиль конкретного файла, например % PUNRD (распределение времени по строкам исходного текста в процентах и миллисекундах);
  • %- — обнулить счётчики; с версии 3.08 очищает счётчики тиков не только в файлах-коэффициентах, но и в отчётных формах и first.rpt.

Область, выделенная пользователем, помечается спецкомментариями *{{{ (начало) и *}}} (конец). Хронометрирующая часть включена по умолчанию; выключается файлом NOPROFIL в каталоге данных. Если ни одна строка формы не набрала более 10 тиков, профиль формы вообще не показывается — приём: охватить форму внешним циклом.

16.7. Ключевые слова (файлы-ключи) каталога данных

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

fastfact — быстрые факты

Эквивалент опции «Быстрые факты» однопользовательской ultraH; включён в Сервер 2.9H, но по умолчанию выключен. Включение: создать файл fastfact в каталоге данных; при старте появится сообщение ...FastFactMode....

Программа строит для ленты фактов индексирующую структуру; операторы SEARCH, NEWS, TOTAL и др. ускоряются во много раз («прыжки» по индексу вместо сканирования ленты). Внешняя логика операторов прежняя; при индексировании учитываются и прототипы фактов, и значения их полей. Цена: расход памяти на хранение фактов увеличивается (иногда в несколько раз); оператор FACT замедляется примерно в два раза. Зато формы выполняются очень быстро: поиск факта в ленте любой длины занимает примерно одно и то же малое время.

fastged — ускоренная функция [ged...] с 2.33

Ускоряет [ged...] в 10–20 раз за счёт зеркала данных из extrd.dat в оперативной памяти. Включение: файл fastged в каталоге данных (может быть пустым — важно присутствие). Размер зеркала в ОЗУ в несколько раз меньше размера extrd.dat на диске.

fastrun — приоритет процесса сервера

По умолчанию приоритет Сервера NORMAL_PRIORITY. При наличии файла fastrun (до старта Сервера) приоритет повышается до HIGH_PRIORITY. В Linux-версии fastrun переводит ожидаемый приоритет (nice) в состояние «-10».

noprofile — отключение Профилера

Файл NOPROFIL в каталоге данных: если Сервер увидит его при старте, Профилер работать не будет (хронометрирующая часть расходует небольшую долю процессора, но отключаема). В Linux-версии опция игнорируется (там Профилер работает на счётчике тактов процессора RDTSC).

logmode — протоколирование работы Сервера

По умолчанию протоколирование выключено. Файл logmode в каталоге данных (до старта) включает протокол работы Сервера в файле log в каталоге данных.

free-at — ослабление проверки @-обращений с 3.14

В присутствии файла-ключа FREE-AT Сервер не проверяет, что субсчёт, введённый в @-обращении, имеет предком именно тот счёт, который указан в листе дерева. Это компенсирует запрет на *-подстановки в @-обращениях клиент-серверной версии. На мнемосхеме Сервера в правом нижнем углу отображаются знаки @!.

Пример: вместо трёх ветвей

50 @61
50 @71
50 @76

достаточно одной ветви 50 @X и формы X.RPT:

Заголовок
A=[im 'какой счет','61','71','76']
if A=1 A='61'; elseif A=2 A='71'; else A='76'; end
*
#*A
n=[na #]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^n
#

Считается, что в плане счетов есть и 61, и 71, и 76, и X — все с субсчетами (для X достаточно одного фиктивного субсчёта). При раскрытии @X предлагается меню «какой счет», затем разлистываются субсчета выбранного счёта; выбранный субсчёт подставляется в фразу операции, а FREE-AT запрещает Серверу проверку родителя. Дерево сокращается втрое.

При нажатии F7 внутри раскрытого списка субсчетов DOS/Windows-клиенты в качестве имени счёта-родителя всегда выбирают имя формы (т.е. попытались бы создать субсчёт с родителем X), независимо от того, какой список реально показан. Затруднение обходится приёмом: форма запоминает пользователя и счёт-родителя через [sed ...], а отдельная форма (на пользовательской клавише) создаёт субсчёт нужному счёту.

freeacnt — свободное создание/удаление счетов с 3.27

Файл-ключ freeacnt разрешает пользователю с правом U:

  • создавать счета первого уровня;
  • создавать субсчета к счёту первого уровня, даже если у него ещё не было субсчетов;
  • удалять субсчета и счета директивой K (например, K Y1).
Сервер не следит за последствиями директивы K — целостность и логичность базы полностью на усмотрении администратора.

ta1 — только два ta-факта на проводку с 3.27

В присутствии файла TA1 проводка между счетами, имеющими предков, создаёт только два ta-факта — отражающие отношения между самими счетами, а не их предками. Экономит память и значительно уменьшает время выполнения проводок (команд folio-177). Ограничение: системные формы, работающие в режиме TA1, на момент написания документа ещё не встроены в Сервер. В версии 3.27 режим работает с ошибкой — нормализован в 3.28.

notail — без замечаний о незавершённых вычислениях с 3.27

В присутствии файла NOTAIL форма не дополняется замечаниями о незавершённых вычислениях в прошлом. На замечания об ошибках в прошлом этот файл влияния не оказывает.

ukraine — украинские числительные с 3.27

В присутствии файла UKRAINE функция [wn ..] излагает числительные на украинском языке; иначе — на русском. Отпадает необходимость создания многих вариантов файлов. (Для сравнения: в Linux-поставке существовали отдельные модификации flxr — русские числительные — и flxu — украинские; обе воспринимают и создают файлы, в именах которых только малые латинские буквы — строго proto.rpt, а не PROTO.RPT или Proto.rpt.)

Все файлы-ключи (fastfact, fastged, fastrun, logmode, NOPROFIL, FREE-AT, freeacnt, TA1, NOTAIL, UKRAINE) должны лежать в каталоге данных до старта Сервера — создание файла «на ходу» не подействует до перезапуска.

16.8. Подводные камни: сводка

  • Многострочный запрос выполняется как одна транзакция.
  • Уникальный код операции уникален только в пределах года (отсюда требование отключать ta-факты при Q).
  • Комментарий операции в сетевой версии короче 56 знаков на длину штампа.
  • PATH= в директиве J — всегда последняя опция; WORD с пробелами — в обрамлении ·.
  • Сумма в SUM=1000 сравнивается с точностью ±0.000001.
  • Директива K: память не освобождается; авария отложена до перезагрузки Сервера.
  • W FORM (с 3.05) не перекомпилирует первичные документы, first.rpt, proto.rpt.
  • Q требует права U; при Q отключайте ta-факты.
  • Счета не должны начинаться с цифры и не должны быть однобуквенными вида a/A.
  • Новый субсчёт через A: 9-й параметр = 1 («признак новизны», [bp ...,9]).
  • Профилер: менее 10 тиков на строку — профиль не показывается; время call не включает вызываемую процедуру.

17. Графика в формах

Отчётная форма умеет не только печатать текст, но и строить картинки: диаграммы, графики, схемы. Для этого изображение рисуется в массиве (по точкам, линиям и фигурам), а затем сохраняется в файл формата PNG оператором makepng. Дальше файл можно показать пользователю — например, вставить в web-страницу, которую строит форма, выполняемая через NULLCG.CGI (см. главу «Web-приложения через NULLCG.CGI»).

Всё рисование происходит в обычном массиве (см. главу «Массивы»): каждый элемент массива отвечает за одну точку изображения. Графические операторы — это удобная «обёртка» над массивом: они сами вычисляют нужные индексы и защищают от выхода за границы картинки.

17.1. Система координат и связь с массивом

Изображение — прямоугольник шириной w и высотой h точек. Действуют простые соглашения:

  • все видимые точки имеют положительные целые координаты: 1 ≤ x ≤ w, 1 ≤ y ≤ h;
  • левый верхний угол изображения — точка x=1, y=1 (ось Y направлена вниз, как принято в компьютерной графике, а не в математике);
  • точке (x,y) соответствует элемент массива с индексом (y-1)*w + x;
  • в описаниях синтаксиса ниже: a — латинская буква (имя массива), w,h — ширина и высота в точках, c — строка (код цвета).

Если элементу массива не присваивалось значение, при создании файла он считается равным [ch 0] (чёрная непрозрачная точка). Числовое значение элемента игнорируется — вместо него тоже берётся [ch 0]: цвет задаётся только строкой.

17.2. Как кодируется цвет

Цвет точки определяется первыми четырьмя знаками строкового значения элемента массива. Каждый знак — это один байт, полученный функцией [ch n] (см. главу «Функции: строки, даты, математика»):

Знак строкиКомпонентЗначение
1-йRed (красный)код 0–255: 0 — нет красного, 255 — максимум
2-йGreen (зелёный)код 0–255
3-йBlue (синий)код 0–255
4-йAlpha (прозрачность)[ch 0] — полная непрозрачность, [ch 255] — полная прозрачность

Если строка короче четырёх знаков, недостающие компоненты считаются равными [ch 0]. Поэтому строка из одного знака [ch 0] — чёрный цвет, а из одного [ch 255] — самый яркий красный.

[ch 255]                             - самый яркий красный
[ch 0] + [ch 255]                    - самый яркий зеленый
[ch 0] + [ch 0] + [ch 255]           - самый яркий синий
[ch 0]                                - черный
[ch 0]+[ch 0]+[ch 0]+[ch 255]         - полная прозрачность
[ch 255] + [ch 153] + [ch 153]        - розоватый
Цвет удобно собирать конкатенацией: c=[ch 255]+[ch 0]+[ch 128]. Четвёртый знак (Alpha) можно не задавать вовсе — тогда точка непрозрачна.

17.3. Создание файла изображения

makepng a, w,h, f — сохранить массив в PNG-файл

Создаёт изображение в формате PNG из элементов массива a размером w×h точек. f — строковое выражение с именем файла (включая полный путь).

w=127; h=127; f='c:\p1.png'
for y=1 to h
for x=1 to w
r=[ch 2*y]; g=[ch 2*x]; b=[ch x+y]
[a (y-1)*w+x, r+g+b]
endfor
endfor
makepng a,w,h,f

Пример заполняет массив «вручную», обращением к элементам по формуле (y-1)*w+x, — получается плавный цветной градиент 127×127.

В ultraH-32 оператор makepng воспринимается только синтаксически (изображение не создаётся) — там для просмотра картинок из массивов служит оператор showimg (см. ниже). В ФБП:Сервере, наоборот, есть makepng, а showimg отсутствует.

17.4. Рисующие операторы

Операторы этой группы изменяют элементы массива, избавляя от ручного вычисления индексов. Все они допускают координаты за пределами изображения — лишние точки просто отбрасываются.

drawpixel a,w,h, x,y, c — поставить точку

Делает то же, что присваивание [a (y-1)*w+x, c], но если x выходит за диапазон 1..w или y — за 1..h, присваивания не происходит: оператор безопасен при любых координатах.

w=127; h=127; f='c:\p1.png'
for y=1 to h
for x=1 to w
r=[ch 2*y]; g=[ch 2*x]; b=[ch x+y]
drawpixel a,w,h, x,y, r+g+b
endfor
endfor
makepng a,w,h,f

Это в точности тот же градиент, что и в примере к makepng, но без ручного вычисления индекса.

drawline a,w,h, x1,y1, x2,y2, c — прямая линия

Рисует прямую линию цвета c от точки (x1,y1) к точке (x2,y2). Координаты концов могут лежать за пределами изображения — видна будет только часть линии, попавшая в кадр.

w=120; h=100
drawline a,w,h, 50,50, -20,-10, [ch 255]
drawline a,w,h, 1, 1, 100, 40, [ch 0]+[ch 0]+[ch 255]
makepng a,w,h, 'c:\p3.png'

drawbar a,w,h, x1,y1, x2,y2, c — закрашенный прямоугольник

Рисует закрашенный прямоугольник цвета c: левый верхний угол — (x1,y1), правый нижний — (x2,y2). Выходы за границы изображения допускаются. Этим же оператором удобно заливать фон всей картинки.

w=120; h=100
drawbar a,w,h, 1, 1, w, h, [ch 255]
drawbar a,w,h, 20, 20, 40,50, [ch 0]+[ch 255]
makepng a,w,h, 'c:\p4.png'

drawtxt a,w,h, x,y, n,t, c — надпись на изображении

Выводит строку текста t цвета c начиная от точки (x,y). Число n задаёт шрифт и направление вывода:

nШрифтНаправление
15×12слева направо
210×18слева направо
115×12сверху вниз
1210×18сверху вниз
215×12снизу вверх
2210×18снизу вверх
w=100; h=100; c=[ch 255]+[ch 255]
drawtxt a,w,h, 10,10, 1, 'n=1', c
drawtxt a,w,h, 10,25, 2, 'n=2', c
drawtxt a,w,h, 50,50, 11, 'n=11', c
drawtxt a,w,h, 65,50, 12, 'n=12', c
drawtxt a,w,h, 10,60, 21, 'n=21', c
drawtxt a,w,h, 25,60, 22, 'n=22', c
makepng a,w,h, 'c:\p5.png'

Вертикальные надписи (n=11,12,21,22) полезны для подписей вдоль оси Y графика.

drawarc a,w,h, x,y, f1,f2, r, e, k, c — дуга, сектор, закрашенный сектор

Рисует дугу окружности или эллипса с центром в точке (x,y) — или целый сектор:

  • f1, f2 — начальный и конечный угол в градусах;
  • r — радиус;
  • e — признак эллиптичности: e=1 — окружность; 0<e<1 — эллипс с большей горизонтальной осью; e>1 — эллипс с меньшей горизонтальной осью;
  • k — уточнение вида: k=0 — дуга, k=1 — сектор (дуга плюс два радиуса), k=2 — закрашенный сектор;
  • c — цвет.

Пример — круговая диаграмма («пирог») из пяти закрашенных секторов с чёрными контурами на прозрачном фоне:

w=130; h=130; x=65; y=65; r=60; e=1
c0=[ch 0]; c1=[ch 255]; c2=[ch 0] + [ch 255]
c3=[ch 0]+[ch 0]+[ch 255]; c4=[ch 255]+[ch 255]
c5=[ch 255]+[ch 0]+[ch 255]
drawbar a,w,h, 1,1,w,h, [ch 0]+[ch 0]+[ch 0]+[ch 255]
drawarc a,w,h, x,y, 0, 15, r,e,2, c1
drawarc a,w,h, x,y, 15, 55, r,e,2, c2
drawarc a,w,h, x,y, 55,120, r,e,2, c3
drawarc a,w,h, x,y, 120,280, r,e,2, c4
drawarc a,w,h, x,y, 280,360, r,e,2, c5
drawarc a,w,h, x,y, 0, 15, r,e,1, c0
drawarc a,w,h, x,y, 15, 55, r,e,1, c0
drawarc a,w,h, x,y, 55,120, r,e,1, c0
drawarc a,w,h, x,y, 120,280, r,e,1, c0
drawarc a,w,h, x,y, 280,360, r,e,1, c0
makepng a,w,h, 'c:\p6.png'

Обратите внимание: сначала рисуются закрашенные сектора (k=2), затем те же сектора чёрным цветом c0 в режиме k=1 — так «пирог» получает аккуратные разграничительные линии.

На компонент Alpha для PNG полагаться нельзя: браузеры обрабатывают прозрачность по-разному. Netscape Navigator 4.7 её вовсе не воспринимает (фон станет чёрным); NN 6.0 воспринимает правильно; IE 4.0/5.0 и Opera 5 воспринимают, но подставляют фон по умолчанию, а не фон документа. Надёжнее заливать фон нужным цветом явно, через drawbar.

fillarea a,w,h, x,y, c — заливка области

Закрашивает цветом c область, содержащую точку (x,y) и ограниченную контуром того же цвета c. Если контур не встретился или незамкнут, заливка идёт до границы изображения.

w=100; h=100; c=[ch 255]
drawline a,w,h, 1,30, 90,10, c
drawline a,w,h, 1,70, 90,10, c
fillarea a,w,h, 1,31, c
makepng a,w,h, 'c:\p7.png'

Здесь две линии образуют замкнутую вместе с левым краем картинки область, и fillarea закрашивает её изнутри, начав с точки (1,31).

17.5. Другие операторы показа изображений

showimg a,w,h,x,y — показ изображения в ultraH-32

Позволяет просматривать изображения из массивов вместе с текстом отчёта прямо в ultraH-32. x,y — смещения в точках от строки, соответствующей оператору showimg. Если отчёт содержит хотя бы один showimg, используется графический режим 640×480 с 256 или 32768 цветами; текст выводится шрифтом 8×16, цвет и фон наследуются из текстового режима (опция «Палитра»). Прокрутка — клавишами Home, End, PgDn, PgUp и стрелками, завершение просмотра — Esc.

showimg a,w,h,10,20
Оператор showimg отсутствует в языке ФБП:Сервера — там следует использовать makepng и показывать полученный файл (например, через web-страницу, см. главу «Web-приложения через NULLCG.CGI»).

insertimage a,w,h,x,y — изображение в ultraLX (Linux)

Родственный оператор версии ultraLX для Linux: создаёт изображение из элементов массива a (размер w×h, смещение x,y) при показе отчёта. Играет ту же роль, что showimg в ultraH-32.

17.6. Пример: простая столбиковая диаграмма

Соберём всё вместе: нарисуем диаграмму продаж по кварталам — фон, оси, четыре столбика и подписи. Данные здесь заданы прямо в форме (массив v); в реальной форме их получают функциями оборотов [od ...], [op ...] и т.п. (см. главу «Функции: данные учёта»).

* диаграмма продаж по кварталам
w=220; h=160
[m 1,45,70,30,90]
* фон и оси
drawbar a,w,h, 1,1,w,h, [ch 255]+[ch 255]+[ch 255]
drawline a,w,h, 30,10, 30,140, [ch 0]
drawline a,w,h, 30,140, 210,140, [ch 0]
* столбики: масштаб 1 единица = 1.4 точки
for i=1 to 4
bh=[tr [m i]*1.4]
x1=40+(i-1)*45
drawbar a,w,h, x1,140-bh, x1+25,140, [ch 0]+[ch 0]+[ch 255]
drawtxt a,w,h, x1+5,145, 1, 'Q'+[intsn i], [ch 0]
endfor
drawtxt a,w,h, 5,10, 11, 'продажи', [ch 0]
makepng a,w,h, 'c:\sales.png'

Здесь виден типовой приём: «мир» диаграммы (числа 30–90) пересчитывается в точки картинки умножением на масштаб, а столбик рисуется от базовой линии y=140 вверх — то есть координата y2=140 у drawbar больше, чем y1.

17.7. Уникальные имена файлов и уборка: [unicfile] и deleteobsolete

Когда форму с картинкой запускают многие пользователи (типичный случай — web-приложение через NULLCG.CGI), каждый должен получить свой файл изображения, а отработанные файлы нужно своевременно удалять, чтобы каталог не разрастался. Для этого служат функция [unicfile] и оператор deleteobsolete.

[unicfile] — уникальное имя файла

Выдаёт строку — уникальное имя файла. К нему остаётся добавить путь и расширение:

u=[unicfile]; e='.png'
f0='c:\webshare\wwwroot\dynamic\' + u + e

deleteobsolete d, n — удаление устаревших файлов

Удаляет файлы, заданные маской d, от момента создания которых прошло более n секунд.

deleteobsolete f1,1000
Ограничение: маска d может включать только расширения .PNG, .GIF, .HTM; маска .* не допускается. Это защита от случайного удаления файлов данных.

Законченный пример динамической картинки для web-страницы (Web-сервер работает с каталогом c:\webshare\wwwroot, в нём создан подкаталог dynamic с пустым index.html, чтобы браузер не показывал содержимое каталога; ФБП:Сервер работает с данными из c:\data, и при обращении к http://localhost/cgi-bin/nullcg.cgi выполняется форма c:\data\ax.rpt):

Это форма ax
*
u=[unicfile]; e='.png'
f0='c:\webshare\wwwroot\dynamic\' + u + e
f1='c:\webshare\wwwroot\dynamic\*' + e
f2='http://localhost/dynamic/' + u + e
*
deleteobsolete f1,1000
... заполняем массив a ...
makepng a,100,100,f0
printstr '<img src=' + f2 + '>'

Схема работы: deleteobsolete чистит PNG старше 1000 секунд, форма рисует новый файл с уникальным именем, а printstr вставляет в страницу тэг <img> со ссылкой на него. Подробнее о построении web-страниц — в главе «Web-приложения через NULLCG.CGI».

18. Web-приложения через NULLCG.CGI

NULLCG.CGI — это CGI-скрипт, шлюз между Web-сервером и ФБП:Сервером. Он принимает запросы браузера, передаёт их ФБП:Серверу и генерирует HTML-страницы, которые браузер видит как особое клиентское место ФБП. Так на языке форм можно строить web-приложения — с формами ввода, кнопками, таблицами и картинками — и для Internet, и для локальной офисной сети.

  • Работает на Windows 95/98/NT/2000 в комбинации с ФБП:Сервером версии 3.14 и выше. По возможностям аналогичен МикроСерверу, но без ограничений на язык ФБП — доступен весь язык форм.
  • NULLCG.CGI не входит в комплект сетевой версии — это отдельный продукт (приобретается на Расчётном Пункте). В комплект входят исходные тексты Расчётного Пункта, Платного Консультационного Пункта и исходники nullcg.cgi на C — это одновременно и готовые образцы web-приложений.
  • Нужен установленный Web-сервер: в Windows 98 это Personal Web Server, в Windows NT — Microsoft Peer Web Service (Control Panel → Services), для Windows 95 подойдёт подходящий Web-сервер от IBM или Microsoft.

18.1. Пользователи, имена и пароли

Количество пользователей web-приложения не ограничено, а их имена и пароли определяются программно, в самих формах — например, через факты, extrd.dat или обозначения и наименования счетов. В файле _rights_.fbp следует указать общее имя USER.

При любом запросе NULLCG.CGI передаёт серверу контекст $$TISIG (его видно при включённом logmode). Сервер извлекает имя пользователя из extrd.dat по ключу (схематично):

x=[ged ah+'.ax.who']

где ah — комбинация IP-адреса пользователя и времени первого старта NULLCG.CGI, ax — «корневая» форма ax.rpt, who — имя поля. Если x окажется числом (в том числе нулём), операции будут штамповаться именем UNKNOWN.

В корневой форме ax.rpt имя пользователя обязательно должно быть введено в предопределённое поле who — иначе все его операции уйдут в журнал под именем UNKNOWN.

Приложение — это цепочка форм, передающих друг другу управление. Промежуточная информация хранится в extrd.dat индивидуально для каждого пользователя: уникальность достигается комбинацией IP-адреса и времени первого старта NULLCG.CGI (см. функции [ha] и [sf ...] ниже).

18.2. Структура web-формы

Начальная («корневая») форма приложения — AX.RPT: сразу после старта NULLCG.CGI посылает серверу запрос R AX (выполнить форму AX). Любая форма web-приложения — одновременно HTML-страница: результат выполнения формы становится содержимым страницы, а нажатие кнопки на странице передаёт управление обратно NULLCG.CGI, и выполняется предусмотренное действие (например, переход к BX.RPT через поле nextform).

При генерации страницы NULLCG.CGI вставляет тэг <pre> перед текстом из *.out-файла. Поэтому форму, содержащую конструкции HTML, надо начинать тэгом </pre> — он «закрывает» автоматически открытый preформатированный блок. Тэги <form> и </form> в формах-гибридах не используются: NULLCG.CGI предваряет страницу одним тэгом <form> автоматически.

Содержимое форм, как обычно, пишется в кодировке CP866 (DOS) — NULLCG.CGI преобразует текст в Windows-1251 при генерации страницы.

18.3. Комментарий <!--hybride-->

Форма-гибрид — форма ввода со смесью HTML и конструкций языка ФБП. В самом начале такой формы обязателен комментарий:

<!--hybride-->
В формах-гибридах синтаксический анализатор ФБП работает только с конструкциями языка ФБП; для HTML-тэгов диагностики нет, и ошибки в HTML трудно классифицировать. Чтобы понять, что пошло не так, смотрите исходный текст сгенерированной страницы в браузере (View → Page Source / Source).

18.4. Переходы между формами и обмен данными

Переход между формами выполняется нажатием кнопки Send:

<input type=submit name=Send value="название кнопки">

После нажатия Send введённая на странице информация автоматически передаётся в EXTRD.DAT через [sed...]: запрос предваряется цепочкой директив e [sed i,v], где i — комбинация имени формы, имени HTML-поля, IP-адреса пользователя и времени первого старта NULLCG.CGI, а v — значение поля (введённый текст или имя нажатой кнопки). Таким образом, в следующей форме можно через extrd.dat прочесть всё, что произошло в предыдущей до нажатия кнопки.

Функции [ha] и [sf ...] (ФБП:Сервер 3.15 и выше)

[ha] — IP-адрес пользователя с 3.15

Выдаёт IP-адрес пользователя. Числа адреса разделены подчёркиваниями: для локального адреса это строка 127_0_0_1.

[sf ...] — специальная функция с 3.15

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

ВызовРезультат
[sf 0,98]время первого старта NULLCG.CGI в виде цепочки цифр
[sf 0,9]длинная строка, новая при каждом вызове — применяется для «борьбы» с кэшами браузеров
[sf 0,99]время в формате, типичном для log-файлов Web-серверов
Ненужное данное в EXTRD.DAT можно обнулить — оно будет удалено при рестарте ФБП:Сервера.

18.5. Служебное поле document: передача данных между формами

Скрытое поле document (type=hidden) задаёт имя текущей формы — оно становится частью ключа при сохранении значений полей в extrd.dat. Пример формы-гибрида AX.RPT:

</pre>
<!--hybride-->
<input type=hidden name="document" value="ax">
Имя: <input name="field1" value="Иван">
Фамилия: <input name="field2" value="Петров">
<input type=submit name=Send value="Отправить">

В поле value допускается запись ax вместо ax.rpt. Порядок и место размещения поля document в форме значения не имеют. После нажатия Send NULLCG.CGI посылает серверу список из семи директив в одном *.in файле.

Извлечение значений полей. В first.rpt определяется переменная — комбинация IP-адреса и времени первого старта NULLCG.CGI:

ah=[ha]+'_'+[sf 0,98]

В следующей за AX.RPT форме значения полей читаются функцией [ged ...] (см. главу «Экстрапараметры и дисковая база»):

b=[ged ah+'.ax.Send']
b=[ged ah+'.ax.field1']

где ax — имя формы, Send, field1 — имена полей. Доступны и служебные ключи:

  • [ged ah+'.ax.N'] — сколько всего полей;
  • [ged ah+'.ax.1'] — имя первого поля, [ged ah+'.ax.2'] — второго и т.д.

18.6. Служебное поле nextform: запуск следующей формы

<input type=hidden name="nextform" value="y">

При нажатии Send на сервер уйдёт директива r y (выполни форму y). Поле может стоять в любом месте формы. Данные, только что введённые в предыдущей форме, в новой форме уже доступны через extrd.dat.

18.7. Служебное поле execute: директива серверу

Из любой формы можно отправить директиву ФБП:Серверу — зарегистрировать операцию, ввести новый субсчёт и т.д. (о директивах см. главу «Язык запросов к серверу»). Для этого служит скрытое поле execute.

Ключевая особенность: данные, введённые в ax.rpt, попадают в extrd.dat только после нажатия Send, поэтому извлечь их и подставить в директиву можно лишь в следующей форме (bx.rpt). Отсюда типовая цепочка из промежуточных форм: ax.rpt (ввод) → bx.rpt (показать введённые данные, предложить кнопку регистрации или «откат назад») → передача директивы и переход к следующей форме.

Наиболее распространённая ошибка — недостаточное количество ^^^ в поле value: директиву собирают в строковой переменной и выводят оператором printstr, который не добавляет ни пробелов, ни перевода строки:

b='"o ·Касса·расход··номер:·'+K+'"'
printstr '<input type=hidden name=execute value='+b+'>'

Обратите внимание на разделитель · — знак с кодом 250, стандартный разделитель в директиве o регистрации операции (см. главу «Язык запросов к серверу»).

Место поля execute в форме не имеет значения. Порядок директив в *.in файле всегда одинаков: сначала все [sed...], затем то, что предписывает поле execute, и наконец то, что nextform.

При успешной регистрации операции и запуске следующей формы сервер помещает в левом верхнем углу страницы уникальный код операции. Подавить его можно, введя в лист дерева видов операций псевдодокумент create document LINE0, где LINE0 — пустой файл.

18.8. Тэги <input> и <select>, собственные кнопки

Стандартный ввод-вывод ФБП (?, is, ir, im, iy, ia) в приложениях NULLCG.CGI не используется — он заблокирован. Вместо него — HTML-тэги <input> и <select> (они же служат для многострочного ввода).

Наряду со штатными кнопками Send и Reset допускаются собственные и графические кнопки:

<input type=submit name=Send value="Отправить данные">
<input type=reset name=Reset value="Очистить данные">

Графическая кнопка готовится заранее в виде файла (например, knob.gif) и подключается стандартными средствами HTML.

Избегайте большого числа таблиц и слишком больших таблиц на странице: загрузка таблицы из 10000 строк — тяжёлое испытание для браузера (по испытаниям руководства на P133/64 Мб с Personal Web Server и IIS), ощутимо «тормозит» и таблица в 2000 строк.

18.9. Проблемы Back/Forward и кэша браузеров

Клавиши Back/Forward. Пользователь может зарегистрировать операцию, вернуться кнопкой Back и нажать кнопки снова — и в журнале накапливаются «ненужные» повторные операции. Стандартных способов блокировать клавиши браузера нет. В исходных текстах Расчётного Пункта есть готовые процедуры для решения этой проблемы: serial (блокирование повторов), distiller и cleaner (чистка таких операций).

Кэш браузера. Включённый cache может помешать активизации CGI: браузер покажет старую страницу вместо нового запроса к серверу. Приём против этого — функция [sf 0,9], выдающая новую длинную строку при каждом вызове: вставленная в URL, она делает каждый запрос уникальным, и кэш не срабатывает.

18.10. Подключение изображений

Web-формы хорошо сочетаются с графическими операторами: форма строит диаграмму оператором makepng и вставляет её в страницу тэгом <img>. Чтобы каждый пользователь видел свой (а не чужой или устаревший) файл, имя файла получают функцией [unicfile], а старые файлы удаляют оператором deleteobsolete — подробно с законченным примером это разобрано в разделе «Уникальные имена файлов и уборка» главы «Графика в формах».

19. Производительность и отладка

Формы на языке ФБП выполняет виртуальная машина внутри сервера (в однопользовательских версиях — внутри программы). Обычно скорости хватает, но когда данных много — тысячи операций, длинная лента фактов, большая дисковая база — разница между «правильно написанной» и «просто написанной» формой измеряется минутами. Эта глава о трёх вещах: какие рычаги ускорения даёт система, как найти медленное место профилером и какие приёмы ускорения применять в первую очередь.

19.1. Опции и файлы-ключи, влияющие на скорость

В однопользовательских версиях поведение задаётся пунктами меню «Опции», а ФБП:Сервером управляют файлы-ключи — пустые (или с произвольным содержимым) файлы в каталоге данных. Важно само присутствие файла.

Все файлы-ключи (fastfact, fastged, fastrun, NOPROFIL, TA1, NOTAIL и др.) должны лежать в каталоге данных до старта Сервера — он замечает их только при запуске.

Опция «Быстрые факты» / файл-ключ fastfact ultraH от 7.01.99, Сервер 2.9H

Главный ускоритель форм, работающих с лентой фактов. Программа строит для ленты индексирующую структуру, и операторы search, news, total и др. ускоряются во много раз: вместо сканирования ленты выполняются «прыжки» по индексу. При индексировании учитываются и прототипы фактов, и значения их полей. Внешняя логика операторов не меняется — формы переписывать не нужно.

Цена: расход памяти на хранение фактов увеличивается (иногда в несколько раз), а оператор fact замедляется примерно в 2 раза — то есть пересчёт состояния становится медленнее. Если в файлах-коэффициентах есть ускоряемые search/news, выигрыш в формах обычно перекрывает потерю.

Включение: в однопользовательской ultraH — опция «Быстрые факты»; на Сервере (по умолчанию режим выключен) — создать файл fastfact в каталоге данных; при старте появится сообщение ...FastFactMode....

Файл-ключ fastged — ускорение [ged …] Сервер 2.33

Ускоряет функцию [ged …] в 10–20 раз за счёт зеркала данных из extrd.dat в оперативной памяти (размер зеркала в несколько раз меньше размера файла на диске). Включение — файл fastged в каталоге данных. Имеет смысл при формах, интенсивно читающих дисковую базу (см. главу «Экстрапараметры и дисковая база»).

Файл-ключ fastrun — приоритет Сервера Сервер 3.02

По умолчанию Сервер работает с приоритетом NORMAL_PRIORITY. При наличии файла fastrun приоритет повышается до HIGH_PRIORITY — Сервер получает больше процессорного времени по сравнению с другими программами на том же компьютере. В Linux-версии fastrun переводит ожидаемый приоритет (nice) в состояние «-10».

Файл-ключ TA1 — только два ta-факта на проводку Сервер 3.27

По умолчанию проводка между счетами, имеющими предков, порождает ta-факты и для предков. В присутствии файла TA1 создаются только два ta-факта — отражающие отношения между самими счетами. Это экономит память и значительно уменьшает время выполнения проводок (команд folio-177 виртуальной машины). Ограничение: системные формы, работающие в режиме TA1, должны быть встроены в Сервер — проверяйте применимость к вашей версии. В версии 3.27 режим работал с ошибкой; нормализован в 3.28.

Файл-ключ NOTAIL — без «хвостов» в формах Сервер 3.27

В присутствии файла NOTAIL результат формы не дополняется замечаниями о незавершённых вычислениях в прошлом. На замечания об ошибках в прошлом этот файл влияния не оказывает — ошибки будут показаны в любом случае.

Файл-ключ NOPROFIL — отключение профилера

Хронометрирующая часть профилера Сервера включена по умолчанию и расходует небольшую долю процессора. Если файл NOPROFIL есть в каталоге данных при старте, профилер работать не будет. Профилер также не работает, если ОС не разрешит таймер 1000 Гц (например, на медленных машинах). В Linux-версии NOPROFIL игнорируется (там профилер построен на счётчике тактов RDTSC).

Для отладки алгоритмов полезны ещё две опции однопользовательских версий:

  • Проводки в динамике. Включена — при показе проводок (F6 в режиме ввода операций) программа выполняет вычисления по алгоритмам листьев дерева и файлов-коэффициентов; выключена — показываются только тексты выражений и имена файлов, что существенно быстрее.
  • Быстрые формы. Опция однопользовательских версий (ускоряет функцию [op …]); в ФБП:Сервере включена навсегда. При профилировании с целью переноса выводов на Сервер эту опцию следует включать.

19.2. Кэширование результатов форм

Сервер умеет запоминать ответы на директиву R (выполнить форму): результат копируется в подкаталог cache каталога данных (cache\10000001 и т.д.), а ключом служит составной индекс — имя формы, пожелания клиента, имя пользователя, ответы на вопросы, контекст. Повторный вызов с тем же ключом отдаётся из кэша без выполнения формы. Если ответ устарел, в его конец добавляется символ с кодом 7 — клиенты показывают по нему предложение «Обновить». Возможность появилась в версиях Сервера 2.31–2.32.

Управление кэшем — файл cache\config.txt: первая строка задаёт режим, далее идут строки с именами форм:

Режим (первая строка config.txt)Смысл
NOКэширование выключено.
ALL- / ALL+Кэшировать все формы; + — с фоновой актуализацией.
THESE- / THESE+Кэшировать только перечисленные далее формы.
EXCEPT- / EXCEPT+Кэшировать все, кроме перечисленных.
Не включайте кэширование форм-историй вместе с фоновой актуализацией (режимы с +).

Посмотреть состояние кэша можно директивой H (требуется право U), очистить — директивой H- (запоминание ответов начинается заново):

Кэшированные вызовы:
10000003 (1) ( 53) SUPERVISOR··610A0L@@·R F0
10000004 (1) ( 50) SUPERVISOR··610A0L@@·R F2
10000005 (1) (  0) SUPERVISOR··610A0L@@·R F1
10000006 (1) (  0) SUPERVISOR··610A0L@@·R F1 ·23·
4 (вызовы) 329 (байты) 584 (тики)

В каждой строке: имя файла в каталоге cache; признак актуальности (1 — актуален, 0 — нет, 2 — в процессе актуализации); количество тиков при последнем выполнении запроса (в секунде примерно 18 тиков); сама строка запроса со служебными частями.

Увидеть, какие формы кэшируются и от чего зависят, помогает директива F+ — список форм с отметками:

A Ngscuf > *Это форма A
B Y..... > *Это форма B

Для формы A: кэш не включён (N; у B — включён, Y); далее буквы показывают, что используется в форме: g — [ged…], s — [sed…], c — [ct…], u — [user], f — [f1]/[f2]; скобками помечается зависимость от переменных MF, DF, DL, ML.

С версии 2.32 Сервер минимизирует ключ кэша: если форма не использует [ct…], информация о контекстах из ключа исключается. Но распознаются только строковые константы: x=[ct 'MODOKEY'] — правильно, а a='MODOKEY'; x=[ct a] — не распознается, и кэширование будет ошибочным! То же относится к [user], [f1]/[f2], MF/ML/DF/DL. Использование этих функций и переменных в FIRST.RPT считается использованием в каждой форме — минимизация становится невозможной вообще. Наконец, формы с [sed…] инвалидируют кэш форм, зависимых от [ged…].

19.3. Профилер ФБП:Сервера

Профилер — это измеритель того, куда уходит время. Он встроен в Сервер начиная с версии 3.03 и показывает распределение времени между фазами пересчёта состояния, между файлами-коэффициентами, между командами виртуальной машины и между строками исходного текста файла-коэффициента или формы.

Как устроен. Каждая команда виртуальной машины хранит номер строки исходника и счётчик. По тику таймера (1000 Гц) счётчик текущей команды увеличивается на 1 — то есть число тиков примерно равно суммарному времени выполнений команды в миллисекундах. Хронометрирующая часть включена по умолчанию; отключается файлом-ключом NOPROFIL (см. выше).

Директива % — общий отчёт профилера с 3.03

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

Расходы времени на пересчет состояния Сервера:
                                     всего,мс   %
Чтение файлов операций (*.f3p):        1510    3.4  #
Анализ фраз операций:                   355    0.8
Выполнение проводок:                   2791    6.4  #
Выполнение файлов-коэффициентов:      31291   71.4  #####################
Вычисление выражений в дереве:           51    0.1
Дескрипторы первичных документов:       110    0.3
Создание первичных документов:            0    0.0
Инициализация внутренних таблиц:       6567   15.0  ####
Индикация на мнемосхеме:               1136    2.6
Итого:                                43811  100.0
За эти 43811 мс было пересчитано 5165 операций.
Средняя скорость пересчета состояния Сервера: 118 оп/с.

Далее в ответе следуют таблицы «Расходы времени на выполнение файлов-коэффициентов» (колонки: файл, всего мс, сколько раз выполнялся, мс/раз, абсолютный и относительный проценты) и «Расходы времени на выполнение команд» ([GET], folio-177, push var, [SET], fact и т.д.). Из примера видно типичную картину: почти три четверти времени съедают файлы-коэффициенты — их и надо оптимизировать в первую очередь.

Директива % ИМЯ — профиль одного файла

% PUNRD показывает распределение времени по строкам исходного текста файла-коэффициента или формы — в процентах и миллисекундах, с «градусником» из #:

Временной профиль файла-коэффициента PUNRD
Количество выполнений: 2
На это количество выполнений потребовалось 15295 мс = 100%
              ~    ~ |0063 :setm
       2.4%   360    |0064 for im=1 to as   цикл по материалам
#      6.9%  1059    |0065 kt=[sa SKLAD,im]  обозначение материала
###### 33.9% 5183    |0066 s=[get *xt,kt+'rnorm']
#      5.0%   763    |0067 if s>0.001        применяется
              ~     9|0068 call primm
              ~     3|0069 endif
       1.0%   154    |0070 endfor
              ~    ~ |0071 return

Читается так: номер после | — строка исходника; слева — её доля и абсолютное время. Здесь одна строка 0066 ([get …] экстрапараметра) стоит треть всего времени — вот и узкое место.

Директива %- — обнуление счётчиков с 3.03; расширена в 3.08

Сбрасывает накопленные тики, чтобы измерить «чистый» эксперимент. Начиная с версии 3.08 %- очищает счётчики не только в файлах-коэффициентах, но и в отчётных формах и в first.rpt.

Тонкости чтения профиля:

  • Таймер 1000 Гц грубоват для быстрой виртуальной машины: форму надо выполнять достаточно долго или много раз. Если ни одна строка формы не набрала более 10 тиков, профилер вообще не показывает профиль такой формы. Приём — охватить профилируемый участок внешним циклом, чтобы форма многократно делала одно и то же.
  • Даже если в дереве несколько ссылок на один файл-коэффициент, в памяти размещается один его экземпляр: счётчики аккумулируют тики всех операций соответствующих видов. Поэтому профилер Сервера не рисует дерево видов операций с процентами (в отличие от однопользовательских версий).
  • Время по строке call показывает только стоимость самого вызова, а не вызываемой процедуры — её строки учитываются на своих местах.
  • FIRST тоже хранится в одном экземпляре: его процедуры собирают тики от всех форм. Надёжная методика измерения формы, вызывающей процедуры FIRST:
M ON      (монополизировать доступ к Серверу)
%-        (обнулить счетчики)
R FORM    (выполнить форму, лучше несколько раз)
% FORM    (профиль формы)
% FIRST   (профиль FIRST)
M OFF     (отказаться от монополии)

Пользовательская область *{{{ / *}}} — «секундомер», который вы размечаете сами. Спецкомментарий *{{{ открывает область, *}}} закрывает; профилер измеряет время между прохождениями этих точек:

********************
*{{{
call PR
*}}}
stop
:PR
for i=1 to 1000000
[a i,i*i]
endfor
return
********************

После R A и % A вы получите профиль по строкам, а время процедуры PR можно узнать двумя способами: суммировать строки профиля (1439 + 7637 + 759 = 9835 мс) или посмотреть блок в ответе на директиву %:

Есть область профилирования, определенная пользователем:
В эту область попало тиков таймера, мс: 9835
*{{{ (начало области) было пройдено, раз: 1
*}}} (конец области) был пройден, раз: 1
Начинать и заканчивать область необязательно в одном rpt-файле: можно поставить *{{{ в начале листа дерева в одном файле-коэффициенте, а *}}} — в конце листа в другом, и измерить время пересчёта операций одного вида. А область, которую начали и никогда не заканчивают, превращается в «часы», отсчитывающие время с момента прохождения начала.

19.4. Профилирование в однопользовательских версиях

16-разрядная ФБП для DOS. Профилер не работает на процессорах ниже Pentium 100 и запускается ключом командной строки p:

fine p

Успешный старт возможен только в чистом DOS (не в окне DOS под Windows); компьютер характерно пощёлкивает, программа работает заметно медленнее. Принцип: таймер ускоряется до 10 кГц, обработчик прерываний определяет, какой участок дерева или формы застало прерывание, и наращивает его счётчик. Как смотреть:

  • профиль дерева видов операций — после построения баланса перейти в дерево (Alt+F6): указаны проценты времени по ветвям при сбивке баланса;
  • профиль файла-коэффициента — поставить на него селектор и нажать F4 (весь файл = 100%);
  • профиль формы — после выполнения формы вернуться в меню форм (Esc) и нажать F4.

Достоверность тем выше, чем больше тиков приходится на время выполнения: форма длительностью 1 с и длиной 100–1000 строк даёт ~10000 тиков — надёжный результат. Быструю форму оборачивайте во внешний цикл. Результаты можно экстраполировать на ФБП:Сервер, но при опытах включайте опцию «Быстрые формы» (в Сервере она включена навсегда).

ФБП/ultraH-32 (от 15.10.99) содержит постоянно действующий профилер — выключить его невозможно. Он опрашивает счётчик тактов процессора (сотни МГц) при выполнении каждой команды виртуальной машины, поэтому точность в статистике — до 0,1 мкс, и достаточно одного выполнения формы там, где Серверу нужны тысячи тиков. Как смотреть:

  • профиль видов операций: построить оборотную ведомость (Alt+F3), перейти в дерево (Alt+F6), выбрать пункт меню «Профиль видов операций»; у файла-коэффициента нажать F4;
  • профиль отчётной формы: выполнить форму один раз, раскрыть по Alt+F4, нажать F5;
  • профиль команд в файлах-коэффициентах: Alt+F3, пункт меню «Профиль команд в файлах-коэффициентах».

Пример профиля формы из трёх одинаковых операторов (одно выполнение, машина P133 MMX):

мкс
-------------------
68.5% 16.5| A=1
16.5%  4.0| A=1
15.0%  3.6| A=1

Первая строка работала с данными из основной памяти, дальше проявился эффект кэша процессора. В профилере Сервера такой «разгон» не виден из-за усреднения по тикам 1000 Гц. При сравнении цифр помните: из-за разных компиляторов ultraH-32 в 1,5–3 раза уступает Серверу в скорости расчётов, так что профили переносятся качественно, но не количественно.

Файлы *.prf. Содержат количества времени, проведённые в строках формы, — побочный продукт профилирования. Их создают однопользовательские версии ultraH для DOS: 16-разрядная — только при запуске с ключом p, 32-разрядная — всегда. ФБП:Сервер файлов *.prf не создаёт вовсе. Удалять *.prf можно без всякого вреда.

19.5. Типичные приёмы ускорения форм

Прежде чем что-то переписывать, снимите профиль — почти всегда время сосредоточено в двух-трёх строках. Дальше действуйте по списку:

  • Работаете с лентой фактов — включите «Быстрые факты» (опция в ultraH, файл-ключ fastfact на Сервере). Поиск факта в ленте любой длины начинает занимать примерно одно и то же малое время (см. 19.1).
  • Сужайте поиск по датам. Оператор rewind facts range задаёт интервал дат для search, select, news, total, а с ultraH от 23.01.99 лента индексируется по датам и позиционирование выполняется за малое постоянное время. Измерение из документации: 100 повторений «rewind facts range 1.06,1.06 + подсчёт 10 фактов среди 200010» — примерно 0,2 с вместо ~22 с. Удобны переменные MF, ML, DF, DL (границы отчётного периода). Подробности — в главе «Лента фактов».
  • Заменяйте erase на nerase, где это эквивалентно: nerase сначала ищет последний подходящий факт (как news) и удаляет только его — такая замена сильно ускоряет пересчёт.
  • Выносите инварианты из циклов. Выражение после to/downto в for вычисляется на каждом витке; выгодно заранее присвоить его переменной (но помните: изменение этой переменной в теле цикла влияет на число витков). То же относится к «тяжёлым» функциям внутри тела цикла — если значение не зависит от итерации, вычислите его один раз до цикла.
  • Кэшируйте тяжёлые отчёты через cache\config.txt — повторный вызов формы с теми же параметрами отдаётся мгновенно (см. 19.2).
  • Интенсивно читаете дисковую базу — включите fastged (ускорение [ged …] в 10–20 раз).
  • Если тормозит пересчёт состояния, смотрите отчёт %: доля «Выполнение файлов-коэффициентов» обычно самая большая. Профиль конкретного коэффициента — % ИМЯ. Проверьте также, не тащит ли каждая проводка лишние ta-факты предков — файл-ключ TA1 (см. 19.1).
  • Для точного микрозамера пользуйтесь ultraH-32 (точность 0,1 мкс за одно выполнение) или пользовательской областью *{{{…*}}} на Сервере.
Форма, которая зациклилась или работает неприемлемо долго, прерывается директивой B (требуется право U) — это особенно полезно при отладке новых форм из клиента. О директивах см. главу «Язык запросов к серверу».

20. Примеры и приёмы

Эта глава — сборник готовых решений, проверенных многолетней практикой пользователей ФБП. Здесь не вводится ничего нового: все приёмы построены на средствах, описанных в предыдущих главах, — но собраны в законченные «рецепты», каждый из которых решает типовую задачу.

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

20.1. Сумма прописью с правильными падежами

Задача. В печатных документах (счетах, накладных) сумму требуется писать словами: не «1250.37», а «Одна тысяча двести пятьдесят рублей 37 копеек». Функция [wn …] строит числительное (см. главу «Функции: строки, даты, математика»), но не склоняет слова «рубль» и «копейка» — это приходится делать самим.

Идея. Падеж определяется последней цифрой числа, поэтому вместо точных сравнений используются диапазоны in с небольшими погрешностями (так надёжнее при вещественной арифметике). Подпрограмму помещают в first.rpt (см. главу «Файлы форм и общие файлы»), чтобы она была доступна всем формам. Вход — сумма в переменной S, выход — строка в переменной wn.

:wnf
kp=[tr [ce S]+0.5];rb=[tr S];kc='00';ss=[wn rb] коп.руб.коп.симв.руб.прописью
if ~rb ss='Ноль ';endif сумма меньше рубля
if kp in 9.999..99.999 kc=[sn kp,2,0]
elseif kp in 1..9 kc='0'+[sn kp,1,0] копейки символьно
else endif
ru='рублей ';cs=[tr [ce rb/100]+0.5] падежи рубля
if cs in 0.9999..1.0001 ru='рубль '
elseif cs in 1.999..4.0001 ru='рубля '
elseif [ce cs/10]/10 in 0.9999..1.0001 ru='рубль '
elseif [ce cs/10]/10 in 1.9999..4.0001 ru='рубля '
else endif
ko='копеек'
if kp in 0.999..1.001 ko='копейка' падежи копейки
elseif kp in 1.999..4.001 ko='копейки'
elseif [ce kp/10]/10 in 0.999..1.001 ko='копейка'
elseif [ce kp/10]/10 in 1.999..4.001 ko='копейки'
endif
wn=ss+ru +kc +' '+ko сумма прописью
return wnf

Пояснения для новичка. kp — копейки (дробная часть, округлённая до целого), rb — рубли, kc — копейки двумя знаками («05», «37»). Приём [ce cs/10]/10 выделяет предпоследнюю цифру: он нужен, чтобы числа 11–14 («одиннадцать рублей», а не «рубль») попали в общий случай. Строки-комментарии справа от операторов ФБП просто игнорирует. Вызов из формы:

S = [ea 50]
goto wnf
В кассе: ^^^^^^^^^^^^^^^^^^^^^^^^^wn
Подпрограммы в first.rpt должны быть компактными и быстрыми: этот файл участвует в пересчёте баланса, и каждый лишний оператор там сказывается на скорости всей системы.

20.2. Почему нельзя зацикливать формы с вопросами

Задача. Хочется, чтобы форма-ввод спрашивала данные снова и снова, пока пользователь не закончит, — например, при вводе длинной накладной. Так делать нельзя, и вот почему.

Как сервер задаёт вопросы. Встретив в форме вопрос (?x, [is…], [ir…] и т.п.), на который ещё нет ответа, сервер прерывает выполнение и возвращает клиенту сообщение вида Q-?:введи x. Клиент спрашивает пользователя и запускает форму заново уточнённой директивой R Z ·123· (· — знак с кодом 250). На следующий вопрос директива станет R Z ·123·qwerty· — и так далее: форма каждый раз выполняется с начала, а все накопленные ответы передаются в строке директивы.

Подводные камни. Длина строки директивы ограничена 255 символами: у зацикленной формы цепочка ответов растёт, пока не упрётся в предел, после чего осмысленно задаётся только последний вопрос. Кроме того, на каждый новый вопрос сервер заново пробегает все накопленные ответы и выполняет каждый встречный [sed …] — это 10–50 мс и заметная активность диска на каждый повтор.

Правильные решения. Во-первых, не зацикливайте формы с вопросами — лучше ассоциировать с формой кнопку и запускать её заново. Во-вторых, концентрируйте все [sed …] в заключительном блоке формы. Вместо:

:M
?x введи x
[sed ...]
?y введи y
[sed ...]
goto M

пишите:

?x введи x
?y введи y
* здесь все [sed] сконцентрированы в последнем блоке:
[sed...]
[sed...]
Подробно механизм вопросов и директива R разобраны в главах «Функции: ввод и оформление» и «Язык запросов к серверу».

20.3. Эквивалент формы-истории: факты ta и [jf]

Задача. В отчёте по проводкам нужен доступ к компонентам самой операции — комментарию, номеру документа (первые шесть символов комментария) — не создавая параллельных собственных фактов.

Идея. Если включено запоминание ta-фактов, простая проводка порождает факт ta dt,sd,sk,kt,ea,m3, где dt — счёт/субсчёт дебета, sd и sk — суммы дебета и кредита, kt — субсчёт кредита, ea — результат проводки в дебетуемом счёте, m3 — штамп операции. Найдя факт, по штампу достаём саму операцию функцией [jf …]. Схема поиска:

rewind facts range t1,t2
search ta x1,x2,x3,x4,x5,x6

Здесь x1..x6 — либо конкретные значения полей, либо ?un (поле не важно, но вернуть в переменную un), либо ?? (поле не важно). При [success] = 1 компоненты операции отыскиваются так:

J = [jf 'o key=' + m3]

Пояснения для новичка. [jf] выполняет директиву J (показ операции, см. главу «Язык запросов к серверу») и возвращает первую строку ответа; остальные строки доступны через дисковую базу: [ged 'jf.N'] — число строк, [ged 'jf.1'], [ged 'jf.2']… — сами строки.

Плюс по сравнению с формами-историями: не нужно фильтровать «пустые» операции (null) и границы групп { } — в ленте фактов есть только реальные проводки. А помещение штампа операции в первые 8 байт строки отчёта даёт выход в журнал операций по Enter в клиенте.

Текст операции в extrd.dat хранится кусками примерно по 80 байт, а сам текст может превысить 256 байт — одной строковой переменной мало: собирайте куски конкатенацией в массив или в резерв из 3–4 переменных.

20.4. «Выделение» операции в журнале: переопределение __SYS000

Задача. Клиент не передаёт в директиве R код операции, на которой стоит селектор журнала, — а форме этот код нужен (например, чтобы построить отчёт «от этой операции»).

Идея. Системная форма показа проводок __SYS000.RPT вызывается по F6 — то есть именно тогда, когда пользователь «смотрит» операцию. Подменим её своей копией, которая попутно запомнит код операции в дисковой базе (приём «нажатие F6 = выделение операции»). Шаг 1 — сохраните следующую форму под именем __SYS00.RPT:

* Список проводок по операции
? O операция
OO=[strip O]
u=[user];[sed u+'+stamp3',OO]
====== ^^^^^^OO ======
Дебет Кредит Сумма
rewind facts
X=0
:LOOP
search ta ?A, ?S, 0, ?K, ??, O
if [success]=0 goto QUIT;endif
AS=0
if [pa *A]='План' AS=[as *A];endif
if AS = 0
X=X+1
S=[sn S,13,2]
^^^^^^^^A ^^^^^^^^K ^^^^^^^^^^^^S
endif
goto LOOP
:QUIT
if X = 0
Нет ta-фактов для этой операции
endif

и добавьте в users.rpt переопределение системной формы:

__sys000 = ___sys00

Шаг 2 — в целевой форме читайте «выделенную» операцию:

o = [ged [user]+'stamp3']

Пояснения для новичка. Индекс дисковой базы составлен из имени пользователя ([user]), поэтому разные пользователи не мешают друг другу. Поиск search ta ?A, ?S, 0, ?K, ??, O берёт только те ta-факты, где сумма дебета нулевая (поле 3 = 0), — это вторая половина пары фактов проводки; проверка через [pa *A] отсекает предков счетов, оставляя строки самого плана счетов. Таким же приёмом, например, имитируют режим «Баланс до текущей» из однопользовательских версий.

20.5. Многоуровневая оборотная ведомость (факты father и pa)

Задача. Нужна оборотная ведомость по синтетическим счетам произвольной глубины — «от двух до шести» уровней. Примем соглашение: счёт-предок получается из потомка отрезанием последнего знака (5 – 50 – 500 – 5000 – 50000 – 500000). Все реальные счета — счета первого уровня ФБП, а иерархию храним фактами:

fact father 1 '5' '50'
fact father 2 '50' '500'

Идея. Три отдельных приёма: генерация фактов-«родителей» по префиксам имён счетов; наполнение агрегатных фактов pa по ta-фактам; вывод ведомости «по одному разу на счёт» через экстрапараметры фиктивного счёта % (см. главу «Экстрапараметры и дисковая база»). В PROTO.RPT нужны прототипы father %, %, % и pa %, %, %, %, %, %.

Построение иерархии (форма H.RPT):

* Построение иерархии
array %
p='План'
for i=1 to [as *p]
rewind facts
n=[sa *p,i]
if [length n] = 6
n1=[cp n,1,[length n]-1]
fact father 5, n1, n
n2=[cp n,1,[length n]-2]
news father 4, n2, n1
if [success] = 0
fact father 4, n2, n1
endif
... (аналогично n3, n4, n5)
endif
endfor
$=0

Обратите внимание на пару news + fact: сначала ищем факт, и только если его нет — создаём. Это защита от дубликатов при повторных запусках.

Наполнение — по каждому ta-факту генерируются pa-факты для всех предков (структура как у ta, первое поле — номер уровня):

:loop
search ta ?x1, ?x2, ?x3, ?x4, ?x5, ?x6
if [success]
news father 5, ?y1, x1; news father 5, ?z4, x4
fact pa 5, y1, x2, x3, z4, x5
news father 4, ?y2, y1; news father 4, ?z5, z4
fact pa 4, y2, x2, x3, z5, x5
... (уровни 3,2,1)
goto loop
endif

Ведомость по уровню N — обход pa-фактов с агрегированием total, а отмечание уже выведенных счетов — экстрапараметром счёта %:

array %
:loop
search pa N, ?x, ?d, ?k, ?y, ?zz
if [success]
if [get %,x]=0
[set %,x,1]
d=0; k=0; z=0
total pa N, x, ?d, ?k, ??, ?z
b= z - d + k
^^^^^^^^^x ^^^^^^^^^^^^^^b ^^^^^^^^^^^^^^d ^^^^^^^^^^^^^^k ^^^^^^^^^^^^^^z
endif
goto loop
endif

Пояснения для новичка. Приём if [get %,x]=0 … [set %,x,1] — универсальный способ пройти по списку «по одному разу на каждый ключ»: фиктивный счёт % здесь играет роль набора отметок в оперативной памяти. Остаток считается как «сальдо − дебет + кредит» (b = z - d + k). Красивое оформление такой ведомости даёт средство директив N: в Windows-клиенте (см. главу «Директивы N:»).

20.6. Партионный учёт: всё о «куске» одним оператором news

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

Идея. Объявим в PROTO.RPT прототип факта (см. главу «Лента фактов»):

Марка провода % длина куска % носитель % код операции %

Когда пользователь выбрал строку в форме и стал известен код операции оприходования O, вся информация о куске извлекается одним оператором:

news Марка провода ?M длина куска ?L носитель ?N код операции O

После успешного поиска M — марка, L — длина куска, N — носитель. Операции отпуска снабжаются фактами вида fact Отпущено GAAA-017 покупателю М дата …, и по ним строится накладная.

Форму с таким выбором нельзя использовать в @-обращении в Windows-клиенте: @-обращение не решает задачу многострочного ввода. Вводите операции по одной, а отчётность стройте отдельными формами по ленте фактов.

20.7. Динамические шаблоны поиска: {}-способ и `-способ

Задача. Шаблон поиска в search/news обычно жёстко зашит в форму: порядок полей фиксирован, неважные поля помечаются ??. А если пользователь сам хочет выбрать, по какому полю искать?

Идея (требуется Сервер 3.07 и выше). В шаблоне допустимы пары {номер_поля, значение}; поля, не охваченные парами, считаются ??. Запись

search Товар {3,X} {5,Y}

эквивалентна search Товар ??, X, ??, Y. Можно обойтись и вовсе без прототипа: news {X,Y}, а news {1,'Товар'} {X,Y} — то же, что news Товар {X,Y}. Главное: номера полей — любые выражения, поэтому шаблон собирается на лету из ответов пользователя:

? X по какому полю искать
? Y что искать
news Товар {X,Y}
if [success]
нашли: ^^^^^^^^^^^[fact_f 2]
endif

Ещё один приём — `-способ (знак `, код 96): `выражение в шаблоне вычисляется, и результат подставляется как литерал шаблона. Этот способ подходит и для total (куда суммировать — есть), тогда как {}-способ с total работает только для [found].

В паре с динамическими шаблонами удобна функция [fact_f i] — i-е поле последнего найденного факта ([fact_f 0] — число полей, [fact_f 1] — первое слово прототипа). Типовой обход факта с неизвестной заранее структурой:

news Товар ??,??,??,??
if [success]
for i=1 to [fact_f 0]
^^^^^^^^^^^^[fact_f i]
endfor
endif
[fact_f] осмыслен только при [success] = 1 — всегда проверяйте результат поиска перед обращением к полям факта.

20.8. Каскадные @-обращения: [CT]

Задача. При вводе операции пользователь сначала выбирает фирму, потом — только из счетов этой фирмы, потом — только из товаров выбранного счёта. Каждый следующий выбор должен зависеть от предыдущего.

Идея. Функция [CT s] с 2.02 возвращает первое слово строки, выбранной в предыдущем @-обращении к счёту s в текущем листе дерева; если @-обращений к этому счёту не было — пустую строку. Следующий пункт дерева операции (см. главу «Виды операций и проводки») строит свой список выбора, исходя из значения [CT …] предыдущего пункта: фирма → её счета → товары по счёту.

Тот же приём «первое слово выбранной строки» работает и в ветвях-вопросах с уточнением ·R имяформы ·ответы·: пользователь выбирает строку из отчёта формы, а в ответ ветви попадает первое слово этой строки.

20.9. Сортированный обход субсчетов: # счёт sort выражение

Задача. Цикл # счёт … # обходит субсчета в порядке списка счетов (см. главу «Операторы»). А если в отчёте их нужно упорядочить, скажем, по наименованию или по остатку?

Идея. Ключевое слово sort с выражением задаёт порядок обхода — субсчета перебираются по возрастанию значения выражения:

# 70 sort [n2 #]
n=[na #]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^n
#

Здесь субсчета счёта 70 печатаются в алфавитном порядке наименований. Обход по остаткам — sort [ea #]. Выражение вычисляется для каждого субсчёта, и в нём доступен текущий субсчёт #.

20.10. Подпрограммы с параметрами и функции пользователя

Задача. Код формы разрастается: одни и те же фрагменты повторяются, переменные подпрограмм портят переменные вызывающей формы. Хочется настоящих функций — с параметрами и возвращаемым значением.

Идея (требуется версия 4.00, ultraH-II; см. главу «Операторы»). Три средства:

  • local переменная {, переменная} — локальные переменные подпрограммы, уничтожаются при return и не затрагивают одноимённые переменные вызывающей программы:
A=5
call P
^^A
stop
:P
local A,B
A=1; B=2
^^A ^^B
return

Эта форма печатает 1 2 (внутри подпрограммы), затем 5 — внешняя переменная A сохранилась.

  • Параметры подпрограмм передаются «по значению»: вызов call SUM(1,2), метка с формальными параметрами :SUM(a,b):
call SUM(1,2)
stop
:SUM(a,b)
local c; c = a+b
^^^^^c
return
  • Функция пользователя возвращает значение оператором return (выражение), а вызывается как [:метка] или [:метка параметры]. Рекурсия допустима:
x=[:max 3,5]
stop
:max(x,y)
if x > y return (x); else return (y); endif
x=[:factorial 5]
stop
:factorial(n)
if n=0 return (1); else return (n*[:factorial n-1]); endif

Вторая форма вычислит 120. Общие для всех форм подпрограммы и функции выносят в LIBRARY.RPT, а с версии 4.21 — и в файлы *.lib рабочего каталога (их можно защитить шифрованием Enigma).

20.11. Рабочие приёмы с массивами

Задача. Массивы (см. главу «Массивы») — главный контейнер для списков внутри формы. Несколько приёмов экономят десятки строк кода.

Групповое присваивание. Начальные значения заносятся одним оператором:

[m 1,'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']

Текущая длина. [m 0] возвращает число элементов массива — удобно для обхода без счётчиков-флагов. Индексы допустимы от 1 до 1000000 и округляются до ближайшего целого, так что [m i] и [m [tr i]] — один и тот же элемент.

Сортировка. sort array a сортирует массив «на месте». Вариант sort array a,b массив A не меняет, а в B записывает новые номера: если [a 1] после сортировки оказался пятым, то [b 1] = 5. Это позволяет отсортировать один массив, а переупорядочить по тем же номерам сколько угодно других — например, отсортировать суммы и тем же порядком вывести наименования.

sort array a,b
for i=1 to [a 0]
^^^^^^^^^^[n [b i]] ^^^^^^^^^^^^^^[a [b i]]
endfor

Очистка. array a,b,c обнуляет массивы с освобождением памяти; при старте формы это выполняется автоматически для всех 26 массивов.

Массив и одноимённая переменная — разные объекты: m и [m 1] не связаны. После завершения формы сохранность массивов не гарантируется. В разделах PROLOG и EVERYDEBET форм-историй массивы запрещены (в EPILOG — можно).

20.12. Вывод в одну строку: знак \ и операторы через ;

Задача. Форма должна печатать заранее неизвестное число полей подряд — например, список значений через пробел — а каждая форматная строка формы обычно завершается переводом строки.

Идея. Знак \ в конце выводимой строки подавляет перевод строки — следующая порция вывода продолжит ту же строку отчёта:

for i=1 to 10
^^i \
endfor

получится 1 2 3 4 5 6 7 8 9 10. Пустая строка формы, наоборот, просто переводит строку — ею «закрывают» накопленный вывод после цикла.

Попутно полезное сокращение: несколько операторов в одной строке записываются через ; (A=1; B=2), а в старом условном операторе ! после условия можно дать сразу цепочку операторов:

! a > b x=1; y=2; z=3

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

20.13. Картинки из формы: makepng, [unicfile] и deleteobsolete

Задача. Отчёт должен содержать диаграмму — столбики, линии, «торт», — а для Web-отчёта (см. главу «Web-приложения») — картинку, которую браузер сможет показать.

Идея (требуется Сервер 3.22 и выше; см. главу «Графика»). Рисунок строится в массиве: точка (x,y) — элемент (y-1)*w+x, координаты от 1. Заполненный массив сохраняется оператором makepng в PNG-файл. Для Web-страниц важны два помощника: [unicfile] даёт уникальное имя файла (чтобы параллельные запросы и кэши браузеров не мешали друг другу), а deleteobsolete маска, n удаляет файлы по маске старше n секунд — иначе каталог картинок растёт бесконечно. Типовая форма:

u=[unicfile]; e='.png'
f0='c:\webshare\wwwroot\dynamic\' + u + e
f1='c:\webshare\wwwroot\dynamic\*' + e
f2='http://localhost/dynamic/' + u + e
deleteobsolete f1,1000
... заполняем массив a ...
makepng a,100,100,f0
printstr '<img src=' + f2 + '>'

Пояснения для новичка. Рисуют операторы drawpixel (точка), drawline (линия), drawbar (закрашенный прямоугольник), drawtxt (текст), drawarc (дуга/сектор), fillarea (заливка области). Цвет — строка из четырёх знаков Red/Green/Blue/Alpha, например красный: [ch 255]+[ch 0]+[ch 0]. Маска для deleteobsolete допускает только расширения .PNG/.GIF/.HTM — .* не допускается.

На базе этих примитивов построены и готовые библиотечные процедуры: pie — объёмный «торт», разрезанный на куски (в массиве V — проценты, в C — цвета, в D — вынос кусков от центра), и draw3dplot — трёхмерная поверхность функции (массив значений + вектор на глаз наблюдателя; внутри используются lsolve и sort array для правильного закрытия невидимых частей).

20.14. Линейная алгебра: [det], lsolve, invertm

Задача. В расчётных формах встречаются системы линейных уравнений, определители, обратные матрицы — от плановых моделей до межфилиальных перекрёстных расчётов.

Идея (требуется Сервер 3.22 и выше). Матрица n×n хранится в массиве построчно: элемент (i,j) — это [a (i-1)*n+j]. Дальше работают три средства:

СредствоЧто делает
[det a,n]Определитель матрицы n×n.
lsolve a,b,y,nРешение системы: a — матрица, b — правая часть, y — массив решения.
invertm a,c,nОбращение матрицы a в массив c (a и c могут совпадать).

Пример — решение системы 2×2 (2x + y = 5, x + 3y = 5):

[a 1, 2, 1, 1, 3]
[b 1, 5, 5]
lsolve a,b,y,2
if [y 1]='fail'
Решения нет
else
x = ^^^^^^[y 1] y = ^^^^^^[y 2]
endif

Пояснения для новичка. Если в матрице или правой части встретились строковые значения, [y 1] вернёт 'string'; при неуспехе (вырожденная матрица) — 'fail'. Всегда проверяйте результат, прежде чем печатать числа. Математические функции того же поколения — [abs], [sqrt], [fsin], [fcos], [arctan], [rad] (градусы в радианы), [dgr] (радианы в градусы) — описаны в главе «Функции: строки, даты, математика».

21. Приложения: ограничения, ошибки, версии

Эта глава — справочное завершение руководства. Здесь собраны количественные ограничения языка и файлов данных, сообщения об ошибках с пояснениями «что делать», краткая хронология развития языка по версиям и мини-глоссарий терминов ФБП, которые встречались по всему руководству.

21.1. Количественные ограничения

База знаний, с которой работают ФБП, включает: счета и начальные условия (acnt.a3p), дерево видов операций (cas.rul), отчётные формы и первичные документы (*.rpt), журналы операций (*.f3p), ленту фактов и экстрапараметры (facts.b, extra.b), дисковую базу (extrd.dat). Ограничения по ним сведены в таблицы.

Емкость основных файлов:

Файл / объектОграничение
*.f3p — журнал операций за месяцдо 32767 записей в месяц (ФБП для DOS, 16-разрядная); до 100000 записей (ultraH-32 для DOS, ultraH-II, ultraH-x64 для Windows x64); до 15000 записей (ФБП для Windows)
acnt.a3p — план счетов и субсчетовявно заданных ограничений нет; упор в память: у 16-разрядной ФБП для DOS — барьер 16 Мб ОЗУ (при большом плане включать опцию «Экономить память»); у ФБП для Windows опция «Экономить память» всегда включена, барьера 16 Мб нет; у ultraH-32 этой опции нет, барьер — до 2 Гб ОЗУ; ultraH-x64 преодолевает барьер 2 Гб (практически — до 32 Гб)
cas.rul — дерево видов операцийдо 15 ветвей первого уровня, у каждой — до 15 ветвей второго уровня и т.д.; вложенность до 12 уровней (в расчёт берутся и ветви дерева, и @-обращения в описании проводок); до 15 проводок в листе дерева; размер дерева — до 16000 строк (и в DOS-, и в Windows-версии)
extra.b, facts.b — экстрапараметры и лента фактовограничений нет: лента и индексы фактов, а также все экстрапараметры должны просто поместиться в ОЗУ
extrd.dat — дисковая базазависит только от свободного места на диске
*.rpt — формы, первичные документы, файлы-коэффициентыне более 500 файлов

Пределы длин строк и имён (вытекают из структур файлов cas.rul, *.f3p, acnt.a3p):

ЭлементПредел
Комментарий операции56 символов
Глубина вида операции12 уровней, каждый уровень — строка до 25 символов
Обозначение счёта10 символов (в версии 3.1 было: счёт — 6, субсчёт — 9)
Наименование счёта100 символов
Экстрапараметров у счётадо 9; наименование параметра — до 14 символов
Текст ветви или листа дерева50 символов
Текст баллона (подсказки к ветви)200 символов
Потомков у ветви дерева15
Метка в формеслово до 10 символов
Индекс массива1..1000000, округляется до ближайшего целого; массивов 26 (по числу латинских букв)
Точность чиселс версии 3.+ — до 15 разрядов без потери точности; сумма операции хранится как 8-байтовый double
В записи журнала *.f3p поле PosInDay принимает значения -1, 0, +1 — это позиция операции внутри одного дня (та самая перестановка операций клавишей F9). Поле DFN (индекс первичного документа *.txa) Сервером не используется — оно актуально только для однопользовательских версий.

21.2. Сообщения синтаксического анализатора и компилятора

Ошибки в формах и дереве ловятся на трёх уровнях. Во-первых, при включённой опции «Синтаксис подсвечен» анализатор непрерывно просматривает тексты и раскрашивает операторы, комментарии и ошибочные места; в редакторах места ошибок выделяются мерцающим свечением. Во-вторых, опция «Сначала все ошибки» включает полный анализ, включая проверку существования упомянутых счетов (без неё проверяются только скобки и запятые, а про отсутствующие счета вы узнаете лишь при компиляции или выполнении). В-третьих, компилятор дерева обнаруживает большинство формальных ошибок «на первой секунде» попытки построить баланс.

Ориентироваться в найденных ошибках помогают: в Windows-версии — клавиша «Где ошибка» / Alt+E (первое нажатие активирует окно с ошибкой, повторное может открыть окно с дополнительной информацией: например, при делении на нуль — сначала место в дереве, затем операцию); в DOS-версии — при построении оборотной ведомости (Alt+F3) несоответствующие дереву операции автоматически отыскиваются в журнале, на них ставится селектор с сообщением «Неправильно указан вид операции».

СообщениеПричина и что делать
ПУСТАЯ СТРОКА В ОПИСАНИИ ПРОВОДКИВ листе дерева видов операций и проводок есть пустая строка — уберите её.
НЕПРАВИЛЬНО ОПИСАНА ПРОВОДКАНеправильное описание проводки в листе дерева — проверьте синтаксис строки (дебетуемый, кредитуемый, коэффициент).
НЕПРАВИЛЬНЫЙ КОЭФФИЦИЕНТ В ОПИСАНИИ ПРОВОДКИЧисловое выражение коэффициента задано неверно — проверьте формулу.
@-ОБРАЩЕНИЕ К НЕСУЩЕСТВУЮЩЕМУ СЧЕТУ@-обращение ссылается на счёт, не описанный в списке счетов. Введите новый счёт и его субсчета.
@-ОБРАЩЕНИЕ К СЧЕТУ БЕЗ СУБСЧЕТОВ@-обращение должно ссылаться на счёт, имеющий субсчета: либо субсчета ещё не созданы, либо упомянут не тот счёт.
НЕИЗВЕСТНЫЙ (ИЛИ СОСТОЯЩИЙ ИЗ СУБСЧЕТОВ) ДЕБЕТУЕМЫЙ СЧЕТ / КРЕДИТУЕМЫЙ СЧЕТСчёт не описан в списке счетов либо состоит из субсчетов, а @-обращение не применяется.
НЕТ ОПИСАНИЙ ПРОВОДОК ДЛЯ ЭТОЙ ОПЕРАЦИИДля данного вида операции нет листа с описаниями проводок — добавьте лист в дерево.
НЕПРАВИЛЬНО УКАЗАН ВИД ОПЕРАЦИИВетвь дерева изменилась после того, как были указаны виды операций; отредактируйте операцию и заново укажите вид (или удалите и введите снова).
В ОПИСАНИИ ПРОВОДКИ НЕТ ССЫЛКИ НА ФАЙЛ-КОЭФФИЦИЕНТВозникает при редактировании дерева по F4, если текущее описание проводки не содержит коэффициента или содержит числовой коэффициент вместо имени файла.

21.3. Сообщения времени выполнения и аварийные коды

При ошибке во время выполнения формы однопользовательская версия переходит к редактированию текста: на экране сообщение, курсор указывает место ошибки. Оператор error в файле-коэффициенте прекращает его выполнение, выводит остаток строки как сообщение в красной полосе и переводит программу в режим операций с селектором на операции-источнике.

Часть «ошибок» языка — это не прерывания, а специальные строки-значения, которые функции возвращают вместо результата. Их нужно проверять в форме самостоятельно:

ЗначениеКогда возникает
'NO RU TXT'[ru …]: внешняя программа не создала файл RU.TXT (сама функция возвращает первую строку этого файла).
'NO ELEM'search: частей в образце больше, чем в найденном факте — избыточным переменным присваивается эта строка.
ОШИБКА TOTAL - сложение строки с числомtotal: при накоплении суммы встретилось сложение строки с числом.
NO DDE DATA (1) / (2)[DDErequest …]: отказ в передаче данных / прошло более 10 с ожидания.
#ERR … / #BUSYУдалённые формулы DDE-сервера: ошибка в выражении (например #ERR: Счет 52 отсутствует) / идут фоновые вычисления.
'string' / 'fail'lsolve, invertm: в матрице встретилось строковое значение / решить систему или обратить матрицу не удалось (результат — в первом элементе выходного массива).
-1 / '0'[fe s] возвращает -1, если файл не существует; [crc s] возвращает строку '0' для отсутствующего файла.

Отдельное сообщение ultraF — Вызов типа [..,будущее]. Оно возникает, когда файл-коэффициент вызывает функции остатков/оборотов с явным интервалом, уходящим в будущее относительно текущей проводки:

* файл-коэффициент — так делать нельзя
a=[ok 70,mf,ml]
$=a
* результат: сообщение "Вызов типа [OK ,.., будущее]"

Решение — использовать вызовы без явных интервалов: [ok xx], [ak xx], [mk xx] и их комбинации.

Аварийные завершения (коды исполняющей среды) — самый тяжёлый случай:

КодСообщениеПояснение
202Stack overflow errorПереполнение стека.
203Heap overflow errorПереполнение «кучи». Ошибки 202/203 возникают, как правило, на самой ранней фазе загрузки из-за недостатка оперативной памяти.
205Floating point overflowПри вычислениях получилось слишком большое число (переполнение плавающей точки).
207Invalid floating point operationНаиболее вероятная причина: аргумент функций tr или ro превышает 2147483648 (2³¹).
216General Protection faultОбращение к памяти, на которую нет прав. Вероятные причины: ошибка файловой структуры диска (пересекающиеся кластеры), искажение данных в *.f3p, acnt.a3p, cas.rul из-за внезапного выключения питания, вирусы.
Функции [tr x] и [ro x] принимают аргументы в диапазоне longint: превышение 2³¹ = 2147483648 приводит к аварийной ошибке 207, а не к контролируемому сообщению. Ошибка 216 чаще всего означает повреждение файлов данных после сбоя — это основание запустить процедуры восстановления, а не искать ошибку в форме.

21.4. Хронология новшеств языка по версиям

Язык ФБП рос постепенно, сохраняя совместимость снизу вверх. Таблица помогает ответить на вопрос «с какой версии доступна эта конструкция» и оценить переносимость старых настроек.

ВерсияГлавные новшества языка
2.2 (04.1992)@-обращения к счетам; упрощение дерева видов операций; до 100 счетов (260 со субсчетами).
2.+ (08.1992)Файлы-коэффициенты; функции АК, AD, МК, MD, ОР, АР, MP; развёрнутое сальдо в BD, BK, ED, EK; параметры $, $D, $К; предопределённые MF, MC, ML.
3.0 (12.1992)Ветвь-вопрос, *-обращение; формулы прямо в листьях дерева; тип счёта (активный/пассивный/активно-пассивный/внебалансовый); до 10000 операций в месяц, до 600 счетов и субсчетов.
3.1 (07.1993)Компилятор в промежуточный код вместо интерпретатора; первичные документы (создать документ); параметры счетов (псевдопроводки =, ==); циклы #; функции BP, EP, NP, N1, N2, NA, PA, AA, AS, WN, CE, TR, RO, DT, CO, RE, CH; операторы stop, error, метки, комментарий *; строковые литералы и конкатенация +; FIRST.RPT.
3.1/0.4 (11.1993)Функции диалога: IY, IM, IA, IR, IS; TM, SA, CP, SN.
3.+ (03.1994)До 9 параметров счёта (=1…=9); глубина дерева до 12; форматы *.f3p, acnt.a3p; 15 разрядов точности; произвольные границы отчётного периода; YC; функция DA.
3.+/h1Формы-истории: разделы PROLOG / EVERY DAY / EVERY OPERATION / EVERY DEBET / EPILOG; переменные ND, NK, SS, OT, CO, OC; функция DW.
4000 (06.1994)Опции «Экономить память», «Быстрые формы», баллонная помощь; горячие клавиши в дереве (горячая клавиша / hot key); функции RU, VL, HZ.
Windows (03.1995)Многооконность, «Где ошибка» (Alt+E); DDE-функции (DDEinitiate, DDEpoke, DDErequest, DDEexecute и др.), DDE-сервер FINWDDE; DLLcall.
ultraFЭкстрапараметры: set, plus, get, [type]. Механизм фактов: fact, rewind facts, search, news, total, erase, select, rewind buffer, sort buffer, rewind facts range; индикаторы [success], [found], [selected], [fact_y/m/d]; прототипы в proto.rpt; псевдопроводки продолжить если, break if; intsn, ex, ps, length.
ultraF 04.1997Дисковая база extrd.dat: [sed], [ged]; подпрограммы call/return; nerase; strip; ct, user, ta, stamp, ver.
ultraF 07.1997Уточнения ветвей-вопросов ·C·, ·N·, ·S· (символ · = код 250); переменные реальной даты YR, MR, DR, WR.
ultraG (12.1998)Условие — любое выражение (ложь = |x| < 0.000001); сравнения >=, <=, <>; операции &, |, ~, %; оператор проводки ▒ (код 177) в файлах-коэффициентах; циклы # *A и # счет sort; массивы (индексы 1..1000000, [a 0] — длина); array, sort array; операторы if/elseif/else/endif, for/to/downto/endfor, while/endwhile; несколько операторов через ;; вызов функции без присваивания; \ в конце строки; отступы перед операторами.
ultraH (01.1999)«Быстрые факты» (индексация ленты); массивы запрещены в разделах PROLOG и EVERY DEBET; индексация ленты фактов по датам (rewind facts range за постоянное время); запрет присваиваний YC, MC, DC, YR, MR, DR, WR.
ultraH-32 (10.1999)Постоянный профилер (точность 0,1 мкс); до 100000 операций в месяц; функция [jf …]; опция [USER]/NOSTAMP — штампы уникальных кодов.
ultraH-32 (04.2000)Файл-ключ FREE-AT; спецкомментарии *.on/*.off/*,on/*,off/*.,df для [sn …]; оператор printstr; [ha], [sf 0,98/99/9].
ultraH-32 (03.2001)printstr … to file; renamefile, deletefile, loaddir, loadtxt, loadbin; функции fe, tf, uf, pw, crc, hc.
ultraH-32 (05.2001)Графика из массивов: makepng, drawpixel, drawline, drawbar, drawtxt, drawarc, fillarea; unicfile, deleteobsolete; математика abs, fsin, fcos, arctan, sqrt, rad, dgr; перекодировки win/dos; линейная алгебра det, lsolve, invertm.
ultraH-32 (06.2001)showimg — показ изображения из массива (только в ultraH-32; на Сервере — makepng).
ultraH-II (10.2004)local — локальные переменные; параметры подпрограмм call МЕТКА(…) ↔ :МЕТКА(…); пользовательские функции return (выражение) и вызовы [:метка …] (рекурсия допустима); библиотека LIBRARY.RPT.
ultraH-x64 (07.2005)64-битная версия (преодолён барьер 2 Гб ОЗУ); языковых изменений нет.
Текущую версию программы можно узнать из формы функцией [ver], а версию и модификацию Сервера — директивой V (см. главу «Язык запросов к серверу»).

21.5. Мини-глоссарий терминов ФБП

ТерминЗначение
СчётУзел плана счетов (файл acnt.a3p): обозначение до 10 символов, наименование до 100, признак актив/пассив, начальный остаток, до 9 именованных параметров. Может иметь субсчета.
СубсчётСчёт, вложенный в другой счёт (объект учёта); глубина вложенности не ограничена явно. Обращение из проводок — через @-обращения (см. главу «@-обращения и контекст»).
Вид операцииПуть от корня дерева cas.rul к листу: цепочка ветвей (до 12 уровней, каждый уровень до 25 символов). Определяет, какие проводки будет выполнять операция.
ОперацияЗапись в журнале *.f3p: вид операции + дата + сумма + комментарий (до 56 символов). Регистрация операции запускает выполнение проводок её вида.
ПроводкаСтрока листа дерева: дебетуемый счёт, кредитуемый счёт и коэффициент (формула или файл-коэффициент), вычисляющий сумму. Существуют и псевдопроводки — присваивания параметрам (=, ==, …), break if, продолжить если и др.
ФормаПрограмма на языке ФБП в файле *.rpt: отчётная форма, форма первичного документа, файл-коэффициент или форма-история (H-*). Выполняется сервером.
ФактЗапись из одной или нескольких безымянных частей (число или строка), созданная оператором fact в файле-коэффициенте. Факты попадают в общий список facts в порядке операций и несут отметку даты операции; при удалении операции вычёркиваются. Читаются операторами search, news, total и др. (см. главу «Лента фактов»).
ta-фактФакт, который Сервер создаёт сам для запоминания детальной информации о выполненных проводках: на каждую проводку — два факта (для дебетуемого и кредитуемого счёта: сумма, корреспондент, остаток после проводки, штамп операции). Даёт возможность видеть проводки операций и строить оборотные ведомости за любой период. Запоминание можно включать не для всех месяцев; проверка из формы — функцией [ta m1,m2]. Потребность в ОЗУ при этом существенно растёт; извлечь значения параметров через ta-факты нельзя.
ЭкстрапараметрДополнительный именованный параметр счёта/субсчёта, числовой или строковый, сверх штатных девяти. Число экстрапараметров не ограничено; меняются функциями [set]/[plus] (только в листьях дерева и файлах-коэффициентах), читаются [get]; хранятся в extra.b (см. главу «Экстрапараметры и дисковая база»).
Каталог обменаОбщий каталог, через который клиент и сервер обмениваются файлами запросов и ответов (*.req/*.in от клиента, *.ans/*.out от сервера) — сетевые протоколы не используются. С версии 3.23 возможны персональные подкаталоги обмена для пользователей.
ШтампМетка, которой Сервер (или однопользовательская версия с опцией [USER]) снабжает каждую зарегистрированную операцию: имя пользователя, время регистрации и уникальный код. Читается из файла-коэффициента функцией [stamp n] (1 — имя, 2 — время, 3 — код).
Уникальный кодНе повторяющийся код операции вида CAAA-170: буква — месяц, три буквы после неё и три цифры после дефиса — порядковый номер. По коду операцию можно найти в журнале (директива J, функция [jf]) и отличить перебитую операцию от исходной.
Термины, относящиеся к конкретным темам (лента фактов, директивы сервера, экстрапараметры, первичные документы), подробно разобраны в профильных главах: 8, 13, 14, 16. Приёмы повседневной работы и готовые решения — в главе «Примеры и приёмы».