При разработке учетной конфигурации для сферы ЖКХ и коммунальных расчетов начинающие разработчики часто совершают типовую ошибку: создают отдельные справочники, документы и регистры под каждый вид услуги (газ, вода, электроэнергия). Это приводит к неоправданному разрастанию структуры метаданных, усложнению программного кода, форм отчетов и трудностям при добавлении новых услуг.
Разберем по шагам, как грамотно построить архитектуру решения на платформе 1С 7.7, организовать структуру регистров, настроить хранение тарифов и реализовать разграничение доступа для пользователей.
Вместо пложения однотипных объектов метаданных (ОплатаГаза, ОплатаЭлектроэнергии, ОплатаВоды) спроектируем универсальную модель. Рассмотрим основные элементы структуры:
Абоненты (или ЛицевыеСчета): содержит данные о плательщиках. Для привязки к сеансу пользователя добавим реквизит Логин (тип «Строка»).Услуги: содержит перечень коммунальных услуг (водоснабжение, газоснабжение, электроэнергия, отопление).Тарифы: цены на услуги. Для хранения тарифов используем периодический реквизит Цена (или Тариф), так как стоимость услуг регулярно меняется со временем.ВводПоказанийСчетчиков: фиксирует текущие показания приборов учета по абоненту и услуге, рассчитывает объем потребления и формирует сумму начисления.ОплатаУслуг: фиксирует факт поступления денежных средств от абонента в разрезе конкретной услуги или по общему сальдо.Проанализируем структуру регистров. Для решения задачи начисления и взаиморасчетов бухгалтерский план счетов и проводки не всегда удобны, так как баланс в рамках простой подсистемы не сойдется. Оптимальным решением является использование механизма оперативного учета (регистров накопления).
Для корректного учета нам потребуются два регистра типа «Остатки»:
ПоказанияСчетчиков:
Абонент (Справочник.Абоненты), Услуга (Справочник.Услуги).Значение (Число) — фиксирует текущее конечное показание прибора учета.Взаиморасчеты:
Абонент, Услуга.Сумма (Число) — положительный остаток отражает задолженность абонента, отрицательный — переплату (аванс).Выясним логику движений регистров при проведении документов:
ВводПоказанийСчетчиков:
ПоказанияСчетчиков на момент документа.Расход = ТекущееПоказание - ПредыдущееПоказание.Тариф = СпрТарифы.Цена.Получить(ДатаДок).СуммаНачисления = Расход * Тариф.ПоказанияСчетчиков с новым значением.Взаиморасчеты на сумму начисления.ОплатаУслуг:
Взаиморасчеты на сумму платежа.Посмотрим на пример логики проведения документа ввода показаний в модуле документа 1С 7.7:
Процедура ОбработкаПроведения()
// Получаем предыдущие показания по регистру
РегПоказания = СоздатьОбъект("Регистр.ПоказанияСчетчиков");
РегПоказания.УстановитьЗначениеФильтра("Абонент", Абонент);
РегПоказания.УстановитьЗначениеФильтра("Услуга", Услуга);
РегПоказания.Остатки(ТекущийДокумент(), 1);
ПредыдущееПоказание = РегПоказания.Значение;
Если НовоеПоказание < ПредыдущееПоказание Тогда
Сообщить("Новое показание не может быть меньше предыдущего!");
НеПроводить();
Возврат;
КонецЕсли;
Расход = НовоеПоказание - ПредыдущееПоказание;
// Получение периодического тарифа на дату документа
Тариф = Услуга.Тариф.Получить(ДатаДок);
СуммаНачисления = Расход * Тариф;
// Движение по приборам учета
Регистр.ПоказанияСчетчиков.Абонент = Абонент;
Регистр.ПоказанияСчетчиков.Услуга = Услуга;
Регистр.ПоказанияСчетчиков.Значение = НовоеПоказание;
Регистр.ПоказанияСчетчиков.ДвижениеРасходВыполнить();
// Движение по взаиморасчетам (увеличение долга абонента)
Регистр.Взаиморасчеты.Абонент = Абонент;
Регистр.Взаиморасчеты.Услуга = Услуга;
Регистр.Взаиморасчеты.Сумма = СуммаНачисления;
Регистр.Взаиморасчеты.ДвижениеПриходВыполнить();
КонецПроцедуры
В платформе 1С 7.7 отсутствует механизм RLS (Record Level Security), который позволяет на уровне СУБД фильтровать данные по правам доступа. Поэтому разграничение доступа для абонентов реализуется программными методами интерфейса.
Разберем основные шаги по изоляции данных абонента:
ИмяПользователя() и находим соответствующий элемент в справочнике Абоненты по совпадению с реквизитом Логин.ПриОткрытии() документов оплаты и ввода показаний автоматически подставляем найденного абонента и программно отключаем доступность поля ввода для исключения выбора другого лицевого счета.ПриЗаписи() обязательно проверяем соответствие реквизита Абонент текущему авторизованному пользователю.УстановитьОтбор("Абонент", ТекАбонент), скрывая записи других пользователей, а также отключаем стандартные панели навигации и меню «Операции» для профиля абонента.Посмотрим на пример процедуры инициализации формы документа:
Процедура ПриОткрытии()
Если НазваниеНабораПрав() = "Абонент" Тогда
СпрАбоненты = СоздатьОбъект("Справочник.Абоненты");
Если СпрАбоненты.НайтиПоРеквизиту("Логин", ИмяПользователя(), 1) = 1 Тогда
Если Выбран() = 0 Тогда
Абонент = СпрАбоненты.ТекущийЭлемент();
КонецЕсли;
Форма.Абонент.Доступность(0);
Иначе
Предупреждение("Пользователь не сопоставлен с карточкой абонента!");
СтатусВозврата(0);
Возврат;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Использование единых документов и связанных регистров позволяет создать масштабируемую систему, пригодную для добавления любых видов коммунальных услуг без изменения базовой структуры конфигурации.