Часть 3. Управление магазином

Часть 3. Управление магазином

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

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

Глава 10. Управление информацией о товарах

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

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

Проектирование программы управления магазином

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

На самом верхнем уровне иерархии пользователь регистрируется для работы в программе управления магазином. Ему на выбор предоставляется несколько возможностей - список товаров, список разделов, налоги, доставки и отчеты по заказам. С этого уровня структура разделяется на отдельные операции для каждого варианта. В табл. 10.1 перечислены основные операции, описанные в этой главе.

Таблица 10.1. Основные операции с товарами и разделами
Операция
Описание
Административный вход
Проверка данных пользователя при входе в управляющую программу
Создание нового товара
Добавить новый товар в базу данных магазина
Список товаров
Вывести список товаров в базе данных
Поиск товаров
Найти товары в базе данных
Удаление товаров
Удалить товар из базы данных
Обновление товаров
Обновляет данные товара
Операции с атрибутами товара
Создание, обновление и удаление атрибутов товара
Список разделов
Вывести список разделов в базе данных
Обновление разделов
Обновить данные раздела
Создание нового раздела
Создать новый раздел в базе данных
Обновление раздела
Обновляет данные раздела в базе данных

Одним из ключевых элементов Web-сайта является простая система перехода между различными управляющими операциями. Навигационный интерфейс управляющей программы нашего магазина показан на рис. 10.2.

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

Листинг 10.1. NavInclude.asp

<!-- NavInclude.asp - заголовочный файл программы управления электронным магазином.-->
<hr>

<center>

<!-- Link to the listing of products -->
<a href="ListProducts.asp">
Manage Products<a> |

<!-- Link to the listing of departments -->
<a href="ListDepts.asp">
Manage Departments</a> |

<!-- Link to the management of the tax settings -->
<a href="ManageTax.asp">
Manage Tax</a> |

<!-- Link to the management of the shipping settings. -->
<a href="ManageShipping.asp">
Manage Shipping</a> |

<!-- Link to the management of the orders. -->
<a href="ManageOrders.asp">
Manage Orders</a> |

</center>

<hr>

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

Безопасность

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

Рис. 10.3. Страница ввода регистрационных данных

Страница регистрации Login.asp представляет собой простую форму с текстовыми полями для ввода имени пользователя и пароля. Введенные данные передаются основной странице управляющей программы.

Листинг 10.2. Login.asp

<%@ Language=VBScript %>
<HTML>
<!--
Login.asp - Login in page for the site
administrator.
-->

<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<B>Please login:</b><BR><BR>

<!-- Start the form for the user to enter in
their username and password. -->
<form method="post" action="ManagerMenu.asp">

<table>
<tr>
<td align="right">Username:</td>
<td>
<!-- The input text box for the username. -->
<input type="text" value="" name="username">
</td>
</tr>

<tr>
<td align="right">Password:</td>
<td>
<!-- The input text box for the password. -->
<input type="password" value="" name="password">
</td>
<tr>

<tr>
<td colspan="2">
<!-- The submit button for the form. -->
<input type="Submit" value="Submit" name="Submit">
</td>
</tr>

</table>

</form>

</BODY>
</HTML>

Данные, введенные пользователем при заполнении формы, передаются странице ManagerMenu.asp. Страница проверяет правильность имени пользователя и пароля. Фрагмент страницы, связанный с проверкой данных, приведен в листинге 10.3.

Листинг 10.3. ManagerMenu.asp

ManagerMenu.asp -

<%@ Language=VBScript %>
<%
' ****************************************************
' ManagerMenu.asp - меню управляющих операций.
' ****************************************************

' Убедиться в том, что введенные данные соответствуют
' данным администратора.
if request("username") <> "Admin" and _
request("password") <> "Password" then

' Вернуться на страницу login.asp.
Response.Redirect "login.asp"

else

' Установить признак успешной проверки пользователя
Session("Validated") = true

end if

%>

Этот фрагмент проверяет, совпадают ли введенные данные со строками Admin и Password. Если данные оказываются неправильными, пользователь возвращается на страницу Login.asp. Если данные совпали, мы устанавливаем сеансовую переменную - признак успешной регистрации пользователя. Значение этой переменной будет проверяться на других страницах сайта.

Листинг 10.4. ManagerMenu.asp (продолжение)

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<!-- Welcome the user -->
<center>
<BR><BR><b>
Welecome to Wild Willie's CD Store Order Manager.
Select a function below:
</b><br></br>

<!-- Build a table to show the management opitons. -->
<table border="1" cellpadding="3" cellspacing="3">
<tr>
<th>Function</th>
</tr>
<tr>
<!-- Manage products -->
<td><a href="ListProducts.asp">
Manage Products<a></td>
</tr>
<tr>
<!-- Manage departments. -->
<td><a href="ListDepts.asp">
Manage Departments</a></td>
</tr>
<tr>
<!-- Manage tax -->
<td><a href="ManageTax.asp">
Manage Tax</a></td>
</tr>
<tr>
<!-- Manage shipping -->
<td><a href="ManageShipping.asp">
Manage Shipping</a></td>
</tr>
<tr>
<!-- Manage orders -->
<td><a href="ManageOrders.asp">
Manage Orders</a></td>
</tr>
</table>
</center>

</BODY>
</HTML>

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

Рис. 10.4. Основная страница управляющей программы

Последний заголовочный файл ValidateCheck.asp (см. листинг 10.5) содержит простую проверку, выполняемую в начале каждой страницы.

Листинг 10.5. ValidateCheck.asp

<%
' ****************************************************
' ValidateCheck.asp - проверка регистрации пользователя.
' ****************************************************

' Check our session variable to see if the user has
' been validated. This will help to ensure that
' none of the admin pages are accessed with out
' authorization.
if Session("Validated") <> true then

' Redirect back to the login page.
Response.Redirect("login.asp")

end if

%>

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

Вернуться на страницу login.asp. Response.Redirect("login.asp")

Мы просто проверяем, что сеансовой переменной было присвоено значение true. В противном случае пользователь возвращается на страницу ввода регистрационных данных.

ПРИМЕЧАНИЕ
Если во время сеанса работы пользователя произойдет тайм-аут, то значение сеансовой переменной будет потеряно, и пользователю придется регистрироваться заново. По умолчанию интервал тайм-аута равен 20 минутам, но это значение можно изменить в административных утилитах IIS или установить тайм-аут в коде ASP.

ВЫБОР СИСТЕМЫ БЕЗОПАСНОСТИ
В нашем примере для аутентификации пользователя и защиты страниц использована очень простая модель безопасности. Однако существует немало других, более совершенных вариантов. Например, одним пользователям можно разрешить операции с товарами и разделами, а других пользователей ограничить операциями с заказами, то есть реализовать различные уровни доступа к системе. Для этого можно воспользоваться базой данных, содержащих имена пользователей и перечни разрешенных операций.

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

Операции с товарами

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

В листинге 10.6 приведено начало страницы Li stProducts.asp, отображающей фиксированное количество товаров. Страница начинается с включения стандартных элементов: заголовочного файла ValidateCheck.asp, проверяющего аутентификацию пользователя, и файла NavInclude.asp, отображающего панель ссылок в верхней части страницы.

Листинг 10.6. ListProducts.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
ListProducts.asp - Lists the products in the
store.
-->

<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<!-- #include file="include/navinclude.asp" -->

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

Листинг 10.7. ListProducts.asp (продолжение)

<%

' Create an ADO database connection
set dbProducts = server.createobject("adodb.connection")

' Create the record set
set rsProducts = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProducts.open("filedsn=WildWillieCDs")

' The products will not all be displayed at once. We
' want to set the Product Increment in a session variable.
' If the product increment is not set, then we will set
' it. In this case, we are defaulting it to 4.
if session("ProdInc") = "" then

' Default it to 4
session("ProdInc") = "4"

end if

Затем, как показано в листинге 10.8, из URL извлекается значение переменной StartProd, определяющей начальную позицию выводимого списка. Значение переменной задается при выборе ссылок, расположенных в конце страницы, в зависимости от того, к какой позиции списка пожелал перейти пользователь. Мы также убеждаемся в том, что переменной StartProd было присвоено значение и что оно не меньше 1.

Листинг 10.8. ListProducts.asp (продолжение)

' Определить начальную позицию для вывода списка.
StartProd = request("StartProd")

' If there is no starting point, then we will
' default it to 1.
if StartProd = "" then
StartProd = 1
end if

' If the user tries to decrement past the first product,
' we default back to 1 for the first product.
if StartProd < 1 then

StartProd = 1

end if

Далее выполняется хранимая процедура sp_ManagerRetrieveProducts, которая возвращает несколько записей товаров, начиная с заданной (см. листинг 10.9).

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

Листинг 10.9. ListProducts.asp (продолжение)

' Построить команду вызова хранимой процедуры для чтения
' списка товаров, начиная с заданной начальной позиции
' с заданным приращением.
sql = "execute sp_ManagerRetrieveProducts " & _
StartProd & ", " & session("ProdInc")

' Execute the statement
set rsProducts = dbProducts.Execute(sql)

' Ensure some products are returned.
if rsProducts.EOF then

' If none were, then lets return to the
' beginning of the list.
StartProd = 1

' Build the stored procedure
sql = "execute sp_ManagerRetrieveProducts " & _
StartProd & ", " & session("ProdInc")

' Execute the statement
set rsProducts = dbProducts.Execute(sql)

end if

%>

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

Листинг 10.10. ListProducts.asp (продолжение)

<!-- Ссылка на страницу создания нового товара. -->
<BR><b>Click <a href="NewProduct.asp">here</a>
to add a new product.</b>

<!-- Start the display of the product listing. -->
<BR><BR>
<b>To edit a product, select from the list below:</b>
<BR><BR>

<table cellpadding="3" cellspacing="3">
<tr>
<th>Product ID</th>
<th>Name</th>
<th>Price</th>
</tr>

Далее мы в цикле перебираем полученные записи списка (см. листинг 10.11). Для каждого продукта создается ссылка на страницу ManageProduct.asp, где выполняются операции с конкретным товаром. Название товара также оформляется в виде ссылки. В третьем столбце таблицы выводится цена товара.

Листинг 10.11. ListProducts.asp (продолжение)

<%

' Перебор возвращенных записей.
do until rsProducts.EOF

%>

<!-- Build a row to display the list of products. -->
<tr>
<!-- A link is built to the ManageProduct.asp page.
The id of the product is passed on the URL and
the ID of the product is displayed.
-->
<td>
<a href="ManageProduct.asp?idProduct=<%=rsProducts("idProduct")%>">
<%=rsProducts("idProduct")%></a></td>

<!-- A link is built to the ManageProduct.asp page.
The id of the product is passed on the URL and
the name of the product is displayed.
-->
<td>
<a href="ManageProduct.asp?idProduct=<%=rsProducts("idProduct")%>">
<%=rsProducts("chrProductName")%></a></td>

<!-- Display the product price. NOte that the price is
stored as an integer. -->
<td><%=formatcurrency(rsProducts("intPrice")/100, 2)%></td>
</tr>

<%

' Move to the next row
rsProducts.MoveNext

' Loop back
Loop

%>

</table>

В конце страницы создается панель со ссылками first, previous и next. Ссылка first присваивает переменной StartProd значение 1. Ссылка previous возвращает список товаров на один экран назад. Наконец, ссылка next увеличивает сеансовую переменную StartProd на количество записей товаров, одновременно выводимых на экране (см. листинг 10.12).

Листинг 10.12. ListProducts.asp (продолжение)

<BR>
<!-- Панель ссылок, предназначенная для перемещения по списку товаров в прямом и обратном направлении. В URL каждой ссылки передается новая начальная позиция в списке. Значение переменной StartProd присваивается в соответствии со значением сеансовой переменной ProdIlnc. -->
<a href="ListProducts.asp?StartProd=1">First Product</a> |
<a href="ListProducts.asp?StartProd=
<%=StartProd - cint(Session("ProdInc"))%>">Previous</a> |
<a href="ListProducts.asp?StartProd=
<%=StartProd + cint(Session("ProdInc"))%>">Next</a>

<BR><BR>

Страница завершается средствами поиска. В нижней части страницы расположено текстовое поле, в котором вводится искомый текст. Введенные данные передаются странице SearchProducts.asp (см. листинг 10.13).

Листинг 10.13. ListProducts.asp (продолжение)

<!-- Форма для поиска конкретных товаров в базе данных. Данные формы передаются странице SearchProducts.asp. -->
<form method="post" action="SearchProducts.asp">

<!-- The table is created to display the search
option -->
<table>
<tr>
<td align="right">Search Text:</td>
<!-- Текстовое поле для ввода искомого текста. -->
<td><input type="text" value="" name="SearchText"></td>
</tr>
<tr>
<td colspan="2">
<!-- Кнопка отправки данных. -->
<input type="submit" value="Submit" name="Submit">
</td>
</tr>
</table>

</form>

</BODY>
</HTML>

В работе страницы ListProducts.asp используется одна хранимая процедура, sp_ManagerRetrieveProducts. Значение rowcount задается таким образом, чтобы возвращаемое количество записей совпадало с количеством одновременно отображаемых товаров. Вывод начинается с заданной начальной позиции (см. листинг 10.14).

Листинг 10.14. Хранимая процедура sp_ManagerRetrieveProducts

CREATE PROCEDURE sp_ManagerRetrieveProducts

@intStartProdID int,
@intRowCount int

AS

set rowcount @intRowCount

select idProduct, chrProductName, intPrice
from products where idProduct >= @intStartProdID

На этом завершается построение основных средств работы со списком товаров. Эта страница является отправной точкой для операций с отдельными товарами. На рис. 10.5 показано исходное состояние страницы со списком товаров.

Рис. 10.5. Первая страница списка товаров

Щелкните на ссылке next - в списке отобразится следующая группа товаров.

Страница поиска работает точно так же, однако в данном случае отображаются только те товары, которые содержат искомые ключевые слова.

Страница SearchProduct.asp (см. листинг 10.15) начинается с включения стандартных заголовочных файлов.

Листинг 10.15. SearchProducts.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
SearchProducts.asp - Provides a feature to search
for products from the list.
-->

<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<!-- #include file="include/navinclude.asp" -->

Программный код страницы начинается с подключения к базе данных. Из переданного запроса извлекается искомый текст. Если запрос не передавался, искомый текст читается из сеансовой переменной, значение которой было присно после предыдущей передачи данных (см. листинг 10.16).

Листинг 10.16. SearchProducts.asp (продолжение)

<%

' Create an ADO database connection
set dbProducts = server.createobject("adodb.connection")

' Create the record set
set rsProducts = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProducts.open("filedsn=WildWillieCDs")

' Check to see if there is any search text.
if request("SearchText") <> "" then

' Retrieve the search text
session("SearchText") = request("SearchText")

end if

Как и при выводе списка товаров, мы проверяем переменные Prodlnc и StartProd (см. листинг 10.17). Если значения этих переменных не заданы, им присваиваются значения по умолчанию.

Листинг 10.17. SearchProducts.asp (продолжение)

' Проверить значение переменной, определяющей количество
' одновременно отображаемых товаров.
if session("ProdInc") = "" then

' По умолчанию количество товаров на экране равно 4.
session("ProdInc") = "4"

end if

' Определить начальную позицию для вывода списка
StartProd = request("StartProd")

' Проверить, присвоено ли значение переменной.
if StartProd = "" then

' Если значение не присвоено, начинать с первой позиции
StartProd = 1

end if

' Проверить, не пытается ли пользователь выйти
' за пределы списка
if StartProd < 1 then

' Перейти к первому товару
StartProd = 1

end if

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

Листинг 10.18. Search Products.asp (продолжение)

' Построить команду вызова хранимой процедуры
' sp_ManagerRetrieveProdSearch для отбора записей
' товаров, удовлетворяющих критерию поиска.
sql = "execute sp_ManagerRetrieveProdSearch " & _
StartProd & ", " & session("ProdInc") & ", '" & _
Session("SearchText") & "'"

' Execute the statement
set rsProducts = dbProducts.Execute(sql)

%>

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

Листинг 10.19. SearchProducts.asp (продолжение)

<!-- Build a link to the NewProduct.asp page in case the
user wants to add a new product. -->
<BR><b>Click <a href="NewProduct.asp">here</a>
to add a new product.</b>

<!-- Build a link to list the full product selection -->
<BR><BR><b>Click <a href="ListProducts.asp">here</a>
to see the full listing.</b>

<!-- Start the display of the search list. -->
<BR><BR><b>To edit a product, select from the
list below:</b><BR><BR>

<table cellpadding="3" cellspacing="3">
<tr>
<th>Product ID</th>
<th>Name</th>
<th>Price</th>
</tr>

<%

' Loop through the products.
do until rsProducts.EOF

%>

<tr>
<td>
<!-- Display the product id. And, build a link to the
ManageProduct.asp with the product id -->
<a href="ManageProduct.asp?idProduct=<%=rsProducts("idProduct")%>">
<%=rsProducts("idProduct")%></a></td>

<td>
<!-- Display the product name. And, build a link to the
ManageProduct.asp with the product id -->
<a href="ManageProduct.asp?idProduct=<%=rsProducts("idProduct")%>">
<%=rsProducts("chrProductName")%></a></td>

<!-- Show the product price. Note it is stored as an integer. -->
<td><%=formatcurrency(rsProducts("intPrice")/100, 2)%></td>
</tr>

<%

' Move to the next row
rsProducts.MoveNext

' Loop back
Loop

%>

</table>

Как видно из листинга 10.20, страница завершается тем же набором ссылок, что и ListProducts.asp. Однако на этот раз данные возвращаются этой же странице, чтобы пользователь мог провести поиск заново и получить другой набор товаров.

Листинг 10.20. SearchProducts.asp (продолжение)

<BR>
<!-- Build the navigation to move backwards and forwards
between the product screens. -->
<a href="SearchProducts.asp?StartProd=1">First Product</a> |
<a href="SearchProducts.asp?StartProd=
<%=StartProd - cint(Session("ProdInc"))%>">Previous</a> |
<a href="SearchProducts.asp?StartProd=
<%=StartProd + cint(Session("ProdInc"))%>">Next</a>

<BR><BR>

<!-- Build the form to execute a new search. -->
<form method="post" action="SearchProducts.asp">

<!-- Build the table -->
<table>

Текстовое поле создается на случай, если пользователь захочет снова провести поиск, но уже с другим искомым текстом (см. листинг 10.21).

Листинг 10.21. SearchProducts.asp (продолжение)

<!-- Build the input HTML element for the search text. -->
<tr>
<td align="right">Search Text:</td>
<td><input type="text" value="" name="SearchText"></td>
</tr>

<!-- Build a submit button for the search. -->
<tr>
<td colspan="2">
<input type="submit" value="Submit" name="Submit">
</td>
</tr>
</table>

</form>

</BODY>
</HTML>

В работе страницы используется одна хранимая процедура, sp_ManagerRetrieveProdSearch. Она находит записи товаров, в названиях которых присутствует искомый текст, введенный пользователем (см. листинг 10.22).

Листинг 10.22. Хранимая процедура sp_ManagerRetrieveProdSearch

CREATE PROCEDURE sp_ManagerRetrieveProdSearch

@intStartProdID int,
@intRowCount int,
@chrSearchText varchar(100)

AS

set rowcount @intRowCount

select idProduct, chrProductName, intPrice
from products
where idProduct >= @intStartProdID and
chrProductName like '%' + @chrSearchText+ '%'

Теперь вы можете проводить поиск на странице списка товаров. Введите в текстовом поле строку "Dog".

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

Начало страницы NewProduct.asp, предназначенной для ввода информации о новом товаре, приведено в листинге 10.23. В сущности, страница представляет собой простую форму ввода данных без непосредственного включения кода VBScript. Страница начинается со стандартной проверки пользователя и создания панели ссылок.

Листинг 10.23. NewProduct.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<html>
<!--
NewProduct.asp - Handles adding in a new product
into the store.
-->

<head>
<meta NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</head>
<body>

<!-- #Include file="include/navinclude.asp" -->

Форма отправляет данные странице AddNewProduct.asp (см. листинг 10.24). На форме создается набор элементов HTML, предназначенных для ввода названия товара, описания, графического изображения и признака активности. Атрибуты и принадлежность товара к тем или иным разделам определяются не здесь, а на странице ManageProduct.asp.

Листинг 10.24. NewProduct.asp (продолжение)

<!-- Form to post the new product to the database -->
<form method="post" action="AddNewProduct.asp">

<!-- Table build the form for adding the new product -->
<table cellpadding="3" cellspacing="3">

<!-- Product Name Input -->
<tr>
<td align="right"><b>Product Name:</b></td>
<td>
<input type="text" value="" name="chrProductName" size="60">
</td>
</tr>

<!-- Product Description Input -->
<tr>
<td align="right"><b>Product Description:</b></td>
<td>
<textarea cols="50" rows="10" name="txtDescription"></textarea>
</td>
</tr>

<!-- Product Image Input -->
<tr>
<td align="right"><b>Product Image:</b></td>
<td><input type="text" value="" name="chrProductImage"></td>
</tr>

<!-- Product Price Input -->
<tr>
<td align="right"><b>Product Price:</b></td>
<td><input type="text" value="" name="intPrice"></td>
</tr>

<!-- Check box to indicate the product is active -->
<tr>
<td align="right"><b>Active:</b></td>
<td>
<input type="checkbox" value="1" name="intActive">
</td>
</tr>

<!-- Submit button to add the product -->
<tr>
<td colspan="2" align="center">
<input type="submit" value="Add Product" name="Submit">
</td>
</tr>

<!-- Close out the page. -->
</table>
</form>

</body>
</html>

Страница AddNewProduct.asp (см. листинг 10.25) включает новый товар в базу данных. Необходимые значения берутся из полей формы. Обратите внимание - цена товара умножается на 100 для того, чтобы значение хранилось в базе в виде целого числа.

Непосредственное занесение данных в базу осуществляется хранимой процедурой sp_InsertProduct. После создания записи процедура возвращает идентификатор нового товара, и пользователь направляется на страницу ManageProduct.asp для продолжения редактирования.

Листинг 10.25. AddNewProduct.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' AddNewProduct.asp - занесение нового товара в базу данных магазина.
' ****************************************************

' Получить название товара и удвоить одиночные апострофы.
chrProductName = replace(request("chrProductName"), "'", "''")

' Retrieve the product description and ensure that
' any single quotes are doubled.
txtDescription = replace(request("txtDescription"), "'", "''")

' Retrieve the product image.
chrProductImage = request("chrProductImage")

' Retrieve the price. Ensure that we multiply
' times 100 to store as a whole integer.
intPrice = request("intPrice") * 100

' Retrieve the active setting.
intActive = request("intActive")

' We have to check and see if any setting is made.
' It will not be set if the box is not checked.
if intActive = "" then

' Set the flag to 0 so it is not active.
intActive = 0

else

' Set the flag to 0 so it is active.
intActive = 1

end if

' Create an ADO database connection
set dbProduct = server.createobject("adodb.connection")

' Create the record set
set rsProduct = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProduct.open("filedsn=WildWillieCDs")

' Execute the sp_InsertProduct stored procedure
' to add the product into the database.
sql = "execute sp_InsertProduct '" & _
chrProductName & "', '" & _
txtDescription & "', '" & _
chrProductImage & "', " & _
intPrice & ", " & _
intActive

' Execute the statement
set rsProduct = dbProduct.Execute(sql)

' Send the user to the ManageProduct.asp page to allow
' the user to edit the new product.
Response.Redirect "ManageProduct.asp?idProduct=" & _
rsProduct("idProduct")

%>

Занесение нового товара в базу осуществляется хранимой процедурой sp_InsertProduct (см. листинг 10.26). Данные передаются в виде параметров. Процедура возвращает идентификатор нового товара.

Листинг 10.26. Хранимая процедура sp_InsertProduct

CREATE PROCEDURE sp_InsertProduct

@chrProductName varchar(255),
@txtDescription text,
@chrProductImage varchar(100),
@intPrice int,
@intActive int

AS

insert into products(chrProductName, txtDescription, chrProductImage, intPrice, intActive)
values(@chrProductName, @txtDescription, @chrProductImage, @intPrice, @intActive)

select idProduct = @@identity

Страница ввода информации о новом товаре изображена на рис. 10.8. Заполните форму и нажмите кнопку Add Product, данные поступят в базу, после чего активизируется режим редактирования на странице ManageProduct.asp.

Длинная страница ManageProduct.asp, предназначенная для операций с товаром, начинается в листинге 10.27. На этой странице обеспечивается централизованное редактирование всех данных товара. Страница начинается со стандартного включения заголовочных файлов для проверки пользователя и создания панели ссылок.

Листинг 10.28. ManageProduct.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<html>
<!--
ManageProduct.asp - Provides the tools to manage
the product data.
-->

<head>
<meta NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</head>
<body>

<!-- #Include file="include/navinclude.asp" -->

Непосредственная работа начинается с загрузки данных товара, идентификатор которого передается в составе URL. Страница читает идентификатор и вызывает хранимую процедуру sp_RetrieveProduct, загружающую данные товара (см. листинг 10.28).

Листинг 10.28. ManageProduct.asp (продолжение)

<%

' Create an ADO database connection
set dbProduct = server.createobject("adodb.connection")

' Create the record set
set rsProduct = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProduct.open("filedsn=WildWillieCDs")

' The sp_RetrieveProduct stored procedure is utilized to
' get the data for the specified product.
sql = "execute sp_RetrieveProduct " & request("idProduct")

' Execute the statement
set rsProduct = dbProduct.Execute(sql)

%>

Выводимые данные начинаются с формы, передающей основные данные товара странице UpdateProduct.asp (см. листинг 10.29). Данные оформляются в виде таблицы. Обратите внимание на ссылку для предварительного просмотра - при помощи этой ссылки можно оценить, как будет выглядеть товар в электронном магазине, перед установкой флага активности.

Листинг 10.29. ManageProduct.asp (продолжение)

<!-- Start the form to update the product data. -->
<form method="post" action="UpdateProduct.asp">

<!-- Start the table to display the product data. -->
<table cellpadding="3" cellspacing="3">

<tr>
<td align="right"><b>Preview Product:</b></td>
<!-- To preview the product a link is built to
the product.asp page in the live store.
The Id of the product is passed on the URL. -->
<td><a href="/ecstore/wildwilliecds/product.asp?idProduct=<%=request("idProduct")%>">Preview
</a></td>
</tr>

<tr>
<td align="right"><b>Delete Product:</b></td>
<!-- A link to the deleteproduct.asp page is
created to remove the product from the
database. The ID of the product is
passed. -->
<td><a href="../Manager/DeleteProduct.asp?idProduct=<%=request("idProduct")%>">Delete
</a></td>
</tr>

<tr>
<td colspan="2"><hr></td>
</tr>
<tr>
<!-- The product ID is displayed. To ensure the ID
can be retrieved for the update a hidden
HTML element is created. -->
<td align="right"><b>Product ID:</b></td>
<td><%=rsProduct("idProduct")%>
<input type="hidden"
value="<%=request("idProduct")%>" name="idProduct">
</td>
</tr>
<tr>
<td align="right"><b>Product Name:</b></td>
<!-- Display the product name. -->
<td><input type="text"
value="<%=rsProduct("chrProductName")%>"
name="chrProductName" size="60">
</td>
</tr>
<tr>
<td align="right"><b>Product Description:</b></td>
<!-- Display the product description. -->
<td><textarea cols="50" rows="10" name="txtDescription"><%=rsProduct("txtDescription")%></textarea></td>
</tr>

В текстовом поле HTML выводится имя файла, содержащего графическое изображение товара, а также само изображение (см. листинг 10.30).

Листинг 10.30. ManageProduct.asp (продолжение)

<tr>
<td align="right"><b>Product Image:</b></td>
<!-- Display the product image file name and display
the image as well. -->
<td><input type="text" value="<%=rsProduct("chrProductImage")%>"
name="chrProductImage"> &nbsp;&nbsp;&nbsp;&nbsp;
<img src="../wildwilliecds/images/products/sm_
<%=rsProduct("chrProductImage")%>" align="center"></td>
</tr>
<tr>
<td align="right"><b>Product Price:</b></td>
<!-- The product price is displayed. -->
<td><input type="text" value="<%=rsProduct("intPrice")/100%>"
name="intPrice"></td>
</tr>
<tr>
<td align="right"><b>Active:</b></td>
<td>

Флажок активности товара по умолчанию восстанавливается в предыдущем состоянии. Мы проверяем поле активности товара в базе данных и затем генерируем код HTML для установленного или сброшенного флажка (см. листинг 10.31).

Листинг 10.31. ManageProduct.asp (продолжение)

<%

' Check to see if the product is active.
if rsProduct("intActive") = 1 then

%>

<!-- Display the check box checked if the
product is active. -->
<input type="checkbox" value="1" CHECKED name="intActive">

<%
else
%>

<!-- Display the check box with out the check. -->
<input type="checkbox" value="1" name="intActive">

<% end if %>

</td>
</tr>

<tr>
<td colspan="2" align="center">
<!-- Submit button for the form update -->
<input type="submit" value="Update Product" name="Submit">
</td>
</tr>

</table>
</form>

<hr>

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

Листинг 10.32. ManageProduct.asp (продолжение)

<%

' Create an ADO database connection
set dbDeptProd = server.createobject("adodb.connection")

' Create the record set
set rsDeptProd = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDeptProd.open("filedsn=WildWillieCDs")

' Execute the sp_RetrieveDeptByProd to retrieve
' the departments for the product being edited.
sql = "execute sp_RetrieveDeptByProd " & request("idProduct")

' Execute the statement
set rsDeptProd = dbDeptProd.Execute(sql)

%>

Список разделов выводится в таблице. В каждой строке таблицы имеется ссылка для исключения товара из соответствующего раздела. Идентификатор товара включается в URL и передается странице RemoveProdDept.asp (см. листинг 10.33).

Листинг 10.33. ManageProduct.asp (продолжение)

<!-- Start the table to display the
department list. -->
<table cellpadding="3" cellspacing="3" border="1">
<tr>
<th>Department</th>
<th>Delete</th>
<tr>
<%
' Loop through the departments.
do until rsDeptProd.eof
%>

<tr>
<!-- Display the department name. -->
<td><%=rsDeptProd("chrDeptName")%></td>
<!-- Build a link to the RemoveProdDept.asp page
to remove the department from the list. -->
<td><a href="RemoveProdDept.asp?idProduct=<%=request("idProduct")%>
&idDepartmentProduct=<%=rsDeptProd("idDepartmentProduct")%>">
Delete</a>
</td>
</tr>

</tr>

<%
' Move to the next department product
rsDeptProd.movenext
Loop
%>

</table>

После перечисления разделов, связанных с товаром в настоящий момент, создается список всех существующих разделов, чтобы мы могли отнести товар к новому разделу. Список создается на новой форме, передающей данные странице ProdAddDept.asp.

ПРИМЕЧАНИЕ
В данном примере в список включаются все существующие разделы, хотя логичнее было бы включить в него только те разделы, к которым товар не относится в настоящий момент. Кроме того, при отнесении товара к новому разделу следовало бы организовать проверку ошибок.

Хранимая процедура sp_RetrieveDepts получает из базы записи всех существующих разделов. Затем список заполняется в цикле Do Loop. Обратите внимание: идентификатор товара сохраняется на форме в скрытом поле. Это позволяет легко определить, с каким товаром связывается добавляемый раздел (см. листинг 10.34).

Листинг 10.34. ManageProduct.asp (продолжение)

<br>

<!-- Start a new form to add an addition
department for the product. -->
<form method="post" action="ProdAddDept.asp">
<b>Add a department:</b>

<%

' Create an ADO database connection
set dbDepts = server.createobject("adodb.connection")

' Create the record set
set rsDepts = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDepts.open("filedsn=WildWillieCDs")

' Retrieve all of the departments in the database
sql = "execute sp_RetrieveDepts"

' Execute the statement
set rsDepts = dbDepts.Execute(sql)

%>

<!-- Start the select box for the list of
departments. -->
<select name="idDepartment">
<%
' Loop through the departments
do until rsDepts.eof
%>

<!-- Build the option list for each
department. -->
<option value="<%=rsDepts("idDepartment")%>">
<%=rsDepts("chrDeptName")%>

<%
' Move to the next row
rsDepts.movenext
loop
%>

</select>

<!-- Build a hidden variable in this form so
we know what product to assign this
department to. -->
<input type="hidden" name="idProduct"
value="<%=request("idProduct")%>">

<!-- Submit button form teh form. -->
<input type="submit" value="Submit" name="Submit">

</form>

<hr>

От разделов мы переходим к атрибутам товаров (см. листинг 10.35). В данном примере используется фиксированный набор категорий атрибутов - цвет и размер.

Сначала обрабатываются атрибуты категории "цвет". Чтение всех атрибутов, назначенных товару, осуществляется хранимой процедурой sp_Attributes.

Листинг 10.35. ManageProduct.asp (продолжение)

<%

' Create an ADO database connection
set dbAttributes = server.createobject("adodb.connection")

' Create a record set
set rsAttributes = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbAttributes.open("filedsn=WildWillieCDs")

' Execute the stored procedure to retrieve the attributes
' for the products.
sql = "execute sp_Attributes " & request("idProduct")

' Execute the SQL statement
set rsAttributes = dbProduct.Execute(sql)

%>

Перечень атрибутов выводится в таблице (см. листинг 10.36). Как и при выводе разделов, рядом с каждым атрибутом выводится ссылка для удаления атрибута из информации текущего товара.

Листинг 10.36. ManageProduct.asp (продолжение)

<!-- Start the table to build list of color and
size attributes. -->
<table>

<tr>
<td>
<b>COLOR:</b>

<!-- Build a list of current color assignments. -->
<table cellpadding="3" cellspacing="3" border="1">

<%

' Loop through the attributes.
do until rsAttributes.EOF

' Check to see if we have moved beyond the color
' attribute in the list..
if rsAttributes("chrCategoryName") <> "Color" then

' Exit the do loop
exit do

end if

%>

<tr>
<td>
<!-- Display the attribute name. -->
<%=rsAttributes("chrAttributeName")%>
</td>
<td>
<!-- Build a link to the DeleteAttribute.asp page
to remove the attribute for the product. -->
<a href="DeleteAttribute.asp
?idProduct=<%=request("idProduct")%>
&idProductAttribute=<%=rsAttributes("idProductAttribute")%>">
Delete</a>
</td>
</tr>
<%

' Move to the next row
rsAttributes.MoveNext

' Loop back
loop

%>

</table>
</td>
</tr>
<!-- Build a buffer between the listings. -->
<tr><td>&nbsp;</td></tr>

Далее выводятся текущие значения атрибутов размера (см. листинг 10.37). Они, как и атрибуты цвета, выводятся в таблице. Рядом с каждым атрибутом размера отображается ссылка удаления, в URL которой включается идентификатор атрибута.

Листинг 10.37. ManageProduct.asp (продолжение)

<tr>
<!-- Start the size listings -->
<td>
<b>SIZE: </b>

<!-- Start the size listing table. -->
<table cellpadding="3" cellspacing="3" border="1">

<%

' Loop through the size attributes
do until rsAttributes.EOF

%>

<tr>
<td>
<!-- Display the name of the size attribute -->
<%=rsAttributes("chrAttributeName")%>
</td>
<td>
<!-- Build a link to the DeleteAttribute.asp
page to remove the size setting for the
product. -->
<a href="DeleteAttribute.asp
?idProduct=<%=request("idProduct")%>
&idProductAttribute=<%=rsAttributes("idProductAttribute")%>">
Delete</a>
</td>
</tr>

<%

' Move to the next row
rsAttributes.MoveNext

' Loop back
loop

%>

</table>

</td>

</tr>

</table>

Затем создаются списки атрибутов, в которых пользователь выбирает варианты расцветки и размера текущего товара. Чтобы упростить структуру страницы, для построения этого фрагмента используется процедура ShowAttributeList (см. листинг 10.38). За вызовом процедуры следуют стандартные завершающие теги.

Листинг 10.38. ManageProduct.asp (продолжение)

<!-- Вызвать процедуру ShowAttributeList -->
<% ShowAttributeList %>

<!-- Завершить страницу -->
</body>
</html>

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

Листинг 10.39. ManageProduct.asp (продолжение)

<!-- Start the ShowAttributeList subroutine. -->

<%

' Start the subroutine.
Sub ShowAttributeList()

' Create an ADO database connection
set dbAttributes = server.createobject("adodb.connection")

' Create a record set
set rsAttributes = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbAttributes.open("filedsn=WildWillieCDs")

' Execute the stored procedure to retrieve the attributes
' in the database.
sql = "execute sp_RetrieveAttributes"

' Execute the SQL statement
set rsAttributes = dbProduct.Execute(sql)

%>

<br>

<!-- Start the option to build the list of
attributes in the database. -->
<b>Select an Attribute to Add:</b>

Списки атрибутов заносятся в таблицу (см. листинг 10.40). Каждое значение атрибута заносится в список отдельной строкой. Обратите внимание - для каждого списка создается специальная форма, передающая данные странице AddAttribute.asp. В скрытом поле на форме сохраняется идентификатор товара, которому назначается новый атрибут.

Листинг 10.40. ManageProduct.asp (продолжение)

<table>

<tr>
<!-- Color column -->
<td>
<!-- Build a form to post the adding of the
attribute. -->
<form method="post" action="AddAttribute.asp">
Color:

<!-- Select box for display of the color options -->
<select name="idAttribute">

<%

' Loop through the attributes.
do until rsAttributes.EOF

' Check to see if we have moved beyond the color
' attribute in the list..
if rsAttributes("chrCategoryName") <> "Color" then

' Exit the do loop
exit do

end if

%>

<!-- Build the option value for the color. The value will be
the ID of the color -->
<option value="<%=rsAttributes("idAttribute")%>">
<%=rsAttributes("chrAttributeName")%>

<%

' Move to the next row
rsAttributes.MoveNext

' Loop back
loop

%>

</select>

<!-- Build a hidden variable to store the id
of the product so we know what product to
add the attribute to. -->
<input type="hidden" value="<%=rsProduct("idProduct")%>"
name="idProduct">

<!-- Submit button to add the attribute to the list. -->
<input type="Submit" value="Add" name="Submit">
</form>
</td>
</tr>

После списка цветов аналогичным образом создается список размеров (см. листинг 10.41).

ПРИМЕЧАНИЕ
Вероятно, в списки не следует включать размеры и цвета, уже назначенные текущему товару.

Листинг 10.41. ManageProduct.asp (продолжение)

<tr>
<!-- Build the size attributes select box. -->
<td>
<form method="post" action="AddAttribute.asp">
Size:

<!-- Start the size select box -->
<select name="idAttribute">

<%

' Loop through the size attributes
do until rsAttributes.EOF

%>

<!-- Display the options -->
<option value="<%=rsAttributes("idAttribute")%>">
<%=rsAttributes("chrAttributeName")%>

<%

' Move to the next row
rsAttributes.MoveNext

' Loop back
loop

%>

</select>

<!-- Build the hidden HTML element to store the
product id. -->
<input type="hidden" value="<%=rsProduct("idProduct")%>"
name="idProduct">

<!-- Build the submit button for the form. -->
<input type="Submit" value="Add" name="Submit">
</form>
</td>

</tr>

</table>

<%
End Sub
%>

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

На странице используется несколько хранимых процедур. Первая, sp_RetrieveProduct, возвращает основные данные товара по идентификатору товара, переданному в качестве параметра (см. листинг 10.42).

Листинг 10.42. Хранимая процедура sp_RetrieveProduct

/* Загрузка данных товара */
CREATE PROCEDURE sp_RetrieveProduct

/* При вызове процедуре передается идентификатор товара */
@idProduct int

AS

/* Выборка сведений о товаре */
select * from products
where idProduct = @idProduct

Хранимая процедура sp_RetrieveDeptByProd (см. листинг 10.43) возвращает информацию о разделах, к которым относится заданный товар. Идентификатор товара передается процедуре в качестве параметра.

Листинг 10.43. Хранимая процедура sp_RetrieveDeptByProd

CREATE PROCEDURE sp_RetrieveDeptByProd

@idProduct int

AS

select * from department, departmentproducts
where departmentproducts.idProduct = @idProduct and
department.iddepartment = departmentproducts.iddepartment

Кроме того, нам понадобится получить из базы данных список всех текущих разделов. Задача решается хранимой процедурой sp_RetrieveDepts (см. листинг 10.44).

Листинг 10.44. Хранимая процедура sp_RetrieveDepts

/* Загрузка информации обо всех разделах магазина из базы данных */
CREATE PROCEDURE sp_RetrieveDept

/* Выборка всех записей разделов */
@idDepartment int

AS

/* Select all of the data on the
department */
select * from department
where idDepartment = @idDepartment

Следующая хранимая процедура sp_Attributes загружает из базы все атрибуты, назначенные текущему товару (см. листинг 10.45). Идентификатор товара передается процедуре в качестве параметра. Для получения необходимых данных следует объединить таблицы Products, ProductAttribute, Attribute и AttributeCategory; объединение осуществляется средствами SQL.

Листинг 10.45. Хранимая процедура sp_Attributes

/* Загрузка атрибутов заданного товара из базы данных */
CREATE PROCEDURE sp_Attributes

/* При вызове процедуре передается идентификатор товара */
@idProduct int

AS

/* select statement to return attributes for the product. */
select products.idproduct,
attribute.idattribute,
attribute.chrattributename,
attributecategory.chrcategoryname,
productattribute.idproductattribute

from products, productattribute, attribute, attributecategory

where

products.idproduct = @idProduct and
productattribute.idproduct = @idProduct and
productattribute.idattribute = attribute.idattribute and
attribute.idattributecategory = attributecategory.idattributecategory

order by chrcategoryname

Наконец, в листинге 10.46 приведена хранимая процедура sp_RetrieveAttributes, возвращающая все атрибуты текущей базы данных.

Листинг 10.46. Хранимая процедура sp_RetrieveAttributes

CREATE PROCEDURE sp_RetrieveAttributes AS

select * from attribute, attributecategory
where attribute.idattributecategory = attributecategory.idAttributeCategory
order by attributecategory.chrCategoryName

Экран редактирования товара. Все текущие данные, прочитанные из базы, заносятся в поля формы. Обратите внимание: на странице отсутствует информация о разделах или атрибутах, назначенных товару.

Давайте свяжем товар с новым разделом. Выберите в списке Add a Department строку Cool Backstreet Jazz и щелкните на кнопке Submit. На рис. 10.10 показан вид страницы после назначения нового раздела.

Повторите операцию и свяжите товар со вторым разделом. На этот раз в списке следует выбрать строку Punked Out.

Теперь можно воспользоваться ссылкой Delete для удаления только что назначенного раздела.

Перейдем к операциям с атрибутами. Выберите из списков атрибуты red и small и включите их в информацию товара. При желании попробуйте поэкспериментировать с удалением атрибутов.

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

Рассмотрим вспомогательные страницы, обеспечивающие работу ManageProduct.asp. В листинге 10.47 приведена страница DeleteProduct.asp, удаляющая товар из базы данных. Она состоит исключительно из программного кода и возвращает пользователя к списку товаров.

Листинг 10.47. DeleteProduct.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' DeleteProduct.asp - Удаление товаров из магазина.
' ****************************************************

' Create an ADO database connection
set dbProduct = server.createobject("adodb.connection")

' Create the record set
set rsProduct = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProduct.open("filedsn=WildWillieCDs")

При вызове хранимой процедуры sp_DeleteProduct передается идентификатор товара (см. листинг 10.48). Товар удаляется из базы данных, и пользователь возвращается к списку товаров.

Листинг 10.48. DeleteProduct.asp (продолжение)

' Call the sp_DeleteProduct stored procedure to
' remove the product from the database.
sql = "execute sp_DeleteProduct " & request("idProduct")

' Execute the statement
set rsProduct = dbProduct.Execute(sql)

' Send the user back to the list of producs.
Response.Redirect "ListProducts.asp"

%>

Программный код хранимой процедуры приведен в листинге 10.49. Процедура содержит команду SQL DELETE, удаляющую данные товара из базы.

СОВЕТ
При удалении товара в базе остаются сведения о присвоенных ему разделах и атрибутах. Для обеспечения целостности данных в базе эту информацию следовало бы удалить в хранимой процедуре или на уровне базы данных с использованием триггеров.

Листинг 10.49. Хранимая процедура sp_DeleteProduct

CREATE PROCEDURE sp_DeleteProduct

@idProduct int

AS

delete from products where idProduct = @idProduct

Следующая вспомогательная страница, UpdateProduct.asp(см. листинг 10.50), обновляет базу данных информацией, введенной пользователем. Ее работа начинается со сбора данных, введенных пользователем. Перед сохранением строковых данных в базе все одиночные апострофы необходимо удвоить.

Листинг 10.50. UpdateProduct.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' UpdateProduct.asp - Handles updating the product
' data.
' ****************************************************

' Retrieve the product id
idProduct = request("idProduct")

' Retrieve the product name and ensure any single
' quotes are doubled.
chrProductName = replace(request("chrProductName"), "'", "''")

' Retrieve the product description and ensure any single
' quotes are doubled.
txtDescription = replace(request("txtDescription"), "'", "''")

' Retrieve the product image
chrProductImage = request("chrProductImage")

' Retrieve the product price and multiply by 100 to
' ensure it is stored as an integer.
intPrice = request("intPrice") * 100

' Retrieve the active status.
intActive = request("intActive")

Флаг активности проверяется дополнительно (см. листинг 10.51). Если флаг не установлен, в базе данных сохраняется значение 0.

Листинг 10.51. UpdateProduct.asp (продолжение)

' Проверить, установлен ли флажок активности товара.
if intActive = "" then

' Если флажок сброшен, товар неактивен
intActive = 0

else

' Если флажок установлен, товар активен
intActive = 1

end if

После получения данных с формы мы создаем подключение к базе данных и передаем изменения в базу (см. листинг 10.52). Соответствующая команда SQL вызывается в хранимой процедуре sp_UpdateProduct. Наконец, пользователь возвращается на страницу ManageProduct.asp и продолжает редактирование товара.

Листинг 10.52. UpdateProduct.asp (продолжение)

' Create an ADO database connection
set dbProduct = server.createobject("adodb.connection")

' Create the record set
set rsProduct = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProduct.open("filedsn=WildWillieCDs")

' Execute the SQL stored procedure to update the
' product data
sql = "execute sp_UpdateProduct " & _
request("idProduct") & ", '" & _
chrProductName & "', '" & _
txtDescription & "', '" & _
chrProductImage & "', " & _
intPrice & ", " & _
intActive

' Execute the statement
set rsProduct = dbProduct.Execute(sql)

' Send the user back to the product manager page and
' pass back the product i.
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")

%>

Хранимая процедура sp_UpdateProduct обновляет сведения о товаре по значениям, переданным в качестве параметров (см. листинг 10.53).

Листинг 10.53. Хранимая процедура sp_UpdateProduct

CREATE PROCEDURE sp_UpdateProduct

@idProduct int,
@chrProductName varchar(255),
@txtDescription text,
@chrProductImage varchar(100),
@intPrice int,
@intActive int,
@intFeatured int,
@dtFeatureStart datetime,
@dtFeatureEnd datetime,
@intSalePrice int,
@dtSaleStart datetime,
@dtSaleEnd datetime

AS

update products set
chrProductName = @chrProductName, txtDescription = @txtDescription,
chrProductImage = @chrProductImage,
intPrice = @intPrice,
intActive = @intActive,
intFeatured = @intFeatured,
dtFeatureStart = @dtFeatureStart,
dtFeatureEnd = @dtFeatureEnd,
intSalePrice = @intSalePrice,
dtSaleStart = @dtSaleStart,
dtSaleEnd = @dtSaleEnd
where
idProduct = @idProduct

Страница RemoveProdDept.asp (см. листинг 10.54) исключает товар из раздела. Странице передаются идентификаторы товара и связи "товар-раздел". Полученное значение передается хранимой процедуре sp_DeleteProdDept, после чего пользователь возвращается на страницу редактирования товара.

Листинг 10.54. RemoveProdDept.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' RemoveProdDept.asp - Removes the product from a
' specific department.
' ****************************************************

' Create an ADO database connection
set dbProdDept = server.createobject("adodb.connection")

' Create the record set
set rsProdDept = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProdDept.open("filedsn=WildWillieCDs")

' Execute the sp_DeleteProdDept stored procedure to remove
' the department assignment for the product.
sql = "execute sp_DeleteProdDept " & request("idDepartmentProduct")

' Execute the statement
set rsProdDept = dbProdDept.Execute(sql)

' Send the user back to the product management page
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")

%>

Хранимая процедура sp_DeleteProdDept удаляет из таблицы DepartmentProducts информацию о связи между товаром и разделом (см. листинг 10.55). Идентификатор связи передается процедуре в качестве параметра.

Листинг 10.55. Хранимая процедура sp_DeleteProdDept

CREATE PROCEDURE sp_DeleteProdDept

@idDepartmentProduct int

AS

delete from departmentproducts where idDepartmentProduct = @idDepartmentProduct

Следующая вспомогательная страница, ProdAddDept.asp (см. листинг 10.56), создает связь между товаром и разделом. Идентификаторы товара и раздела передаются в качестве параметров. После создания связи пользователь возвращается на страницу ManageProduct.asp и продолжает редактирование товара.

Листинг 10.56. ProdAddDept.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' ProdAddDept.asp - Adds a new product into a
' department.
' ****************************************************

' Create an ADO database connection
set dbProdDept = server.createobject("adodb.connection")

' Create the record set
set rsProdDept = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProdDept.open("filedsn=WildWillieCDs")

' Execute the sp_AddProdDept stored procedure to
' tie together the product and the department
sql = "execute sp_AddProdDept " & _
request("idProduct") & ", " & _
request("idDepartment")

' Execute the statement
set rsProdDept = dbProdDept.Execute(sql)

' Send the user back to the manageproduct.asp page
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")

%>

Хранимая процедура sp_AddProdDept (см. листинг 10.57) создает в таблице DepartmentProducts новую запись. Для построения связи используются значения идентификаторов товара и раздела.

Листинг 10.57. Хранимая процедура sp_AddProdDept

CREATE PROCEDURE sp_AddProdDept

@idProduct int,
@idDepartment int

AS

insert into DepartmentProducts(idProduct, idDepartment)
values(@idProduct, @idDepartment)

Переходим к операциям с атрибутами. Первая страница, Del eteAttri bute.asp (см. листинг 10.58), удаляет связь между товаром и атрибутом. Задача решается при помощи хранимой процедуры sp_Del eteProductAttr i bute. Идентификатор связи "товар-атрибут" передается процедуре в качестве параметра. После удаления атрибута пользователь возвращается на страницу ManageProduct.asp.

Листинг 10.58. DeleteAttribute.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' DeleteAttribute.asp - Deletes an attribute assigned
' to a product.
' ****************************************************

' Create an ADO database connection
set dbProductAttribute = server.createobject("adodb.connection")

' Create the record set
set rsProductAttribute = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProductAttribute.open("filedsn=WildWillieCDs")

' The SQL statement removes the product
' attribute setting for the specified product.
sql = "sp_DeleteProductAttribute " & request("idProductAttribute")

' Execute the statement
set rsProductAttribute = dbProductAttribute.Execute(sql)

' Send the user to the ManageProduct.asp page
' to allow the user to continue editing the
' product.
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")

%>

Идентификатор связи "товар-атрибут" в таблице ProductAttribute передается при вызове хранимой процедуры sp_DeleteProductAttribute (см. листинг 10.59), удаляющей запись из таблицы.

Листинг 10.59. Хранимая процедура sp_DeleteProductAttribute

CREATE PROCEDURE sp_DeleteProductAttribute

@idProductAttribute int

AS

delete from productattribute where idProductAttribute = @idProductAttribute

Последняя из вспомогательных страниц назначает атрибуты товару (см. листинг 10.60). Связь между товаром и атрибутом создается хранимой процедурой sp_AddProductAttri bute. При вызове этой процедуре передаются идентификаторы товара и атрибута.

Листинг 10.60. AddAttribute.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' AddAttribute.asp - Adds the attribute setting for
' the specified product.
' ****************************************************

' Create an ADO database connection
set dbProdAttr = server.createobject("adodb.connection")

' Create the record set
set rsProdAttr = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbProdAttr.open("filedsn=WildWillieCDs")

' Execute the sp_AddProductAttribute stored procedure to
' indicate the attribute is to be assigned to the product.
sql = "execute sp_AddProductAttribute " & _
request("idAttribute") & ", " & _
request("idProduct")

' Execute the statement
set rsProdAttr = dbProdAttr.Execute(sql)

' Send the user back to the product management page.
Response.Redirect "ManageProduct.asp?idProduct=" & _
request("idProduct")

%>

Хранимая процедура sp_AddProductAttribute создает новую запись в таблице ProductAttribute (см. листинг 10.61).

Листинг 10.61. Хранимая процедура sp_AddProductAttribute

CREATE PROCEDURE sp_AddProductAttribute

@idAttribute int,
@idProduct int

AS

insert into ProductAttribute(idAttribute, idProduct)
values(@idAttribute, @idProduct)

На этом рассмотрение операции с товарами подходит к концу. Мы рассмотрели вывод списка товаров, поиск, операции создания и редактирования товаров. Остается лишь упомянуть о нескольких дополнительных аспектах.

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

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

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

Еще одна проблема - графическое изображение товара. Предполагается, что пользователь обладает прямым сетевым доступом к файлам изображений. Если пользователь работает с информацией о товарах через Интернет, нам придется реализовать дополнительные средства для работы с графикой. Например, можно предоставить FTP-доступ к каталогу изображений или реализовать пересылку файлов средствами HTML.

Наша следующая тема - операции с разделами.

Операции с разделами

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

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

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

Листинг 10.62. ListDepts.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
ListDepts.asp - Lists the departments in the
store.
-->

<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<!-- #include file="include/navinclude.asp" -->

<%

' Create an ADO database connection
set dbDepts = server.createobject("adodb.connection")

' Create the record set
set rsDepts = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDepts.open("filedsn=WildWillieCDs")

' Call the sp_RetrieveDepts stored procedure to
' return the departments in the database.
sql = "execute sp_RetrieveDepts"

' Execute the statement
set rsDepts = dbDepts.Execute(sql)

%>

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

Листинг 10.63. ListDepts.asp (продолжение)

<!-- Build a link the new department page
if the user wants to add a department. -->
<BR><b>Click <a href="NewDept.asp">here</a> to add
a new department.</b>

<!-- Start out the structure to show the list
of departments.
-->
<BR><BR>
<b>To edit a department, select from the list below:</b>
<BR><BR>

<table cellpadding="3" cellspacing="3">
<tr>
<th>Department ID</th>
<th>Name</th>
</tr>

Построение строк таблицы происходит в цикле (см. листинг 10.64). Для каждого раздела создается ссылка на страницу ManageDept.asp, содержащая идентификатор раздела и его название.

Листинг 10.64. ListDepts.asp (продолжение)

<%

' Loop through the list of departments.
do until rsDepts.EOF

%>

<!-- Create a link to the ManageDept.asp to work
with the department. -->
<tr>
<td>
<!-- The link to the department needs to include the
id of the department. Following that the id
of the deparment is displayed. -->
<a href="ManageDept.asp?idDepartment=<%=rsDepts("idDepartment")%>">
<%=rsDepts("idDepartment")%></a></td>

<!-- The link to the department needs to include the
id of the department. Following that the name
of the deparment is displayed. -->
<td>
<a href="ManageDept.asp?idDepartment=<%=rsDepts("idDepartment")%>">
<%=rsDepts("chrDeptName")%></a></td>

</tr>

<%

' Move to the next row
rsDepts.MoveNext

' Loop back
Loop

%>

</table>

</BODY>
</HTML>

В работе страницы используется хранимая процедура sp_RetrieveDepts (см. листинг 10.65). Эта процедура просто загружает данные всех разделов при помощи команды SQL SELECT.

Листинг 10.65. Хранимая процедура sp_RetrieveDepts

/* Stored procedure to retrieve all of
the departments in the database */
CREATE PROCEDURE sp_RetrieveDepts AS

/* Выборка всех записей разделов */
select * from department

После создания списка можно переходить к операциям создания или редактирования разделов. Начнем с создания новых разделов. Начало страницы NewDept.asp приведено в листинге 10.66.

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

Листинг 10.66. NewDept.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
NewDept.asp - Handles adding in a new department
into the store.
-->

<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!-- #Include file="include/navinclude.asp" -->

На странице создается форма, которая передает данные нового раздела странице AddNewDepartment.asp. В форме присутствуют поля для ввода названия раздела, описания и имени файла с графическим изображением (см. листинг 10.67).

Листинг 10.67. NewDept.asp (продолжение)

<!-- Форма для ввода сведений о новом разделе -->
<form method="post" action="AddNewDepartment.asp">

<!-- Таблица для вывода полей формы -->
<table cellpadding="3" cellspacing="3">

<!-- Название раздела -->
<tr>
<td>Department Name</td>
<td><input type="text" value="" name="chrDeptName"</td>
</tr>

<!-- Описание раздела -->
<tr>
<td>Department Description</td>
<td><textarea name="txtDeptDesc" cols="40" rows="5"></textarea></td>
</tr>

<!-- Имя файла с графическим изображением -->
<tr>
<td>Department Image</td>
<td><input type="text" value="" name="chrDeptImage"></td>
</tr>

Страница завершается кнопкой Submit, которая отправляет данные формы, и закрывающими тегами формы, таблицы и страницы (см. листинг 10.68).

Листинг 10.68. NewDept.asp (продолжение)

<!-- Кнопка отправки данных формы. -->
<tr>
<td colspan="2">
<input type="Submit" value="Add Department"
name="Submit"></td>
</tr>

<!-- Close out the page -->
</table>

</form>

</BODY>
</HTML>

В листинге 10.69 приведен код страницы AddNewDepartment.asp, которой передает данные страница NewDept.asp. Страница получает значения, введенные на странице создания нового раздела, и сохраняет их в базе данных.

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

Листинг 10.69. AddNewDepartment.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' AddNewDepartment.asp - Handles adding a new
' department to the store.
' ****************************************************

' Retrieve the department name and ensure any single
' quotes are doubled.
chrDeptName = replace(request("chrDeptName"), "'", "''")

' Retrieve the department description and ensure any
' single quotes are doubled.
txtDeptDesc = replace(request("txtDeptDesc"), "'", "''")

' Retrieve the department image file name.
chrDeptImage = request("chrDeptImage")

Затем страница создает подключение к базе данных (см. листинг 10.70). Хранимая процедура sp_InsertDept создает запись нового раздела в базе. После создания раздела пользователь возвращается на страницу ManageDept.asp, а новый раздел появляется в общем списке.

Листинг 10.70. AddNewDepartment.asp

' Create an ADO database connection
set dbDept = server.createobject("adodb.connection")

' Create the record set
set rsDept = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDept.open("filedsn=WildWillieCDs")

' Execute the sp_InsertDept stored procedure to add
' the new department into the database.
sql = "execute sp_InsertDept '" & _
chrDeptName & "', '" & _
txtDeptDesc & "', '" & _
chrDeptImage & "'"

' Execute the statement
set rsDept = dbDept.Execute(sql)

' Redirect the user to the ManageDept.asp page to do some
' any editing on the product. Note that the ID of the
' new department is returned from the stored procedure.
Response.Redirect "ManageDept.asp?idDepartment=" & _
rsDept("idDepartment")

%>

В работе страницы используется хранимая процедура sp_InsertDept (см. листинг 10.71), которой в качестве параметров передаются три значения. Процедура создает команду SQL INSERT для сохранения данных в таблице и возвращает идентификатор нового раздела.

Листинг 10.71. Хранимая процедура sp_InsertDept

CREATE PROCEDURE sp_InsertDept

@chrDeptName varchar(255),
@txtDeptDesc text,
@chrDeptImage varchar(100)

AS

insert into department(chrDeptName, txtDeptDesc, chrDeptImage)
values(@chrDeptName, @txtDeptDesc, @chrDeptImage)

select idDepartment = @@identity

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

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

Листинг 10.72. ManageDept.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
ManageDept.asp - Handles the management of the the
department and allows the shopper to update the
data.
-->

<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<!-- #Include file="include/navinclude.asp" -->

Страница открывает подключение ADO и загружает данные раздела при помощи хранимой процедуры sp_RetrieveDept (см. листинг 10.73), которой в качестве параметра передается идентификатор раздела.

Листинг 10.73. ManageDept.asp (продолжение)

<%

' Create an ADO database connection
set dbDept = server.createobject("adodb.connection")

' Create the record set
set rsDept = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDept.open("filedsn=WildWillieCDs")

' The retrieve sp_RetrieveDept stored procedure to
' retrieve the data on the specified departments.
sql = "execute sp_RetrieveDept " & request("idDepartment")

' Execute the statement
set rsDept = dbDept.Execute(sql)

%>

В листинге 10.74 создается форма, передающая новые данные раздела странице UpdateDept.asp для их обновления в базе данных.

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

После ссылки предварительного просмотра создается ссылка для удаления раздела из базы данных. Идентификатор удаляемого раздела передается в URL.

Листинг 10.74. ManageDept.asp (продолжение)

<!-- The form is created to post the changes -->
<form method="post" action="UpdateDept.asp">

<!-- Start the table to display the product data -->
<table cellpadding="3" cellspacing="3">
<tr>
<td>Preview Department:</td>
<td>
<!-- Build a link to the products.asp page
in the live store. The id of the department
is passed into the page. This will provide a
quick preview of the department. -->
<a href="/ecstore/wildwilliecds/products.asp?idDept=<%=request("idDepartment")%>">Preview</a>
</td>
</tr>
<tr>
<td>Delete Department:</td>
<!-- A link is built to the DeleteDept.asp page. The id
of the department is sent into the page. -->
<td><a href="DeleteDept.asp?idDepartment=<%=request("idDepartment")%>">
Delete</a></td>
</tr>
<!-- Build a buffer between this setion and the next -->
<tr>
<td colspan="2"><hr></td>
</tr>

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

Листинг 10.75. ManageDept.asp (продолжение)

<tr>
<td>Department ID</td>
<!-- Идентификатор раздела только выводится, редактировать его нельзя. -->
<td><%=rsDept("idDepartment")%>
<input type="hidden"
value="<%=rsDept("idDepartment")%>" name="idDepartment">
</td>
</tr>
<!-- Текстовое поле для редактирования названия раздела. -->
<tr>
<td>Department Name</td>
<td><input type="text"
value="<%=rsDept("chrDeptName")%>" name="chrDeptName"</td>
</tr>
<!-- Текстовое поле для редактирования описания раздела. -->
<tr>
<td>Department Description</td>
<td><textarea name="txtDeptDesc" cols="40" rows="5"><%=rsDept("txtDeptDesc")%></textarea></td>
</tr>

Наряду с графическим изображением раздела выводится имя графического файла, в котором оно хранится (см. листинг 10.76) Как и при работе с изображениями товаров, предполагается, что файл каким-либо способом загружается в систему.

Листинг 10.76. ManageDept.asp (продолжение)

<!-- Текстовое поле для редактирования имени файла, содержащего изображение раздела. Изображение выводится рядом с именем файла. -->
<tr>
<td>Department Image</td>
<td><input type="text" value="<%=rsDept("chrDeptImage")%>"
name="chrDeptImage">&nbsp;&nbsp;&nbsp;&nbsp;
<img src="../wildwilliecds/images/<%=rsDept("chrDeptImage")%>"
align="center"></td>
</tr>
<!-- Кнопка отправки данных. -->
<tr>
<td colspan="2"><input type="Submit"
value="Update Department" name="Submit"></td>
</tr>

</table>
</form>

</BODY>
</HTML>

В работе страницы используется хранимая процедура sp_RetrieveDept. Она получает идентификатор раздела и загружает информацию о нем при помощи команды SELECT (см. листинг 10.77).

Листинг 10.77. Хранимая процедура sp_RetrieveDept

/* Загрузка информации об одном разделе */
CREATE PROCEDURE sp_RetrieveDept

/* При вызове процедуре передается идентификатор раздела */
@idDepartment int

AS

/* Выборка данных раздела с заданным идентификатором */
select * from department
where idDepartment = @idDepartment

Перейдем к рассмотрению функций обновления и удаления, присутствующих на странице ManageDept.asp. Начнем со страницы UpdateDept.asp, обновляющей раздел.

Сначала страница читает с полученной формы данные - идентификатор раздела, название, описание и имя графического файла (см. листинг 10.78).

Листинг 10.78. UpdateDept.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' UpdateDept.asp - Handles updating the department
' department data.
' ****************************************************

' Retrieve the id of the department
idDepartment = request("idDepartment")

' Retrieve the department name and ensure that any
' single quotes are doubled.
chrDeptName = replace(request("chrDeptName"), "'", "''")

' Retrieve the department description and ensrue that
' any single quotes are doubled.
txtDeptDesc = replace(request("txtDeptDesc"), "'", "''")

' Retrieve the department image
chrDeptImage = request("chrDeptImage")

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

Листинг 10.78. UpdateDept.asp

' Create an ADO database connection
set dbDept = server.createobject("adodb.connection")

' Create the record set
set rsDept = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDept.open("filedsn=WildWillieCDs")

' Execute the sp_UpdateDepartment stored procedure to
' update the department data.
sql = "execute sp_UpdateDepartment " & _
request("idDepartment") & ", '" & _
chrDeptName & "', '" & _
txtDeptDesc & "', '" & _
chrDeptImage & "'"

' Execute the statement
set rsDept = dbDept.Execute(sql)

' Send the user back to department manager and pass back
' the department id.
Response.Redirect "ManageDept.asp?idDepartment=" & _
request("idDepartment")

%>

Код хранимой процедуры sp_UpdateDepartment приведен в листинге 10.80. Процедура получает новые данные раздела, переданные в качестве параметров, и обновляет информацию в базе данных командой SQL UPDATE.

Листинг 10.80. Хранимая процедура sp_UpdateDepartment

CREATE PROCEDURE sp_UpdateDepartment

@idDepartment int,
@chrDeptName varchar(255),
@txtDeptDesc text,
@chrDeptImage varchar(100)

AS

update department set
chrDeptName = @chrDeptName,
txtDeptDesc = @txtDeptDesc,
chrDeptImage = @chrDeptImage
where idDepartment = @idDepartment

Переходим к следующей операции - к удалению разделов. Страница DeleteDept.asp приведена в листинге 10.81.

Идентификатор раздела передается в URL. Страница создает подключение ADO к базе данных и выполняет хранимую процедуру sp_DeleteDept. После удаления пользователь возвращается к списку разделов.

Листинг 10.81. DeleteDept.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' DeleteDept.asp - Deletes the department from the
' store.
' ****************************************************

' Create an ADO database connection
set dbDept = server.createobject("adodb.connection")

' Create the record set
set rsDept = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDept.open("filedsn=WildWillieCDs")

' The sp_DeleteDept stored procedure removes the
' department from the database.
sql = "execute sp_DeleteDept " & request("idDepartment")

' Execute the statement
set rsDept = dbDept.Execute(sql)

' Send the user back to the list of departments.
Response.Redirect "ListDepts.asp"

%>

В листинге 10.82 приведен код хранимой процедуры sp_DeleteDept. Процедура получает идентификатор раздела, переданный в качестве параметра, и удаляет данные из базы командой SQL DELETE.

Листинг 10.82. Хранимая процедура sp_DeleteDept

CREATE PROCEDURE sp_DeleteDept

@idDepartment int

AS

delete from department where idDepartment = @idDepartment

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

Начнем с создания нового раздела. Щелкните на верхней ссылке - страница добавления нового раздела. Заполните текстовые поля и отправьте данные формы.

После создания нового раздела можно отредактировать его данные. Щелкните на названии раздела в списке, и вы попадете на страницу редактирования. На рис. 10.16 показаны данные раздела, готовые к редактированию.

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

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

Итоги

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

Глава 11. Управление информацией о налогах и стоимости доставки

Налоги и стоимость доставки - две ключевые области бизнес-логики, для управления которыми необходим удобный интерфейс. В главе 10 мы написали на Visual Basic объект СОМ, использующий информацию в базе данных для расчета налога и стоимости доставки. В этой главе мы создадим ASP-интерфейс для работы с информацией в базе данных.

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

Операции с налогами

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

Страница ManageTax.asp строит форму, которая позволяет ввести данные о налоговых ставках всех штатов на одном экране и передать внесенные изменения в базу данных. Страница начинается со стандартных тегов и включения заголовочных файлов (см. листинг 11.1).

Листинг 11.1. ManageTax.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
ManageTax.asp - Supports managing the tax table
settings for the store.
-->

<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!-- #include file="include/navinclude.asp" -->

<!-- Таблица налоговых ставок -->
<BR><B>Update the tax tables below:</b><BR>

Текущие значения налоговых ставок загружаются хранимой процедурой sp_RetrieveTaxRates (см. листинг 11.2). Процедура загружает налоговые ставки сразу для всех штатов.

Листинг 11.2. ManageTax.asp (продолжение)

<%

' Create an ADO database connection
set dbTax = server.createobject("adodb.connection")

' Create the record set
set rsTax = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbTax.open("filedsn=WildWillieCDs")

' Execute the Retrieve Tax Rates stored
' procedure to retrieve all of the current
' tax rates.
sql = "execute sp_RetrieveTaxRates"

' Execute the statement
set rsTax = dbTax.Execute(sql)

Далее мы в цикле перебираем все полученные записи и читаем значения налоговых ставок командой Select Case (см. листинг 11.3). Каждое значение сохраняется в переменной для последующего отображения на странице.

Листинг 11.3. ManageTax.asp (продолжение)

' Loop through the tax rates.
do until rsTax.EOF

' We will retrieve the tax rate for each state
' and store it in a variable.
select case ucase(rsTax("chrState"))
case "AL"
fltAL = rsTax("fltTaxRate")
case "AK"
fltAK = rsTax("fltTaxRate")
case "AZ"
fltAZ = rsTax("fltTaxRate")
case "AR"
fltAR = rsTax("fltTaxRate")
case "CA"
fltCA = rsTax("fltTaxRate")
case "CT"
fltCT = rsTax("fltTaxRate")
case "CO"
fltCO = rsTax("fltTaxRate")
case "DC"
fltDC = rsTax("fltTaxRate")
case "DE"
fltDE = rsTax("fltTaxRate")
case "FL"
fltFL = rsTax("fltTaxRate")
case "GA"
fltGA = rsTax("fltTaxRate")
case "HI"
fltHI = rsTax("fltTaxRate")
case "ID"
fltID = rsTax("fltTaxRate")
case "IL"
fltIL = rsTax("fltTaxRate")
case "IN"
fltIN = rsTax("fltTaxRate")
case "IA"
fltIA = rsTax("fltTaxRate")
case "KS"
fltKS = rsTax("fltTaxRate")
case "KY"
fltKY = rsTax("fltTaxRate")
case "LA"
fltLA = rsTax("fltTaxRate")
case "ME"
fltME = rsTax("fltTaxRate")
case "MA"
fltMA = rsTax("fltTaxRate")
case "MD"
fltMD = rsTax("fltTaxRate")
case "MI"
fltMI = rsTax("fltTaxRate")
case "MN"
fltMN = rsTax("fltTaxRate")
case "MS"
fltMS = rsTax("fltTaxRate")
case "MO"
fltMO = rsTax("fltTaxRate")
case "MT"
fltMT = rsTax("fltTaxRate")
case "NE"
fltNE = rsTax("fltTaxRate")
case "NV"
fltNV = rsTax("fltTaxRate")
case "NH"
fltNH = rsTax("fltTaxRate")
case "NJ"
fltNJ = rsTax("fltTaxRate")
case "NM"
fltNM = rsTax("fltTaxRate")
case "NY"
fltNY = rsTax("fltTaxRate")
case "NC"
fltNC = rsTax("fltTaxRate")
case "ND"
fltND = rsTax("fltTaxRate")
case "OH"
fltOH = rsTax("fltTaxRate")
case "OK"
fltOK = rsTax("fltTaxRate")
case "OR"
fltOR = rsTax("fltTaxRate")
case "PA"
fltPA = rsTax("fltTaxRate")
case "RI"
fltRI = rsTax("fltTaxRate")
case "SC"
fltSC = rsTax("fltTaxRate")
case "SD"
fltSD = rsTax("fltTaxRate")
case "TN"
fltTN = rsTax("fltTaxRate")
case "TX"
fltTX = rsTax("fltTaxRate")
case "UT"
fltUT = rsTax("fltTaxRate")
case "VT"
fltVT = rsTax("fltTaxRate")
case "VA"
fltVA = rsTax("fltTaxRate")
case "WA"
fltWA = rsTax("fltTaxRate")
case "WY"
fltWY = rsTax("fltTaxRate")
case "WI"
fltWI = rsTax("fltTaxRate")
case "WV"
fltWV = rsTax("fltTaxRate")
end select

rsTax.movenext

Loop

В листинге 11.4 создается форма для вывода налоговых ставок всех штатов. Данные формы передаются странице UpdateTaxes.asp. Для каждого штата в таблице создается текстовое поле, позволяющее отредактировать налоговую ставку. Таблица начинается со строки заголовка.

Листинг 11.4. ManageTax.asp (продолжение)

%>

<!-- The form is created to post the changes -->
<form method="post" action="UpdateTaxes.asp">

<!-- Start the table to dispay the tax rates -->
<table cellpadding="3" cellspacing="3" border="1">
<!-- Show the header row -->
<tr>
<th>State</th><th>Rate</th>
<th>State</th><th>Rate</th>
<th>State</th><th>Rate</th>
<th>State</th><th>Rate</th>
</tr>

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

Листинг 11.5. ManageTax.asp (продолжение)

<!-- Build the first row of four tax rates. -->
<tr>
<!-- Display the Alabama symbol and the Alabama tax rate -->
<td>Alabama</td><td><input type="text" name="AL" value="<%=fltAL%>" size="5"></td>
<td>Alaska</td><td><input type="text" name="AK" value="<%=fltAK%>" size="5"></td>
<td>Arizona</td><td><input type="text" name="AZ" value="<%=fltAZ%>" size="5"></td>
<td>Arkansas</td><td><input type="text" name="AR" value="<%=fltAR%>" size="5"></td>
</tr>
<tr>
<td>California</td><td><input type="text" name="CA" value="<%=fltCA%>" size="5"></td>
<td>Connecticut</td><td><input type="text" name="CT" value="<%=fltCT%>" size="5"></td>
<td>Colorado</td><td><input type="text" name="CO" value="<%=fltCO%>" size="5"></td>
<td>District of Columbia</td><td><input type="text" name="DC" value="<%=fltDC%>" size="5"></td>
</tr>
<tr>
<td>Delaware</td><td><input type="text" name="DE" value="<%=fltDE%>" size="5"></td>
<td>Florida</td><td><input type="text" name="FL" value="<%=fltFL%>" size="5"></td>
<td>Georgia</td><td><input type="text" name="GA" value="<%=fltGA%>" size="5"></td>
<td>Hawaii</td><td><input type="text" name="HI" value="<%=fltHI%>" size="5"></td>
</tr>
<tr>
<td>Idaho</td><td><input type="text" name="ID" value="<%=fltID%>" size="5"></td>
<td>Illinois</td><td><input type="text" name="IL" value="<%=fltIL%>" size="5"></td>
<td>Indiana</td><td><input type="text" name="IN" value="<%=fltIN%>" size="5"></td>
<td>Iowa</td><td><input type="text" name="IA" value="<%=fltIA%>" size="5"></td>
</tr>
<tr>
<td>Kansas</td><td><input type="text" name="KS" value="<%=fltKS%>" size="5"></td>
<td>Kentucky</td><td><input type="text" name="KY" value="<%=fltKY%>" size="5"></td>
<td>Lousiana</td><td><input type="text" name="LA" value="<%=fltLA%>" size="5"></td>
<td>Maine</td><td><input type="text" name="ME" value="<%=fltME%>" size="5"></td>
</tr>
<tr>
<td>Massachusetts</td><td><input type="text" name="MA" value="<%=fltMA%>" size="5"></td>
<td>Maryland</td><td><input type="text" name="MD" value="<%=fltMD%>" size="5"></td>
<td>Michigan</td><td><input type="text" name="MI" value="<%=fltMI%>" size="5"></td>
<td>Minnesota</td><td><input type="text" name="MN" value="<%=fltMN%>" size="5"></td>
</tr>
<tr>
<td>Mississippi</td><td><input type="text" name="MS" value="<%=fltMS%>" size="5"></td>
<td>Missouri</td><td><input type="text" name="MO" value="<%=fltMO%>" size="5"></td>
<td>Montana</td><td><input type="text" name="MT" value="<%=fltMT%>" size="5"></td>
<td>Nebraska</td><td><input type="text" name="NE" value="<%=fltNE%>" size="5"></td>
</tr>
<tr>
<td>Nevada</td><td><input type="text" name="NV" value="<%=fltNV%>" size="5"></td>
<td>New Hampshire</td><td><input type="text" name="NH" value="<%=fltNH%>" size="5"></td>
<td>New Jersey</td><td><input type="text" name="NJ" value="<%=fltNJ%>" size="5"></td>
<td>New Mexico</td><td><input type="text" name="NM" value="<%=fltNM%>" size="5"></td>
</tr>
<tr>
<td>New York</td><td><input type="text" name="NY" value="<%=fltNY%>" size="5"></td>
<td>North Carolina</td><td><input type="text" name="NC" value="<%=fltNC%>" size="5"></td>
<td>North Dakota</td><td><input type="text" name="ND" value="<%=fltND%>" size="5"></td>
<td>Ohio</td><td><input type="text" name="OH" value="<%=fltOH%>" size="5"></td>
</tr>
<tr>
<td>Oklahoma</td><td><input type="text" name="OK" value="<%=fltOK%>" size="5"></td>
<td>Oregon</td><td><input type="text" name="OR" value="<%=fltOR%>" size="5"></td>
<td>Pennsylvania</td><td><input type="text" name="PA" value="<%=fltPA%>" size="5"></td>
<td>Rhode Island</td><td><input type="text" name="RI" value="<%=fltRI%>" size="5"></td>
</tr>
<tr>
<td>South Carolina</td><td><input type="text" name="SC" value="<%=fltSC%>" size="5"></td>
<td>South Dakota</td><td><input type="text" name="SD" value="<%=fltSD%>" size="5"></td>
<td>Tennessee</td><td><input type="text" name="TN" value="<%=fltTN%>" size="5"></td>
<td>Texas</td><td><input type="text" name="TX" value="<%=fltTX%>" size="5"></td>
</tr>
<tr>
<td>Utah</td><td><input type="text" name="UT" value="<%=fltUT%>" size="5"></td>
<td>Vermont</td><td><input type="text" name="VT" value="<%=fltVT%>" size="5"></td>
<td>Virginia</td><td><input type="text" name="VA" value="<%=fltVA%>" size="5"></td>
<td>Washington</td><td><input type="text" name="WA" value="<%=fltWA%>" size="5"></td>
</tr>
<tr>
<td>Wyoming</td><td><input type="text" name="WY" value="<%=fltWY%>" size="5"></td>
<td>Wisconsin</td><td><input type="text" name="WI" value="<%=fltWI%>" size="5"></td>
<td>West Virgnia</td><td><input type="text" name="WV" value="<%=fltWV%>" size="5"></td>
<td></td><td></td>
</tr>

Страница завершается кнопкой отправки данных формы и закрывающими тегами (см. листинг 11.6).

Листинг 11.6. ManageTax.asp (продолжение)

<tr>
<td colspan="8" align="center">
<input type="submit" value="Update Tax Table" name="Submit">
</td>
</tr>
</table>

</form>

</BODY>
</HTML>

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

В листинге 11.7 приведен код хранимой процедуры sp_RetrieveTaxRates, которая возвращает все данные из таблицы Tax.

Листинг 11.7. Хранимая процедура sp_RetrieveTaxRates

CREATE PROCEDURE sp_RetrieveTaxRates AS

select * from tax

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

Листинг 11.8. UpdateTaxes.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' UpdateTaxes.asp - Handles the updates of the tax
' tables.
' ****************************************************

' Create an ADO database connection
set dbTaxUpd = server.createobject("adodb.connection")

' Create the record set
set rsTaxUpd = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbTaxUpd.open("filedsn=WildWillieCDs")

' Create an ADO database connection
set dbTax = server.createobject("adodb.connection")

' Create the record set
set rsTax = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbTax.open("filedsn=WildWillieCDs")

' Exeute the stored procedure to retrieve the
' current tax rate settings.
sql = "execute sp_RetrieveTaxRates"

' Execute the statement
set rsTax = dbTax.Execute(sql)

Затем мы перебираем записи всех штатов в базе данных и читаем соответствующие значения с формы (см. листинг 11.9). Для получения текущей налоговой ставки каждого штата используется команда Select Case, Обновление налоговых ставок производится хранимой процедурой sp_UpdateTaxRate.

Листинг 11.9. UpdateTaxes.asp

' Loop through the tax rate settings.
do until rsTax.EOF

' Do a select case to check which state we are
' going to update.
select case ucase(rsTax("chrState"))

' Check for Alabama
case "AL"
' Execute the sp_UpdateTaxRate stored procedure
' to change the tax rate setting for Alabama
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("AL") & ", " & rsTax("idState")

case "AK"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("AK") & ", " & rsTax("idState")

case "AZ"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("AZ") & ", " & rsTax("idState")

case "AR"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("AR") & ", " & rsTax("idState")

case "CA"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("CA") & ", " & rsTax("idState")

case "CT"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("CT") & ", " & rsTax("idState")

case "CO"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("CO") & ", " & rsTax("idState")

case "DC"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("DC") & ", " & rsTax("idState")

case "DE"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("DE") & ", " & rsTax("idState")

case "FL"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("FL") & ", " & rsTax("idState")

case "GA"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("GA") & ", " & rsTax("idState")

case "HI"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request ("HI") & ", " & rsTax("idState")

case "ID"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("ID") & ", " & rsTax("idState")

case "IL"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("IL") & ", " & rsTax("idState")

case "IN"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("IN") & ", " & rsTax("idState")

case "IA"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("IA") & ", " & rsTax("idState")

case "KS"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("KS") & ", " & rsTax("idState")

case "KY"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("KY") & ", " & rsTax("idState")

case "LA"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("LA") & ", " & rsTax("idState")

case "ME"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("ME") & ", " & rsTax("idState")

case "MA"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("MA") & ", " & rsTax("idState")

case "MD"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("MD") & ", " & rsTax("idState")

case "MI"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("MI") & ", " & rsTax("idState")

case "MN"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("MN") & ", " & rsTax("idState")

case "MS"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("MS") & ", " & rsTax("idState")

case "MO"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("MO") & ", " & rsTax("idState")

case "MT"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("MT") & ", " & rsTax("idState")

case "NE"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("NE") & ", " & rsTax("idState")

case "NV"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("NV") & ", " & rsTax("idState")

case "NH"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("NH") & ", " & rsTax("idState")

case "NJ"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("NJ") & ", " & rsTax("idState")

case "NM"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("NM") & ", " & rsTax("idState")

case "NY"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("NY") & ", " & rsTax("idState")

case "NC"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("NC") & ", " & rsTax("idState")

case "ND"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("ND") & ", " & rsTax("idState")

case "OH"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("OH") & ", " & rsTax("idState")

case "OK"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("OK") & ", " & rsTax("idState")

case "OR"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("OR") & ", " & rsTax("idState")

case "PA"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("PA") & ", " & rsTax("idState")

case "RI"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("RI") & ", " & rsTax("idState")

case "SC"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("SC") & ", " & rsTax("idState")

case "SD"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("SD") & ", " & rsTax("idState")

case "TN"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("TN") & ", " & rsTax("idState")

case "TX"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("TX") & ", " & rsTax("idState")

case "UT"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("UT") & ", " & rsTax("idState")

case "VT"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("VT") & ", " & rsTax("idState")

case "VA"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("VA") & ", " & rsTax("idState")

case "WA"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("WA") & ", " & rsTax("idState")

case "WY"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("WY") & ", " & rsTax("idState")

case "WI"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("WI") & ", " & rsTax("idState")

case "WV"
dbTaxUpd.execute "execute sp_UpdateTaxRate " & _
request("WV") & ", " & rsTax("idState")

end select

' Move to the next state.
rsTax.movenext

Loop

После обновления налоговых ставок штатов пользователь возвращается на страницу ManageTax.asp для просмотра внесенных изменений (см. листинг 11.10).

Листинг 11.10. UpdateTaxes.asp

' Send the user back to the tax manager
' for a final check on the tax rate
' settings.
Response.Redirect "managetax.asp"

%>

Хранимая процедура sp_UpdateTaxRate обновляет поле fltTaxRate таблицы Tax для заданного штата (см. листинг 11.11). При вызове процедуре передаются налоговая ставка и идентификатор штата.

Листинг 11.11. Хранимая процедура sp_UpdateTaxRate

CREATE PROCEDURE sp_UpdateTaxRate

@fltTaxRate float,
@idState int

AS

update tax set fltTaxRate = @fltTaxRate
where idState = @idState

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

На рис. 11.2 показана обновленная таблица. У Виржинии и Техаса сохранились прежние значения, а налоговые ставки Алабамы и Аляски заменились введенными величинами. Верните этим штатам нулевую ставку.

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

Операции со стоимостью доставки

На следующем этапе мы займемся вычислением стоимости доставки. Страница ManageShipping.asp, с которой начинается рассмотрение этой темы, приведена в листинге 11.12.

Страница начинается с включения стандартных заголовочных файлов для проверки пользователя и создания панели ссылок, после чего открывается подключение ADO к базе данных.

Листинг 11.12. ManageShipping.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
ManageShipping.asp - Handles the listing of the
shipping rates. And, it provides functions for
deleting shipping rates and adding new ones.
-->

<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!-- #include file="include/navinclude.asp" -->

<%

' Create an ADO database connection
set dbShipping = server.createobject("adodb.connection")

' Create the record set
set rsShipping = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbShipping.open("filedsn=WildWillieCDs")

Текущие интервалы расценок на доставку загружаются из базы данных хранимой процедурой sp_GetShippingRate (см. листинг 11.13).

Листинг 11.13. ManageShipping.asp (продолжение)

' Execute the Retrieve Shipping Rates stored
' procedure to retrieve all of the current
' shipping rates.
sql = "execute sp_GetShippingRate"

' Execute the statement
set rsShipping = dbShipping.Execute(sql)

%>

В листинге 11.14 начинается форма для вывода расценок на доставку товаров. Данные формы передаются странице UpdateShipping.asp.

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

Листинг 11.14. ManageShipping.asp (продолжение)

<!-- Start the form for updating the shipping -->
<form method="post" action="UpdateShipping.asp">

<!-- Start the table to display the shipping
rates. -->
<table border="1" cellpadding="3" cellspacing="3">
<tr>
<th>ID</th>
<th>Low<BR>Quantity</th>
<th>High<BR>Quantity</th>
<th>Fee</th>
<th>Delete</th>
</tr>

В листинге 11.15 происходит циклический перебор записей расценок. При каждой итерации соответствующие значения заносятся в текстовые поля таблицы для последующего редактирования.

Имена полей объединяются с идентификаторами интервалов, что упрощает доступ к содержимому полей при обновлении данных.

Листинг 11.15. ManageShipping.asp (продолжение)

<%

' Loop through the shipping rates.
do until rsShipping.EOF

%>

<!-- Loop through the shipping rate ranges -->
<tr>
<!-- Show the quantity range id -->
<td><%=rsShipping("idQuantityRange")%></td>
<td>
<!-- Built input for the low quantity. Note that
the field name is built to include the id
of the quantity range. -->
<input type="text" name="intLow<%=rsShipping("idQuantityRange")%>"
value="<%=rsShipping("intLowQuantity")%>" size="6">
</td>
<td>
<!-- Built input for the high quantity. Note that
the field name is built to include the id
of the quantity range. -->
<input type="text" name="intHigh<%=rsShipping("idQuantityRange")%>"
value="<%=rsShipping("intHighQuantity")%>" size="6">
</td>
<td>
<!-- Built input for the fee. Note that the field
name is built to include the id of the quantity
range. -->
<input type="text" name="intFee<%=rsShipping("idQuantityRange")%>"
value="<%=rsShipping("intFee")%>" size="6">
</td>

Для удаления интервальных расценок мы создаем ссылки на страницу DeleteShipping.asp и передаем в URL идентификатор интервала (см. листинг 11.16). Затем цикл переходит к следующей записи, и все действия повторяются для новой строки таблицы.

Листинг 11.16. ManageShipping.asp (продолжение)

<td>
<!-- Build a delete link with the id of the
quantity range. -->
<a href="deleteshipping.asp?idQuantityRange=
<%=rsShipping("idQuantityRange")%>">Delete</a>
</td>
</tr>

<%

' Move to the next row.
rsShipping.MoveNext

' Loop back
Loop

%>

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

Листинг 11.17. ManageShipping.asp (продолжение)

<!-- Build the submit button -->
<tr>
<td colspan="5" align="center">
<input type="submit" value="Submit" name="Submit">
</td>
</tr>

</table>
</form>

<BR><hr><BR>

В листинге 11.18 создается новая форма, предназначенная для создания новых интервальных расценок. Данные этой формы передаются странице AddShipping.asp. На форме создается таблица, аналогичная описанной выше, но без ссылки Delete.

Листинг 11.18. ManageShipping.asp (продолжение)

<!-- Start the form for adding new quantity ranges -->
<form method="post" action="AddShipping.asp">

<!-- Start the table -->
<table border="1" cellpadding="3" cellspacing="3">

<!-- Build the header -->
<tr>
<th>Low<BR>Quantity</th>
<th>High<BR>Quantity</th>
<th>Fee</th>
</tr>

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

Листинг 11.19. ManageShipping.asp (продолжение)

<!-- Build the first new quantity option -->
<tr>
<td>
<input type="text" name="intLow1" value="" size="6">
</td>
<td>
<input type="text" name="intHigh1" value="" size="6">
</td>
<td>
<input type="text" name="intFee1" value="" size="6">
</td>
</tr>
<!-- Build the second new quantity option -->
<tr>
<td>
<input type="text" name="intLow2" value="" size="6">
</td>
<td>
<input type="text" name="intHigh2" value="" size="6">
</td>
<td>
<input type="text" name="intFee2" value="" size="6">
</td>
</tr>
<!-- Build the third new quantity option -->
<tr>
<td>
<input type="text" name="intLow3" value="" size="6">
</td>
<td>
<input type="text" name="intHigh3" value="" size="6">
</td>
<td>
<input type="text" name="intFee3" value="" size="6">
</td>
</tr>

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

Листинг 11.20. ManageShipping.asp (продолжение)

<!-- Build the submit button -->
<tr>
<td colspan="3" align="center"><input type="submit" value="Submit" name="Submit"></td>
</tr>
</table>
</form>

</BODY>
</HTML>

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

Листинг 11.21. Хранимая процедура sp_GetShippingRate

/* Загрузка интервальных расценок доставки, используемых магазином */
CREATE PROCEDURE sp_GetShippingRate

AS

/* Выборка всех записей из таблицы Shipping */
select * from shipping

Перейдем к обновлению интервальных расценок. Эта задача решается страницей UpdateShipping.asp, приведенной в листинге 11.22.

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

Листинг 11.22. UpdateShipping.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' UpdateShipping.asp - Handles updating the existing
' shipping rates.
' ****************************************************

' Create an ADO database connection
set dbShipUpdate = server.createobject("adodb.connection")

' Open the connection using our ODBC file DSN
dbShipUpdate.open("filedsn=WildWillieCDs")

' Create an ADO database connection
set dbShipping = server.createobject("adodb.connection")

' Create the record set
set rsShipping = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbShipping.open("filedsn=WildWillieCDs")

' Execute the sp_GetShippingRate to retrieve
' the shipping rates.
sql = "execute sp_GetShippingRate"

' Execute the statement
set rsShipping = dbShipping.Execute(sql)

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

Листинг 11.23. UpdateShipping.asp (продолжение)

' Loop through the shipping rates.
do until rsShipping.EOF

' Retrieve the low and high shipping quantities
' along with the shipping rate. On the
' ManageShipping.asp page, the field names are
' built with the id of the quantity range tacked
' onto the field name. We build the same naming
' here to retrieve the values.
intLow = request("intLow" & rsShipping("idQuantityRange"))
intHigh = request("intHigh" & rsShipping("idQuantityRange"))
intFee = request("intFee" & rsShipping("idQuantityRange"))

После получения параметров интервала вызывается хранимая процедура sp_UpdateShippingRate, обновляющая информацию в базе данных (см. листинг 11.24), после чего цикл переходит к следующей записи. После того как обновление информации будет завершено, пользователь возвращается на страницу ManageShipping.asp.

Листинг 11.24. UpdateShipping.asp (продолжение)

' Execute the update stored procedure to change the range.
dbShipUpdate.Execute "execute sp_UpdateShippingRate " & _
rsShipping("idQuantityRange") & ", " & _
intLow & ", " & _
intHigh & ", " & _
intFee

' Move to the next row
rsShipping.MoveNext

' Loop back
loop

' Send the user back to the shipping manager
' range.
Response.Redirect "ManageShipping.asp"

%>

Хранимой процедуре sp_UpdateShippingRate (см. листинг 11.25) передаются значения, описывающие интервал, и его идентификатор. Процедура конструирует команду SQL UPDATE, которая обновляет запись указанного интервала.

Листинг 11.25. Хранимая процедура sp_UpdateShippingRate

CREATE PROCEDURE sp_UpdateShippingRate

@idQuantityRange int,
@intLowQuantity int,
@intHighQuantity int,
@intFee int

AS

update shipping set
intLowQuantity = @intLowQuantity,
intHighQuantity = @intHighQuantity,
intFee = @intFee
where
idQuantityRange = @idQuantityRange

Перейдем к странице DeleteShipping.asp (см. листинг 11.26), удаляющей интервалы из базы. Страница создает подключение ADO и вызывает хранимую процедуру sp_DeleteShippingRate, которая удаляет соответствующую запись интервала, определяемую по значению идентификатора. Затем пользователь возвращается на страницу ManageShipping.asp.

Листинг 11.26. DeleteShipping.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' DeleteShipping.asp - Handles deleting the specific
' shipping range.
' ****************************************************

' Create an ADO database connection
set dbShipping = server.createobject("adodb.connection")

' Create the record set
set rsShipping = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbShipping.open("filedsn=WildWillieCDs")

' Execute the stored procedure to delete the
' specified shipping rate.
sql = "execute sp_DeleteShippingRate " & request("idQuantityRange")

' Execute the statement
set rsShipping = dbShipping.Execute(sql)

' Send the user back to the shipping manager
Response.Redirect "ManageShipping.asp"

%>

Хранимая процедура sp_DeleteShippingRate (см. листинг 11.27) удаляет интервал с переданным идентификатором из таблицы Shipping.

Листинг 11.27. Хранимая процедура sp_DeleteShippingRate

CREATE PROCEDURE sp_DeleteShippingRate

@idQuantityRange int

AS

delete from shipping where idQuantityRange = @idQuantityRange

Остается лишь рассмотреть создание новых интервальных расценок. Задача решается страницей AddShipping.asp (см. листинг 11.28). Страница считывает содержимое полей в трех строках таблицы и проверяет внесенные изменения. В начале своей работы она открывает подключение ADO к базе данных.

Листинг 11.28. AddShipping.asp

<%@ Language=VBScript %>
<%

' Create an ADO database connection
set dbShipping = server.createobject("adodb.connection")

' Create the record set
set rsShipping = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbShipping.open("filedsn=WildWillieCDs")

Содержимое полей каждой строки сохраняется в локальных переменных (см. листинг 11.29). Затем страница проверяет, указал ли пользователь стоимость доставки в данном интервале. Если стоимость указана, выполняется хранимая процедура sp_InsertShippingRate, которая создает новую запись в базе данных. После создания новой расценки пользователь возвращается на страницу ManageShipping.asp.

Листинг 11.29. AddShipping.asp (продолжение)

intLow1 = request("intLow1")
intHigh1 = request("intHigh1")
intFee1 = request("intFee1")

if intFee1 <> "" then

dbShipping.Execute "execute sp_InsertShippingRate " & _
intLow1 & ", " & _
intHigh1 & ", " & _
intFee1

end if

intLow2 = request("intLow2")
intHigh2 = request("intHigh2")
intFee2 = request("intFee2")

if intFee2 <> "" then

dbShipping.Execute "execute sp_InsertShippingRate " & _
intLow2 & ", " & _
intHigh2 & ", " & _
intFee2

end if

intLow3 = request("intLow3")
intHigh3 = request("intHigh3")
intFee3 = request("intFee3")

if intFee3 <> "" then

dbShipping.Execute "execute sp_InsertShippingRate " & _
intLow3 & ", " & _
intHigh3 & ", " & _
intFee3

end if

Response.Redirect "ManageShipping.asp"

%>

Хранимая процедура sp_InsertShippingRate (см. листинг 11.30) создает в таблице Shipping новую запись и присваивает ее полям переданные значения - нижнюю границу, верхнюю границу и стоимость доставки в интервале.

Листинг 11.30. Хранимая процедура sp_InsertShippingRate

CREATE PROCEDURE sp_InsertShippingRate

@intLowQuantity int,
@intHighQuantity int,
@intFee int

AS

insert into shipping(intLowQuantity, intHighQuantity, intFee)
values(@intLowQuantity, @intHighQuantity, @intFee)

Все готово к тестированию нового интерфейса. Страница ManageShipping.asp с текущими значениями расценок из базы данных. Обратите внимание на ссылки удаления и обновления существующих интервалов. Таблица в нижней части страницы предназначена для ввода границ и стоимости доставки в новых интервалах.

Удалите одну из существующих расценок при помощи ссылки Delete.

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

Итоги

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

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

В главе 12 мы займемся средствами администрирования заказов.

Глава 12. Управление заказами

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

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

Поиск заказов

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

Начало страницы ввода критериев поиска приведено в листинге 12.1. Как обычно, страница включает стандартные заголовочные файлы для проверки пользователя и создания панели ссылок.

Листинг 12.1. ManageOrders.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<!-- ManageOrders.asp - страница для ввода критериев, по которым осуществляется поиск заказов. -->

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<!-- #include file="include/navinclude.asp" -->

<!-- Форма для ввода критериев поиска. -->
<BR><BR>
<b>Enter in your search criteria:</b>
<BR><BR>

Мы создаем форму, которая передает данные странице OrderSearch.asp, и начинаем строить таблицу с текстовыми полями. В первых двух полях вводится имя покупателя и адрес электронной почты, по которым осуществляется поиск.

За ним следуют поля, предназначенные для интервального поиска по дате оформления заказа, а еще дальше - поля для интервального поиска по итоговой стоимости заказа (см. листинг 12.2).

Листинг 12.2. ManageOrders.asp (продолжение)

<form method="post" action="OrderSearch.asp">

<!-- Start the table to display the search fields. -->
<table>

<!-- Shopper name input field. -->
<tr>
<td align="right">Search by Shopper Name:</td>
<td><input type="text" name="srchShopperName" value=""></td>
</tr>

<!-- Shopper email address input field. -->
<tr>
<td align="right">Search by Shopper Email Address:</td>
<td><input type="text" name="srchShopperEmail" value=""></td>
</tr>

<!-- Fields are created to enter in a starting
date and end date. -->
<tr>
<td align="right">Search by Order Date Range:</td>
<td>
<input type="text" name="srchStartDate" value="">
<input type="text" name="srchEndDate" value="">
</td>
</tr>

<!-- Fields are created to enter in a starting
amount and end amount. -->
<tr>
<td align="right">Search by Order Total Amount:</td>
<td>
<input type="text" name="srchStartAmount" value="">
<input type="text" name="srchEndAmount" value="">
</td>
</tr>

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

Листинг 12.3. ManageOrders.asp (продолжение)

<!-- Поле для ввода названия товара. -->
<tr>
<td align="right">Search by Product Name:</td>
<td><input type="text" name="srchProductName" value=""></td>
</tr>

<!-- Кнопка отправки данных формы. -->
<tr>
<td colspan="2" align="Center">
<input type="Submit" value="Submit" name="Submit">
</td>
</tr>

</table>

</form>

</BODY>
</HTML>

После ввода критериев поиска данные передаются странице OrderSearch.asp (см. листинг 12.4). Страница выводит список товаров, найденных по указанным критериям.

В первой части страницы создается запрос к базе данных. На этот раз запрос строится непосредственно на VBScript без использования хранимых процедур SQL. Строка запроса генерируется во время выполнения программы.

Листинг 12.4. OrderSearch.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->

<!--
OrderSearch.asp - Provides input fields for performing the
order search against the order data and the basket.
-->


<HTML>
<BODY>
<!-- #include file="include/navinclude.asp" -->

Работа страницы, как показано в листинге 12.5, начинается с чтения значений, введенных пользователем на форме. Содержимое полей Amount умножается на 100, чтобы оно соответствовало целочисленным значениям, хранящимся в базе данных. В строках названия товара и имени покупателя одиночные апострофы, как обычно, заменяются двойными.

Листинг 12.5. OrderSearch.asp (продолжение)

<%

' The key fields are retrieved from the order
' search page.
srchShopperName = replace(request("srchShopperName"), "'", "''")
srchShopperEmail = request("srchShopperEmail")
srchStartDate = request("srchStartDate")
srchEndDate = request("srchEndDate")
srchStartAmount = request("srchStartAmount")
srchEndAmount = request("srchEndAmount")

' Now we begin the building of the SQL statement.
' In this case instead of calling a stored procedure
' we are going to create a SQL statement based upon
' the input parameters on the search form.

' Retrieve the starting search amount
if srchStartAmount <> "" then

' Remember to mulitply the search amount
' by one hundred to make it an integer.
srchStartAmount = srchStartAmount * 100

end if

' Retrieve the ending search amount
if srchEndAmount <> "" then

' Remember to mulitply the search amount
' by one hundred to make it an integer.
srchEndAmount = srchEndAmount * 100

end if

' Retrieve the product name to search by.
srchProductName = replace(request("srchProductName"), "'", "''")

На следующей фазе поиска начинается построение запроса SQL (см. листинг 12.6). При этом используются таблицы OrderData, Basket, BasketItem и OrderStatus. Данные этих таблиц объединяются для проведения поиска.

Листинг 12.6. OrderSearch.asp (продолжение)

' Первая часть команды SQL строится автоматически.
' В запросе определяются возвращаемые поля, а ключевое слово
' Distinct используется для того, чтобы для каждого заказа
' возвращалась только одна запись. Для получения всех
' необходимых данных используется объединение основных таблиц.
sql = "select distinct orderdata.idOrder, OrderData.idShopper, dtOrdered, " & _
"chrShipLastName, chrBillLastName, chrShipEmail, " & _
"chrBillEmail, intTotal, idStage, chrShippingNum " & _
"from OrderData, Basket, basketitem, orderstatus " & _
"where OrderData.idBasket = Basket.idBasket and " & _
"BasketItem.idBasket = Basket.idBasket and " & _
"OrderStatus.idorder = orderdata.idOrder "

Далее начинается серия дополнительных проверок. Сначала проверяется адрес электронной почты (см. листинг 12.7). Если пользователь ввел адрес в критериях поиска, мы ищем похожие адреса в реквизитах доставки и выписки счета. К построенной части команды добавляется соответствующая синтаксическая конструкция SQL.

В данном примере проверяется не точное совпадение адреса электронной почты, а наличие в нем указанной строки. Это позволяет ввести критерий типа "@sybex.com" и получить все заказы, у которых в адресе электронной почты присутствует указанный образец.

Листинг 12.7. OrderSearch.asp (продолжение)

' Проверить, используется ли в критериях поиска
' адрес электронной почты.
if srchShopperEmail <> "" then

' Команда SQL дополняется конструкцией для поиска
' введенного текста в адресах электронной почты,
' указанных в реквизитах доставки и выписки счета.
' Обратите внимание на использование ключевого слова LIKE.
sql = sql & " and (" & _
"chrShipEmail like '%" & srchShopperEmail & "%' or " & _
"chrBillEmail like '%" & srchShopperEmail & "%') "

end if

Следующая проверка относится к имени покупателя. На этот раз искомый текст также может находиться в нескольких полях. В данном примере поиск осуществляется в полях имени и фамилии, указанных в реквизитах доставки и выписки счета. При проведении поиска используется ключевое слово SQL LIKE. Обратите внимание - условия поиска объединяются операцией OR, чтобы результат возвращался при обнаружении искомого текста хотя бы в одном из полей (см. листинг 12.8).

Листинг 12.8. OrderSearch.asp (продолжение)

' Затем проверяется поле имени покупателя. Если
' в нем присутствует текст, мы снова при помощи
' команды LIKE проверяем, присутствует ли текст
' хотя бы в одном имени или фамилии, указанных
' в реквизитах доставки и выписки счета.
if srchShopperName <> "" then

sql = sql & "and ( " & _
"chrShipFirstName like '%" & srchShopperName & "%' or " & _
"chrShipLastName like '%" & srchShopperName & "%' or " & _
"chrBillFirstName like '%" & srchShopperName & "%' or " & _
"chrBillLastName like '%" & srchShopperName & "%') "

end if

В листинге 12.9 проверяется нижняя и верхняя граница даты оформления заказа. Мы проверяем каждое поле, чтобы узнать, был ли в нем введен какой-либо текст. Если текст присутствует, в запрос SQL включается сравнение поля dtOrdered с введенной датой (или датами).

Листинг 12.9. OrderSearch.asp (продолжение)

' Проверить верхнюю границу даты. Если ее
' значение было введено на форме, запрос SQL
' обновляется соответствующим условием
if srchStartDate <> "" then

sql = sql & " and dtOrdered >= '" & srchStartDate & "' "

end if

' Next we check the ending date for the search. If it
' is set then the SQL query is updated.
if srchEndDate <> "" then

sql = sql & " and dtOrdered <= '" & srchEndDate & "' "

end if

Аналогичная проверка выполняется и для поиска по итоговой стоимости заказа, чтобы в запросе SQL итоговая сумма проверялась на принадлежность к заданному интервалу. Соответствующие условия включаются в создаваемую команду SQL (см. листинг 12.10).

Листинг 12.10. OrderSearch.asp (продолжение)

' Проверить, ввел ли пользователь нижнюю границу
' итоговой стоимости заказа. Если граница введена,
' обновить запрос.
if srchStartAmount <> "" then

sql = sql & " and intTotal >= " & srchStartAmount

end if

' Проверить верхнюю границу итоговой стоимости.
' Если граница введена, обновить запрос.
if srchEndAmount <> "" then

sql = sql & " and intTotal <= " & srchEndAmount

end if

Наконец, в листинге 12.11 проверяется название товара. Как и прежде, команда SQL LIKE проверяет, совпадает ли искомый текст с произвольной частью названия товара.

Листинг 12.11. OrderSearch.asp (продолжение)

' Проверить, ввел ли пользователь название товара.
' Если название введено, обновить запрос и включить
' название в поиск.
if srchProductName <> "" then

sql = sql & "and chrName like '%" & srchProductName & "%'"

end if

Теперь все готово к выполнению команды SQL (см. листинг 12.12). Страница открывает подключение ADO к базе данных и выполняет команду SQL. После этого можно переходить к выводу найденных записей.

Листинг 12.12. OrderSearch.asp (продолжение)

' Create an ADO database connection
set dbOrderSearch = server.createobject("adodb.connection")

' Create the record set
set rsOrderSearch = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbOrderSearch.open("filedsn=WildWillieCDs")

' Execute our built on the fly query.
set rsOrderSearch = dbOrderSearch.Execute(sql)

%>

<!-- Net we work on the dispaly of the search
results. A table is started to display
the data. -->
<BR><BR>

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

Листинг 12.13. OrderSearch.asp (продолжение)

<table border="1" cellpadding="3" cellspacing="3">

<!-- Build the header. -->
<tr>
<th>Order ID</th>
<th>Date Ordered</th>
<th>Order Total</th>
<th>Ship to<BR>Last Name</th>
<th>Ship to<BR>Email Address</th>
<th>Bill to<BR>Last Name</th>
<th>Bill to<BR>Email Address</th>
<th>Order Status</th>
<th>Delete</th>
</tr>

!-- Заголовок таблицы. --

В этой точке начинается перебор записей полученного набора (см. листинг 12.14). В первом столбце выводится идентификатор заказа, оформленный в виде ссылки для получения подробной информации о заказе.

Листинг 12.14. OrderSearch.asp (продолжение)

<%

' Loop through the returned results.
do until rsOrderSearch.EOF

%>

<!-- Start a results row. -->
<tr>
<td align="center">
<!-- A link is built on the order id to give
the user a link to the order details. -->
<a href="OrderDetail.asp?idOrder=<%=rsOrderSearch("idOrder")%>&
idShopper=<%=rsOrderSearch("idShopper")%>">
<%=rsOrderSearch("idOrder")%>
</a>
</td>

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

Листинг 12.15. OrderSearch.asp (продолжение)

<!-- Next we display the date of the order, the order
total and the shipping last name. -->
<td align="center"><%=rsOrderSearch("dtOrdered")%></td>
<td align="center"><%=formatcurrency(rsOrderSearch("intTotal")/100, 2)%></td>
<td align="center"><%=rsOrderSearch("chrShipLastName")%></td>

<td align="center">
<!-- Next the shipping email address is displayed
and it built as a link so an email can be
easily sent. -->
<a href="mailto:<%=rsOrderSearch("chrShipEmail")%>">
<%=rsOrderSearch("chrShipEmail")%></a>
</td>

<!-- Show the billing last name. -->
<td align="center"><%=rsOrderSearch("chrBillLastName")%></td>

<td align="center">
<!-- Next the billing email address is displayed
and it built as a link so an email can be
easily sent. -->
<a href="mailto:<%=rsOrderSearch("chrBillEmail")%>">
<%=rsOrderSearch("chrBillEmail")%></a>
</td>

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

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

Листинг 12.16. OrderSearch.asp (продолжение)

<td>
<%

stat0 = ""
stat1 = ""
stat2 = ""
stat3 = ""

' Check the status of the order. A check is
' done to build a defaulted selected variable
' so the current shipping status is defaulted.
select case rsOrderSearch("idStage")

' Case 0 is that the order has been retrieved.
case 0
Stat0 = "Selected"

' Case 1 is that the order is fulfilled and ready
' for shipping.
case 1
Stat1 = "Selected"

' Case 2 indicates the order has been shipped and we
' display the shipping number.
case 2
Stat2 = "Selected"

' If none of these are set then we indicate that
' customer service should be called.
case 3
Stat3 = "Selected"

end select

%>

В листинге 12.17 создается форма, которая передает обновленную информацию о состоянии заказа странице UpdateStatus.asp. На форме создается скрытое поле, в котором сохраняется идентификатор заказа. По содержимому этого скрытого поля страница UpdateStatus.asp определяет то, состояние какого заказа ей следует обновить.

Листинг 12.17. OrderSearch.asp (продолжение)

<!-- A form is built to update the
order status. -->
<form method="post" action="UpdateStatus.asp">

<!-- In order to know what form will be updated, a
hidden variable is stored with the order id. -->
<input type="hidden" name="intOrder"
value="<%=rsOrderSearch("idOrder")%>">

В листинге 12.18 на форме создается список, содержащий все допустимые состояния заказа. Вставка переменных в теги OPTION обеспечивает выбор в списке текущего состояния заказа.

Листинг 12.18. OrderSearch.asp (продолжение)

<!-- A slect box is created with the different order
status variables. Note the default selected
variables are utilized in each option. And,
the shipping number is shown if the status
is shipped.-->
<select name="intStage">
<option value="0" <%=Stat0%>>Order Received and to be Processed
<option value="1" <%=Stat1%>>Order Fulfilled and Ready to be Shipped
<option value="2" <%=Stat2%>>Order Shipped -
<%=rsOrderSearch("chrShippingNum")%>
<option value="3" <%=Stat3%>>Call Customer Service for Assistance
</select><BR>

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

Листинг 12.19. OrderSearch.asp (продолжение)

<!-- An input element is created so the shipping number can
be entered. -->
<input type="text" value="<%=rsOrderSearch("chrShippingNum")%>"
name="chrShippingNum">

<!-- Submit button for the form. -->
<input type="Submit" value="Update" name="Submit"></form></td>

В листинге 12.20 создается ссылка для удаления заказа из системы. Ссылка связывается со страницей DeleteOrder.asp, идентификатор заказа передается в URL. Затем мы переходим к следующей записи и продолжаем вывод результатов. С завершением цикла завершается работа страницы.

Листинг 12.20. OrderSearch.asp (продолжение)

<td>
<!-- Build a link to delete teh order from
the database. -->
<a href="DeleteOrder.asp?idOrder=<%=rsOrderSearch("idOrder")%>">
Delete</a>
</td>

</tr>

<%

' Move to the next row.
rsOrderSearch.MoveNext

Loop

%>

</table>
</body></html>

Перейдем к тестированию нового поискового интерфейса. На рис. 12.1 изображена страница ввода критериев. Начнем с простого поиска всех заказов в системе (не забудьте создать примеры заказов в базе данных).

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

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

Результаты поиска. Обратите внимание: даты и итоговые суммы всех найденных заказов принадлежат заданным интервалам.

Удаление и изменение состояния заказов

От поиска и фильтрации перейдем к основным операциям с заказами: удалению и изменению состояния.

Сначала рассмотрим процедуру удаления заказов. Как говорилось выше, идентификатор заказа передается странице в составе URL В листинге 12.21 страница открывает подключение ADO к базе данных, после чего создает запрос SQL для вызова хранимой процедуры sp_DeleteOrder, передавая ей идентификатор заказа. После выполнения запроса пользователь возвращается на страницу поиска заказов.

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

Листинг 12.21. DeleteOrder.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' DeleteOrder.asp - удаление заказа из базы данных.
' ****************************************************

' Create an ADO database connection
set dbDeleteOrder = server.createobject("adodb.connection")

' Create the record set
set rsDeleteOrder = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbDeleteOrder.open("filedsn=WildWillieCDs")

' Call the sp_DeleteOrder stored procedure to
' delete the specified order from the database.
sql = "execute sp_DeleteOrder " & request("idOrder")

' Execute the statement
set rsDeleteOrder = dbDeleteOrder.Execute(sql)

Response.Redirect "OrderSearch.asp"

%>

Хранимая процедура sp_DeleteOrder предназначена для удаления заказа из базы данных. Не забывайте, что информация удаляется не только из таблицы OrderData - сведения о заказе также необходимо удалить из таблиц PaymentData, OrderStatus, Basket и BasketItem.

Сначала хранимая процедура определяет идентификатор корзины, по которому удаляются записи из других таблиц. После этого выполняются соответствующие команды SQL DELETE (см. листинг 12.22).

Листинг 12.22. Хранимая процедура sp_DeleteOrder

CREATE PROCEDURE sp_DeleteOrder

@idOrder int

AS

declare @idBasket int

select @idBasket = idBasket from OrderData where
idOrder = @idOrder

delete from orderdata where idOrder = @idOrder
delete from paymentdata where idOrder = @idOrder
delete from OrderStatus where idOrder = @idOrder

delete from Basket where idBasket = @idBasket

delete from BasketItem where idBasket = @idBasket

Перейдем к изменению состояния заказов. Для решения этой задачи необходимы три параметра: идентификатор заказа, передаваемый в скрытом поле, идентификатор нового состояния, и номер транспортной накладной (если он имеется).

Страница вызывает хранимую процедуру sp_UpdateOrderStatus и передает ей три параметра. После обновления состояния заказа пользователь возвращается на страницу поиска заказов.

Листинг 12.23. UpdateStatus.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' UpdateStatus.asp - Handles updating the order status
' for the specified order.
' ****************************************************

' Create an ADO database connection
set dbUpdateStatus = server.createobject("adodb.connection")

' Create the record set
set rsUpdateStatus = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbUpdateStatus.open("filedsn=WildWillieCDs")

' Retrieve the order it, order stage
' and shipping number
intOrder = request("intOrder")
intStage = request("intStage")
chrShippingNum = request("chrShippingNum")

' Build the sql statement and call the
' sp_UpdateOrderStatus stored procedure
sql = "execute sp_UpdateOrderStatus " & _
intOrder & ", " & _
intStage & ", '" & _
chrShippingNum & "'"

' Execute the statement
set rsUpdateStatus = dbUpdateStatus.Execute(sql)

' Send the user back to the order search
Response.Redirect "OrderSearch.asp"

%>

Текущее состояние заказа обновляется хранимой процедурой sp_UpdateOrderStatus (см. листинг 12.24). В качестве параметров передается идентификатор заказа, код состояния и номер транспортной накладной. Процедура обновляет текущие данные командой SQL UPDATE. Как говорилось выше, запись состояния создается в таблице OrderStatus при оформлении заказа покупателем.

Листинг 12.24. Хранимая процедура sp_UpdateOrderStatus

CREATE PROCEDURE sp_UpdateOrderStatus

@idOrder int,
@intStage int,
@chrShippingNum varchar(100)

AS

update OrderStatus set
idStage = @intStage,
chrShippingNum = @chrShippingNum
where
idOrder = @idORder

Давайте посмотрим, как работают средства обновления состояния заказов. На рис. 12.5 изображено исходное состояние нескольких заказов. Измените состояние первого заказа на "Обратитесь в службу поддержки за помощью" ("Call Customer Service for Assistance") и нажмите кнопку Update.

Рис. 12.5. Текущее состояние заказов

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

Просмотр и редактирование заказов

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

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

Листинг 12.25. OrderDetail.asp

<%@ Language=VBScript %>
<!-- #Include file="include/validatecheck.asp" -->
<HTML>
<!--
OrderDetail.asp - Displays the detail on the order
and allows for the key fields to be updated.
-->

<body>
<!-- #include file="include/navinclude.asp" -->

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

Листинг 12.26. OrderDetail.asp (продолжение)

<%

' Create an ADO database connection
set dbOrderReceiptHeader = server.createobject("adodb.connection")
set rsOrderReceiptHeader = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbOrderReceiptHeader.open("filedsn=WildWillieCDs")

' Call the stored procedure to retrieve the
' receipt header.
sql = "execute sp_RetrieveReceiptHeader " & _
Request("idShopper") & ", " & _
Request("idOrder")

' Execute the SQL statement
set rsOrderReceiptHeader = dbOrderReceiptHeader.execute(sql)

%>

Далее начинается форма, которая передает обновленную информацию странице UpdateOrder.asp (см. листинг 12.27). Затем начинается таблица для вывода адресной информации и сведений о заказе.

Листинг 12.27. OrderDetail.asp (продолжение)

<!-- Форма редактирования данных заказа. -->
<form method="post" action="UpdateOrder.asp">

<!-- Таблица для вывода заголовка -->
<table>
<!-- Идентификатор и дата оформления заказа -->

Идентификатор заказа, разумеется, только отображается; ведь пользователь не может редактировать его, поскольку идентификатор является уникальным признаком заказа. Мы создаем несколько скрытых полей, используемых для идентификации записи в базе данных. В них сохраняются идентификаторы заказа, покупателя и корзины. После скрытых полей выводится дата оформления заказа. Она также не может редактироваться пользователем (см. листинг 12.28).

Листинг 12.28. OrderDetail.asp (продолжение)

<tr>
<td><B>
Order # <%=rsOrderReceiptHeader("idOrder")%></b>

<!-- Several hidden variables are built in so we
can easily update by the key fields for each
table. -->
<input type="hidden" name="idOrder" value="<%=request("idOrder")%>">
<input type="hidden" name="idShopper" value="<%=request("idShopper")%>">
<input type="hidden" name="idBasket" value="<%=rsOrderReceiptHeader("idBasket")%>">
</td>
<td width="75"></td>
<td><b>Order Date: <%=rsOrderReceiptHeader("dtOrdered")%></b></td>
<tr>

<!-- Blank column -->
<tr>
<td colspan="3">&nbsp;</td>
</tr>

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

Листинг 12.29. OrderDetail.asp (продолжение)

<!-- Bill to and Ship to header -->
<tr>
<td><b>Bill To:</b></td>
<td width="75"></td>
<td><b>Ship To:</b></td>
<tr>
<!-- Shipping and Billing information -->
<tr>
<td>
<input type="text" name="chrBillFirstName"
value="<% = rsOrderReceiptHeader("chrBillFirstName")%>">

<input type="text" name="chrBillLastName"
value="<% = rsOrderReceiptHeader("chrBillLastName")%>">
</td>

<td width="75"></td>

<td>

<input type="text" name="chrShipFirstName"
value="<% = rsOrderReceiptHeader("chrShipFirstName")%>">

<input type="text" name="chrShipLastName"
value="<% = rsOrderReceiptHeader("chrShipLastName")%>">

</td>
<tr>
<!-- Billing and shipping address. -->
<tr>
<td>
<input type="text" name="chrBillAddress"
value="<% = rsOrderReceiptHeader("chrBillAddress")%>">
</td>
<td width="75"></td>
<td>
<input type="text" name="chrShipAddress"
value="<%=rsOrderReceiptHeader("chrShipAddress")%>">
</td>
<tr>
<!-- Billing and shipping address. -->
<tr>
<td>
<input type="text" name="chrBillCity"
value="<% = rsOrderReceiptHeader("chrBillCity")%>">,
<input type="text" name="chrBillState"
value="<% = rsOrderReceiptHeader("chrBillState")%>" size="2">
<input type="text" name="chrBillZipCode"
value="<% = rsOrderReceiptHeader("chrBillZipCode")%>" size="10">

</td>

<td width="75"></td>

<td>
<input type="text" name="chrShipCity"
value="<% = rsOrderReceiptHeader("chrShipCity")%>">,
<input type="text" name="chrShipState"
value="<% = rsOrderReceiptHeader("chrShipState")%>" size="2">
<input type="text" name="chrShipZipCode"
value="<% = rsOrderReceiptHeader("chrShipZipCode")%>" size="10">
</td>
<tr>
<!-- Billing and shipping phone. -->
<tr>
<td>
<input type="text" name="chrBillPhone"
value="<% = rsOrderReceiptHeader("chrBillPhone")%>">
</td>
<td width="75"></td>
<td>
<input type="text" name="chrShipPhone"
value="<% = rsOrderReceiptHeader("chrShipPhone")%>">
</td>
<tr>
<!-- Billing and shipping email. -->
<tr>
<td>
<input type="text" name="chrBillEmail"
value="<% = rsOrderReceiptHeader("chrBillEmail")%>" size="35">
</td>
<td width="75"></td>
<td>
<input type="text" name="chrShipEmail"
value="<% = rsOrderReceiptHeader("chrShipEmail")%>" size="35">
</td>
<tr>

</table>

После адресов и сведений о заказах выводятся платежные реквизиты (см. листинг 12.30). Обратите внимание: эта информация не включается в сведения о заказе, выводимые для покупателя.

Загрузка платежных реквизитов выполняется хранимой процедурой sp_RetrievePaymentData, которой при вызове передается идентификатор заказа.

Листинг 12.30. OrderDetail.asp (продолжение)

<!-- Display teh payment data . -->
<BR><BR>
<b>Payment Data:</b>
<BR>

<%

' Create an ADO database connection
set dbPaymentData = server.createobject("adodb.connection")
set rsPaymentData = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbPaymentData.open("filedsn=WildWillieCDs")

' Call the stored procedure to retrieve the
' payment data on the order.
sql = "execute sp_RetrievePaymentData " & _
Request("idOrder")

' Execute the SQL statement
set rsPaymentData = dbPaymentData.execute(sql)

%>

Для вывода платежных реквизитов создается новая таблица (см. листинг 12.31). В ней выводится имя владельца, номер и тип кредитной карты, а также срок ее действия. Вся эта информация может редактироваться пользователем.

Листинг 12.31. OrderDetail.asp (продолжение)

<!-- A table is created to dispaly the payment
data. -->
<table cellpadding="3" cellspacing="3">
<tr>
<!-- Dispaly the card name -->
<td align="right">Card Name:</td>
<td>
<input type="text" name="chrCardName"
value="<%=rsPaymentData("chrCardName")%>">
</td>
<!-- Display the card number -->
<td align="right">Card Number:</td>
<td>
<input type="text" name="chrCardNumber"
value="<%=rsPaymentData("chrCardNumber")%>">
</td>
</tr>
<tr>
<!-- Display the card type. -->
<td align="right">Card Type:</td>
<td>
<input type="text" name="chrCardType" value="<%=rsPaymentData("chrCardType")%>">
</td>
<!-- Display the card expiration date -->
<td align="right">Card Expiration Date:</td>
<td>
<input type="text" name="chrExpDate"
value="<%=rsPaymentData("chrExpDate")%>">
</td>
</tr>
</table>

После платежных реквизитов выводится перечень товаров, входящих в заказ (см. листинг 12.32). Для получения всего содержимого корзины используется хранимая процедура sp_RetrieveReceiptItems. В качестве параметров процедуре передаются идентификаторы покупателя и заказа.

Листинг 12.32. OrderDetail.asp (продолжение)

<%

' Create an ADO database connection
set dbOrderReceiptItems = server.createobject("adodb.connection")
set rsOrderReceiptItems = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbOrderReceiptItems.open("filedsn=WildWillieCDs")

' SQL statement to retrieve the items orders.
sql = "execute sp_RetrieveReceiptItems " & _
Request("idShopper") & ", " & _
Request("idOrder")

' Execute the SQL statement
set rsOrderReceiptItems = dbOrderReceiptItems.execute(sql)

%>

Для вывода полученных данных создается таблица (см. листинг 12.33). Выводится идентификатор товара, название, атрибуты, количество, цена и стоимость позиции корзины. Все эти поля могут редактироваться пользователем.

Листинг 12.33. OrderDetail.asp (продолжение)

<!-- Build the basket table -->
<table border="0" cellpadding="3" cellspacing="2" width="750">

<tr><td colspan="6"><HR></td></tr>

<!-- Build the header row -->
<tr>
<th>Item Code</th>
<th>Name</th>
<th>Attributes</th>
<th>Quantity</th>
<th>Price</th>
<th>Total</th>
</tr>

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

Проблема решается объединением имени поля (например, chrName) с идентификатором текущего товара. Например, полю названия товара с кодом 99 присваивается имя chrName99. Это позволит нам прочитать уникальные значения на странице UpdateOrder.asp и соответствующим образом обновить содержимое базы данных.

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

Листинг 12.34. OrderDetail.asp (продолжение)

<%

' Loop through the basket items.
do until rsOrderReceiptItems.EOF

%>

<!-- Show the row -->
<tr>
<!-- Show the product id -->
<td align="center">
<input type="hidden" name="idBasketItem<% = rsOrderReceiptItems("idProduct")%>"
value="<% = rsOrderReceiptItems("idBasketItem")%>">
<input type="text" name="<% = rsOrderReceiptItems("idProduct")%>"
value="<% = rsOrderReceiptItems("idProduct")%>" size="3">
</td>

<!-- Show the product name -->
<td>
<input type="text" name="chrName<% = rsOrderReceiptItems("idProduct")%>"
value="<% = rsOrderReceiptItems("chrName")%>">
</td>

<!-- Show the product attributes -->
<td>
<% if rsOrderReceiptItems("chrColor") <> " " then %>

<input type="text" name="chrSize<% = rsOrderReceiptItems("idProduct")%>"
value="<% = rsOrderReceiptItems("chrSize")%>" size="7">,

<input type="text" name="chrColor<% = rsOrderReceiptItems("idProduct")%>"
value="<% = rsOrderReceiptItems("chrColor")%>" size="7">

<% end if %>
</td>

<!-- Show the product quantity -->
<td align="center">
<input type="text" name="intQuantity<% = rsOrderReceiptItems("idProduct")%>"
value="<%=rsOrderReceiptItems("intQuantity")%>" size="4">
<%'=rsOrderReceiptItems("basketitem.intQuantity")%>
</td>

<!-- Show the product price -->
<td>
<input type="text" name="intPrice<% = rsOrderReceiptItems("idProduct")%>"
value="<%=formatcurrency(rsOrderReceiptItems("intPrice")/100, 2)%>"
size="7">
</td>

<!-- Show the product total cost -->
<td align="right"><%Response.write _
formatcurrency(rsOrderReceiptItems("intPrice")/100 * rsOrderReceiptItems("intQuantity"), 2)%>
</td>
</tr>

<%

' Move to the next row
rsOrderReceiptItems.MoveNext

loop

%>

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

Листинг 12.35. OrderDetail.asp (продолжение)

<!-- Build a break -->
<tr>
<td colspan="6"><HR></td>
</tr>

<!-- Show the sub total of the basket -->
<tr>
<td colspan="5" align="right"><b>Subtotal:</b></td>
<td align="right"><%Response.Write _
formatcurrency(rsOrderReceiptHeader("intSubtotal")/100, 2) %></td>
</tr>
<!-- Show the shipping total of the basket -->
<tr>
<td colspan="5" align="right"><b>Shipping:</b></td>
<td align="right"><%Response.Write _
formatcurrency(rsOrderReceiptHeader("intShipping")/100, 2) %></td>
</tr>
<!-- Show the tax total of the basket -->
<tr>
<td colspan="5" align="right"><b>Tax:</b></td>
<td align="right"><%Response.Write _
formatcurrency(rsOrderReceiptHeader("intTax")/100, 2) %></td>
</tr>
<!-- Show the total of the basket -->
<tr>
<td colspan="5" align="right"><b>Total:</b></td>
<td align="right"><%Response.Write _
formatcurrency(rsOrderReceiptHeader("intTotal")/100, 2) %></td>
</tr>

Как показано в листинге 12.36, страница завершается кнопкой отправки данных формы и закрывающими тегами страницы.

Листинг 12.36. OrderDetail.asp (продолжение)

<!-- Submit button to process the updates. -->
<tr>
<td colspan="6">
<input type="Submit" value="Submit Changes" name="Submit">
</td>
<tr>

</table>

</form>
</BODY>
</HTML>

В работе страницы используется несколько хранимых процедур. Первая, sp_RetrieveReceiptHeader, загружает данные из таблиц OrderData и Basket и позволяет вывести основные сведения о заказе (см. листинг 12.37).

Листинг 12.37. Хранимая процедура sp_RetrieveReceiptHeader

/* Чтение данных заголовка отчета по идентификаторам покупателя и заказа */
CREATE PROCEDURE sp_RetrieveReceiptHeader

/* При вызове процедуре передаются идентификаторы покупателя и заказа */
@idShopper int,
@idOrder int

AS

/* Получить основную информацию о заказе посредством объединения таблиц OrderData и Basket */
select * from OrderData, Basket
where Orderdata.idOrder = @idOrder and
OrderData.idShopper = @idShopper and
OrderData.idBasket = Basket.idBasket

Хранимая процедура sp_RetrievePaymentData (см. листинг 12.38) загружает платежные реквизиты, относящиеся к заказу. В сущности, она просто возвращает запись с заданным идентификатором заказа.

Листинг 12.38. Хранимая процедура sp_RetrievePaymentData

CREATE PROCEDURE sp_RetrievePaymentData

@idOrder int

AS

select * from paymentdata
where idOrder = @idOrder

Наконец, хранимая процедура sp_RetrieveReceiptItems читает информацию о товарах, заказанных пользователем. Поиск осуществляется по идентификаторам заказа и покупателя.

Листинг 12.39. Хранимая процедура sp_RetrieveReceiptItems

/* Stored Procedure to retrieve the receipt items. */
CREATE PROCEDURE sp_RetrieveReceiptItems

/* Pass in the ID of the shopper and the Id of the order */
@idShopper int,
@idOrder int

AS

/* Select the contents of the basketitem, basket and orderdata tables. */
select * from basketitem, orderdata
where orderdata.idshopper = @idShopper and
orderdata.idOrder = @idOrder and
basketitem.idbasket = orderdata.idbasket

ПРИМЕЧАНИЕ
На странице OrderDetail.asp пользователю предоставляется полная свобода действий. Он может ввести абсолютно любое название, идентификатор товара и т.д. Вероятно, выбор пользователя следует ограничить значениями из заранее заполненных списков.

От вывода заказа и подготовки его к редактированию мы переходим к обработке внесенных изменений. В листинге 12.40 приведено начало страницы UpdateOrder.asp, предназначенной для обработки всех изменений на странице редактирования заказов.

Страница открывает подключение ADO к базе данных, после чего читает основные сведения о заказе - идентификатор заказа, идентификатор покупателя и идентификатор корзины.

Листинг 12.40. UpdateOrder.asp

<%@ Language=VBScript %>
<%
' ****************************************************
' UpdateOrder.asp - Handles updating the order data
' posted from the order detail page.
' ****************************************************

' Create an ADO database connection
set dbOrderUpdate = server.createobject("adodb.connection")
set rsOrderUpdate = server.CreateObject("adodb.recordset")

' Open the connection using our ODBC file DSN
dbOrderUpdate.open("filedsn=WildWillieCDs")

' Retrieve our three key values, the id of the order
' the id of the shopper and the id of the basket for
' the order.
idOrder = request("idOrder")
idShopper = request("idShopper")
idBasket = request("idBasket")

Затем из базы данных загружаются реквизиты выписки счета и доставки. Как обычно, одиночные апострофы в текстовых полях необходимо удвоить (см. листинг 12.41).

Листинг 12.41. UpdateOrder.asp (продолжение)

' Next we retrieve the core order data which
' includes the billing address and shipping
' address. Note that the key fields are updated
' to ensure any single quotes are doubled.
chrBillFirstName = replace(request("chrBillFirstName"), "'", "''")
chrBillLastname = replace(request("chrBillLastName"), "'", "''")
chrShipFirstName = replace(Request("chrShipFirstName"), "'", "''")
chrShipLastname = replace(request("chrShipLastName"), "'", "''")
chrBillAddress = replace(request("chrBillAddress"), "'", "''")
chrShipAddress = replace(request("chrShipAddress"), "'", "''")
chrBillCity = replace(request("chrBillCity"), "'", "''")
chrBillState = request("chrBillState")
chrBillZipCode = request("chrBillZipCode")
chrShipCity = replace(request("chrShipCity"), "'", "''")
chrShipState = request("chrShipState")
chrShipZipCode = request("chrShipZipCode")
chrBillPhone = request("chrBillPhone")
chrShipPhone = request("chrShipPhone")
chrBillEmail = request("chrBillEmail")
chrShipEmail = request("chrShipEmail")

Основные данные заказа обновляются хранимой процедурой sp_UpdateOrderData. Процедура получает всю адресную информацию и обновляет таблицу OrderData (см. листинг 12.42).

Листинг 12.42. UpdateOrder.asp (продолжение)

' The order data is updated by calling
' the sp_UpdateOrderData stored procedure
sql = "execute sp_UpdateOrderData " & _
idOrder & ", '" & _
chrBillFirstName & "', '" & _
chrBillLastname & "', '" & _
chrBillAddress & "', '" & _
chrBillCity & "', '" & _
chrBillState & "', '" & _
chrBillZipCode & "', '" & _
chrBillPhone & "', '" & _
chrBillEmail & "', '" & _
chrShipFirstName & "', '" & _
chrShipLastname & "', '" & _
chrShipAddress & "', '" & _
chrShipCity & "', '" & _
chrShipState & "', '" & _
chrShipZipCode & "', '" & _
chrShipPhone & "', '" & _
chrShipEmail & "'"

' Execute the SQL statement
set rsOrderUpdate = dbOrderUpdate.execute(sql)

Переходим к обработке платежных реквизитов (см. листинг 12.43). Мы получаем их значения со страницы OrderDetail.asp, как обычно, удваиваем одиночные апострофы. Затем хранимая процедура sp_UpdatePayment вносит изменения в базу данных.

Листинг 12.43. UpdateOrder.asp (продолжение)

' Next we are going to update the
' payment data. Each of the payment
' data fields are retrieved. Note that
' we check for single quotes in key
' fields.
chrCardName = replace(request("chrCardName"), "'", "''")
chrCardNumber = request("chrCardNumber")
chrCardType = request("chrCardType")
chrExpDate = request("chrExpDate")

' Build the SQL statement to update the
' payement data.
sql = "execute sp_UpdatePaymentData " & _
idOrder & ", '" & _
chrCardType & "', '" & _
chrCardNumber & "', '" & _
chrExpDate & "', '" & _
chrCardName & "'"

' Now execute the SQL statement
set rsOrderUpdate = dbOrderUpdate.execute(sql)

Все готово к внесению изменений в базу данных. Вспомните: для построения имен полей использовалась комбинация постоянного имени и идентификатора товара. Чтобы построить эти имена заново, необходимо загрузить текущий список товаров, входящих в заказ. Для получения списка используется хранимая процедура sp_RetrieveReceiptHeader (см. листинг 12.44).

Листинг 12.44. UpdateOrder.asp (продолжение)

' Создать объект подключения к базе данных
set dbOrderReceiptItems = server.createobject("adodb.connection")
set rsOrderReceiptItems = server.CreateObject("adodb.recordset")

' Открыть подключение, используя файловый DSN ODBC
dbOrderReceiptItems.open("filedsn=WildWillieCDs")

' Построить команду вызова хранимой процедуры для загрузки
' содержимого заказа.
sql = "execute sp_RetrieveReceiptItems " & _
Request("idShopper") & ", " & _
Request("idOrder")

' Выполнить команду SQL
set rsOrderReceiptItems = dbOrderReceiptItems.execute(sql)

Получив набор записей, мы перебираем их и генерируем имена полей, по которым мы будем обращаться к их содержимому. Для этого идентификатор товара объединяется с именем поля (см. листинг 12.45).

Листинг 12.45. UpdateOrder.asp (продолжение)

' To read the input fields for each basket item,
' we are going to have to loop through the
' basket items again. Then that item can
' be updated.
do until rsOrderReceiptItems.EOF

' Retrieve the product id. That will be utilized
' to build the name of the HTML input fields
' from the posting page.
idProduct = rsOrderReceiptItems("idProduct")

' The name of each basket item field is built with
' the id of the product tacked onto the end.
idBasketItem = request("idBasketItem" & idProduct)
chrName = replace(request("chrName" & idProduct), "'", "''")
chrColor = request("chrColor" & idProduct)
chrSize = request("chrSize" & idProduct)
intQuantity = cint(request("intQuantity" & idProduct))
intPrice = request("intPrice" & idProduct) * 100

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

Листинг 12.46. UpdateOrder.asp (продолжение)

' При каждой итерации общая стоимость корзины
' увеличивается на стоимость текущей позиции.
subtotal = subtotal + (intPrice * intQuantity)

' При каждой итерации общее количество единиц товара
' увеличивается на количество единиц в текущей позиции.
TotalQuantity = TotalQuantity + intQuantity

' Построить команду вызова хранимой процедуры
' для обновления позиции корзины.
sql = "execute sp_UpdateBasketItem " & _
idBasketItem & ", '" & _
chrName & "', '" & _
chrColor & "', '" & _
chrSize & "', " & _
intQuantity & ", " & _
intPrice

' Выполнить команду SQL
set rsOrderUpdate = dbOrderUpdate.execute(sql)

' Перейти к следующей записи
rsOrderReceiptItems.MoveNext

' Вернуться к началу цикла Loop
Loop

Последняя операция - обновление итоговой стоимости заказа - приведена в листинге 12.47. Для ее проведения необходимо пересчитать налог и стоимость доставки. При этом будет использоваться объект COM ECStoreBizLogiс, созданный нами в главе 8.

Страница создает экземпляр этого объекта и вызывает функции Shipping и Tax для пересчета стоимости доставки и налога. При вызове функции Shipping передается общее количество заказанных единиц товара, а при вызове функции Tax - общая стоимость корзины.

Листинг 12.47. UpdateOrder.asp (продолжение)

' Create the Bussiness Logic component to
' calculate the tax and shipping.
set BizLogic = server.CreateObject("ECStoreBizLogic.TaxShip")

' Call the shipping function of our component. The quantity
' is passed in and must be in a long data type format. The
' Shipping fee is returned.
Shipping = BizLogic.Shipping(cLng(TotalQuantity))

' Calculate the tax by calling the Tax function of
' our component. We pass in the shipping state and the
' order subtotal. The value is also stored in a session
' variable.
Tax = BizLogic.tax(cstr(chrShipState), clng(subtotal))

Зная общую стоимость, налог и стоимость доставки, можно вычислить итоговую стоимость заказа. Хранимая процедура sp_UpdateBasket обновляет соответствующие параметры корзины. После этого пользователь направляется на страницу OrderDetail.asp для просмотра изменений (см. листинг 12.48).

Листинг 12.48. UpdateOrder.asp (продолжение)

' Calculate teh new total.
Total = subtotal + shipping + tax

' Build a SQL statement to update the basket data
sql = "execute sp_UpdateBasket " & _
idBasket & ", " & _
TotalQuantity & ", " & _
SubTotal & ", " & _
Shipping & ", " & _
Tax & ", " & _
Total & ", 1"

' Execute the SQL statement
set rsOrderUpdate = dbOrderUpdate.execute(sql)

' Send the user back to the order detail
' page
Response.Redirect "OrderDetail.asp?idOrder=" & idOrder & _
"&idShopper=" & idShopper

%>

При обновлении данных заказа эта страница использует несколько хранимых процедур. Процедура sp_UpdateOrderData обновляет все основные реквизиты доставки и выписки счета в таблице OrderData. При вызове она получает набор параметров и конструирует команду SQL UPDATE (см. листинг 12.49).

Листинг 12.49. Хранимая процедура sp_UpdateOrderData

CREATE PROCEDURE sp_UpdateOrderData

@idOrder int,
@chrBillFirstName varchar(255),
@chrBillLastName varchar(255),
@chrBillAddress varchar(255),
@chrBillCity varchar(255),
@chrBillState varchar(25),
@chrBillZipCode varchar(25),
@chrBillPhone varchar(255),
@chrBillEmail varchar(255),
@chrShipFirstName varchar(255),
@chrShipLastname varchar(255),
@chrShipAddress varchar(255),
@chrShipCity varchar(255),
@chrShipState varchar(25),
@chrShipZipCode varchar(25),
@chrShipPhone varchar(255),
@chrShipEmail varchar(255)

AS

update orderdata set

chrBillFirstName = @chrBillFirstName,
chrBillLastname = @chrBillLastname,
chrBillAddress = @chrBillAddress,
chrBillCity = @chrBillCity,
chrBillState = @chrBillState,
chrBillZipCode = @chrBillZipCode,
chrBillPhone = @chrBillPhone,
chrBillEmail = @chrBillEmail,
chrShipFirstName = @chrShipFirstName,
chrShipLastname = @chrShipLastname,
chrShipAddress = @chrShipAddress,
chrShipCity = @chrShipCity,
chrShipState = @chrShipState,
chrShipZipCode = @chrShipZipCode,
chrShipPhone = @chrShipPhone,
chrShipEmail = @chrShipEmail

where

idOrder = @idOrder

Хранимая процедура sp_UpdatePaymentData обновляет платежные реквизиты заказа (см. листинг 12.50). Она тоже получает набор параметров и строит соответствующую команду SQL UPDATE.

Листинг 12.50. Хранимая процедура sp_UpdatePaymentData

CREATE PROCEDURE sp_UpdatePaymentData

@idOrder int,
@chrCardType varchar(100),
@chrCardNumber varchar(50),
@chrExpDate varchar(25),
@chrCardName varchar(150)

AS

update PaymentData set
chrCardType = @chrCardType,
chrCardNumber = @chrCardNumber,
chrExpDate = @chrExpDate,
chrCardName = @chrCardName
where
idOrder = @idOrder

Хранимая процедура sp_UpdateBasketItem получает параметры, описывающие отдельную позицию корзины, и обновляет ее соответствующим образом (см. листинг 12.51). Обновляемая запись определяется переданным идентификатором позиции.

Листинг 12.51. Хранимая процедура sp_UpdateBasketItem

CREATE PROCEDURE sp_UpdateBasketItem

@idBasketItem int,
@chrName varchar(255),
@chrColor varchar(50),
@chrSize varchar(50),
@intQuantity int,
@intPrice int

AS

update BasketItem set
chrName = @chrName,
chrColor = @chrColor,
chrSize = @chrSize,
intQuantity = @intQuantity,
intPrice = intPrice

where idBasketItem = @idBasketItem

Наконец, хранимая процедура sp_UpdateBasket (см. листинг 12.52) обновляет ключевые поля заданной корзины.

Листинг 12.52. Хранимая процедура sp_UpdateBasket

/* Обновление содержимого корзины */
CREATE PROCEDURE sp_UpdateBasket

/* При вызове процедуре передается идентификатор корзины, количество единиц товара, общая стоимость заказа, стоимость доставки, налог, итоговая стоимость и признак размещения заказа. */
@idBasket int,
@intQuantity int,
@intSubTotal int,
@intShipping int,
@intFreeShipping int,
@intTax int,
@intTotal int,
@intOrderPlaced int

AS

/* Обновление корзины */
update basket set
intQuantity = @intQuantity,
intSubtotal = @intSubtotal,
intShipping = @intShipping,
intFreeShipping = @intFreeShipping, intTax = @intTax,
intTotal = @intTotal,
intOrderPlaced = @intOrderPlaced
where idBasket = @idBasket

Давайте посмотрим, как обновление заказов работает на практике. На рис. 12.7 изображена страница с подробными сведениями о заказе. Обратите внимание: все ключевые поля готовы к редактированию.

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

Обновленная страница с подробными сведениями о заказе. Обратите внимание: все вычисляемые поля были обновлены правильными величинами.

Итоги

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

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