По этой ссылке
вы можете оценить приложение
«Заказы клиентов», написанное на
Oracle HTML DB и работающее
на нашем сервере с базой данных Oracle 10g. Идентификатор
пользователя - а, пароль также а на английском.
Запустить
«Заказы клиентов (Версия 2)»
Oracle HTML DB - это инструмент,
позволяющий быстро и без больших накладных затрат, создавать приложения с
WEB - интерфейсом. Дешевизна заключается
в
том, что не нужно покупать сервер приложений, HTML DB
или последняя версия APEX
входит в комплект поставки Oracle database server 10g,
сервер Apache устанавливается также по умолчанию, в нашей
установке используется именно такая конструкция. Простота обеспечивается
возможностями инструмента Oracle HTML DB
или APEX, которые
демонстрируется нашей публикацией.
Здравствуйте, коллеги!
Вдохновленные первым опытом работы с Oracle HTML DB (начиная с версии 2.1 имеет название Oracle Application Express, или APEX), мы стали искать пути минимизации трафика, да и уменьшения количества скрытых элементов управления, т.к. при достаточно сложном проекте передавать от страницы к странице через HIDDEN достаточно проблематично и некрасиво.
Наши поиски привели к достаточно простой, но, в то же время, достаточно удобной, возможности APEX, а именно COLLECTIONS (коллекции).
В данной публикации постараемся рассказать о создании и работе с коллекциями.
Перед чтением, рекомендуем, ознакомиться с предыдущей публикацией, т.к. останавливаться на таких вещах, как добавлении элементов на страницу и создании динамических отчетов не будем.
Информация по работе с коллекциями доступна в официальной документации по APEX.
В нашем примере будем использовать 2 коллекции: SALEORDER_MAIN и SALEORDER_SUB – шапка и детали заказа, соответственно. Для создания коллекций будем использовать функцию APEX_COLLECTION.CREATE_OR_TRUNCATE_COLLECTION('имя коллекции') – данная функция создает, если не была создана, коллекцию или очищает коллекцию от элементов, если коллекция была создана.
Для доступа к элементам коллекции используется представление APEX_COLLECTIONS. Представление содержит следующие поля:
ИМЯ ПУСТО ТИП
_______________ ________ _____________
COLLECTION_NAME NOT NULL VARCHAR2(255)
SEQ_ID NULL NUMBER
C001 VARCHAR2(4000)
C002 VARCHAR2(4000)
... ...
C050 VARCHAR2(4000)
CLOB001 CLOB
MD5_ORIGINAL VARCHAR2(4000)
ОТОБРАЖЕНИЕ ЗАКАЗА
Для нашего примера примем следующее:
в коллекции SALEORDER_MAIN в поле C001 содержится имя «переменной», в поле С002 – значение. В коллекции SALEORDER_SUB – каждая запись соответствует каждой записи в детали заказа, а каждый атрибут содержит атрибут данной строки заказа, например, поле С001 содержит номер кода товара, С005 количество, С003 – цену и проч.
Итак, приступим, сначала опишем процесс, который заполняет информационные поля страницы: номер заказа, клиент, сумма заказа.
Создаем процесс, который будет срабатывать ON LOAD – BEFORE HEAD, содержащий следующий PL/SQL anonymous block:
-- объявляем требуемые переменные
DECLARE
ls_login VARCHAR2(100);
ls_invoice VARCHAR2(100);
ln_cnt NUMBER;
ls_accnum VARCHAR2(100);
ls_subaccnum VARCHAR2(100);
ls_contractor VARCHAR2(100);
ls_total VARCHAR2(100);
BEGIN
-- есть ли INVOICE (подномер) у текущего заказа?
-- Наличие подномера – условие того, что заказ записан
SELECT COUNT(*)
INTO ln_cnt
FROM APEX_COLLECTIONS
WHERE (UPPER(c001) = 'INVOICE') AND (UPPER(COLLECTION_NAME) = 'SALEORDER_MAIN');
-- если да - прочитать
IF ln_cnt > 0 THEN
SELECT c002
INTO ls_invoice
FROM APEX_COLLECTIONS
WHERE (UPPER(c001) = 'INVOICE') AND (UPPER(COLLECTION_NAME) = 'SALEORDER_MAIN');
ELSE
ls_invoice := 'нов';
END IF;
-- прочитать LOGIN (номер заказа)
SELECT c002
INTO ls_login
FROM APEX_COLLECTIONS
WHERE (UPPER(c001) = 'LOGIN') AND (UPPER(COLLECTION_NAME) = 'SALEORDER_MAIN');
-- прочитать номер счета клиента
SELECT c002
INTO ls_accnum
FROM APEX_COLLECTIONS
WHERE (UPPER(c001) = 'ACCNUM') AND (UPPER(COLLECTION_NAME) = 'SALEORDER_MAIN');
-- прочитать номер субсчета клиента
SELECT c002
INTO ls_subaccnum
FROM APEX_COLLECTIONS
WHERE (UPPER(c001) = 'SUBACCNUM') AND (UPPER(COLLECTION_NAME) = 'SALEORDER_MAIN');
-- прочитать название контрагента
SELECT CONTRACTOR
INTO ls_contractor
FROM #OWNER#.EA_ACCOUNT
WHERE (ACCOUNT_NUM = ls_accnum) AND (SUBACCOUNT_NUM = ls_subaccnum);
-- есть ли детали у текущего заказа?
SELECT COUNT(*)
INTO ln_cnt
FROM APEX_COLLECTIONS
WHERE (UPPER(COLLECTION_NAME) = 'SALEORDER_SUB');
IF ln_cnt > 0 THEN
-- если есть, то просуммировать количество*цена. Т.к. поля С001, С002 … С050 – строковые, поэтому приходится помучиться со сменой «.» на «,»
SELECT
TO_CHAR(SUM(DECODE(INSTR(NVL(c003,'0'),'.'),0,TO_NUMBER(NVL(c003,'0')),(TO_NUMBER(SUBSTR(c003,1,INSTR(c003,'.')-1)||','||SUBSTR(c003,INSTR(c003,'.')+1))))
*
DECODE(INSTR(NVL(c005,'0'),'.'),0,TO_NUMBER(NVL(c005,'0')),(TO_NUMBER(SUBSTR(c005,1,INSTR(c005,'.')-1)||','||SUBSTR(c005,INSTR(c005,'.')+1))))))
INTO ls_total
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_SUB');
ELSE
ls_total := '0';
END IF;
-- присвоить элементам на странице полученные значения
:P7_HEAD := ls_login||'/'||ls_invoice;
:P7_ACC := ls_accnum||'/'||ls_subaccnum||' '||ls_contractor;
:P7_TOTAL := ls_total;
END;
Теперь сформируем отображение деталей заказа, для этого добавим динамический отчет, который будет брать данные из следующего запроса:
SELECT 'Изменить',
APEX_COLLECTIONS.c001,
COMMODITY.COMMODITY,
APEX_COLLECTIONS.c002,
APEX_COLLECTIONS.c003,
APEX_COLLECTIONS.c004,
APEX_COLLECTIONS.c005,
'Рассчет цены'
FROM #OWNER#.COMMODITY, APEX_COLLECTIONS
WHERE (TO_CHAR(COMMODITY.CODE) = APEX_COLLECTIONS.c001)
ORDER BY COMMODITY.COMMODITY, APEX_COLLECTIONS.c001
#OWNER# - это имя схемы БД, в которой мы работаем.
Теперь разработаем алгоритм добавления элементов в заказ:
• Пользователь выбирая группы в дереве фильтрует необходимый товар
• Набирает товар
• Указывает его количество
ДОБАВЛЕНИЕ ТОВАРА
Опишем процесс добавления товара в заказ.
Для отображения доступного товара используется динамический отчет, запрос для которого формирует следующая функция:
DECLARE
SQLQ varchar2(3000);
ls_accnum varchar2(100);
ls_subaccnum varchar2(100);
ls_pricetype varchar2(100);
ls_branch varchar2(100);
BEGIN
-- получим необходимые параметры
SELECT c002
INTO ls_accnum
FROM APEX_COLLECTIONS
WHERE (UPPER(c001) = 'ACCNUM') AND (UPPER(COLLECTION_NAME) = 'SALEORDER_MAIN');
SELECT c002
INTO ls_subaccnum
FROM APEX_COLLECTIONS
WHERE (UPPER(c001) = 'SUBACCNUM') AND (UPPER(COLLECTION_NAME) = 'SALEORDER_MAIN');
SELECT c002
INTO ls_branch
FROM APEX_COLLECTIONS
WHERE (UPPER(c001) = 'BRANCH') AND (UPPER(COLLECTION_NAME) = 'AUTH');
SELECT TRADE_DEF
INTO ls_pricetype
FROM #OWNER#.EA_ACCOUNT
WHERE (ACCOUNT_NUM = ls_accnum) AND (SUBACCOUNT_NUM = ls_subaccnum);
SQLQ := 'SELECT ALL TO_CHAR(''Выбрать''),
DECODE(NVL(a.ISIN,0), 0, 0, 1),
COMMODITY.CODE,
COMMODITY.COMMODITY,
COMMODITY.TYPE1,
COMMODITY.TYPE2,
COMMODITY.TYPE3,
COMMODITY.SECOND_NAME,
SELL_PRICES.SELL_PRICE,
SUM(NVL(REMAINS.QUANTITY,0)) ';
-- если установлен флаг выбора товара, только того, который находится на складе, то отображать только этот товар
IF v('P15_INSTORAGE') = '1' THEN
SQLQ := SQLQ||'FROM COMMODITY,
SELL_PRICES,
REMAINS,
STORAGE_GROUP,
STORAGE, (SELECT COMMODITY.CODE AS CODE, COUNT(*) AS ISIN FROM APEX_COLLECTIONS, COMMODITY WHERE (TO_CHAR(COMMODITY.CODE) = APEX_COLLECTIONS.c001) AND (UPPER(COLLECTION_NAME) = ''SALEORDER_SUB'') GROUP BY COMMODITY.CODE) a
WHERE (COMMODITY.CODE = a.CODE (+)) AND
(REMAINS.CODE = COMMODITY.CODE) AND
(STORAGE_GROUP.STORAGE_GROUP = STORAGE.STORAGE_GROUP) AND
(STORAGE.STORAGE = REMAINS.STORAGE) AND
(UPPER(STORAGE_GROUP.BRANCH) = UPPER('''||ls_branch||''')) AND
(SELL_PRICES.CODE = REMAINS.CODE ) AND
(UPPER(SELL_PRICES.PRICE_NAME) = UPPER('''||ls_pricetype||''')) ';
ELSE
SQLQ := SQLQ||'FROM COMMODITY,
SELL_PRICES,
REMAINS,
(SELECT COMMODITY.CODE AS CODE, COUNT(*) AS ISIN FROM APEX_COLLECTIONS, COMMODITY WHERE (TO_CHAR(COMMODITY.CODE) = APEX_COLLECTIONS.c001) AND (UPPER(COLLECTION_NAME) = ''SALEORDER_SUB'') GROUP BY COMMODITY.CODE) a
WHERE (COMMODITY.CODE = a.CODE (+)) AND
(COMMODITY.CODE = REMAINS.CODE (+)) AND
(SELL_PRICES.CODE = COMMODITY.CODE) AND
( SELL_PRICES.PRICE_NAME = '''||ls_pricetype||''' ) ';
END IF;
-- Добавим фильтры по группам товара
SQLQ := SQLQ||' AND (UPPER(NVL(COMMODITY.TYPE1, '' '')) like UPPER('''||V('P15_TYPE1')||''')) ';
SQLQ := SQLQ||' AND (UPPER(NVL(COMMODITY.TYPE2,'' '')) like UPPER('''||V('P15_TYPE2')||'''))';
SQLQ := SQLQ||' AND (UPPER(NVL(COMMODITY.TYPE3,'' '')) like UPPER('''||V('P15_TYPE3')||'''))';
SQLQ := SQLQ||'GROUP BY a.ISIN,
COMMODITY.CODE,
COMMODITY.TYPE1,
COMMODITY.TYPE2,
COMMODITY.TYPE3,
COMMODITY.SECOND_NAME,
COMMODITY.COMMODITY,
SELL_PRICES.SELL_PRICE
ORDER BY COMMODITY.COMMODITY,
COMMODITY.CODE';
RETURN SQLQ;
END;
По нажатию на поле «Выбрать» переходим на данную же страницу, устанавливая элементам P15_ISADD и P15_CODE значения 1 и #COL03# (код товара) соответственно.
Т.е. когда значение у элемента P15_ISADD становится равным 1, то срабатывает следеющий процесс (Condition type: Value of Item in Expression 1 = Expression 2, Expression 1: P15_ISADD, Expression 2: 1)
DECLARE
ln_cnt NUMBER;
ls_price VARCHAR2(100);
ls_branch VARCHAR2(100);
ls_pricesale VARCHAR2(100);
ls_date VARCHAR2(100);
ls_code VARCHAR2(100);
ln_code NUMBER;
ls_quantity VARCHAR2(100);
ln_price NUMBER;
ln_pricesale NUMBER;
ln_trade_per NUMBER; -- Наценка по клиенту
ln_type_per NUMBER; -- Наценка по товару
ln_delay_per NUMBER; -- Наценка по отсрочке
ld_datefrom DATE;
ld_dateto DATE;
ln_accnum NUMBER;
ln_subaccnum NUMBER;
ls_tradedef VARCHAR2(100);
ls_curr VARCHAR2(100);
ls_currtype VARCHAR2(100);
ls_type1 VARCHAR2(100);
ls_type2 VARCHAR2(100);
ls_type3 VARCHAR2(100);
BEGIN
SELECT BRANCH
INTO ls_branch
FROM OR_BRANCH
WHERE FL_IS_LOCAL = 1;
SELECT COUNT(*)
INTO ln_cnt
FROM APEX_COLLECTIONS
WHERE collection_name = 'SALEORDER_SUB';
-- Если нет такой коллекции, то создаем
IF ln_cnt = 0 THEN
APEX_COLLECTION.CREATE_OR_TRUNCATE_COLLECTION('SALEORDER_SUB');
END IF;
SELECT COUNT(*)
INTO ln_cnt
FROM APEX_COLLECTIONS
WHERE (collection_name = 'SALEORDER_SUB') AND (c001 = V('P15_CODE'));
-- Если есть такой товар в заказе, то сначала удалим
IF ln_cnt <> 0 THEN
APEX_COLLECTION.DELETE_MEMBERS( 'SALEORDER_SUB', 1, V('P15_CODE'));
END IF;
ls_date := TO_CHAR(sysdate());
ls_code := V('P15_CODE');
ln_code := TO_NUMBER(ls_code)
;
ls_quantity := V('P15_QUANTITY');
ld_datefrom := trunc(sysdate());
// Здесь пропущены операции чтения и применения разного рода наценок
ln_pricesale := ln_price;
ls_price := TO_CHAR(ln_price);
ls_pricesale := TO_CHAR(ln_pricesale);
// Добавляем нового члена
APEX_COLLECTION.ADD_MEMBER('SALEORDER_SUB', ls_code, ls_price, ls_pricesale, ls_date, ls_quantity );
END;
СОХРАНЕНИЕ ЗАКАЗА
Шапку сделали, детали добавили – теперь сохраним данный заказ, думаем, тут комментарии излишни:
DECLARE
ln_login NUMBER;
ln_invoice NUMBER;
ln_cnt NUMBER;
ln_accnum NUMBER;
ln_subaccnum NUMBER;
ls_branch VARCHAR2(100);
ls_customer VARCHAR2(100);
ls_pricename VARCHAR2(100);
ls_billnote VARCHAR2(3000);
ls_drivenote VARCHAR2(3000);
ln_percents NUMBER;
ld_date DATE;
ldt_datetime DATE;
ln_total NUMBER;
ld_saledate DATE;
BEGIN
SELECT c002
INTO ls_billnote
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_MAIN') AND (c001 = 'BILL_NOTE');
SELECT c002
INTO ls_drivenote
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_MAIN') AND (c001 = 'DRIVE_NOTE');
SELECT TO_NUMBER(c002)
INTO ln_login
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_MAIN') AND (c001 = 'LOGIN');
SELECT COUNT(*)
INTO ln_cnt
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_MAIN') AND (c001 = 'INVOICE');
IF ln_cnt > 0 THEN
SELECT TO_NUMBER(c002)
INTO ln_invoice
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_MAIN') AND (c001 = 'INVOICE');
ELSE
SELECT INVOICE
INTO ln_invoice
FROM ID
WHERE (LOGIN = ln_login);
UPDATE ID SET INVOICE = INVOICE + 1
WHERE (LOGIN = ln_login);
END IF;
SELECT TO_NUMBER(c002)
INTO ln_accnum
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_MAIN') AND (c001 = 'ACCNUM');
SELECT TO_NUMBER(c002)
INTO ln_subaccnum
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_MAIN') AND (c001 = 'SUBACCNUM');
SELECT c002
INTO ls_branch
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'AUTH') AND (c001 = 'BRANCH');
SELECT CONTRACTOR, TRADE_DEF, TRADE_PERCENT
INTO ls_customer, ls_pricename, ln_percents
FROM EA_ACCOUNT
WHERE (ACCOUNT_NUM = ln_accnum) AND (SUBACCOUNT_NUM = ln_subaccnum);
SELECT TRUNC(SYSDATE), SYSDATE
INTO ld_date, ldt_datetime
FROM DUAL;
SELECT
SUM(
DECODE(INSTR(NVL(c003,'0'),'.'),0,TO_NUMBER(NVL(c003,'0')),(TO_NUMBER(SUBSTR(c003,1,INSTR(c003,'.')-1)||','||SUBSTR(c003,INSTR(c003,'.')+1))))
*
DECODE(INSTR(NVL(c005,'0'),'.'),0,TO_NUMBER(NVL(c005,'0')),(TO_NUMBER(SUBSTR(c005,1,INSTR(c005,'.')-1)||','||SUBSTR(c005,INSTR(c005,'.')+1)))))
INTO ln_total
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_SUB');
SELECT TRUNC(TO_DATE(c002))
INTO ld_saledate
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_MAIN') AND (c001 = 'SALE_DATE');
SELECT COUNT(*)
INTO ln_cnt
FROM SALEORDER
WHERE (LOGIN = ln_login) AND (INVOICE = ln_invoice);
IF ln_cnt > 0 THEN
UPDATE SALEORDER SET TOTAL = ln_total WHERE (LOGIN = ln_login) AND (INVOICE = ln_invoice);
DELETE FROM SALEORDER_SUB WHERE (LOGIN = ln_login) AND (INVOICE = ln_invoice);
ELSE
INSERT INTO SALEORDER ("LOGIN", "INVOICE", "CASH_ACC_NUM", "CASH_SUB_ACC_NUM", "BRANCH", "CUSTOMER", "PRICE_NAME", "PERCENTS", "OP_DATE", "OP_DATETIME", "SALEORDER"."TOTAL", "SALE_DATE", "CLOSED_SIGN", "ACCEPT_SIGN", "ORDER_PRIORITY", "BILL_NOTE", "DRIVE_NOTE")
VALUES (ln_login, ln_invoice, ln_accnum, ln_subaccnum, ls_branch, ls_customer, ls_pricename, ln_percents, ld_date, ldt_datetime, ln_total, ld_saledate, 0, 0, 1, ls_billnote, ls_drivenote);
END IF;
INSERT INTO SALEORDER_SUB ("LOGIN", "INVOICE", "CODE", "PRICE", "PRICESALE", "DATE_SUB", "QUANTITY", "COMMENT" )
SELECT ln_login, ln_invoice, TO_NUMBER(c001), TO_NUMBER(NVL(c002,'0')), TO_NUMBER(NVL(c003,'0')), TO_DATE(c004), TO_NUMBER(NVL(c005,'0')), c006
FROM APEX_COLLECTIONS
WHERE (COLLECTION_NAME = 'SALEORDER_SUB');
COMMIT;
END;
ЧТЕНИЕ ЗАКАЗА
Вот, собственно, заказ и готов. Можно лишь заметить, что для чтения заказа используется следующая функция:
DECLARE
ls_login VARCHAR2(100);
ls_invoice VARCHAR2(100);
ls_accnum VARCHAR2(100);
ls_subaccnum VARCHAR2(100);
ls_billnote VARCHAR2(100);
ls_drivenote VARCHAR2(100);
ls_saledate VARCHAR2(100);
ls_sql VARCHAR2(3000);
BEGIN
-- получим идентификатор заказа
ls_login := V('P3_LOGIN');
ls_invoice := V('P3_INVOICE');
SELECT TO_CHAR(CASH_ACC_NUM), TO_CHAR(CASH_SUB_ACC_NUM), TO_CHAR(BILL_NOTE), TO_CHAR(DRIVE_NOTE), TO_CHAR(SALE_DATE)
INTO ls_accnum, ls_subaccnum, ls_billnote, ls_drivenote, ls_saledate
FROM SALEORDER
WHERE(TO_CHAR(LOGIN) = ls_login) AND (TO_CHAR(INVOICE) = ls_invoice);
-- установим значения элементов в странице
APEX_UTIL.SET_SESSION_STATE('P3_CASH_SUB_ACC_NUM',ls_subaccnum);
APEX_UTIL.SET_SESSION_STATE('P3_SALE_DATE',ls_saledate);
APEX_UTIL.SET_SESSION_STATE('P3_BILL_NOTE',ls_billnote);
APEX_UTIL.SET_SESSION_STATE('P3_DRIVE_NOTE',ls_drivenote);
-- установим добавим элементы в коллекции
APEX_COLLECTION.CREATE_OR_TRUNCATE_COLLECTION('SALEORDER_MAIN');
APEX_COLLECTION.ADD_MEMBER('SALEORDER_MAIN','LOGIN',ls_login);
APEX_COLLECTION.ADD_MEMBER('SALEORDER_MAIN','INVOICE',ls_invoice);
APEX_COLLECTION.ADD_MEMBER('SALEORDER_MAIN','ACCNUM',ls_accnum);
APEX_COLLECTION.ADD_MEMBER('SALEORDER_MAIN','SUBACCNUM', ls_subaccnum);
APEX_COLLECTION.ADD_MEMBER('SALEORDER_MAIN','BILL_NOTE', ls_billnote);
APEX_COLLECTION.ADD_MEMBER('SALEORDER_MAIN','DRIVE_NOTE', ls_drivenote);
APEX_COLLECTION.ADD_MEMBER('SALEORDER_MAIN','SALE_DATE', ls_saledate);
-- заполним коллекцию из запроса
ls_sql := 'SELECT TO_CHAR(CODE), TO_CHAR(PRICE), TO_CHAR(PRICESALE), TO_CHAR(DATE_SUB), TO_CHAR(QUANTITY), "COMMENT"
FROM SALEORDER_SUB
WHERE (LOGIN = '||ls_login||') AND (INVOICE = '||ls_invoice||')';
APEX_COLLECTION.CREATE_OR_TRUNCATE_COLLECTION('SALEORDER_SUB');
APEX_COLLECTION.DELETE_COLLECTION('SALEORDER_SUB');
APEX_COLLECTION.CREATE_COLLECTION_FROM_QUERY( 'SALEORDER_SUB',ls_sql);
END;
Теперь все готово.
В заключение хочется отметить, что использование коллекций открывает большой простор для разработки приложений на APEX. Быть может, но только пользователи, но и разработчики полюбят данный продукт.
Спасибо за внимание!
И следите за публикациями на сайте www.azsoft.ru!
C уважением,
группа разработчиков.
Вернуться
На страницу для программистов
>>>
На главную
>>>
Реклама:
return_links();
?>
©Copyright 2011
АЗСофт (AZSoft)