d9e5a92d

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА

Так же как и при расчете зарплаты, мы создадим процедуру ЛистокСотрудника, формирующую расчетный листок (РЛ) для одного сотрудника, и будем использовать ее нужное число раз, когда задача решается для выбранного подразделения.

Вывод РЛ будем осуществлять в специально созданную в форме списка ЖЗ таблицу, которой дадим имя РЛ (рис. 7.79).




1 2 3 4 s
Заголовок 1 Расчетный листок
2 Предприятие [Константа.Название0рганиэации[>
3 «"Подразделение * ¦ Объект Родитель ¦ Обь?кт.Наименование»
4 «Табельный номер" ¦ Обьект.Код ¦Оклад" ¦ окл ¦¦руб"»
твбЗаг 5 «?(начУд = 1, "Начисления". "Удержания")»
9 Мес. "Вид" ¦ ?(начУд= 1 ."начисления", "удержания") Сумма Часы
Расчет 7 <мес> «ВР» « рез> «часы»
Пусто 1
сНач 9 Остаток прошлого месяца сальдоНач»
Итог 19 Начислено <нач>
'11 Удержано «УД*
12 Полагается к выплате: <вып>
13 Перечислено в банк «вБанк»
сКон 14 Остаток на конец месяца «сальдоКон»
t~~] Диалог 1 f J Монель / А]РЛ * ~f
Рис. 7. 79. Образец для расчетного листка

Из образца видно, что для вывода нам понадобятся переменные окл, мес, ВР, рез, часы, начУд, нач, уд, вып, вБанк, сальдоНач и сальдоКон. Их смысл разъясняет рис. 7.79.

Алгоритм получения РЛ следующий:

1. Вывести заголовок РЛ и заголовок таблицы начислений.

2. Сформировать списки тЗначНач и тЗначУд с начислениями и удержаниями сотрудника, попутно вычисляя значения переменных нач, уд, вып, вБанк, сальдоНач и сальдоКон.

3. Вывести заголовок таблицы начислений, а затем, используя таблицу тЗначНач, и сами начисления.

4. Вывести заголовок таблицы удержаний, а затем, используя таблицу тЗначУд, и сами удержания.

5. Вывести итоговую часть РЛ.

Этот алгоритм реализует процедура ЛистокСотрудника, вызываемая из процедуры ПечатьРЛ. После программ на рис. 7.80 приведен пример сформированного РЛ.

процедура СоздатьТЗнач(тЗнач) далее // Вспомогательные процедуры формирования РЛ процедура НовСтрокаВТЗнач(тЗнач, мес) далее процедура ВыводНачУд(табл, тЗнач, начУд) далее

// Процедура ЛистокСотрудника формирует РЛ одного сотрудника // Поскольку процедура присутствует в модуле формы списка ЖЗ,

// то все методы ЖР вызываются без префикса процедура ЛистокСотрудника(Объект, табл, тЗначНач, тЗначУд) перем нач, уд, вып, вБанк, сальдоНач, сальдоКон, мес;

// Инициализация переменных

нач = 0; уд = 0; вып = 0; вБанк = 0; сальдоНач = 0; сальдоКон = 0;

// Помним, что Оклад - это периодический реквизит справочника Сотрудники_2 окл = Объект.Оклад.Получить(КонецТекущегоПериода());

// При выводе применяем заданные по умолчанию параметры таблицы;

// для их изменения следует обратиться к методу Опции // Выводим секцию Заголовок табл.ВывестиСекцию("Заголовок");

// Открываем выборку расчетов объекта, зарегистрированных в текущем периоде ВыбратьПериодПоОбъекту(Объект); пока ПолучитьЗапись( ) = 1 цикл мес = ДатаМесяц(ДатаНачала);

если ВидРасч.ВходитВГруппу(ГруппаРасчетов.ВсеНачисления_2) = 1 тогда нач = нач + Результат;

// Добавляем в тЗначНач номер месяца, ВидРасч, Результат и ВсегоЧасов НовСтрокаВТЗнач(тЗначНач, мес);

иначеЕсли ВидРасч.ВходитВГруппу(ГруппаРасчетов.ВсеУдержания_2) = 1 тогда уд = уд + Результат;

// Добавляем в тЗначУд номер месяца, ВидРасч, Результат и ВсегоЧасов НовСтрокаВТЗнач(тЗначУд, мес); иначеЕсли ВидРасч = ВидРасчета.НачСальдо_2 тогда сальдоНач = Результат;

Вид расчета ВБанк_2

Пустая строка в РЛ

Выводим сальдо на начало месяца Форматируем данные



вБанк = Результат; конецЕсли; конецЦикла; // пока

Полагается к выплате Сальдо на конец месяца //Вывод начислений

//Вывод удержаний











нач = Формат(нач, "4-10.2"); уд = Формат(уд, "4-10.2"); вБанк = Формат(вБанк, "Ч-10.2"); табл.ВывестиСекцию("Итог"); сальдоКон = Формат(сальдоКон, "4-10.2");

табл.ВывестиСекцию("сКон"); // Вывод сальдо на конец месяца

// Запрещаем редактирование результирующей таблицы табл.ТолькоПросмотр(1);

// В методе Показать задаем пустой заголовок окна с результирующей таблицей табл.Показать("");

конецПроцедуры // ЛистокСотрудника процедура ПечатьРЛ()

перем сотр, сЗначСотр, ин, табл, тЗначНач, тЗначУд;

// тЗначНач, тЗначУд - таблицы значений для начислений и удержаний сотрудника ОчиститьОкноСообщений();

СоздатьТЗнач(тЗначНач);

СоздатьТЗ нач(тЗ начУ д);

табл = СоздатьОбъект("Таблица");

// Свяжем переменную табл с таблицей РЛ, содержащей макет расчетного листка табл.ИсходнаяТ аблица("РЛ");

если кто = 1 тогда // РЛ выбранного сотрудника

Состояние("Формируем расчетный листок сотрудника " + Объект.Наименование); ЛистокСотрудника(Объект, табл, тЗначНач, тЗначУд);

// Вывод РЛ сотрудников выбранного подразделения

// Используя метод ЖР ВыбратьПоЗначению, занесем (без повторов) значение // атрибута Объект расчетов выбранного подразделения в список сЗначСотр

иначе

Состояние("Формируем список сотрудников подразделения " + Обьект.Родитель);

// Код процедуры СоздатьСЗнач см. в предшествующем разделе СоздатьСЗнач(сЗначСотр); // Формируем список сотрудников подразделения для ин = 1 по сЗначСотр.РазмерСписка() цикл сотр = сЗначСотр.ПолучитьЗначение(ин);

Состояние("Формируем расчетный листок сотрудника " + сотр.Наименование); ЛистокСотрудника(сотр, табл, тЗначНач, тЗначУд);

// Подготовка таблиц значений для РЛ следующего сотрудника тЗначНач.УдалитьСтроки(); тЗначУд.УдалитьСтроки(); конецЦикла; // для // Поправляем закладку отбора УстановитьОтбор("Родитель", сотр.Родитель); конецЕсли;

конецПроцедуры // ПечатьРЛ

// Создает таблицу значений с четырьмя столбцами процедура СоздатьТЗнач(тЗнач)

// Таблица значений для начислений (удержаний) сотрудника тЗнач = СоздатьОбъект("ТаблицаЗначений");

// Формируем столбцы таблицы значений. Опуская имена необязательных параметров,

// сохраняем разделяющие их запятые тЗнач.НоваяКолонка("Месяц", "Число",,,, 2); тЗнач.НоваяКолонка("ВидРасчета", "Вид Расчета",,,,); тЗнач.НоваяКолонка("Сумма", "Строка",,,, 10); тЗнач.НоваяКолонка("Часы", "Строка",,,, 3); конецПроцедуры // СоздатьТЗнач

// Добавляем в таблицу значений номер месяца, ВР, Результат и ВсегоЧасов процедура НовСтрокаВТЗнач(тЗнач, мес)

тЗнач.НоваяСтрока(); // Добавляем новую строку

// Определяем, используя атрибут идентификатор столбца, ячейки новой строки тЗнач.Месяц = мес; тЗнач.ВидРасчета = ВидРасч;

// Форматируем данные тЗнач.Сумма = Формат(Результат, "Ч-10.2"); тЗнач.Часы = Формат(ВсегоЧасов, "Ч-3.0"); конецПроцедуры // НовСтрокаВТЗнач

// Выводит в РЛ, если начУд = 1, таблицу с начислениями или с удержаниями, если начУд = 2 процедура ВыводНачУд(табл, тЗнач, начУд)

// Выводим секцию табЗаг с заголовком для таблицы начислений (удержаний) табл.ВывестиСекцию("табЗаг"); тЗнач.ВыбратьСтроки( ); пока тЗнач.ПолучитьСтроку() = 1 цикл мес = тЗнач.Месяц;

ВР = тЗнач.ВидРасчета; рез = тЗнач.Сумма; часы = тЗнач.Часы; табл.ВывестиСекцию("Расчет"); конецЦикла; // пока конецПроцедуры // ВыводНачУд

Расчетный листок

Предприятие АО ТриТ

Подразделение 03 Цех; Безверхний Игорь Петрович Табельный номер 301;' Оклад 3100 руб._
Начисления
Мес. Вид начисления Сумма Часы
12 Оклад 3100.00 165
12 Премия суммой 700.00
12 Премия коэффициентом 1650.00 165
1 Премия коэффициентом 1650.00 165
12 Премия 1234 3550.00
Удержания
Мес. Видудержания Сумма Часы
12 НДФЛ 1384.50 _
Остаток прошлого месяца 0.20
Начислено 10650.00
Удержано 1384.50
Полагается к выплате: 9265.70
Перечислено в банк 9265.00
Остаток на конец месяца 0.70
Рис. 7.80. Расчетный листок Безверхнего И. П.
Замечание. Формат "Ч-10.2", употребленный во встроенной функции Формат, обеспечит вывод ненулевого значения на поле длиной в 10 символов с двумя знаками после десятичной точки. Если первый параметр функции Формат равен нулю, то вместо нуля согласно формату "Ч-10.2" будет выводиться символ -.

7.17.3. ВЕДОМОСТЬ ПЕРЕЧИСЛЕНИЙ В БАНК

Формируется в виде текстового файла, который затем можно отправить по электронной почте в банк назначения. Содержит для каждого сотрудника его ФИО, табельный номер, номер счета в банке и перечисленную сумму. При этом мы предполагаем, что номер счета отличается от табельного номера сотрудника только префиксом Б-. Если это не так, то в справочник Сотрудники_2 придется добавить новое поле для хранения в нем номера счета сотрудника.

процедура СтрокаВТекст(сотр, текст, ном, всего) далее

// Ведомость перечислений в банк для одного сотрудника или сотрудников // выбранного подразделения. Формируется как текстовый файл // Поскольку процедура присутствует в модуле формы списка ЖЗ,

//то все методы ЖР вызываются без префикса процедура ВедомостьБанк()

перем текст, всего, сЗначСотр, ин, сотр; перем сообщение;

сообщение = "Формируем ведомость перечислений сотрудника "; всего = 0; // Общая сумма перечислений в банк

текст = СоздатьОбъект("Текст"); // Направляем вывод в текстовый файл текст.КодоваяСтраница(1); // Используем DOS-кодировку текста

текст.ДобавитьСтроку(" Список перечислений во вклады из заработной платы на лицевые счета рабочих и служащих"); текст.ДобавитьСтроку(" " + Объект.Родитель + "," +

Константа. НазваниеОрганизации);

текст.ДобавитьСтроку(" за " + ПериодРегистрации.ОписательПериода + " в " + Константа. БанкОрганизации);

текст.ДобавшъСтроку("
ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
если кто — 1 тогда // Ведомость для выбранного сотрудника

Состояние(сообщение + Объект.Наименование);

СтрокаВТекст(Объект, текст, 1, всего);

// Вывод РЛ сотрудников выбранного подразделения

// Используя метод ЖР ВыбратьПоЗначению, занесем (без повторов) значение // атрибута Объект расчетов выбранного подразделения в список сЗначСотр иначе

Состояние("Формируем список сотрудников подразделения " + Объект.Родитель);

// Код процедуры СоздатьСЗнач см. в предшествующем разделе

СоздатьСЗнач(сЗначСотр); // Формируем список сотрудников подразделения для ин = 1 по сЗначСотр.РазмерСписка() цикл сотр = сЗначСотр.ПолучитьЗначение(ин);

Состояние(сообщение + сотр.Наименование);

СтрокаВТекст(сотр, текст, ин, всего); конецЦикла; // для // Поправляем закладку отбора УстановитьОтбор(" Родитель", сотр. Родитель); конецЕсли;

// Завершаем вывод ведомости перечислений в банк текст.ДобавитьСтроку(""); // Пустая строка

// Формат "ЧПД" обеспечит вывод итоговой суммы перечислений прописью текст.ДобавитьСтроку(" Итого: " + Формат(всего, "ЧПД")); текст.ДобавитьСтроку(""); // Пустая строка

текст.ДобавитьСтроку(" Гл. бухгалтер" + СимволТабуляции +

Символ Табуляции + СимволТабуляции + СимволТабуляции +

Константа.Г лБухгалтер .Получить(Т екущаяДата())); текст.ТолькоПросмотр(1); // Запрещаем редактирование текста

текст.Показать("Ведомость перечислений в банк"); конецПроцедуры // ВедомостьБанк

// Добавляет в ведомость строку под номером ном и корректирует значение переменной всего процедура СтрокаВТекст(сотр, текст, ном, всего)

// Открываем выборку расчетов объекта, зарегистрированных в текущем периоде

ВыбратьПериодПоОбъекту(сотр);

пока ПолучитьЗапись() = 1 цикл

если видРасч = ВидРасчета.ВБанк_2 тогда всего = всего + Результат;

// Форматируем данные в соответствии с заголовком табличной части ведомости текст.ДобавитьСтроку(" " + Формат(ном, "С5") + ":" + Формат(Объект.Наименование, "С27") + ": " +

Формат(Объект.Код, "С9") + ":" +

Формат(Результат, "410.2") + " : " +

Формат("Б-" + Строка(Объект.Код), "С9")); возврат; конецЕсли; конецЦикла; // пока конецПроцедуры // СтрокаВТекст

Результат для третьего цеха приведен на рис. 7.81.

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 7.81. Ведомость для третьего цеха
Замечание. Чтобы получить отображение ведомости перечислений в банк равномерным шрифтом, была выполнена цепочка Текст - Текст модуля.

7.17.4. СВЕДЕНИЯ ДЛЯ БУХГАЛТЕРСКОГО УЧЕТА ЗАРПЛАТЫ

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

Запрос запустим из обработки Проба. Для просмотра результатов запроса выгрузим его в таблицу значений.

// Формирует и выполняет запрос по хозяйственным операциям,

// суммируя относящиеся к ним результаты // Параметр зХозОп типа Запрос является входным/выходным функция ЗапрХозОп(зХозОп)

перем текстЗапХозОп; // Содержание запроса

перем жз, нтп, ктп;

жз = СоздатьОбъект(''ЖурналРасчетов.Зарплата_2'');

// Начало и конец текущего периода ЖЗ нтп = жз.НачалоТекущегоПериода(); ктп = жз.КонецТекущегоПериода(); текстЗапХозОп = "

| период с нтп по ктп; // Период запроса

// Переменные запроса | хозОп = журналРасчетов.Зарплата_2.ХозОп;

| рез = журналРасчетов.Зарплата_2.Результат;

// Функция запроса | функция сумХозОп = сумма(рез);

// Задаем порядок выборки данных

| группировка хозОп упорядочить по хозОп.Наименование;";

// Выполняем запрос и возвращаем 1 в случае удачи, или 0, если есть проблемы возврат зХозОп. Выполнить(текстЗапХозОп); конецфункции // ЗапрХозОп

// Запускает запрос зХозОп о хозяйственных операциях и выгружает // его результат для последующего просмотра в таблицу значений тЗнач процедура Выполнить() // Связана с кнопкой Пуск обработки Проба

перем зХозОп, тЗнач;

// Создаем объекты зХозОп и тЗнач зХозОп = СоздатьОбъект("Запрос"); если ЗапрХозОп(зХозОп) = 0 тогда

возврат; // Запрос не выполнен

конецЕсли;

// Создаем объект тЗнач для промежуточной демонстрации выборки запроса тЗнач = СоздатьОбъекг("ТаблицаЗначений");

// Выгружаем все переменные запроса в таблицу значений тЗнач // для его предварительного просмотра зХозОп.Выгрузить(тЗнач, 1);

// Просмотр .таблицы значений

тЗнач.ВыбратьСтроку(, "Запрос о хозяйственных операциях в таблице значений"); конецПроцедуры // Выполнить

Результат приведен на рис. 7.82.

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 7.82. Распределение расчетов по хозяйственным операциям Имеющаяся в тексте запроса строка-функция | функция сумХозОп = сумма(рез);
задает в результирующей таблице поле сумХозОп, в которое для каждой группы, а группировка выполняется по переменной запроса хозОп, заносится сумма результатов всех расчетов, отнесенных на текущую хозяйственную операцию.

Код, повторяющий действия функции сумХозОп, может быть таким:

// Возвращает в заданном периоде для заданной хозяйственной операции хозОп // сумму результатов относящихся к ней расчетов функция сумХозОп2(хозОп, жз, нтп) жз.ВыбратьПериод(нтп);

схо = 0; // Искомый результат

пока жз.ПолучитьЗапись() = 1 цикл если жз.ХозОп = хозОп тогда схо = схо + жз.Результат; конецЕсли; конецЦикла; // пока возврат схо;

конецФункции // сумХозОп2

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

// Формирует список использованных в текущем периоде ЖЗ хозяйственных операций // Вызывает затем функцию сумХозОп2 для каждой хозяйственной операции // и печатает возвращенный функцией результат

процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба

перем зХозОп, текстЗапХ; // Содержание запроса

перем жз, нтп, ктп, сумРез;

ОчиститьОкноСообщенийО;

// Создаем объекты зХозОп и жз

зХозОп = СоздатьОбъект("Запрос");

жз = СоздатъОбъект('ЖурналРасчетов.Зарплата_2");

// Начало и конец текущего периода ЖЗ нтп = жз.НачалоТекущегоПериодаО; ктп - жз.КонецТекущегоПериода(); текстЗапХ = "

| период с нтп по ктп; // Период запроса

// Переменная запроса

[ хозОп = журналРасчетов.Зарплата_2.ХозОп;

| группировка хозОп упорядочить по хозОп.Наименование;";

// Выполняем запрос и возвращаем 1 в случае удачи, или 0, если есть проблемы если зХозОп.Выполнить(текстЗапХ) = 0 тогда

возврат; // Запрос не выполнен

конецЕсли; всего = 0;

пока зХозОп.Группировка("хозОп") = 1 цикл

сумРез = сумХозОп2(зХозОп.хозОп, жз, нтп); всего = всего + сумРез;

Сообщить("Сумма результатов по операции " + СокрП(зХозОп.хозОп) +

" равна " + символТабуляции + сумРез); конецЦикла; // пока

Сообщить("Всего по всем операциям: " + всего); конецПроцедуры // Выполнить

Результат:





Сумма результатов по операции 2017002 равна 13642.6







Всего по всем операциям: 209892.1

Если нужно просмотреть отдельные составляющие каждой группы с указанием объекта, текст запроса нужно сформировать следующим образом:

текстЗапХозОп ="

| период с нтп по ктп; // Период запроса

// Переменные запроса; добавляем переменную сотр | сотр = журналРасчетов.Зарплата_2.Объект;

| хозОп = журналРасчетов.Зарплата_2.ХозОп;

| рез = журналРасчетов.Зарплата_2.Результат;

// Функция запроса | функция сумХозОп = сумма(рез);

// Задаем порядок выборки данных

| группировка хозОп упорядочить по хозОп.Наименование;

| группировка рез;"; // Добавленная строка

Тогда в таблице результатов мы будем наблюдать приведенные на рис. 7.83 данные.
ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 7.83. Фрагмент результата запроса для группы хозОп =20130000

7.18. ПЕРВЫЙ РАСЧЕТНЫЙ ПЕРИОД НОВОГО ГОДА

Теперь, когда выполнены расчеты и выпущены все необходимые документы, можно перейти к следующему расчетному периоду. Это можно сделать интерактивно, выбрав

_о*

на панели инструментов диалога формы списка ЖЗ Зарплата 2 иконку ЭІЛ. В этом случае будут отработаны сопровождающие смену системные действия, предопределенная процедура глобального модуля ПриСменеРасчетногоПериода и, следовательно, добавленная нами в глобальный модуль процедура ФиксироватьСменуРП (разд. 7.3.6.3). В результате ее исполнения в документе Начало периода № 1 появится новая запись, напоминающая о произошедшем событии (рис. 7.84).

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 7.84. Новый расчетный период
Вспомним, что архивные записи ЖЗ (кроме фиксированных) имеют иной цвет фона сопровождающих их иконок - голубой. Изменяется и фон иконок в журнале документов: иконки, сопровождающие документы, соответствующие архивным расчетам, располагаются на темно-синем фоне.

В новом периоде нет пока что ни одного расчета. Но не следует торопиться с их вводом, поскольку период открывает новый год и скорее всего для него не определены используемые справочником Сотрудники_2 календари и праздники. Поэтому предварим ввод расчетов заданием праздников, а вслед выполним автозаполнение календарей. Все необходимые средства для выполнения этих действий у нас есть, доступ к ним осуществляется посредством пунктов Календари и Праздники, расположенных в колонке Календари меню интерфейса Ученик.

7.19. ВЫВОДЫ

1. ЖР состоит из записей, называемых расчетами; они попадают в журнал в результате проведения соответствующих документов.

2. Расчеты могут быть обязательными, дополнительными, вытесняющими, длинными, перерасчетами, зависимыми и обычными. Полезно контролировать, имеют ли объекты ЖЗ все обязательные расчеты.

3. Длинные расчеты могут существовать только в документах. В ЖЗ длинный расчет разбивается на соответствующее число обычных расчетов.

4. В описание группы ВР следует помещать информацию о программах, в которых группа используется.

5. Перенос данных в 1С из других программ можно выполнить, употребляя объекты типа XBase.

6. Основное назначение объектов типа Календарь и Праздники - предоставлять данные для вычисления числа рабочих часов и дней в заданном временном интервале.

7. Сложные документы, журналы и обработки 1С, учитывающие многие случаи жизни, целесообразно заменять на аналогичные по назначению, отвечающие потребностям предприятия более простые и, следовательно, эффективные программы.

8. В окне задания свойств документа, вводящего расчеты, нужно активизировать флажок Расчет.

9. Методы ВвестиРасчет и ЗаписатьРасчет могут быть вызваны только в модулях документов, вводящих расчеты. Вызвать эти методы в модулях иных объектов, например в модуле формы списка ЖЗ или в модуле отчета (обработки), нельзя.

10. Расчеты одного объекта располагаются в ЖЗ в порядке очередности их исполнения;

11. Команда проведения документа при выполнении перепроведения отслеживает все возможные произошедшие в документе изменения.

12. Для обновления самовытесняющего расчета при его повторном вводе другим документом вместо метода ЗаписатьРасчет используется метод ВвестиРасчет.

13. Правила перерасчета обнуляют значение атрибута Рассчитана у зависимых расчетов.

14. Правила перерасчета не срабатывают при расчете записи в результате выбора пункта меню Рассчитать запись.

15. Разбивая длинный расчет на части, 1С сохраняет вычисленный в соответствии с документом, порождающим расчет, результат для каждой из частей разбиения.

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

17. Предопределенные процедуры модуля формы ЖР позволяют эффективно управлять правами доступа пользователей.

8. ПОВЫШЕНИЕ ЭФФЕКТИВНОСТИ ФУНКЦИОНИРОВАНИЯ СИСТЕМЫ

8.1. КРИТЕРИИ ЭФФЕКТИВНОСТИ

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

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

8.2. УВЕЛИЧЕНИЕ БЫСТРОДЕЙСТВИЯ

Частично эта задача решена. Так, программы расчета зарплаты, формирования расчетных листков и ведомостей перечислений в банк, которые мы разместили в модуле формы списка ЖЗ Зарплата_2 и привели разд. 7.17.1-7.17.3, работают существенно быстрее, чем аналогичные встроенные в 1С процедуры. Это достигнуто за счет утраты универсальности, но нам она в нашей задаче и не нужна.

В этом направлении заинтересованные пользователи могут сделать и иные продуктивные шаги.

Иной способ повышения быстродействия - удаление из конфигурации ненужных вам объектов. Сразу это может не получиться. Так, если вы хотите удалить ВР Опла-таБЛ_Северн (больничный для северных регионов), то система выдаст предупреждение "Объект не может быть удален", а в окне сообщений выведет следующие текст:

Данный объект использован в:

ПравилоПерерасчета.ОсновныеУдержания

ПравилоПерерасчетаБольничныеЛисты

ПравилоПерерасчетаНДФЛ_по_13

Однако после устранения объекта из этих правил удаление ВР ОплатаБЛСеверн станет возможным.

8.3. УЛУЧШЕНИЕ КАЧЕСТВА ИНТЕРФЕЙСА

В формах 1С или в созданных взамен их своих формах вы можете разместить облегчающие обработку данных элементы управления. Что мы, собственно, и сделали, например, в форме списка ЖЗ Зарплата_2. Элементы диалога этой формы позволяют ввести все записи, провести их расчеты и выпустить необходимые сопровождающие расчеты документы, не покидая формы списка ЖЗ.

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

Возьмем, к примеру, иконку ^ на панели инструментов ЖЗ, предлагающую создать новый документ. В нашем случае ее желательно убрать, поскольку список предлагаемых для выбора документов содержит много лишних позиций (рис. 8.1).

Выбор вида документа

БОЛЬНИЧНЫЙ ЛИСТ

Кн. делом (Ввод начальных данных по сотрут' Прочие нач (Ввод вспомогательных начисле. Нал льготы (Редактирование затеей справ Нач. сальдо (Ввод начального сальдо по сог[ Устаревший документ

Возвр. в кассу (Регистрц>ует Факт погашет Возвр нал на доходы (Регистрирует факт вс Выг. по вкл (Ввод материальной вьгоды по Выпл делом (Списание выданных депонироі Дивиденды (Приказ на выплату дивидендов

Пдплыыгweewxa fPenjr

ii_____I

ок

Отмена

Помошь

ьнаов і *лв I

±г

Рис. 8.1. Список, открываемый иконкой панели инструментов ЖЗ

Мы заменили эту иконку кнопкой Ввод расчета, показывающей для выбора список документов (см. рис. 7.25), вводящих расчеты в ЖЗ Зарплата_2. Их перечень, напомним, мы внесли в перечисление ВР 2 (разд. 7.3.3).

Однако удалить иконку из панели инструментов можно, только убрав всю панель инструментов (Сервис - Панели инструментов - Дополнительные - Инструментальные панели окон - <Отключены>). Но даже после этого аналогичный пункт останется в колонке Действия меню системы.

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

Во-первых, пользователь, наблюдая ЖЗ, не видит документа, который ему предлагают удалить, - перед ним лишь часть расчетов, порожденных документом (а эти расчеты могут быть рассыпаны по всему ЖЗ), а чтобы принять решение, нужно иметь перед глазами объект, а не его производные.

Во-вторых, пользователь, находясь в ЖЗ, наблюдает удаление не документа, а записей ЖЗ. Такая рассогласованность намерений и визуального результата несколько обескураживает.

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

Ситуацию можно поправить, если в предопределенную процедуру ПриУдалении-Документа, которую нужно расположить в глобальном модуле, добавить вызов нижеприводимой функции ЕстьЛиРучнаяПравка, которая как раз-то и фиксирует наличие расчета с ручной правкой и запрещает при наличии такового удаление породившего его документа. Функцию придется разместить в глобальном модуле, поскольку оттуда осуществляется ее вызов.

// Вернет 0, если хотя бы один расчет удаляемого документа имеет ручную правку функция ЕстьЛиРучнаяПравка(док) перем жз;

жз = СоздатьОбъект('ЖурналРасчетов.Зарплата_2''); жз.ВыбратьЗаписиПоДокументу(док);

// Просматриваем все расчеты, порожденные документом док пока жз.ПолучитьЗапись() = 1 цикл если жз.Исправлена = 1 тогда

возврат 0; // Есть ручная правка

конецЕсли; конецЦикла; // пока

возврат 1; // Нет расчетов с ручной правкой

конецФункции // ЕстьЛиРучнаяПравка

Саму же предопределенную процедуру ПриУдаленииДокумента можно записать следующим образом:

// Предопределенная процедура глобального модуля

// Интерфейс процедуры задан в системе и содержит два входных параметра: док и режим // док - удаляемый документ

// Параметр режим равен единице, если проставляется DBF-пометка удаления,

// и равен нулю, если документ получает Ю-пометку удаления // В нашей проблеме этот параметр неинтересен процедура ПриУдаленииДокумента(док, режим) если док.ПометкаУдаленияО = 1 тогда

возврат; // Если снимается пометка удаления,

конецЕсли; // то никаких проверок не выполняем

// Исследуем документы, порождающие расчеты // Иные документы в этом исследовании не участвуют если (док.ВидО = "Табель") или (док.Вид() = "Премия") или (док.ВидО = "НачПериода_2") тогда

// Статус возврата будет равен нулю, если расчеты документа док имеют ручную // правку. При таком статусе возврата документ удален не будет если ЕстьЛиРучнаяПравка(док) = 0 тогда СтатусВозврата(0);

Предупреждение("Нельзя удалить документ с ручной правкой."); конецЕсли; конецЕсли;

конецПроцедуры // ПриУдаленииДокумента

Подобного рода модификации заинтересованный пользователь может осуществить в великом множестве.

8.4. НАДЕЖНОСТЬ ПРОГРАММ

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

8.4.1. ПРОВЕРКА ДАННЫХ

В любой задаче мы планируем получить некоторый результат. Так, нажав на иконку ®, размещенную в диалоге формы списка ЖЗ Зарплата_2, мы рассчитываем получить ведомость перечислении в банк для выбранного сотрудника или подразделения.

Видимых препятствий для получения такого результата нет, и если нет ошибок в расчетах и коде, формирующем ведомость, то будет верен и документ. В то же время в программу, выводящую ведомость, можно добавить функцию, проверяющую, точно ли подсчитан размер перечисляемой в банк суммы. Ошибка, как мы уже отмечали (разд. 7.10.1), может возникнуть, когда после отмены ручной правки какой-либо записи, имеющей зависимые ВР, выполнен ее перерасчет (иконка ^ панели инструментов ЖЗ), при этом атрибут Рассчитана зависимых ВР остается равным единице, а пользователь забывает выполнить их перерасчет. (Напомним, что ВР ВБанк_2 зависит от всех иных ВР.)

Код такой проверочной функции прост:

// Выполняет сравнение размеров перечислений в банк. Возвращает нуль,

// если перечисления верны, и отличное от нуля число - в противном случае // При ошибке функция выдает соответствующее предупреждение функция СовпадениеПеречислений(сотр, перечислено)

// перечислено = Целая часть(Начальное сальдо + Все начисления - Все удержания) перем ВР, банк; банк = перечислено;

// Открываем выборку расчетов объекта, зарегистрированных в текущем периоде ВыбратьПериодПоОбъекту(сотр); пока ПолучитьЗапись() = 1 цикл ВР - ВидРасч;

если (ВР.ВходитВГруппу(ГруппаРасчетов.ВсеНачисления_2) = 1) или (ВР = ВидРасчета.НачСальдо_2) тогда банк = банк - результат;

иначеЕсли ВР.ВходитВГруппу(ГруппаРасчетов.ВсеУдержания_2) = 1 тогда банк = банк + результат; конецЕсли; конецЦикла; // пока

возврат Цел(банк); // Берем целую часть результата

конецФункции // СовпадениеПеречислений

Тогда приведенная в разд. 7.17.3 процедура СтрокаВТекст, добавляющая строку в ведомость перечислений в банк, примет следующий вид:

// Добавляет в ведомость строку под номером ном и корректирует значение переменной всего процедура СтрокаВТекст(сотр, текст, ном, всего)

// Открываем выборку расчетов объекта, зарегистрированных в текущем периоде

ВыбратьПериодПоОбъекту(сотр);

пока ПолучитьЗапись( ) = 1 цикл

если видРасч = ВидРасчета.ВБанк_2 тогда всего = всего + Результат;

// Форматируем данные в соответствии с заголовком табличной части ведомости текст.ДобавитьСтроку(Формат(ном, "С5") + +

Формат(Объект.Наименование, "С27") + ":" +

Формат(Объект.Код, "С9") + ":" +

Формат(Результат, ”410.2”) + ”: ” +

Формат(Объект.Код, ”С9”)); прервать; конецЕсли; конецЦикла; // пока

// Добавляем вызов проверяющей функции СовпадениеПеречислений если СовпадениеПеречислений(сотр, Результат) о 0 тогда

Сообщить(''Ошибка в размере перечислений сотрудника ” + сотр.Наименование); Предупреждение(''Ошибка в размере перечислений сотрудника ” +

РазделительСтрок + сотр.Наименование); конецЕсли;

конецПроцедуры // СтрокаВТекст

Такой подход, правда, тоже имеет недостаток. Ведь если изменится алгоритм расчета перечислений в банк, то код придется менять в двух местах: в ВР ВБанк_2 и в вышеприведенной функции СовпадениеПеречислений. Дублирования кода, впрочем, можно избежать, если разместить его в глобальном модуле.

8.4.2. ПРЕОДОЛЕНИЕ КОНФЛИКТОВ

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

жз.3аписать();

^:\ПРОБАБКТ(20)}: Объект заблокирован: Журнал расчетов Журнал заработной платы

Чтобы промоделировать такую ситуацию, напишем следующий код:

процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба

перем сСотр_2, сотр, жз, рез, флаг;

ОчиститьОкноСообщений( );

сСотр_2 = СоздатьОбъект(”Справочник.Сотрудники_2”);

// Ищем во всем справочнике сотрудника с кодом 301

сСотр_2.НайтиПоКоду(301,0);

сотр = сСотр_2.ТекущийЭлемент( );

жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2");

// Открываем выборку расчетов объекта, зарегистрированных в текущем периоде

жз.ВыбратьПериодПоОбъекту(сотр);

пока жз.ПолучитьЗапись( ) = 1 цикл

// Пропускаем фиксированные и исправленные расчеты если жз. Фиксирована + жз.Исправлена о 0 тогда продолжить; конецЕсли; рез = жз.Результат;

// Начинаем бесконечный цикл // Чтобы цикл прервать, нужно нажать клавишу Esc // В этом цикле время от времени захватывается ЖЗ флаг= 1;

пока флаг = 1 цикл

// Значение реквизита Результат не меняется жз.УстановитьРеквизит("Результат", рез); жз.3аписать(); конецЦикла; // пока флаг = 1 конецЦикла; // пока жз.ПолучитьЗапись() = 1 конецПроцедуры // Выполнить

Эта программа никаких изменений в ЖЗ не производит, но время от времени, выполняя оператор

жз.3аписать();

захватывает журнал расчетов Зарплата_2.

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

// Ищем во всем справочнике сотрудника с кодом 301 сСотр_2.НайтиПоКоду(301,0);

имеющую оператор

// Ищем во всем справочнике сотрудника с кедом 302 сСотр_2.НайіиПоКоду(302, 0);

То есть в ЖЗ мы будем обновлять результат другой записи, отвечающей объекту с кодом 302.

Тогда если сотрудники с кодами 301 и 302 в справочнике Сотрудники_2 есть и если в текущем периоде ЖЗ каждый из этих сотрудников имеет нефиксированный и неисправленный расчет, то неизбежно в работе одного из пользователей возникнет завершающая ошибка исполнения и приведенное выше сообщение об этой ошибке.

Если же в этом эксперименте вместо операторов

жз.УстановтъРеквизит("Резулкгат", рез); жз.3аписать();

записать оператор

жз.Результат = рез;

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

жз.Результат = рез;

{D:\ПPOБA.ERT(21)}: Запись заблокирована!

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

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

Чтобы продемонстрировать метод устранения такой ошибки, продолжим начатый пример, модифицировав в нем бесконечный цикл Пока флаг = 1, введя в него управляющую конструкцию Попытка.

// Начинаем бесконечный цикл // Чтобы цикл прервать, нужно нажать клавишу Esc // В этом цикле время от времени захватывается ЖЗ флаг = 1;

пока флаг = 1 цикл // Этот цикл подлежит модификации

// Значение реквизита Результат не меняется жз.УстановитьРеквизит("Результат", рез);

// Начало добавляемого кода

// Вводим в цикле Пока флагПопытки - 1 управляющую конструкцию Попытка // Цикл прервется после удачного исполнения метода Записать флагПопытки = 1; пока флагПопытки = 1 цикл

попытка // Управляющая конструкция Попытка

жз.3аписать(); // Этот оператор из старого кода

флагПопытки = 0;

Сообщить("Запись выполнена."); исключение

Сообщить("Ожидаю освобождения таблицы."); конецПопытки;

конецЦикла; // пока флагПопытки = 1 // Конец добавляемого кода

конецЦикла; // пока флаг = 1

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

Запись выполнена.

Запись выполнена.

Ожидаю освобождения таблицы. Ожидаю освобождения таблицы. Запись выполнена.

Продемонстрированный метод преодоления конфликтов при помощи управляющей конструкции Попытка нужно применять каждый раз, когда есть угроза пользовательских конфликтов на почве захвата DBF-таблиц. В частности, при работе такая угроза существует при употреблении методов, выполняющих модификацию ЖЗ, а именно: ВыполнитьРасчет, Рассчитать, ВвестиРасчет, ЗаписатьРасчет, ВвестиПерерасчет, Записать, ФиксироватьЗапись, ОсвободитьЗапись, Исправить, ОтменитьИсправление и УдалитьЗапись.

С учетом сказанного процедура РасчетОбъекта, рассчитывающая записи сотрудника, вызываемая из процедуры РасчетЗП (разд. 7.17.1), должна быть усовершенствована следующим образом:

// Обновленная процедура РасчетОбъекта

// Рассчитывает все записи объекта ЖЗ кроме расчета с ВР НачСальдо_2 // Поскольку процедура присутствует в модуле формы списка ЖЗ,

// то все методы ЖР вызываются без префикса процедура РасчетОбъекта(сотр)

перем флаг, сотр2, флагПопытки;

// Флаг будет равен единице, если у сотрудника есть расчет с ВР Оклад_2 флаг = 0;

сотр2 = сотр; // Запоминаем для вывода сообщения

// Открываем выборку расчетов объекта, зарегистрированных в текущем периоде ВыбратьПериодПоОбъекту(сотр); пока ПолучитьЗапись() = 1 цикл

если видРасч = ВидРасчета.НДФЛ_2 тогда флаг = 1; конецЕсли;

// Фиксированные и исправленные записи рассчитывать нет смысла если Фиксирована + Исправлена = 0 тогда

// Начало добавляемого кода

// Вводим в цикле Пока флагПопытки = 1 управляющую конструкцию Попытка // Цикл прервется после удачного исполнения метода Рассчитать флагПопытки = 1; пока флагПопытки = 1 цикл

попытка //

Управляющая конструкция Попытка Этот оператор из старого кода

//

Рассчитать(); //

флагПопытки = 0;

Состояние ("Выполнен расчет сотрудника " + сотр.Наименование); исключение

Состояние(''Ожидаю освобождения таблицы."); конецПопытки;

конецЦикла; // пока флагПопытки = 1 // Конец добавляемого кода

конецЕсли; конецЦикла; // пока если флаг = 0 тогда

Сообщить(''Оформите табель сотруднику " + сотр2.Наименование); конецЕсли;

конецПроцедуры // РасчетОбъекта

Аналогичные изменения должны быть внесены и в другие процедуры модуля формы списка ЖЗ Зарплата_2, а также в модули форм созданных нами документов и справочников, хотя степень их конфликтности по сравнению с ЖЗ существенно ниже.

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

8.4.3. ТРАНЗАКЦИИ

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

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

Надо сказать, что в модуле документа в предопределенных процедурах Обработка-Проведения и ОбработкаУдаленияПроведения 1С выполняет предусмотренные в этих процедурах действия, употребляя транзакцию. Поэтому добавлять в них свои транзакции нет необходимости.

Известно, что транзакции блокируют используемые в них DBF-файлы, поэтому необходимо максимально содействовать их успешному завершению. В частности,

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

В решаемых нами задачах, кроме упомянутых процессов проведения документов, транзакцию можно было бы применить при расчете зарплаты объекта, записав код процедуры РасчетОбъекта следующим образом:

// Еще один вариант процедуры РасчетОбъекта, на этот раз использующий транзакцию процедура РасчетОбъекта(сотр) перем флаг, сотр2;

// Флаг будет равен единице, если у сотрудника есть расчет с ВР Оклад_2 флаг = 0;

сотр2 = сотр; // Запоминаем для вывода сообщения

// Открываем выборку расчетов объекта, зарегистрированных в текущем периоде если ВыбратьПериодПоОбъекту(сотр) = 0 тогда возврат; конецЕсли;

НачатьТранзакцию(); // Первый добавленный оператор..

пока ПолучитьЗапись() = 1 цикл

если видРасч = ВидРасчета.НДФЛ_2 тогда флаг = 1; конецЕсли;

// Фиксированные и исправленные записи рассчитывать нет смысла если Фиксирована + Исправлена = 0 тогда Рассчитать( ); конецЕсли; конецЦикла; // пока

если флаг = 1 тогда // Транзакция фиксируется, если проведен Табель

ЗафиксироватьТранзакцию(); // Второй добавленный оператор

иначе // флаг = 0

Отменить Транзакцию^); // Третий добавленный оператор

Сообщить("Оформите табель сотруднику " + сотр2.Наименование); конецЕсли;

конецПроцедуры // РасчетОбъекта

В этом обновленном коде 3 новые (для нас) встроенные в 1С процедуры: Начать-Транзакцию, ЗафиксироватьТранзакцию и ОтменитьТранзакцию. Назначение процедур понятно из их названий. Остановимся на особенностях их исполнения.

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

пока ПолучиіьЗапись( ) = 1 цикл

{ЖурнапРасчетов.Зарплага_2.Форма.ФормаСписка.Форма.Модуль(250)}:Таблица: CJ4287 Ошибка обращения к данным при транзакции, выполняемой другим пользователем

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

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

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

В нашем примере осуществлены вызовы всех трех процедур, что несколько изменило характер получаемого результата. Так, если ранее процедура РасчетОбъекта модифицировала результаты расчетов и при отсутствии расчета с ВР Оклад_2, то теперь в такой ситуации данные не меняются, поскольку вызывается .процедура ОтменитьТранзакцию.

Если сравнивать две модификации процедуры РасчетОбъекта (первая с управляющей конструкцией Попытка, а вторая с транзакцией), то предпочтение следует, видимо, отдать первому варианту. На этот счет есть по меньшей мере 3 причины. Первая в том, что нет необходимости рассматривать расчет объекта как единое целое и употреблять для него транзакцию; вполне приемлемо, если расчеты будут выполняться последовательно в соответствии с установленной очередностью, и нет трагедии, если между двумя вашими расчетами выполнится один-другой расчет иного пользователя. Во-вторых, как мы убедились, при длительных транзакциях ждущие процедуры могут завершаться аварийно. И наконец, транзакция может потребовать дополнительных ресурсов (пространственных, например в виде области памяти на диске, и временных), снижающих эффективность исполнения программы.

8.5. ЗАЩИТА ДАННЫХ

8.5.1. ПОСТАНОВКА ЗАДАЧИ

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

В целом в 1С достаточно средств для защиты данных. Однако в ряде случаев для упрочнения защиты требуется вмешательство программиста.

Остановимся на проблеме ограничения прав доступа. (Иные проблемы либо частично затрагивались по мере изложения материала, либо находятся за пределами настоящего пособия.)

Изначально пользователю можно присвоить имя, назначить интерфейс, пароль и снабдить определенными правами доступа (разд. 1.2, 1.3).

Наборы прав задаются на закладке Права окна Конфигурация (рис. 8.2).

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 8.2. Задание набора прав выполняется на этой закладке
Для заданного набора прав, если по строке с именем набора дважды ударить мышью, откроется редактор прав (рис. 8.3).

Y Ученик. - Редактор пользе

|Я i^j} Конфигурация Зарплата ¦ Кадры (от 15.01.200

> аіиаия

К Я Слраео’аыки К CJ Докірлвмты к ы Журналы В К] Отчеты № ,sj Обработки "Y План счетов ^ Операция % Проводка & Т иловая операция Я) Регистры ?© Журналы расчетов Ж Ц Календари

Рис. 8.3. Редактор пользовательских прав

В этом редакторе для выбранного набора прав (в нашем случае - Ученик) можно определить права Ученика в отношении каждого класса объектов в целом (рис. 8.4) и отдельных представителей классов (рис. 8.5).

Y Ученик - Редактор пользовательски» прав

Конфигурация Зарплата * Кадры (от 15 01.2001)

>’ S Константы

іпраеочниг И

свойства элемента прав

_іЫ

Достиг I

? Чтение

Любые изменения

Ввод нового

удаление

Пометка ма удаление

Отмена

Снятие пометки на удаление

) осре»тмрое* а

Обновить

Рис. 8.4. Задание прав доступа Ученика ко всем справочникам системы

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 8.5. Детализация прав Ученика в отношении справочника Сотрудники_2
Напоследок надо не забыть задать права по управлению конфигурацией системы (рис. 8.6).

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 8.6. Задание прав по управлению системой
Далее встает проблема ограничения прав доступа в пределах справочника Сотрудники_2, журнала Зарплата_2 и других журналов. Суть проблемы в том, что начисление зарплаты осуществляется несколькими расчетчиками, которые, разумеются, используют один и тот же справочник сотрудников, журнал расчетов и одинаковые журналы документов. И в отношении к имеющимся в этих объектах данным расчетчики обладают равными правами. Более того, они, как правило, пользуются единым по своему содержанию интерфейсом.

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

Реализация этих ограничений проста. Создается справочник ПраваРасчетчика; в нем для каждого расчетчика указываются подразделения, к которым он имеет доступ. Далее эта информация используется при работе со справочниками и журналами 1С.

Используется следующим образом. В модуль формы списка справочника Сотруд-ники_2 мы добавим две предопределенные процедуры - ПриВыбореРодителя и При-СменеИерархии. В первой мы разрешим каждому расчетчику открывать только доступные ему в соответствии со справочником ПраваРасчетчика группы (подразделения), а во второй запретим всем расчетчикам переходить в режим просмотра всего справочника. При открытии справочника мы зададим режим отображения данных справочника с учетом имеющейся иерархии (см. рис. 5.1).

В модуль формы списка журнала Зарплата_2 добавляется предопределенная процедура ПриУстановкеОтбора, позволяющую расчетчику работать только с подразделениями, назначенными ему справочником ПраваРасчетчика.

В модуле формы списка журнала Зарплата_2 придется также модифицировать процедуру ПриОткрытии, которая теперь, если ЖЗ открывает расчетчик, будет подбирать для работы соответствующие ему подразделения.

Замечание. Рассматриваемые мероприятия по ограничению доступа к данным не являются исчерпывающими, но раскрывают механизм решения этой проблемы.

8.5.2. СПРАВОЧНИК ПраваРасчетчика

Реквизиты и свойства справочника задаются в соответствии с рис. 8.7, а описание реквизитов см. в табл. 8.1.

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 8.7. Свойства и реквизиты справочника ПраваРасчетчика
Таблица 8.1

Реквизиты справочника ПраваРасчетчика
Реквизит Описание Примечание
имяПользователя Имя, с которым расчетчик входит в систему. Задается в конфигурации Строка длиной в 15 символов
подразделение Подразделение, с которым может работать расчетчик. Вводится как группа справочника Сотрудники_2 Имеет разновидность типа Справочник.Сотрудники_2
Правами ввода и редактирования данных справочника обладает только администратор системы.

Редактирование данных осуществляется в форме списка, и после заполнения справочник будет содержать отображенные на рис.'8.8 сведения.

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 8.8. Имена пользователей предприятия и обслуживаемые ими подразделения
Чтобы в справочнике Сотрудники_2 можно было выбирать группы (подразделения), в модуль формы ФормаДляВыбора мы добавили следующий код:

// Предопределенная процедура ПриОткрытии процедура ПриОткрытии()

ВыборГруппы(1); конецПроцедуры // ПриОткрытии-

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

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 8.9. Задание имен пользователей и их прав в конфигурации системы

8.5.3. МЕНЮ И ПРАВА ПОЛЬЗОВАТЕЛЕЙ

В меню пользователя Ученик добавим возможность вызова вновь созданного справочника ПраваРасчетчика, оставив иные пункты меню без изменений. Для работника, отвечающего за кадры, создадим интерфейс Кадры, меню которого построим в соответствии с рис. 8.10.

‘Щэ Документы '—В Приказ о приеме -В Изменение оклада

j=j Справочники —0 Сотрудники_2 —В 0бразование_2

^ Журналы документов 'Щ} Отчеты «• В Кадровые приказы "В Список сотрудкмкое

Рис. 8.10. Меню для лица, регистрирующего кадровые изменения

Также работнику отдела кадров доступен справочник Дети, подчиненный справочнику Сотруцники_2. В отношении указанных в меню объектов пользователь под именем Кадры имеет полные права.

Расчетчик сможет работать с объектами, показанными на рис. 8.11.
ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 8.11. Меню расчетчика
Причем справочник Сотрудники_2 доступен расчетчику только в режиме чтения.

Замечание. Колонка Операции меню системы отключается в обоих создаваемых пользовательских меню. Порядок ее отключения иллюстрирует рис. 1.4.

Иные введенные нами операции, такие, как удаление помеченных объектов, модификация констант, редактирование справочников ПраваРасчетчика, хозяйственных операций и ХозОпДляВР, управление календарями и праздниками, возложим на лицо, выполняющее функции администрирования системы. Для этих целей у нас имеется интерфейс Ученик. В то же время необходимо ограничить права этого лица по отношению к данным, создаваемым в отделе кадров и расчетном отделе. Эти данные должны быть доступны ему только в режиме просмотра.

8.5.4. МОДИФИКАЦИЯ МОДУЛЯ ФОРМЫ СПИСКА ЖУРНАЛА ЗАРПЛАТА_2

Суть вносимых изменений отражена в разд. 8.5.1. Однако, прежде чем заняться кодированием, несколько слов придется сказать об изменениях интерфейса ЖЗ. Во-первых, если работает расчетчик, то нужно отключить возможность вывода ЖЗ с применением закладок отбора 01/1,01/2, 01/3, 02 Цех и 03 Цех (см. рис. 7.26). Дело втом, что ограничить доступ к закладке отбора встроенными средствами 1С нельзя. Это выясняется, когда пытаешься применить предопределенную процедуру модуля формы ПриВыбореЗакладки: она работает лишь с атрибутом формы Закладки, имеющим тип Список значений. Метод ЗакладкиОтбора этот атрибут не изменяет. Так, после применения метода

ЗаклацкиОтбораС'Родитель");

формирующего в случае ЖЗ вышеперечисленные закладки отбора, процедура Сообщить(ТипЗначения(Форма. Закладки));

напечатает в окне сообщений 0 - число, выдаваемое встроенной функцией ТипЗначе-ния для неопределенного типа данных.

Во-вторых, нужно позаботиться о том, чтобы не изменились функции радиокнопок с идентификатором кто группы Режим расчета.

Таким образом, переменная закл при входе расчетчика должна быть равна единице, но закладки отбора отображаться не должны и, конечно же, должны быть в соответствии со справочником ПраваРасчетчика введены ограничения на рабочее пространство расчетчика. Если в систему входит пользователь с правами Ученика, то ее функционирование должно отвечать привычной, изложенной в разд. 7.4.3 схеме. Озвученные изменения поддерживаются следующим кодом:

// Определяем сПраваРасч, расч, значениеОтбора и флагРасч как переменные модуля перем сПраваРасч, расч, значениеОтбора, флагРасч;

// Следует уже имеющийся в модуле код (разд. 7.4.4.1) // Предопределенная процедура модуля формы ЖР

// Эта процедура добавляется в модуль формы списка ЖЗ Зарплата_2

// Интерактивно отбор управляется иконками 5* У*

// лежащими на панели управления ЖЗ

процедура ПриУстановкеОтбора(графаОтбора, значениеОтбора)

если флагРасч = 0 тогда // Если работает не расчетчик, то

возврат; // контроль прав не осуществляется

конецЕсли;

// Простым перебором устанавливаем, можно ли расчетчику расч работать // с интерактивно выбираемым им подразделением сПраваРасч.ВыбратьЭлементы(); пока сПраваРасч.ПолучитьЭлемент() = 1 цикл

если (расч = СокрЛП(сПраваРасч.ИмяПользователя)) и

(сПраваРасч.Подразделение = значениеОтбора) тогда СтатусВозврата(1); // Работать можно

возврат; // Выход из процедуры

конецЕсли; конецЦикла; / / пока

СтатусВозврата(0); // Работать нельзя

Предупреждение("Это не Ваше подразделение."); конецПроцедуры // ПриУстановкеОтбора

процедура При6ткрытии() // Предопределенная процедура

перем реж, значРеж;

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

закл = ВосстановитьЗначение("ЗакладкиВЗарплате");

если ТипЗначения(закл) = 0 тогда // Если значение переменной закл не восстановлено закл = 1; кто = 1; иначе

кто = ВосстановитьЗначение("КтоВЗарплате"); конецЕсли; ктоСтар = кто;

ПоЦехам( );

если флагРасч = 0 тогда // Добавленная строка кода

если закл = 1 тогда

значОтбора = ВосстановитьЗначение("ЗначениеОтбора"); ЗакладкиОтбора("Родитель", значОтбора); конецЕсли;

иначе // Еще 5 добавленных строк кода

Форма.Закл.Видимость(О); // Элемент диалога закл расчетчику не нужен

// Установим разрешенный для расчетчика расч отбор УстановитьОтбор(''Родитель'', значениеОтбора); конецЕсли;

реж = ВосстановитьЗначение(''РежимПредставления''); значРеж = ВосстановитьЗначение("ЗначениеПредставления"); если реж = 1 тогда

УстановитьПредставление( 1); иначе

УстановитьПредставление(реж, значРеж); конецЕсли;

нтп = НачалоТекущегоПериода(); конецПроцедуры // ПриОткрытии

// Включает/отключает режим вывода по цехам. Если закл = 0, то устанавливает // кто = 1 и делает недоступными радиокнопки группы Режим расчета процедура ПоЦехам()

// Эти 3 строчки добавляются в прежнюю процедуру ПоЦехам (разд. 7.4.4.1) если флагРасч = 1 тогда // Начало добавляемого кода

возврат;

конецЕсли; // Конец добавляемого кода

если закл = 1 тогда // Если используются закладки отбора

ЗакладкиОтбора("Родитель"); кто = ктоСтар;

Форма. Кто.Доступность( 1);

Форма. Кто2.Доступность( 1); иначе

ЗакладкиОтбора(""); кто= 1;

Форма. Кто.Доступность(0);

Форма. Кто2 .Доступность(0); конецЕсли;

конецПроцедуры // ПоЦехам()

// А это оператор основной программы модуля формы списка ЖЗ // Переменная модуля флагРасч получит значение 1, если в системе расчетчик, .

// или О-в противном случае. Функция ЕстьЛиРасчетчик включена в глобальный модуль флагРасч = ЕстьЛиРасчетчик(расч, сПраваРасч, значениеОтбора);

Замечания:

1. Функция ЕстьЛиРасчетчик включается в глобальный модуль. Это целесообразно, потому что проверка наличия расчетчика в системе выполняется неоднократно. Можно было бы пойти дальше - включить в глобальный модуль переменную флагРасч, дать ей атрибут Экспорт и вычислять ее значение при загрузке системы, вызывая функцию ЕстьЛиРасчетчик в предопределенной процедуре глобального модуля ПриНачалеРаботыСистемы, но мы остановимся на первом варианте. Функция ЕстьЛиРасчетчик имеет следующий код: // А это функция, которую мы включаем в глобальный модуль

// Следует после всех его программных компонентов

функция ЕстьЛиРасчетчик(расч, сПраваРасч, значениеОтбора) экспорт

// Этот пользователь работает в системе и открывает форму списка ЖЗ Зарплата 2

расч = ИмяПользователя();

сПраваРасч = СоздатьОбьект(''Справочник.ПраваРасчетчика'');

// Смотрим, является ли этот пользователем расчетчиком. Если да, то флагРасч получит // значение 1, или 0 - в противном случае. Проверку осуществляем простым перебором сПраваРасч.ВыбратьЭлементыО;

флагРасч = 0; // Первоначальное предположение

пока сПраваРасч.ПолучитьЭлемент() = 1 цикл

если расч = СокрЛП(сПраваРасч.имяПользователя) тогда флагРасч =1; // В системе расчетчик

значениеОтбора = сПраваРасч.Подразделение; прервать; // Прерываем цикл

конецЕсли; конецЦикла; // пока возврат флагРасч; конецФункции // ЕстьЛиРасчетчик

2.

3.

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

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

ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА
Рис. 8.12. Фрагмент диалога Отбор записей: возможные отборы ЖЗ Зарплата_2 Подробнее об управлении отборами см. в разд. 5.9.1.

8.5.5. МОДИФИКАЦИЯ МОДУЛЯ ФОРМЫ СПИСКА СПРАВОЧНИКА СОТРУДНИКИ_2

Аналогичные дополнения следует сделать в модулях форм списка и для выбора справочника Сотрудники_2. Наша задача в том, чтобы запретить расчетчику переход в режим просмотра списка всех сотрудников, а также не допускать проникновения в запрещенные для него подразделения. В отличие от ЖЗ используемый в справочнике Сотрудники_2 отбор по реквизиту Образование отключать не надо, поскольку отбор осуществляется в пределах выбранного родителя. (Напомним, что расчетчику справочник Сотрудники_2 доступен только в режиме чтения.)

I// Определяем сПраваРасч, расч, родитель и флагРасч как переменные модуля перем сПраваРасч, расч, родитель, флагРасч;

// Следует уже имеющийся в модуле код (разд. 5.12.6)

// Предопределенная процедура модуля формы справочника // Эта процедура добавляется в модули форм списка // (основной и для выбора) справочника Сотрудники_2 процедура ПриВыбореРодителя(родитель)

если флагРасч = 0 тогда // Если работает не расчетчик, то

возврат; // контроль прав не осуществляется

конецЕсли;

// Простым перебором устанавливаем, можно ли расчетчику расч работать // с интерактивно выбираемым им подразделением сПраваРасч.ВыбратьЭлементы(); пока сПраваРасч.ПолучитьЭлемент() = 1 цикл

если (расч = СокрЛП(сПраваРасч.ИмяПользователя)) и (сПраваРасч.Подразделение = родитель) тогда

СтатусВозврата(1); // Можно менять родителя
возврат; //Выход из процедуры
конецЕсли;
конецЦикла; // пока
СтатусВозврата(0); // Родителя менять нельзя
Предупреждение("Это не Ваше подразделение."); конецПроцедуры // ПриВыбореРодителя
// Предопределенная процедура модуля формы справочника // Эта процедура добавляется в модули форм списка // (основной и для выбора) справочника Сотрудники_2
процедура ПриСменеИерархии(иерарх)
если флагРасч = 0 тогда // Если работает не расчетчик, то
возврат; // контроль прав не осуществляется
конецЕсли;

если иерарх = 1 тогда
// Разрешим устанавливать режим
возврат; // иерархического доступа к записям
конецЕсли; // справочника Сотрудники_2
СтатусВозврата(0); // Нельзя просматривать весь список
Предупреждение("Доступ к списку всех сотрудников невозможен.");
конецПроцедуры // ПриСменеИерархии
процедура ПриОткрытии() // Предопределенная процедура
// Этот оператор нужен в форме списка для выбора (разд. 8.5.2);

// в основной форме списка его нужно опустить ВыборГруппы(1);

// Если работает расчетчик, то отображаем справочник Сотрудники_2 // в виде иерархического списка если флагРасч = 1 тогда

ИерархическийСписок(1);

// Позиционируемся в первой разрешенной для расчетчика/?асч группе ИспользоватьРодителя(родитель); конецЕсли;

конецПроцедуры // ПриОткрытии

// А это оператор основной программы модуля формы списка справочника // Переменная модуля флагРасч получит значение 1, если в системе расчетчик,

// или О-в противном случае. Функция ЕстьЛиРасчетчик включена в глобальный модуль флагРасч = ЕстьЛиРасчетчик(расч, сПраваРасч, родитель);

Замечание. Процедуру ПриСменеИерархии лучше изъять из кода, а в предопределенной процедуре ПриОткрытии метод ИерархическийСписок вызвать следующим образом:

если флагРасч = 1 тогда // Нельзя менять режим отображения

ИерархическийСписок(1, 0); //справочника Сотрудники_2

// Позиционируемся в первой разрешенной для расчетчика расч группе ИспользоватьРодителя(родитель); иначе

ИерархическийСписок(1, 1); // Можно менять режим отображения

конецЕсли;

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



Содержание раздела