Один день из жизни сисадмина – Admin-Day

Блог системного администратора об it индустрии и околокомпьютерных тем. Жизненный опыт и истории в статьях и заметках. Один день из жизни сисадмина

Коротко о принципах написания бухгалтерской системы

бух системаКак вы уже, наверняка, догадались, речь в этой заметке пойдет о разработке бухгалтерского “софта”. Было дело, несколько лет назад мне довелось сопровождать парочку бухгалтерских систем, поэтому я рискну высказать пару рекомендаций на тему “как обустроить бухгалтерию”. Хочу сразу сказать, что речь пойдёт о реализациях на платформе реляционных баз данных, а мои советы адресованы в первую очередь тем заказчикам и разработчикам, которые стремятся объединить транзакционную и аналитическую базы данных.
Конечно, вы можете сразу задать вопрос – а почему возникает необходимость написания системы “с нуля”? Почему бы не построить систему, например, на базе “1С-предприятия” или какого-либо из его аналогов? Оказывается, не всё так просто, как кажется. Дело в том, что покупая “закрытую” систему, вы неизбежно принимаете все действующие в этой системе ограничения, и нередко оказывается, что нужного эффекта можно достичь только, как говорится, “танцем с бубнами”. Кроме того, система может быть недостаточно надёжной и плохо работать на больших объёмах данных. Если вспомнить любимую в России 1С, то я не уверен, что она “выживет” на 10 млн. проводок. В отличие от “закрытых” систем, разработки на платформе реляционных СУБД типа Oracle, Sybase, Informix позволяют, с одной стороны, относительно легко решать оптимизационные проблемы, а с другой стороны – использовать всю мощь ОС Unix. Кроме того, не придётся мучиться с непродуманными, ограниченными (если не сказать, убогими) средствами разработки интерфейса, которыми часто отличаются “закрытые” системы.

Чтобы не быть голословным, я рассмотрю некую воображаемую систему, похожую на реально сопровождаемую мной программу. Эту гипотетическую систему я буду использовать для моделирования процесса взаиморасчетов (клиринга) между фирмами, непосредственно предоставляющими туристические услуги и агентами по продаже туристических путёвок. Все организации, участвующие в клиринге, можно поделить на четыре категории: кемпинги (пансионаты), агентства по продаже путёвок, страховые компании различного профиля и клиринговая палата (КП), координирующая взаиморасчёты. Ключевыми в нашей схеме являются сущности “План счетов”, “Проводка” и “Документ”. Как известно, каждая проводка соответствует финансовой операции по передвижению “суммы проводки”со счёта, задаваемого значением поля “счёт по расходу”, на счёт, задаваемый полем “счёт по приходу”. Документ является финансовой макрооперацией и содержит несколько проводок, выполняемых в заданном порядке. Если таблицы плана счетов, документов и проводок являются основным звеном практически любой бухгалтерской системы, то таблицы “Отчет о продаже путевок” и “Продажи путевок” уже специфичны для описываемой нами схемы туристического бизнеса.


Как я уже говорил, каждое агентство ежемесячно отчитывается перед КП. При этом агентства, во-первых, присылают сам отчёт о продажах в электронном или бумажном виде, а во-вторых, переводят на счёт КП деньги для оплаты отчетов. Переводы могут полностью покрывать сумму отчётов, а могут быть частичными, поэтому один и тот же отчёт может оплачиваться в несколько приемов. Это означает, что одному отчёту может соответствовать несколько документов. В принципе, возможны ситуации, когда несколько отчётов оформляется в виде одного финансового документа. Несложно видеть, что отношение между сущностями “Документ” и “Отчет о продаже путёвок” – типа “многие ко многим”. Это отношение реализуется с помощью связующей таблицы “Продажи путевок”.

Ещё раз повторюсь, что основой всей этой схемы является тройка таблиц: документы, счета и проводки, поэтому в первую очередь – нужно отладить процедуры проведения (в том числе задним числом) и отмены документов. Не забывайте, что при добавлении каждой новой проводки, программа должна списывать сумму проводки с одного счёта и переводить её на другой. Это должно работать при любой погоде и вне зависимости от настроения начальника! Лучше всего реализовать этот механизм с помощью триггеров. Но будьте

бдительны: практически невозможно отследить план выполнения запроса внутри триггера, а изменение порядка обработки соединения таблиц в SQL-запросе может существенно увеличить время выполнения этого запроса. Забегая вперед, скажу, что лучше использовать подсказки оптимизатору. В таблице документов, не считая поля “id документа”, всего два атрибута – “дата” и “тип”. Эти два атрибута являются самыми важными в таблице документов. Почему? Ответ прост: индексирование по дате и типу документу – краеугольный камень эффективного формирования отчётов.

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

Чтобы получить “чистый” приход, надо вычесть из полученной величины сумму проводок, в которых счет “выручка” – расходный. Поскольку в процессе функционирования программы количество проведенных документов может составлять сотни тысяч, и даже миллионы – без индекса по дате такое вычисление может затянуться на месяц. По той же самой причине необходимо поле “тип документа”: для того, чтобы максимально быстро выбирать документы, относящиеся к конкретному типу финансовых операций – покрытию отчетов, переводу средств между счетами, или, скажем, выполнению платежных поручений (ПП). Казалось бы, совершенно тривиальные наблюдения, но в сопровождаемой мной системе они были выстраданы. Так, изначально в ней отсутствовала индексация по дате документа, а документы разных типов в некоторых случаях имели одно и то же значение поля “тип документа”. Иногда приходилось реализовывать запутанные алгоритмы определения типа финансовой операции по номерам счетов проводок документа.

Хочу дать еще одну “тривиальную” рекомендацию, которая применима не только к бухгалтерским системам: чаще используйте избыточность представления данных. Так, в нашем примере имеет смысл перенести в таблицу документов поля “агентство” и “кемпинг”- чтобы выбирать документы по оплате отчетов некоторых заданных агентов без обращения к таблице “Отчёты о продажах путевок”.
Можно выразить эту простую мысль и несколько по-другому: храните промежуточные значения. Одним частным, но очень важным случаем промежуточных значений являются величины сумм на счетах агентов и кемпингов (так называемые сальдо). В нашей системе имеет смысл отдельно хранить сальдо счетов на начало и конец каждого обработанного периода. Прочитав предыдущий абзац, некоторые разработчики могут возразить мне, сказав, что избыточность хранения – это уход от нормализации базы данных и замедление выполнения транзакций. Сразу хочу сказать, что это справедливо только в случае, если есть две базы: транзакционная и аналитическая. Спрашивается, а где вы видели такое в российских условиях? Кроме того, вопреки довольно распространенному мнению, нормализация может иногда существенно “затормозить” даже чисто транзакционную базу данных. Простой пример: автоматическая нумерация платежных поручений (ПП). Чтобы реализовать возможность оплаты ПП, расширим нашу систему новой сущностью “Платежное поручение” и сущностью-связкой “Оплата ПП”. Раз уж речь зашла об оптимизации, нельзя умолчать о самих оптимизаторах СУБД.
Работая с СУБД Sybase (далеко не худший продукт на рынке), я пришел к неутешительному выводу: не давайте оптимизатору не единого шанса! Как это ни печально, оптимизатор склонен “заваливаться” на элементарном соединении по двум таблицам – если, например, сисадмин забыл обновить статистику таблиц. Поэтому, чтобы избежать неожиданного замедления работы системы, используйте подсказки оптимизатору. Оптимизатор по определению не знаком с предметной областью, и не всегда может знать параметры запроса. Не надейтесь, что он построит план запроса лучше вас! Если СУБД не позволяет явно указать индекс или порядок выполнения соединения, такой продукт просто нельзя использовать – если, конечно, вы не собираетесь программировать на API низкого уровня. Платёжное поручение (далее – ПП) является, по сути, переводом денег с некоторого расчетного счёта (Р/СЧ) на счёт получателя платежа в другой организации. Каждая “платёжка” обладает своим номером, на единичку превосходящим номер последнего ПП, выполненного с того же расчетного счёта. Программа должна осуществлять нумерацию автоматически, заметьте, что для этого требуется найти ПП, выполненное с того же Р/СЧ, с наибольшим номером. Если вы “забудете” построить соответствующий индекс, то система просто “умрёт”, выполняя единственную операцию несколько секунд или даже минут! Однако, даже если проиндексировать поля “номер ПП” и “расчётный счёт” – всё равно остается проблема блокировок, потому что нужно блокировать таблицу ПП на время выполнения операции, чтобы другой процесс не мог занести туда запись – и изменить, таким образом, максимальный номер платежки. Кроме того, индекс “тормозит ” занесение новых записей, если таблица большая. Все проблемы легко решаются при помощи дополнительной таблицы с двумя полями: “расчётный счёт” и “максимальный номер ПП”, в которой будет храниться максимальный использованный номер ПП для каждого расчетного счета.

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

И в заключение – несколько слов о сопровождении клиент-серверных приложений. Поскольку в России нормативные акты, касающиеся финансовой деятельности в целом и бухучета в частности, сыплются как из рога изобилия – программы приходится менять очень часто. И процессу тестирования, как водится, много времени не уделяется. Значит – очень важно, чтобы изменение одного куска кода не приводило к неработоспособности всей программы. Лучше до минимума сократить зависимости между модулями и таблицами, клиентским и серверным ПО, а также уметь такие зависимости отслеживать. К тому же, необходимо ещё и отслеживать зависимости клиентских модулей от серверных. Разумеется, любая реальная бухгалтерская система будет гораздо сложнее рассмотренной модели, и проблем с её разработкой возникает гораздо больше. Тем не менее – надеюсь, что мои заметки могут быть полезны не только программистам, планирующим написать БД “с нуля”, но и тем, кто сопровождает уже написанную систему.

Поделиться ссылкой:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Пролистать наверх
Мы используем cookie-файлы для наилучшего представления нашего сайта. Продолжая использовать этот сайт, вы соглашаетесь с использованием cookie-файлов.
Принять