Dsvolk > > Oracle > > Dvp > > Billprof My Blog | Search | About
(Not Logged In)
[ welcome! ] [ news ] [ install ] [ jump-jet ] [ app ] [ rac ] [ papers ] [ dba ] [ dvp ] [ racdd4d ] [ oem ] [ statspack ] [ education ] [ tuning ] [ ias ] [ backup ] [ dataprotection ] [ security ] [ oid ] [ options ] [ integration ] [ sales ] [ sun ] [ linux ] [ consulting ] [ faq ]

BillProf pl/sql lib

Соглашение о материалах на этом сайте

Мой oracle blog
true dsvolk!
пишем аккуратно  
Я писал на pl/sql достаточно много. Начиная с версии Oracle 7. Но оформить свою библиотеку удалось не так давно. Ее гордое :))) название BillProf. Это от того, что собралась она в момент разработки билинговой системы. Вся библиотека, это около 500K исходных текстов, тестов на pl/sql и немного java. Тестировалась и проверялась она на версии Oracle 8.1.7 на Sun Solaris. Пользоваться ей полностью или частями можно по принципу "AS IS". Очень приветствуется упоминание автора :)). 

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

Общие принципы

Принципы разработки собственно биллинговой системы собраны в виде архива документов. В этом архиве перечислены сущности планировавшейся системы, ее основные функции. Составлением формальных документов занималась команда в составе: Валера Юхно (vpu), Ян Дербенко(yan), Андрей Колесников (mantis), Дима Козлов (dimka) и я (dsvolk). И права на эту документацию принадлежат Элвис-Телеком.

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

Java модули, кроме jakarta-ora написаны Сергеем Шарана (Модуль). Jakarta-ora - Apache Jakarta Project

Некоторые процедуры написаны по идеям Тоm Kyte.

При разработке бизнес-модулей активно использовался Oracle Designer 6.4 и его table API.  Т.е. физическая модель данных проектировалась в Designer, откуда с помощью Server Generator получался код таблиц, триггеров и пакетов Table API. Принципы  построения кода можно найти здесь.

Вся библиотека разбита на модули, каждый модуль имеет 2(3)-х буквенное обозначение. Часть модулей относиться к Business API (BAPI) и не отделима от структуры данных, другая часть, напротив носит вспомогательный характер и легко может быть отделена. К сожалению не все модули находятся в одинаковой степени завершенности :((.  

В таблице ниже я даю расшифровку модулей  и к какому типу они относятся. 

Наименование  Назначение  Тип 
CG Пакет cg$errors из стандартной поставки Designer  Вспомогательный 
CM Context Managment. Этот модуль отвечает за обработку ошибок (cm_error),
хранение всех констант библиотеки(cm_const), поддержку разных языков сообщений (cm_utils). Благодаря ему в коде библиотеки нет зашитых сообщений, все они храняться в структурах CM.
Вспомогательный
CRD CaRD. Подсистема работы с интернет-картами. Учет производства карт, передача их на реализацию,  активизация и т.п. Постановка задачи - mantis. BAPI
DNS DNS. Хранение информации о DNS. BAPI
FM Finance and Marketing. База клиентов, актов и счетов-фактуры.  BAPI
GL General Ledger. Счета, остатки, проводки.  BAPI
RG Registry. Возможность хранить произвольные типы данных. Этот модуль нужен как вспомогательный модулю IS.   Вспомогательный
SL Standart Library. Содержит пакеты отправки почты из Oracle sl_mail, работы с тектовыми файловыми шаблоными (sl_template), средство создание логов работы процедур (sl_log), пакет работы со стандартными регулярными выражениями - regexp (sl_regexp), утилиты работы со строками (sl_str), набор полезных разработчику подпрограмм (sl_utils) Вспомогательный
SS Security System. Набор и логика процедур описана в документе Security Implimetation.doc Вспомогательный
IS  Internet Services. Содержат BAPI для работы со всеми типами интернет-услуг BAPI
 

На файловой системе выдерживаются следующие соглашения: в корне проекта есть поддиректории 

PLSQL Исходные коды системы
DBA Утилиты для генерации исходного кода
WWW Файлы предназначенные для просмотра через WEB

Раздел PLSQL

 В директории PLSQL расположены модули библиотеки. В директории каждого модуля есть следующие поддиректории:

PLS Исходный код на pl/sql. 
Файлы pks - заголовки пакетов, pkb - тела пакетов. fnc - функции. typ - типы
SETUP Необходимые действия для установки пакета 
TAB Код создания объектов, которые не попали в репозиторий Designer
TEST Тесты данного модуля 
JAVA Исходный код на JAVA
SQL Внешние данные или вспомогательные SQL скрипты 

Вот как в результате выглядит простой модуль типа BAPI (код из файла fm_account.pkb):

-- Открыть позицию Плана управления Ресурсами 

PROCEDURE Ins (
p_fm_plan_string VARCHAR2 -- Строковое представление кода плана счетов 


IS
row cg$fm_accounts.cg$row_type;
ind cg$fm_accounts.cg$ind_type;
exist_errors BOOLEAN := false;
BEGIN
row.fm_plan_code:= fm_account_plan.string2code(p_fm_plan_string);    --- Заполняем массив для TAPI
ind.fm_plan_code:=true;

-- Вызов Table API
cg$fm_accounts.ins(row, ind);           ------ Вызываем TAPI
--

EXCEPTION WHEN cm_const.tapi_exception THEN
cm_error.ProcessError;     --- Единый обработчик всех ошибок
END;

 

Хочется похвалиться обработчиком ошибок. Он умеет, например, вместо сообщения о нарушении уникального ключа, дать сообщение которое Вы сопоставили с именем первичного ключа. Он умеет вместо наименований полей из БД сообщить их русские пользовательские названия (вместо account_no -> номер счета). Он умеет вместо нарушения foreing key, сообщить что на самом деле забыли завести. В общем, за внешней простотой обработчика скрывается его мощь :))

Чуть усложним задачу. Сделаем несколько проверок. Код из файла fm_client.pkb

PROCEDURE upd_denorm ( row IN OUT cg$fm_clients.cg$row_type , 
ind IN OUT cg$fm_clients.cg$ind_type )
IS
exist_errors BOOLEAN := false;
v_tab sl_str.plsql_table;
BEGIN 
-- Проверим что имя действительно введено на русском
IF (cm_utils.checkdomain ('cl_first_name', row.cl_first_name, 'RUSNAME' ) <> 1 ) THEN
exist_errors := true;
END IF;

-- Проверим что имя введено на английском
IF (cm_utils.checkdomain ('cl_international_name', row.cl_international_name, 'ENGNAME' ) <> 1 ) THEN
exist_errors := true;
END IF;


-- Сформулируем короткое имя 
IF (row.cl_short_name IS NULL ) THEN 
IF ( row.cl_tax_type = 'PERS' ) THEN
v_tab.DELETE;
sl_str.intable(v_tab, row.cl_first_name);
sl_str.intable (v_tab, row.cl_last_name);
row.cl_short_name := sl_str.table2string (v_tab, ' ') ; 
ELSE
row.cl_short_name := row.cl_last_name;
END IF;
ind.cl_short_name := true;
END IF;
--
IF ( exist_errors ) THEN   --- Эту переменную мы сами установили в true чтобы отметить 

-- что в стеке есть ошибки
cg$errors.raise_failure;
END IF; 
--
EXCEPTION WHEN cm_const.tapi_exception THEN
cm_error.ProcessError; 
END; 

В этом примере видно, что можно накапливать ошибки в стек. Т.е. пользователь получает ошибки не по одной, а сразу все что ему нужно исправить. И не "field cannot be NULL", а "поле наименование клиента должно быть заполнено". Естественно, что соответствие вы должны заполнить в модуле CM. Пример см. cm/setup/mrus.sql

И наконец "возбуждение" пользовательских ошибок (код из файла fm_order.pkb):

-- Удаляет деталь счета 
-- 
PROCEDURE DelDetail (
p_ord_det_id fm_order_details.ord_det_id%TYPE -- Первичный ключ детали счет 
)
IS
row cg$fm_order_details.cg$row_type;
ind cg$fm_order_details.cg$ind_type;
pk cg$fm_order_details.cg$pk_type;
exist_errors BOOLEAN := false;
--
v_ord_status fm_orders.ord_status%TYPE; -- Статус счета 
BEGIN
row.ord_det_id:=p_ord_det_id; ind.ord_det_id:=true;
cg$fm_order_details.slct(row );

-- Получим статус счета 
SELECT o.ord_status
INTO v_ord_status
FROM fm_orders o
WHERE o.ord_id = row.ord_id;

-- Можно редактировать открытый или частично оплаченный счет 
IF (v_ord_status NOT IN ('OPEN', 'PARTPAID') ) THEN 
cm_error.raisebapierror ('WRONG_ORDER_STATUS', v_ord_status ); 
END IF;

-- Если эта позиция была оплачена 
IF (row.closed_amount > 0 ) THEN 
cm_error.raisebapierror ('ORDER_DET_PAID');  -- Возбуждаем пользовательское сообщение
END IF;

pk.ord_det_id := p_ord_det_id;
cg$fm_order_details.del (pk);

-- Теперь надо общую сумму счета пересчитать 
update_summary (row, ind, 'DEL' ); 
--
EXCEPTION WHEN cm_const.tapi_exception THEN
cm_error.ProcessError;
END; 

Вы можете видеть, что реальное сообщение скрывается за идентификатором ORDER_DET_PAID.

В процедуре UpdCorporateAccount из файла fm_client.pkb Вы можете видеть процедуры проверки прав доступа и журналирования. 

---------------------------------------------------------------------------- 
-- Обновляем счета организации 
-- 
PROCEDURE UpdCorporateAccount
(
p_ca_id fm_corporate_accounts.ca_id%TYPE, -- Внутренний ключ
p_ca_bank_name fm_corporate_accounts.ca_bank_name%TYPE, -- Наименование банка
p_ca_settlement_acccount fm_corporate_accounts.ca_settlement_acccount%TYPE, -- Расчетный счет организации
p_ca_correspondent_account fm_corporate_accounts.ca_correspondent_account%TYPE, -- корреспонденский счет
p_ca_bik fm_corporate_accounts.ca_bik%TYPE, -- Бик банка
p_notes VARCHAR2 -- Объяснение причины изменения
)
IS
row cg$fm_corporate_accounts.cg$row_type;
ind cg$fm_corporate_accounts.cg$ind_type;
exist_errors BOOLEAN := false;
BEGIN

row.ca_id:=p_ca_id; ind.ca_id:=true;
cg$fm_corporate_accounts.lck (row, ind);


row.ca_bank_name:=p_ca_bank_name; ind.ca_bank_name:=true;
row.ca_settlement_acccount:=p_ca_settlement_acccount; ind.ca_settlement_acccount:=true;
row.ca_correspondent_account:=p_ca_correspondent_account; ind.ca_correspondent_account:=true;
row.ca_bik:=p_ca_bik; ind.ca_bik:=true;

-- Security Check for staffs
ss_auth (row.branch, row.access_group, row.access_level)
-- Сохранить в журнальной таблице
row.jn_notes := p_notes;
cg$fm_corporate_accounts.insert_jn (row, 'UPD');
-- Изменить значения
cg$fm_corporate_accounts.upd(row, ind);
--
IF ( exist_errors ) THEN
cg$errors.raise_failure;
END IF;
--
EXCEPTION WHEN cm_const.tapi_exception THEN
cm_error.ProcessError;
END;

Раздел DBA

Здесь содержатся вспомогательные скрипты, в том числе генераторы для модулей BAPI. Эти скрипты имеют префикс gen_*. Так например, gen_upd.sql генерит пару функций Get/Set для поля нужной вам сущности с учетом прав доступа, журналирования, обработки ошибок. Т.е. всего, что мы рассмотрели Выше. Вам остается только вставить этот код к себе и добавить специфические проверки (к примеру new.salary  <= old.salary * 1.5) , если этого требует бизнес.

Заключение

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

P.S. 

Огромное влияние на идеи и принципы, заложенные в этой библиотеке, оказала команда разработчиков, собравшаяся в одно время в Zenon N.S.P: Федор Краснов (теория биллинга, pl/sql, perl) , Андрей Кулага (pl/sql, тарификатор) , Алексей Ващенко (Designer, pl/sql, бухгалтерия), Александр Диментов (теория биллинга, pl/sql, support) , Михаил Кулаков (perl, сервера предоставления услуг), Мария Соболева(perl, интерфейс). Мне было очень приятно работать с ними. Я знаю, что многие из них по прежнему продолжают трудиться на биллинговыми системами и желаю им удачи в этом нелегком деле.  

Dsvolk > > Oracle > > Dvp > > Billprof Last Modified: 26-06-2003 11:48