+ All Categories
Home > Documents > At g Cons Comm Ref Guide

At g Cons Comm Ref Guide

Date post: 24-Oct-2014
Category:
Upload: smart2k10
View: 99 times
Download: 6 times
Share this document with a friend
Popular Tags:
254
Commerce Version 9.4 Consumer Commerce Reference Application Guide Oracle ATG One Main Street Cambridge, MA 02142 USA
Transcript
Page 1: At g Cons Comm Ref Guide

Commerce

Version 9.4

Consumer Commerce Reference Application Guide

Oracle ATG

One Main Street

Cambridge, MA 02142

USA

Page 2: At g Cons Comm Ref Guide

ATG Consumer Commerce Reference Application Guide

Product version: 9.4

Release date: 10-31-11

Document identifier: ConsumerCommerceReferenceGuide1111050007

Copyright © 1997, 2011 Oracle and/or its affiliates. All rights reserved.

This software and related documentation are provided under a license agreement containing restrictions on use and disclosure and are

protected by intellectual property laws. Except as expressly permitted in your license agreement or allowed by law, you may not use, copy,

reproduce, translate, broadcast, modify, license, transmit, distribute, exhibit, perform, publish, or display any part, in any form, or by any

means. Reverse engineering, disassembly, or decompilation of this software, unless required by law for interoperability, is prohibited.

The information contained herein is subject to change without notice and is not warranted to be error-free. If you find any errors, please

report them to us in writing. If this software or related documentation is delivered to the U.S. Government or anyone licensing it on behalf of

the U.S. Government, the following notice is applicable:

U.S. GOVERNMENT RIGHTS

Programs, software, databases, and related documentation and technical data delivered to U.S. Government customers are "commercial

computer software" or "commercial technical data" pursuant to the applicable Federal Acquisition Regulation and agency-specific

supplemental regulations. As such, the use, duplication, disclosure, modification, and adaptation shall be subject to the restrictions and

license terms set forth in the applicable Government contract, and, to the extent applicable by the terms of the Government contract, the

additional rights set forth in FAR 52.227-19, Commercial Computer Software License (December 2007). Oracle America, Inc., 500 Oracle

Parkway, Redwood City, CA 94065.

This software or hardware is developed for general use in a variety of information management applications. It is not developed or intended

for use in any inherently dangerous applications, including applications that may create a risk of personal injury. If you use this software or

hardware in dangerous applications, then you shall be responsible to take all appropriate fail-safe, backup, redundancy, and other measures

to ensure its safe use. Oracle Corporation and its affiliates disclaim any liability for any damages caused by use of this software or hardware in

dangerous applications.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.

Intel and Intel Xeon are trademarks or registered trademarks of Intel Corporation. All SPARC trademarks are used under license and are

trademarks or registered trademarks of SPARC International, Inc. AMD, Opteron, the AMD logo, and the AMD Opteron logo are trademarks or

registered trademarks of Advanced Micro Devices. UNIX is a registered trademark licensed through X/Open Company, Ltd.

This software or hardware and documentation may provide access to or information on content, products, and services from third parties.

Oracle Corporation and its affiliates are not responsible for and expressly disclaim all warranties of any kind with respect to third-party

content, products, and services. Oracle Corporation and its affiliates will not be responsible for any loss, costs, or damages incurred due to

your access to or use of third-party content, products, or services.

For information about Oracle's commitment to accessibility, visit the Oracle Accessibility Program website at http://www.oracle.com/us/

corporate/accessibility/index.html.

Oracle customers have access to electronic support through My Oracle Support. For information, visit http://www.oracle.com/support/

contact.html or visit http://www.oracle.com/accessibility/support.html if you are hearing impaired.

Page 3: At g Cons Comm Ref Guide

ATG Consumer Commerce Reference Application Guide iii

Table of Contents

1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

File Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

Setting Up E-mail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

Viewing the Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

About this Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2. Business Users’ Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

Setting Up the Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

Designing Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

Creating Profile Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

Matching Content with Target Audiences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

Merchandising Capabilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

Processing Orders in Pioneer Cycling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

Pricing in Pioneer Cycling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3. Profile Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

User Profiles and Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

Managing Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

Login and Registration Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

Login . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

Registration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

My Profile page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

Address Book page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Edit Billing Address . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Managing Alternate Shipping Addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Creating an Alternate Shipping Address . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Editing an Alternate Shipping Address . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

Deleting an Alternate Shipping Address . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

Moving an Alternate Shipping Address to the Default Shipping Address . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

Contact Customer Service page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

Credit Card Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

Viewing and Searching History Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

Categories and Products Recently Browsed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

Items Recently Searched For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

Profile Repository Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

4. Extending the Product Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

Adding Properties to Product Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

Adding simple properties to SKU item type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

Adding New Item Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

Creating Subtypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

5. Displaying and Accessing the Product Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

Product Template Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

Creating the Bike Template Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

Using ProductLookup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

Displaying Links to Products or Categories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

Displaying Product Prices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

Adding Items to the Shopping Cart from the Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

Displaying Related Products . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

Displaying Inventory Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

Adding Compare Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

Page 4: At g Cons Comm Ref Guide

iv ATG Consumer Commerce Reference Application Guide

Adding Historical Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

Category Template Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

JSP Fragment Parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

Using the Cache Component to Improve Performance of Java Server Pages . . . . . . . . . . . . . . . . . . . . . . . . . . 92

Adding Catalog Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

Historical Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

Searching and Filtering the Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

Product Searching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

Simple Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

Advanced Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

Filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

Comparing SKUs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

Adding Compare Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

CompareSkusFormHandler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

Selecting two SKUs to Compare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

Displaying the side-by-side values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

Displaying Errors found by CompareSkusFormHandler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

6. Pricing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

Setting Product Prices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

Standard Prices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

Promotional Prices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

Displaying Prices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

Shipping Prices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

Shipping Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

Shipping Calculators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

Creating Your Own Pricing Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

7. Order Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135

Reviewing and Editing Orders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135

The Shopping Cart Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

Item Pricing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

Entering Information Necessary for Checkout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

Secure Checkout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

Choosing Between Basic and Advanced Checkout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

Basic Checkout Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

Advanced Checkout Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

Additional Order Capture Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

Entering a Valid Credit Card . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

Displaying Error Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

Express Checkout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

Enabling Express Checkout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

Deactivating Express Checkout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171

Modifying the Shopping Cart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171

8. Order History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173

Displaying a User’s Orders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173

Displaying a Single Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

Canceling Orders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179

9. Inventory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187

Overview of the Pioneer Cycling Inventory System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187

Implementing an Inventory System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188

Accessing Inventory Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188

10. Merchandising . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

Promotions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

Implementing Promotions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194

Page 5: At g Cons Comm Ref Guide

ATG Consumer Commerce Reference Application Guide v

Targeting Content for Promotions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195

Slots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198

Gift Certificates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200

Purchase of Gift Certificate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200

E-mail Delivery of Gift Certificates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201

Using Gift Certificates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201

Coupons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201

Creating Coupons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202

Claiming Coupons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202

Gift Registry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

ATG Consumer Commerce Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

Pioneer Cycling Gift Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204

Creating Wish Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204

Creating and Displaying Gift Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204

Adding Items to Gift Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214

Gift Purchasing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216

Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226

Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226

Bundle building example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227

Displaying bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227

A. Pioneer Cycling Scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229

Customer Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230

NewGear . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230

NotifyByArea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230

PleaseComeBack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230

Marketing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230

Customer Survey . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230

Helmet No Discount . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

Helmet Promotional Offer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

Loyalty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

New Customers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232

Profiling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232

Browsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232

BuildProfile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232

Sales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233

Buy2Get1Free . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233

Default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233

FreeGift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233

FreeShipping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234

IncreasePurchaseSize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234

ShowProducts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234

Scenario Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235

Changing URLs in E-mail Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235

B. Optimizing Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237

Prioritizing Web Site Concerns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237

Streamlining JSP Construction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238

Limit Excess Droplet Invocations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238

Use the Cache Servlet Bean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239

Avoid “Expensive” Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241

Writing your own Servlet Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241

Using the Broadest Scope Possible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242

Reloading the Catalog Repository Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242

Page 6: At g Cons Comm Ref Guide

vi ATG Consumer Commerce Reference Application Guide

Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245

Page 7: At g Cons Comm Ref Guide

1 Introduction 1

1 Introduction

The Consumer Commerce Reference Application has three purposes:

• To be a demonstration of ATG Consumer Commerce functionality that customers can install and experiment

with before they begin work on their own site.

• To act as a template that customers can adapt to build their own site.

• To be a guide on best practices in implementing a commerce site with ATG Consumer Commerce.

The Pioneer Cycling store is a pre-built, personalized commerce site that is targeted mainly to cyclists. It utilizes

the rich B2C functionality of ATG Consumer Commerce to provide a foundation and a guide to help you quickly

and easily create your own personalized, innovative, commerce-intensive Web sites.

Pioneer Cycling is made up of pre-configured Java components and page templates that provide the core

functionality you need to quickly implement a broad range of merchandising and order processing activities

typical of an online commerce site. We used the basic functionality available in ATG Consumer Commerce, but

extended the functionality to implement specific features.

We included the following features (both standard ATG Consumer Commerce and customized) in the Pioneer

Cycling site:

• Express check-out

• Coupons

• Gift certificates

• Product packages (products composed of multiple SKUs, or Bundles)

• Multiple shipping address and payment types per order

• Wish list and gift registry

• Dynamic product comparison charts

• Product searching

• Inventory information

• Multiple locales

• Customer self-service area

We designed the Pioneer Cycling store as an example of some of the ways that ATG Consumer Commerce can

help you create a better and more effective e-commerce Web site. You will want to evaluate your business goals

and use the ATG Commerce features that are appropriate for your strategy. The processes and functionality that

this manual discusses can be customized to fit the specific needs of your Web application.

Page 8: At g Cons Comm Ref Guide

2 1 Introduction

The rest of this chapter provides you with the basic information you need to understand and get started with

the Pioneer Cycling store.

Getting Started

The instructions for installing and starting the Pioneer Cycling store of ATG Consumer Commerce Reference

Application are specific to your application server. Consult the ATG Commerce Programming Guide for your

application server for information on installing the ATG Commerce and ATG Consumer Commerce Reference

Application.

Pioneer Cycling is part of the standard implementation of ATG Consumer Commerce. (If you chose a

custom installation, Pioneer Cycling may not have been installed.) The standard ATG Consumer Commerce

implementation also includes the ATG Control Center and a demonstration MySQL database. For instructions

on installing demonstration data and starting ATG Consumer Commerce, Pioneer Cycling, and the ATG Control

Center, see the ATG Installation and Configuration Guide.

File Organization

The PioneerCyclingJSP directory contains all files necessary to implement the Pioneer Cycling store using

ATG Consumer Commerce. It contains the following directories:

Directory Description

config contains atg/dynamo/service/j2ee/J2EEContainer.properties, which installs the

PioneerCyclingJSP application in Dynamo’s J2EE container

j2ee-apps contains directories that hold individual J2EE applications

META-INF contains MANIFEST.MF, which declares the PioneerCyclingJSP application dependent on

the Pioneer Cycling module, and specifies other configuration information for the Dynamo

module

All the JSP files for Pioneer Cycling are located at <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/

web-app/en.

Setting Up E-mail

To configure the Pioneer Cycling site so that customers receive e-mail from Pioneer Cycling, you must set the e-

mail host to a valid server and enable several scenarios.

First, specify the e-mail server:

1. In the Pages and Components > Components by path menu of the ACC, go to /atg/dynamo/service/

SMTPEmail.

2. Set the emailHandlerHostName property to a valid mail server.

Page 9: At g Cons Comm Ref Guide

1 Introduction 3

Second, enable the scenarios that send e-mail when an order is fulfilled and delivered.

1. Go to the Scenarios > Scenarios menu.

2. Select and enable the following Scenarios:

DCS/Fulfillment

DCS/ReceiveOrder

Note: If your mail server checks to see if sender e-mail addresses are valid, you must change the sender address

in all e-mail templates, which are located at <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/web-

app/en/emails.

Viewing the Site

Once you assemble and deploy an application that includes the ATG platform, ATG Commerce, and Pioneer

Cycling, you can use the following URL to access the Pioneer Cycling store:

http://hostname:port/PioneerCyclingJSP

The port you use depends on your application server and how it’s configured. For example, JBoss uses the

following URL by default:

http://hostname:8080/PioneerCyclingJSP

For more information on default ports, see the ATG Installation and Configuration Guide.

About this Guide

In this guide, we demonstrate the best practices for building a commerce site such as Pioneer Cycling and

explain how each feature is implemented. We have included many code samples to help you understand the

functionality and use it in your own projects by making small customizations. This guide also describes any

additions or extensions made to the core ATG Consumer Commerce functionality. The commerce areas of the

site are built in a modular fashion, with a minimum of interdependencies—so that the site can be broken apart

and re-implemented in pieces.

Page developers, programmers, and business users should use this manual to see the features available to them

and then make decisions about the most useful ways that they can customize these features for their site.

This guide includes the following chapters:

Business Users’ Overview (page 5)

Outlines the basic functionality implemented in the Pioneer Cycling site.

Profile Management (page 13)

Describes how the Pioneer Cycling Site collects, stores, presents, and retrieves customer data.

It also describes the login, logout, register, and edit profile utilities.

Page 10: At g Cons Comm Ref Guide

4 1 Introduction

Extending the Product Catalog (page 57)

Describes how the database schema, object model, and user interface of the store catalog

work.

Displaying and Accessing the Product Catalog (page 69)

Describes how the catalog is displayed and accessed.

Pricing (page 127)

Describes how prices are calculated, taking into account list price, sales, personalized

promotions, coupons, taxes, and shipping costs.

Order Processing (page 135)

Describes how to collect and process the data necessary for completing a purchase

(including items purchased and shipping and billing information). Also describes

transactions with backend systems such as inventory and credit card authorization.

Order History (page 173)

Describes how to record and display customer’s order history.

Inventory (page 187)

Describes how the inventory system in Pioneer Cycling works with the product catalog and

the ordering process.

Merchandising (page 193)Describes how the personalization and special commerce

features are designed to improve the customers’ shopping experience and increase sales.

Appendix A, Pioneer Cycling Scenarios (page 229)

Describes how business strategies for the Pioneer Cycling store were implemented as

scenarios in DSS.

Appendix B, Optimizing Performance (page 237)

Describes the structural choices you can make that will improve the performance of your site.

Page 11: At g Cons Comm Ref Guide

2 Business Users’ Overview 5

2 Business Users’ Overview

The Commerce Reference Application is a fictional Internet commerce site. This store, Pioneer Cycling, offers

a full set of commerce features demonstrating how the ACC offers easy catalog management, merchandising,

profiling, content targeting, pricing, promotions, and analysis.

In the Pioneer Cycling store, we created a catalog of bicycles and bike accessories to demonstrate ways to use

the core features found in ATG Consumer Commerce and to suggest many ways to build and analyze effective

e-commerce scenarios with ATG Relationship Management Platform. The application also incorporates ATG

Relationship Management Platform comprehensive personalization functionality, which customizes many of the

site’s features according to a customer’s tastes and maintains a complete profile for each user.

We designed the Pioneer Cycling Store to allow visitors to explore the site without registering. However, visitors

are encouraged to register, which gives us greater access to their personal information and gives them a

personalized shopping experience.

The Pioneer Cycling Store:

• Maintains multiple shopping carts for registered users and guests through the use of wish lists.

• Maintains a profile for each of its users containing account information, including a record of all orders. Users

can edit some aspects of their own profiles.

• Creates a customized page for each registered user. This page provides access to an address book, saved

credit cards, express checkout settings, recently visited items and searches, wish lists, a shopping profile,

recent purchases, and full order history.

• Delivers targeted promotions and discounts to the user throughout the store.

• Offers claimable coupons, gift certificates, and a gift registry.

• Provides basic, comprehensive, and express checkout processes. The comprehensive checkout supports

customer registration and multiple shipping addresses. The express checkout streamlines the checkout

process by using predefined shipping and billing information.

• Searches for products and categories by name, keywords, manufacturer, or part number.

• Allows users to compare similar products side by side.

• Allows users to filter product categories to only show certain types of products.

• Provides a historical navigation feature to help customers maintain their place while shopping.

• Cross-sells related products, and suggests replacements for out-of-stock SKUs.

• Displays content and prices for three locales: English, French, and Japanese

Page 12: At g Cons Comm Ref Guide

6 2 Business Users’ Overview

Setting Up the Catalog

We used the ACC to set up and manage the store catalog. Some of these tasks included:

• Managing the catalog hierarchy

• Specifying category, product, and SKU attributes

• Creating promotions

We customized and extended the standard ATG Consumer Commerce catalog to create the Pioneer Cycling

catalog. For more information, see the Using and Extending the Standard Catalog chapter in the ATG Commerce

Programming Guide.

In the ACC, you can see the catalog hierarchy by selecting Catalog Management > Product Catalog. Each

category has subcategories and products. When you look at a category, you can see its attributes, images, and

template. Products provide access to individual SKUs.

The Pioneer Cycling catalog hierarchy in the ACC

We chose product attributes to enable features in the store such as filtering, comparing, searching, and

navigation. Also, we can track these attributes when users browse or purchase products and better understand

customer behavior. We can use that information later to target promotions and featured products to users.

Page 13: At g Cons Comm Ref Guide

2 Business Users’ Overview 7

You can also browse items by selecting Catalog Management > Catalog Elements. This view allows you to see

items by type, such as Category, Product, or SKU. You can also use the query editor to narrow the list of items

to find an exact item more easily. For example, as shown in the screenshot below, you could search for all the

categories that begin with the letter A.

Searching for categories using the Catalog Management > Catalog Elements section of the ACC.

You can select Pricing > Promotions to view all the discounts available to customers in the store. Typically,

scenarios are developed to deliver these discounts to the right customers at the right time. Some examples of

promotions are “10% off everything in the store” or “Free water bottle, with purchase of Jersey.”

Page 14: At g Cons Comm Ref Guide

8 2 Business Users’ Overview

“Free water bottle with jersey” promotion

Designing Profiles

Everything we know about customers from their site activity is stored in their customer profiles. Determining

what information to record for each customer is an important part of designing the store. The profile in the

Pioneer Cycling store records a customer’s:

• name and contact info

• shipping and billing information

• credit card numbers

• checkout preferences

• wish lists or gift registries

Page 15: At g Cons Comm Ref Guide

2 Business Users’ Overview 9

• shopping preferences and items recently browsed

• personal information such as gender, birthday, and clothing sizes

We used several methods of gathering profile information from users in the Pioneer Cycling site. The registration

process asks users to answer specific questions. Also, we implemented scenarios to record profile information

that is then used to target promotions and specific products to users. For example, the “Browsing” scenario

records every item or category that a customer browses. The “BuildProfile” scenario monitors the products a

customer browses or adds to the shopping cart to determine what classes of product interest the customer. For

more information on these scenarios, see Appendix A, Pioneer Cycling Scenarios (page 229).

The Pioneer Cycling “Browsing” scenario in the ACC.

Creating Profile Groups

ATG Relationship Management Platform allows business users to segment a customer population. Often it is

desirable to divide customers into recognizable groups, rather than to treat each individually. For example,

you can design initiatives for key demographic groups. We created a profile group for Pioneer Cycling called

“BMXRiders” for people who have purchased a BMX bike. You can specify profile groups in the ACC by selecting

Targeting > Profile Groups.

Page 16: At g Cons Comm Ref Guide

10 2 Business Users’ Overview

Defining Profile Groups in the ACC

Matching Content with Target Audiences

You can group site content in the same way as you group customers; then, you can match the two using

business rules called content targeters. Business rules define the content that you show to each profile group. In

addition to varying content delivery according to a visitor’s profile, you can also change it according to the date,

the time of day, and other conditions. You can specify business rules by selecting Targeting > Content Targeters

in the ACC.

Merchandising Capabilities

The Pioneer Cycling Store demonstrates many new techniques for enhancing the merchandising capabilities of

an online store. ATG Consumer Commerce allows products to be combined in innovative ways. Using the ACC,

you can organize products in many categories, and group them across categories.

You can give any product in the store a list of specific products to cross-sell or a group of related products. For

example, a branded water bottle may be categorized in both Accessories > Drink Holders and Brand X Products

> Bottles, a member of the product group called “Summer Products,” and cross-sold with a sports drink.

The merchandising powers of business users are also expanded using scenarios. Scenarios allow you to offer

discounts, suggest products, or display incentives to customers according to their actions on the site. For

example, a scenario may track when a customer adds Product A to the shopping cart, then give the customer a

50% discount on Product B. Scenarios can offer different incentives depending upon the user’s profile, the day

of the week, or chance operations. Scenarios maintain their state between site visits and are valuable tools for

designing a complete customer experience.

The Pioneer Cycling store also offers customers enhanced merchandising through coupons, gift certificates, and

wish lists.

Page 17: At g Cons Comm Ref Guide

2 Business Users’ Overview 11

Processing Orders in Pioneer Cycling

We implemented three distinct ordering processes in the Pioneer Cycling store.

• “Basic checkout” is useful for sites that do not employ customer registration and require only simple shipping

and payment information.

• “Full checkout” is appropriate for sites that offer multiple shipping addresses, purchases from gift registries,

and customer address books.

• “Express checkout” allows repeat customers to complete orders without entering any new information each

time they check out. Orders are placed with shipping and billing information saved in their profiles.

Refer to the Order Processing (page 135) chapter for more information on checkout.

Pricing in Pioneer Cycling

In the Pioneer Cycling store, we implemented a flexible pricing engine that can be easily manipulated by the

business user. The pricing engine applies a number of pricing models to a particular cost such as item cost,

shipping cost, or tax cost. The store generates a price for an object and then applies the pricing models against

this list price. You can specify the list price and the pricing models in the ACC. For example, we specified that

Pioneer Cycling customers who order over $400 of merchandise receive free shipping. The pricing engine can

apply this model and other restrictions and promotions to calculate list, item, order, shipping, and tax costs

dynamically.

Each SKU in the store has a list price and a sale price. When an item is flagged “on sale,” its sale price becomes

active. Each SKU also has a cost price field so that you can analyze cost margins if desired.

Gross modifications to item prices can be made using “global promotions.” Global promotions are given to

every customer on the store, and provide a flexible method for modifying pricing with any rule that can be

expressed as a promotion. For example, every product in the store could be marked down by 2%, or, all items in

a category could be $5 off in September. In addition, the store catalog shows customers the difference between

the current price and the list price.

Entire orders may be priced with the same rules. Shipping costs can be similarly discounted. For example, you

can offer a shipping discount on orders above a certain dollar amount, or free shipping, depending upon the

contents of the order.

Refer to the Merchandising (page 193) chapter for more information.

Page 18: At g Cons Comm Ref Guide

12 2 Business Users’ Overview

Page 19: At g Cons Comm Ref Guide

3 Profile Management 13

3 Profile Management

This chapter describes how the Pioneer Cycling Site collects, stores, presents, and retrieves

customer data. It includes the following sections:

User Profiles and Pages (page 13)

Describes the classes of users who visit the site and the pages we used to manage their

profile information.

Login and Registration Page (page 14)

Describes how the Login and Registration pages were customized in the Pioneer Cycling site.

My Profile page (page 26)

Demonstrates how customers can modify their own personal information.

Address Book page (page 29)

Describes how customers can create, edit, and delete billing and shipping addresses.

Contact Customer Service page (page 37)

Describes how we created a customer feedback page.

Credit Card Page (page 39)

Describes how customers can enter and save credit card information.

Viewing and Searching History Page (page 44)

Describes how we created a page to record the categories and products recently viewed and

searched by the customer.

Profile Repository Extensions (page 51)

Describes the extensions made to ATG Consumer Commerce and DPS for the Pioneer Cycling

store site.

User Profiles and Pages

Users

There are three classes of users who access the Pioneer Cycling bike store:

• Anonymous users, about whom the site has no information

• Members, who have registered on the site

• Administrators, who have privileged access to information on the site

Page 20: At g Cons Comm Ref Guide

14 3 Profile Management

When anonymous users visit the site, they can browse the catalog and other pages without providing any

information, or they can register as members through a registration page. Returning registered members can

log in with their user names and passwords, or have cookies remember their login information. Once they log

in, members can access the My Profile page that allows them to maintain personal and commerce profiles and

to track their order information. Certain features or areas of the store require membership. For example, the

Advanced Checkout process requires a user to be a member and logged in.

Administrators can view and manipulate user profile data using the Profile interface in the ACC. This interface

enables them to search, create, edit, duplicate, and delete user profiles. Customers orders can be administered

using ATG Commerce Assist, which is described in the ATG Commerce Assist User Guide.

Managing Profiles

We used several pages to manage user profile information in the Pioneer Cycling site. We customized the user

profile management in the Pioneer Store so that customers can:

• Define multiple shipping addresses in an address book. Each address is identified by a nickname and can be

copied to the default shipping address.

• Store information for multiple credit cards, identified by nicknames, to select during the checkout process.

• See a summary list of products and product categories they recently viewed, with a hyperlink to each product

or category page.

• See a summary of recent searches; each search string is presented as a hyperlink that carries out the search if

invoked.

• Send e-mail to the customer service group at Pioneer Cycling.

• View information on the kind of products they prefer based on their past orders.

Login and Registration Page

You can view the JSP code for the login and registration process in the <ATG9dir>/PioneerCyclingJSP/

j2ee-apps/pioneer/web-app/en/user directory.

Login

We used my_profile.jsp to log in users. The content of the page depends on whether or not the user is

logged in, based on the transient property of the /atg/userprofiling/Profile servlet bean.

Note: The “. . .” markers in the following code sample indicate a place where code has been removed to clarify

the sample.

<dsp:importbean bean="/atg/userprofiling/Profile"/>. . .<dsp:droplet name="/atg/dynamo/droplet/Switch"> <dsp:param bean="Profile.transient" name="value"/> <dsp:oparam name="true"> <dsp:include page="LoginFragment.jsp" flush="true"></dsp:include> </dsp:oparam> <dsp:oparam name="false">

Page 21: At g Cons Comm Ref Guide

3 Profile Management 15

<dsp:include page="ProfileFragment.jsp" flush="true"></dsp:include> </dsp:oparam></dsp:droplet>

If the transient property is true, the user is not logged in, andLoginFragment.jsp is displayed, inviting him

to do so:

LoginFragment.jsp

The following is the portion of LoginFragment.jsp that we used to generate the login form.

<dsp:form action="my_profile.jsp" method="post"><!-- If there is a problem with the login, return to this page to display error msgs --><dsp:input bean="B2CProfileFormHandler.loginErrorURL" beanvalue="/OriginatingRequest.requestURI" type="hidden"/><!-- always display empty username/login fields to our visitor --><dsp:input bean="B2CProfileFormHandler.extractDefaultValuesFromProfile" type="hidden" value="false"/> <table> <tr> <td>Username</td><td><dsp:input bean="B2CProfileFormHandler.value.login"

Page 22: At g Cons Comm Ref Guide

16 3 Profile Management

size="10" type="text"/></td> </tr> <tr> <td>Password</td><td><dsp:input bean="B2CProfileFormHandler.value.password" size="10" type="password"/></td> </tr> </table> <p><dsp:input bean="B2CProfileFormHandler.login" type="submit" value="Log in"/></dsp:form>

We created a new component located at /atg/userprofiling/B2CProfileFormHandler.

(See the Working withForms and Form Handlers chapter in the ATG Programming

Guide for more information on ProfileFormHandler. This component is an

instance of the atg.projects.b2cstore.B2CProfileFormHandler class, which

extends theatg.commerce.profile.CommerceProfileFormHandler, a subclass

ofatg.userprofiling.ProfileFormHandler. (Most often, a web application would just keep the location of

the form handler for the profile in the original location /atg/userprofiling/ProfileFormHandler because

it is easy to find. You can move a component to any location but it is easiest to keep the standard components in

the standard places for easier site maintenance.)

The login process itself is not customized; the login form associates its input tags with the value property of the

handler, and the Log in button invokes thehandleLogin() method to log in the user.

Clicking on the Log in button always brings the user back to my_profile.jsp. If the login attempt is

unsuccessful, error messages are displayed in LoginFragment.jsp; otherwise,ProfileFragment.jsp is

displayed.

If the user is not registered, he is invited to do so with a Become a member link to register.jsp.

Registration

The registration process in the Pioneer Store collects the information that is stored in the user’s profile. Below is

the portion of register.jsp that produces the registration form.

Like most forms in our store, the form submits to itself as directed by the form tag’s action attribute. The form

handler method on the server side redirects to the appropriate URL (indicated by the createSuccessUrl

property) if the creation is successful. The comments interspersed in the JSP code below explain how the various

form fields are used.

<!-- Assume registration failure --> <dsp:getvalueof id="form80" bean="/OriginatingRequest.requestURI" idtype="java.lang.String"><dsp:form action="<%=form80%>" method="POST">

<!-- Don't assign a new repository id during registration - keep transient one: This is particular crucial because we store this ID in the ownerId field of the addresses (see below). --> <dsp:input bean="B2CProfileFormHandler.createNewUser" type="hidden" value="false"/>

<!-- If registration succeeds, go to my profile page and welcome new customer there --> <dsp:input bean="B2CProfileFormHandler.createSuccessURL" type="hidden" value="my_profile.jsp"/>

Page 23: At g Cons Comm Ref Guide

3 Profile Management 17

<input type="hidden" name="first_time" value="yes">

<!-- User must confirm password when registering --> <dsp:input bean="B2CProfileFormHandler.confirmPassword" type="hidden" value="true"/>

<!-- A registered customer is a member --> <dsp:input bean="B2CProfileFormHandler.value.member" type="hidden" value="true"/>

<!-- Need to set Registration Date -->

<table cellspacing=0 cellpadding=0 border=0>

<!-- Setup gutter and make space --> <tr> <td width=30%><dsp:img height="1" width="100" src="../images/d.gif"/><br></td> <td>&nbsp;&nbsp;</td> <td><dsp:img height="1" width="300" src="../images/d.gif"/></td> </tr>

<tr valign=top> <td width=30%> <dsp:droplet name="Switch"> <dsp:param bean="B2CProfileFormHandler.formError" name="value"/> <dsp:oparam name="true"> <span class=registrationerror> <span class=help>There were problems with your registration:</span><p> <UL> <dsp:droplet name="ProfileErrorMessageForEach"> <dsp:param bean="B2CProfileFormHandler.formExceptions" name="exceptions"/> <dsp:oparam name="output"> <LI> <dsp:valueof param="message"/> </dsp:oparam> </dsp:droplet> </UL> </span> </dsp:oparam> </dsp:droplet> </td> <td></td> <td> <!-- 1. I am: --> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-profile>&nbsp;Me</td></tr> </table> <p> Name (first, middle, last)<br> <dsp:input bean="B2CProfileFormHandler.value.firstName" maxlength="40" size="15" type="text" required="<%=true%>"/> <dsp:input bean="B2CProfileFormHandler.value.middleName" maxlength="40" size="10" type="text"/> <dsp:input bean="B2CProfileFormHandler.value.lastName" maxlength="40" size="15" type="text" required="<%=true%>"/> <p> Email address <br>

Page 24: At g Cons Comm Ref Guide

18 3 Profile Management

<dsp:input bean="B2CProfileFormHandler.value.email" maxlength="30" size="30" type="text"/> <p> Daytime telephone<br> <dsp:input bean="B2CProfileFormHandler.value.daytimeTelephoneNumber" maxlength="20" size="20" type="text"/><br> <p>&nbsp;<br> </td> </tr>

<tr valign=top> <!-- 2. Billing/Main Address --> <td width=30%> <span class=help>Please provide us with the address where you wish to be billed.<p> You may use this same address for shipping, or enter a separate shipping address below.</span> </td> <td></td> <td> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-profile>&nbsp;My billing address</td></tr> </table> <p> Street address <br>

<!-- Store the ProfileID in ownerId field of the address. This tells us this address "belongs to" (and can be edited) by the user. --> <dsp:input bean="B2CProfileFormHandler.value.billingAddress.ownerId" beanvalue="Profile.id" type="hidden"/>

<dsp:input bean="B2CProfileFormHandler.value.billingAddress.address1" maxlength="30" size="40" type="text" required="<%=true%>"/><br> <dsp:input bean="B2CProfileFormHandler.value.billingAddress.address2" maxlength="30" size="40" type="text"/><br> <table cellpadding=0 cellspacing=0> <tr> <td>City<br> <dsp:input bean="B2CProfileFormHandler.value.billingAddress.city" maxlength="30" size="20" type="text" required="<%=true%>"/></td> <td>&nbsp;State<br> &nbsp; <dsp:select bean="B2CProfileFormHandler.value.billingAddress.state"> <%@ include file="StatePicker.jspf" %> </dsp:select> </td> <td>&nbsp;Postal Code<br> &nbsp; <dsp:input bean="B2CProfileFormHandler.value.billingAddress.postalCode" maxlength="10" size="10" type="text" required="<%=true%>"/></td> </tr> </table> <table cellpadding=0 cellspacing=0> <tr> <td>Country<br> <dsp:select bean="B2CProfileFormHandler.value.billingAddress.country" required="<%=true%>"> <%@ include file="CountryPicker.jspf" %>

Page 25: At g Cons Comm Ref Guide

3 Profile Management 19

</dsp:select> </td> </tr> <tr> <td>Telephone<br> <dsp:input bean="B2CProfileFormHandler.value.billingAddress.phoneNumber" maxlength="15" size="15" type="text"/></td> </tr> </table> <p>&nbsp;<br> </td> </tr>

<tr valign=top> <!-- 3. Ship it here: --> <td width=30%> <span class=help> If you know that you will want to ship products to a different address, then you may enter an alternate shipping address here. <p>If you will always ship products to your billing address, then indicate that here. </span> </td> <td></td> <td> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-profile>&nbsp;My main shipping address</td></tr> </table> <p>

<dsp:input bean="B2CProfileFormHandler.shipToBillingAddress" type="radio" checked="<%=true%>" value="true"/> Use billing address for shipping<br> <dsp:input bean="B2CProfileFormHandler.shipToBillingAddress" type="radio" value="false"/> Use the new address below for shipping <p> Street address <br>

<!-- Store the ProfileID in ownerId field of the address. This tells us this address "belongs to" (and can be edited) by the user. --> <dsp:input bean="B2CProfileFormHandler.value.shippingAddress.ownerId" beanvalue="Profile.id" type="hidden"/>

<dsp:input bean="B2CProfileFormHandler.value.shippingAddress.address1" maxlength="30" size="40" type="text"/><br> <dsp:input bean="B2CProfileFormHandler.value.shippingAddress.address2" maxlength="30" size="40" type="text"/><br> <table cellpadding=0 cellspacing=0> <tr> <td>City<br> <dsp:input bean="B2CProfileFormHandler.value.shippingAddress.city" maxlength="30" size="20" type="text"/></td> <td>&nbsp;State<br> &nbsp;<dsp:select bean="B2CProfileFormHandler.value.shippingAddress.state"> <%@ include file="StatePicker.jspf" %>

Page 26: At g Cons Comm Ref Guide

20 3 Profile Management

</dsp:select> </td> <td>&nbsp;Postal Code<br> &nbsp;<dsp:input bean="B2CProfileFormHandler.value.shippingAddress.postalCode" maxlength="10" size="10" type="text"/></td> </table> Country<br> <dsp:select bean="B2CProfileFormHandler.value.shippingAddress.country" required="<%=true%>"> <%@ include file="CountryPicker.jspf" %> </dsp:select> <p>&nbsp;<br> </td> </tr>

<tr valign=top> <!-- 4. Username/password: --> <td width=30%> <span class=help>When you return to the site, log in with this username and password to manage your account information.</span> <p> </td> <td></td> <td> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-profile>&nbsp;My profile</td></tr> </table> <p> Choose a username <span class=help>( i.e. 'joeshmoe22' )</span><br> <dsp:input bean="B2CProfileFormHandler.value.login" maxlength="20" size="15" type="text"/> <p> Choose a password <span class=help>( i.e. 'pA$$weRD22' )</span><br> <dsp:input bean="B2CProfileFormHandler.value.password" maxlength="35" size="15" type="password"/><br> Enter password again to be sure you have it right<br> <dsp:input bean="B2CProfileFormHandler.value.confirmPassword" maxlength="35" size="15" type="password"/> <p>&nbsp;<br> </td> </tr>

<tr valign=top> <!-- 5. Optional Personal Data: --> <td width=30%> <span class=help>These questions are optional, but it's nice if you tell us.</span> </td> <td></td> <td> <%@ include file="../common/UserPreferencesFormFragment.jspf" %> </td>

<tr valign=top> <!-- 6. Submit: --> <td width=30%> <td></td> <td>

Page 27: At g Cons Comm Ref Guide

3 Profile Management 21

<table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-profile>&nbsp;All done?</td></tr> </table> <p> <p>&nbsp;<br>

<!-- Submit form to handleCreate() handler --> <dsp:input bean="B2CProfileFormHandler.create" type="submit" value=" Register --> "/> <p> &nbsp;<br> </td>

</tr> </table></dsp:form></dsp:getvalueof>

The User Profile

The traits of the user profile are defined in the DPS, ATG Consumer Commerce, and the Pioneer Store. Some

input fields (first name, last name and several address fields) in the form have the required attribute set to

true in the input tag. This means that the ProfileFormHandler ensures that these fields are filled in before

performing the registration. If the user submits the form with any of those fields blank, the form handler

generates errors that are reported to the user via the formExceptions property. You can make fields required

by editing the JSP file. There are two additional required fields, login and password. Unlike the first name and

last name fields, which are important at an application level, the fields login and password are needed for the

user profiling system to function and so they are set as required in the repository definition file for DPS. If the

user fails to fill in login or password before submitting the form, the result from the user’s point of view is the

same as leaving the last name blank.

Page 28: At g Cons Comm Ref Guide

22 3 Profile Management

top half of register.jsp

Page 29: At g Cons Comm Ref Guide

3 Profile Management 23

bottom half of register.jsp

Profile Trait Defined In

firstName DPS1

middleName DPS

lastName DPS1

email DPS

login DPS2

password DPS2

Page 30: At g Cons Comm Ref Guide

24 3 Profile Management

Profile Trait Defined In

gender DPS

dateOfBirth DPS

daytimeTelephoneNumber ATG Commerce

size Pioneer Store

1 required in the input form tags

2 defined as required in the userProfile.xml in the DPS config layer

The input tags that collect these properties are tied to the value property of the ProfileFormHandler

component; for example, the firstName property is collected with: <dsp:input

bean="B2CProfileFormHandler.value.firstName" type="text" required="<%=true%>"/>

The user profile also defines three user addresses:

Profile Trait Defined in

homeAddress DPS

billingAddress ATG Commerce

shippingAddress ATG Commerce

The Pioneer Store does not use the homeAddress, but collects the billingAddress and shippingAddress.

These addresses are collected using the value property; for example, the address1 field is collected with the

tag: <dsp:input bean="B2CProfileFormHandler.value.billingAddress.address1" type="text"

required="<%=true%>"/>

The following fields are collected for each address (some are marked as required in the input tag):

Address Field Collected? Required?

firstName no yes*

middleName no yes*

lastName no yes*

address1 yes yes

address2 yes no

city yes yes

Page 31: At g Cons Comm Ref Guide

3 Profile Management 25

Address Field Collected? Required?

state yes no

postalCode yes yes

country yes yes

telephone sometimes no

*These three fields are copied from the user’s profile when the address is created during registration. They can

be modified later when the user edits the billing or shipping address. (The telephone is collected for the billing

address, but not for the shipping address.)

Customized Registration

The value property of the ProfileFormHandler, defined as a java.util.Map, collects registration data.

The B2CProfileFormHandler class adds a second map, editValue, which stores customized profiling fields.

This map holds data addresses that go into the property secondaryAddresses. That property is a map, which

requires all values to be stored with a key. The key into seondaryAddresses is the “nickname” which the user

enters, so the form handler takes the editValue from the form, creates an address from it, and stores it under

the nickname provided by the user.

We used the handleCreateUser() method in the ProfileFormHandler class to create a new user. It

invokes two other methods. Before doing anything, it invokes preCreateUser(); before exiting, it invokes

postCreateUser(). Both these methods can be extended to provide additional functionality.

The B2CProfileFormHandler class defines a preCreateUser() method that copies the firstName,

middleName, and lastName fields in the user profile to the billing and shipping addresses, so that the user only

has to enter them once during the registration process. The user can modify these addresses to use a different

name.

The addressProperties property of the B2CprofileFormHandler defines the address properties that are to

be copied. This value may be configured in the properties file, and defaults to:

firstName, middleName, lastName,address1, address2city, state, postalCode,country, ownerId

The B2CProfileFormHandler class also defines a postCreateUser() method that:

1. Sets the registration date to “now.” (We found this method to be very efficient. Setting the date using a

scenario is less efficient but more flexible.)

2. Sets the value of profile.billingAddress to the shipping address if the user checks the

shipToBillingAddress with a radio button in the registration form.

Page 32: At g Cons Comm Ref Guide

26 3 Profile Management

My Profile page

The My Profile page (my_profile.jsp) allows site customers to modify their own profiles (for example, update

e-mail addresses). In addition, customers can monitor their own orders and check on the status of or cancel an

order.

Page 33: At g Cons Comm Ref Guide

3 Profile Management 27

my_profile.jsp

The My Profile page enables a member to edit her personal information. It is divided into three functional areas:

• My personal profile maintains personal contact information.

• My commerce profile holds information about a customer’s shopping experiences on this site, including

shopping cart information, alternate shipping addresses, saved credit cards, lists of recently viewed items, and

the customer’s product preferences.

• My orders lists the number of orders the customer has placed and how many of them have been shipped.

The bulk of the My Profile page is static html text and links. The section under “My Personal Profile” has

dynamic content that is rendered by the file ProfileFragment.jsp a portion of which is shown below. First,

the personal profile information is displayed using dsp:valueof tags for several properties of the /atg/

userprofiling/Profile component. Second, there is a static link to edit my profile and a Logout button.

The Logout button is a very simple form that submits to the B2CProfileFormHandlerhandleLogout method.

We simply provided a URL to which the form handler redirects the user upon success.

A portion of ProfileFragment.jsp:

<table width=100% cellpadding=0 cellspacing=0 border=0><tr><td class=box-top-profile>My personal profile</td></tr></table><p><dsp:valueof bean="Profile.firstName"/><dsp:valueof bean="Profile.middleName"/><dsp:valueof bean="Profile.lastName"/><br>My birthday is <dsp:valueof bean="Profile.dateOfBirth" date="MMMM d, yyyy">??</dsp:valueof>.<br><dsp:valueof bean="Profile.email"/><br>Member since <dsp:valueof bean="Profile.registrationDate" date="MMMM d, yyyy">January 1, 1900</dsp:valueof>.<p>> <dsp:a href="edit_profile.jsp">edit my profile</dsp:a><br>> <dsp:a href="change_password.jsp">change my password</dsp:a><br><p><dsp:form action="../store_home.jsp"> <dsp:input bean="/atg/userprofiling/B2CProfileFormHandler.logoutSuccessURL" beanvalue="/OriginatingRequest.requestURI" type="HIDDEN"/> <dsp:input bean="/atg/userprofiling/B2CProfileFormHandler.logout" type="submit" value="Logout"/></dsp:form>

We extended the userProfile.xml file in the Pioneer Cycling configuration layer to add additional attributes

to the profile definition. We extended the userProfile repository definition file to add the following

properties to the user item-descriptor:

• Clothing size

• Weight preference

• Price preference

• Number of orders

• Cumulative order amount

Page 34: At g Cons Comm Ref Guide

28 3 Profile Management

• Bikes owned

• User keywords

• Items bought

• Categories viewed recently

• Products viewed recently

• Recent user searches

The SQL Repository Architecture chapter in the ATG Repository Guide explains the XML definition language syntax.

PioneerCycling/config/atg/userprofiling/userProfile.xml

<gsa-template xml-combine="append">

<header> <name>Bikestore Related Profile Changes</name> <author>DCS Team</author> <version>$Id: userProfile.xml,v 1.35 2000/07/17 18:14:03 astreb Exp$</version> </header>

<item-descriptor name="user"> <table name="b2c_user" type="auxiliary" id-column-name="id"> <property category="Pioneer Cycling" name="size" data-type="enumerated" default="unknown" column-name="clothing_size" display-name="Clothing size"> <attribute name="useCodeForValue" value="false"/> <option value="unknown" code="0"/> <option value="extra small" code="1"/> <option value="small" code="2"/> <option value="medium" code="3"/> <option value="large" code="4"/> <option value="extra large" code="5"/> </property> <property category="Pioneer Cycling" name="weightPreference" data-type="int" column-name="weight_preference" display-name="Weight preference"/> <property category="Pioneer Cycling" name="pricePreference" data-type="int" column-name="price_preference" display-name="Price preference"/> <property category="Pioneer Cycling" name="numOrders" display-name="Number of orders" data-type="int" column-name="num_orders" default="0"/> <property category="Pioneer Cycling" name="cumulativeOrderAmount" display-name="Cumulative order amount" data-type="double" column-name="cum_order_amt" default="0"/> </table> <table name="b2c_bike_owned" type="multi" id-column-name="id" multi-column-name="sequence_num"> <property category="Pioneer Cycling" name="bikesYouOwn" data-type="list" component-data-type="String" column-name="bike" display-name="Bikes owned"/> </table>

Page 35: At g Cons Comm Ref Guide

3 Profile Management 29

<table name="b2c_user_keyword" type="multi" id-column-name="id" multi-column-name="sequence_num"> <property category="Pioneer Cycling" name="userKeywords" data-type="list" component-data-type="String" column-name="keyword" display-name="User keywords"/> </table> <table name="b2c_item_bought" type="multi" id-column-name="id" multi-column-name="sequence_num"> <property category="Pioneer Cycling" name="itemsBought" data-type="list" component-item-type="sku" repository="/atg/commerce/catalog/ProductCatalog" column-name="item" display-name="Items bought"/> </table> <!-- The following transient properties are used to record categories & pages viewed, and recent searches --> <property category="Pioneer Cycling" name="categoriesViewed" display-name="Categories viewed recently" data-type="set" component-data-type="String" /> <property category="Pioneer Cycling" name="productsViewed" display-name="Products viewed recently" data-type="set" component-data-type="String" /> <property category="Pioneer Cycling" name="recentSearches" display-name="Recent user searches" data-type="set" component-data-type="String"/>

</item-descriptor>

</gsa-template>

Address Book page

The Address Book page allows members to update the addresses associated with their profiles by:

• Editing billing address

• Creating and editing alternate shipping addresses

• Copying an alternate shipping address to the default shipping address

Edit Billing Address

The Address Book page starts with a link to edit_billing_address.jsp. This page simply contains a form

that invokes the handleUpdateUser() handler. There is no customization.

Page 36: At g Cons Comm Ref Guide

30 3 Profile Management

Managing Alternate Shipping Addresses

The Address Book page lists all of the alternate shipping addresses the registered user has defined. Each address

is identified by a nickname, which is the key to the address in the secondaryAddresses map property of the

user’s profile.

Creating an Alternate Shipping Address

To create an alternate shipping address, a registered member clicks on the add new address link to go to

new_shipping_address.jsp, where a form collects the address fields and the nickname for the new address.

The form’s input tags are associated with the editValue property of the B2CProfileFormHandler; the Add

Address button invokes the handleNewAddress() handler as demonstrated in the following example.

Note: The “. . .” markers in the following code sample indicate a place where code has been removed to clarify

the sample.

<dsp:form action="address_book.jsp" method="POST">

. . . Give this address a nickname for your address book:<br> <dsp:input bean="B2CProfileFormHandler.editValue.nickname" maxlength="40" size="20" type="text"/> <p>

<!-- Store the ProfileID in ownerId field of the new address.

Page 37: At g Cons Comm Ref Guide

3 Profile Management 31

This tells us this address "belongs to" (and can be edited) by the user. --> <dsp:input bean="B2CProfileFormHandler.editValue.ownerId" beanvalue="Profile.id" type="hidden"/>

Name<br> <dsp:input bean="B2CProfileFormHandler.editValue.firstName" maxlength="100" size="15" type="text" required="<%=true%>"/> <dsp:input bean="B2CProfileFormHandler.editValue.middleName" maxlength="100" size="10" type="text"/> <dsp:input bean="B2CProfileFormHandler.editValue.lastName" maxlength="100" size="15" type="text" required="<%=true%>"/><br> Street address <br> <dsp:input bean="B2CProfileFormHandler.editValue.address1" maxlength="30" size="40" type="text" required="<%=true%>"/><br> <dsp:input bean="B2CProfileFormHandler.editValue.address2" maxlength="30" size="40" type="text"/><br> <table cellpadding=0 cellspacing=0> <tr> <td>City<br> <dsp:input bean="B2CProfileFormHandler.editValue.city" maxlength="30" size="20" type="text" required="<%=true%>"/></td> <td>State<br> <dsp:select bean="B2CProfileFormHandler.editValue.state"> <%@ include file="StatePicker.jspf" %> </dsp:select> </td> <td>Postal Code<br> <dsp:input bean="B2CProfileFormHandler.editValue.postalCode" maxlength="10" size="10" type="text" required="<%=true%>"/></td> </tr> </table> Country<br> <dsp:select bean="B2CProfileFormHandler.editValue.country" required="<%=true%>"> <%@ include file="CountryPicker.jspf" %> </dsp:select><br> <p>&nbsp; </td></tr><tr valign=top> <!-- 6. Submit: --> <td></td> <td></td> <td> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-profile>&nbsp;Done?</td></tr> </table> <p>&nbsp;<br> <!-- Submit form to handleUpdate() handler --> <dsp:input bean="B2CProfileFormHandler.newAddress" type="submit" value=" Add Address --> "/> . . .

Page 38: At g Cons Comm Ref Guide

32 3 Profile Management

new_shipping_address.jsp

The handleNewAddress() handler creates a new address in the secondaryAddresses map, indexed with the

provided nickname, and sets its properties based on the entries in the editValue map property:

MutableRepository repository = (MutableRepository) profile.getRepository(); Map secondaryAddresses = (Map) profile.getPropertyValue("secondaryAddresses");

// What address fields are we looking for String[] addressProperties = getAddressProperties();

// Get editValue map, containing the user form data

Page 39: At g Cons Comm Ref Guide

3 Profile Management 33

HashMap newAddress = (HashMap) getEditValue(); String nickname = (String) newAddress.get("nickname");

// If we successfully collected all needed user input, create a new address try { // Create a repository item to be filled with stuff. MutableRepositoryItem address = repository.createItem("contactInfo");

// Copy values from the newAddress object Object property; for (int i = 0; i < addressProperties.length; i++) { property = newAddress.get(addressProperties[i]); if (property != null) address.setPropertyValue(addressProperties[i], property); }

// Update adress into the db and insert into secondaryAddresses map repository.addItem(address); secondaryAddresses.put(nickname, address);

Editing an Alternate Shipping Address

The list of alternate shipping addresses in the Address Book page provides a link to the Edit Shipping Address

page, where the member can modify the address or its nickname.

Page 40: At g Cons Comm Ref Guide

34 3 Profile Management

edit_shipping_address.jsp

There are two forms on this page. The first invokes handleChangeNickname() to change the nickname

of the address, the second updates the address data with changes entered by the user by invoking either

handleUpdateAddress() or handleUpdateAddressAndMakeDefault(). The first only updates the address;

the second updates the address and copies it to the default shipping address.

The operation of the upper form for changing the nickname of an address depends on two fields. One field is

hidden and pre-populated with the old nickname of the address. The form handler uses this value to determine

which address is being edited. The second form, which changes the contents of the address, also starts with the

nickname in a hidden field for the same reason.

Page 41: At g Cons Comm Ref Guide

3 Profile Management 35

Because the B2CProfileFormHandler is session scoped, you could create a property in the form handler in

both cases and set it to the value of the address being edited when the user enters the form to edit the shipping

address. There is no real benefit to one way or the other, but the way we implemented it in Pioneer Cycling

would also work for a request scoped form handler.

Note: The “. . .” markers in the following code sample indicate a place where code has been removed to clarify

the sample.

<dsp:form action="address_book.jsp" method="POST">. . . <dsp:input bean="B2CProfileFormHandler.editValue.nickname" type="hidden"/> <dsp:input bean="B2CProfileFormHandler.editValue.newNickname" maxlength="40" size="20" type="text"/><!-- Submit form to handleChangeAddressNickname() handler --> <dsp:input bean="B2CProfileFormHandler.changeAddressNickname" type="submit" value="Change Nickname ->"/>. . .</dsp:form>. . .<dsp:form action="address_book.jsp" method="POST">. . . <dsp:input bean="B2CProfileFormHandler.editValue.nickname" type="hidden"/> Name<br> <dsp:input bean="B2CProfileFormHandler.editValue.firstName" maxlength="100" size="15" type="text" required="<%=true%>"/>. . . Street address <br> <dsp:input bean="B2CProfileFormHandler.editValue.address1" maxlength="30" size="40" type="text" required="<%=true%>"/><br>. . . <!-- Update Address --> <dsp:input bean="B2CProfileFormHandler.updateAddress" type="submit" value=" Update --"/>. . . <dsp:input bean="B2CProfileFormHandler.updateAddressAndMakeDefault" type="submit" value=" Update & Set As Default --"/>. . .

The editValue property must be initialized to the current value of that shipping address before the

Edit Shipping Address page is displayed to the member. The hyperlink in the Address Book page sets the

editAddress property of the ProfileFormHandler to the ID of the address to be edited:

<dsp:a bean="B2CProfileFormHandler.editAddress" href="edit_shipping_address.jsp" paramvalue="key">edit this</dsp:a>

The property has an associated handler, handleEditAddress(), that populates the editValue property with

the address data. It looks at the EditAddress property for the ID of the address to edit:

String nickname = getEditAddress();Map secondaryAddress = (Map) getProfile().getPropertyValue("secondaryAddresses");MutableRepositoryItem theAddress = (MutableRepositoryItem) secondaryAddress.get(nickname);Map edit = getEditValue();

Page 42: At g Cons Comm Ref Guide

36 3 Profile Management

// save nickname in editValue map –- the updateAddress handler will need it edit.put("nickname", nickname);// ...as will, perhaps, the changeAddressNickname handleredit.put("newNickname", nickname);String[] addressProps = getAddressProperties();// Store each property in the map, for use by edit_secondary_address.jspObject property;for (int i = 0; i < addressProps.length; i++) { property = theAddress.getPropertyValue(addressProps[i]); if (property != null) edit.put(addressProps[i], property);}

The member makes any changes to the address and invokes one of the three submit buttons (Change

Nickname, Update, or Update & Set As Default) to commit those changes.

Deleting an Alternate Shipping Address

Like editing an address, when the customer clicks the remove this hyperlink to delete an alternate shipping

address, the setRemoveAddress() method is passed the target address, and the handleRemoveAddress()

method does the actual removal.

The JSP sample below was taken from address_book.jsp and abbreviated for the purpose of demonstrating

how to delete an address using a link. The ForEach servlet bean iterates over the secondaryAddress property,

which is a java.util.Map. When ForEach iterates over a Map or Dictionary or Hashtable (or any multi-valued

object that contains keys and values), it binds the param:key to each key in the object in the very same way

that it binds element to each value in the object. In the case of the secondaryAddresses property, a link

is generated which, when clicked, sets the value of property removeAddress to the key or nickname of the

address and then invokes the method handleRemoveAddress.

<dsp:getvalueof id="requestURL" idtype="java.lang.String" bean="/OriginatingRequest.requestURI"><dsp:droplet name="ForEach"> <dsp:param bean="Profile.secondaryAddresses" name="array"/> <dsp:oparam name="output"> > <dsp:a bean="B2CProfileFormHandler.removeAddress" href="<%=requestURL%>" paramvalue="key">remove this</dsp:a> </dsp:oparam></dsp:droplet></dsp:getvalueof>

Note that all the handlers in the B2CprofileFormHandler class have associated success and error properties.

For example, the handleRemoveAddress() method redirects the site visitor to the page identified by

removeAddressSuccessURL() if the address is successfully removed, and to the page identified by

removeAddressErrorURL() if an error occurred during the removal process. In both cases, redirection only

occurs if the property has a non-null value otherwise this user is sent to the page defined by the action attribute.

Moving an Alternate Shipping Address to the Default Shipping Address

We used a dropdown menu to allow the member to select an alternate shipping address and move it to the

default shipping address. The site visitor cannot change the default shipping address on this page by entering

Page 43: At g Cons Comm Ref Guide

3 Profile Management 37

a new one; he must edit one of the alternate addresses and move it to the default using the Update & Set As

Default button.

The following JSP sample, taken fromaddress_book.jsp demonstrates how the form is created. We listed all

the secondaryAddresses and the user’s billingAddress in a select drop-down list in order to display all of

the addresses that the user could select as the default shipping address. The form submits back to the page it

resides on, address_book.jsp and invokes the method handleSelectDefaultAddress on the form handler

B2CProfileFormHandler.

<dsp:form action="address_book.jsp" method="POST"> Change it to be: <dsp:select bean="B2CProfileFormHandler.editValue.defaultAddressNickname"> <dsp:option value="My Billing Address"/>My Billing Address <dsp:droplet name="ForEach"> <dsp:param bean="Profile.secondaryAddresses" name="array"/> <dsp:oparam name="output"> <dsp:getvalueof id="addressId" param="key" idtype="java.lang.String"> <dsp:option value="<%=addressId%>"/> </dsp:getvalueof><dsp:valueof param="key"/> </dsp:oparam> </dsp:droplet> </dsp:select> <p> <dsp:input bean="B2CProfileFormHandler.selectDefaultAddress" type="submit" value="Change default"/></dsp:form>

Contact Customer Service page

This page allows the member to send e-mail to the Pioneer Cycling customer service group.

Page 44: At g Cons Comm Ref Guide

38 3 Profile Management

contact_customer_service.jsp

The e-mail is generated by the /atg/dynamo/service/EmailFormHandler component, which allows a page

designer to collect user form input, and to create and send an e-mail message. The EmailFormHandler, an

instance of the atg.service.email.EmailFormHandler class, normally collects the following fields: sender,

recipient, subject, and body.

It provides a handler, handleSendEmail(), which calls the protected method SendMail() to perform

the e-mail creation and sending steps. SendMail() was intended to be overridden by a derived

class. In the Pioneer Store, we redefined the EmailFormHandler component to be an instance of the

atg.projects.b2cstore.B2CEmailFormHandler class. This class defines the additional properties,

customerServiceEmailAddress and profile. It overrides SendMail() to set the sender property

to the firstNameMiddleNamelastName<emailAddress> (extracted from the site visitor’s profile)

and to set the recipient to the customerServiceEmailAddress property. (By default, this property is

[email protected].)

Page 45: At g Cons Comm Ref Guide

3 Profile Management 39

Note: e-mail is not actually sent unless the atg/dynamo/service/SMTPEmail component is configured with

an SMTP service. By default, it is not. To configure Pioneer Cycling to send e-mail, see the Setting up e-mail

section in the Introduction (page 1) chapter of this guide.

Credit Card Page

On this page, a member can define a list of named credit cards to be used during the checkout process.

As with other customized pages in the Pioneer Cycling store, we used the editValue map property to collect

the credit card information. The Add Card button invokes the handleCreateNewCreditCard() method,

which creates a credit card in the creditCards map property of the user profile using the information the

user enters. The card is identified by a nickname. If the customer does not provide a nickname, the system

Page 46: At g Cons Comm Ref Guide

40 3 Profile Management

automatically creates a nickname using an abbreviation for the credit card type appended by the credit card

number.

To look up the credit card type abbreviation, we used a ResourceBundle. The ResourceBundle is used for

internationalization and to map key/value pairs in a file.

The file B2CUserResources.properties is a java.util.ResourceBundle used by Java files specific to

Pioneer Cycling. A ResourceBundle basically consists of strings that should be used in the Java code and

user-friendly strings that map to each of those strings. For the credit card abbreviations, we use the actual

creditcard type name (MasterCard, Visa, American_Express, Discover) to look up the abbreviation. This

snippet from B2CUserResources.properties shows how the types and abbreviations of credit cards are

stored.

# CreditCard abbreviationsMasterCard=MCVisa=VISAAmerican_Express=AMEXDiscover=DISC

For security reasons, credit card numbers should never be displayed fully on the browser. To display credit cards

to the user on pages such as the My Saved Credit Cards page, we used a special credit card tag converter that

masks all digits but the last 4 with an ‘X’ or another character chosen by the site developer. (For example, a credit

card with the number 4111 1111 1111 1111 would be displayed as XXXX XXXX XXXX 1111.) The creditcard

converter is invoked in JSP by specifying a creditcard value to the converter attribute of the valueof tag as

shown here:

<dsp:valueof converter="creditcard" param="element.creditCardNumber"/>

We also used the converter to display the credit card nickname because, if it were generated automatically,

it would also contain the credit card number. In this case, the groupingsize property of the creditcard

converter is specified as zero so that the nickname is not divided into groups of four characters:

<dsp:valueof converter="creditcard" groupingsize="4" param="element.creditCardNumber"/>

These are highlights of the handleCreateNewCreditCard() method, stressing the most important sections:

//------------ Submit: CreateNewCreditCard ------------/** * Creates a new credit card using the entries entered in the editValue map * by the credit_card.jsp page * * @param pRequest the servlet's request * @param pResponse the servlet's response * @exception ServletException if there was an error while executing the code * @exception IOException if there was an error with servlet io */public boolean handleCreateNewCreditCard(DynamoHttpServletRequest pRequest, DynamoHttpServletResponse pResponse) throws ServletException, IOException{ Profile profile = getProfile(); MutableRepository repository = (MutableRepository) profile.getRepository(); Map creditCards = (Map) profile.getPropertyValue("creditCards"); ResourceBundle bundle = ResourceUtils.getBundle(RESOURCE_BUNDLE,

Page 47: At g Cons Comm Ref Guide

3 Profile Management 41

getLocale(pRequest));

// Identify credit card properties we need from user String[] cardProperties = getCardProperties();

// Get editValue map, containing the credit card properties HashMap newCard = (HashMap) getEditValue();

boolean isMissingField = false; // Verify all required fields entered before creating new card for (int i = 0; i < cardProperties.length; i++) { if (newCard.get(cardProperties[i]) == null || ((String) newCard.get(cardProperties[i])).length() == 0) { generateFormException(MSG_MISSING_CC_PROPERTY, cardProperties[i], pRequest); isMissingField = true; } } if (isMissingField) return true;

// Verify that card number and expiry date are valid if (!validateCreditCard(newCard, bundle)) { // form exceptions added by validateCreditCard method return true; }

// Check that the nickname is not already used for a credit card String cardNickname = (String) newCard.get("creditCardNickname"); if( creditCards.get(cardNickname) != null ) { String rawErrorStr = bundle.getString(MSG_DUPLICATE_CC_NICKNAME); String formattedErrorStr = (new MessageFormat(rawErrorStr)).format(new String[] {cardNickname}); addFormException(new DropletFormException(formattedErrorStr, new String(getAbsoluteName() + "editValue.creditCardNickname"), MSG_DUPLICATE_CC_NICKNAME)); return true; }

try { // Create a repository item to be filled with stuff. MutableRepositoryItem card = repository.createItem("credit-card");

// Copy values from the newCreditCard object for (int i = 0; i < cardProperties.length; i++) card.setPropertyValue(cardProperties[i], newCard.get(cardProperties[i]));

// Set billing address card.setPropertyValue("billingAddress", profile.getPropertyValue("billingAddress"));

// Insert card into the db repository.addItem(card);

// Insert the credit card into the creditCards map // Did the user provide us with a name?

Page 48: At g Cons Comm Ref Guide

42 3 Profile Management

String key = (String) newCard.get("creditCardNickname"); if (key == null || key.trim().length() == 0) { // If no name, generate unique key // Use the credit card type (convert any spaces to _ so as to find it in // the bundle StringBuffer buffer = new StringBuffer((String) card.getPropertyValue("creditCardType")); for (int i = 0; i < buffer.length(); i++) { if (buffer.charAt(i) == ' ') buffer.setCharAt(i, '_'); } // Generate the key as CARD-ABBREV + CardNumber String abbrev = bundle.getString(buffer.toString()); if (abbrev == null) abbrev = ""; key = abbrev + (String) card.getPropertyValue("creditCardNumber"); }

// Set the new credit card creditCards.put(key, card);

// empty out the map newCard.clear(); } catch (RepositoryException repositoryExc) { generateFormException(MSG_ERR_CREATING_CC, repositoryExc, pRequest); if (isLoggingError()) logError(repositoryExc);

return redirectIfPossible(getCreateCardErrorURL(), pRequest, pResponse); }

return redirectIfPossible(getCreateCardSuccessURL(), pRequest, pResponse);

}

Note that handleCreateNewCreditCard() calls the validateCreditCard() method, which does two

things. First, it checks that the credit number is composed solely of digits and spaces. Second, it checks that the

card has not expired.

validateCreditCard()

//------ Utility method: validate credit card number & expiration date ------/** * Validate the credit card number entered and the expiration date * (must be later than today). * * @param card A hashmap containing the user-entered credit card data * @param bundle A ResourceBundle providing the error message text * @return true if the credit card is valid **/protected boolean validateCreditCard(HashMap card, ResourceBundle bundle) {

try { // 1. Check that the credit card number is composed solely of digits and spaces String cardNumber = (String) card.get("creditCardNumber"); if (cardNumber == null) {

Page 49: At g Cons Comm Ref Guide

3 Profile Management 43

throw new B2CProfileException( MSG_INVALID_CC, "The card number is a required field for a credit card entry", "creditCardNumber"); }

for (int i = 0 ; i < cardNumber.length(); i++) { char c = cardNumber.charAt(i); if ((c < '0' || c > '9') && c != ' ') { throw new B2CProfileException( MSG_INVALID_CC, "The card number must consist only of digits and spaces", "creditCardNumber"); } }

// 2. Get expiration month & year int cardExpirationYear = 0; int cardExpirationMonth = 0;

java.util.Calendar now = java.util.Calendar.getInstance(); int year = now.get(Calendar.YEAR); // convert month from 0-11 to 1-12 int month = now.get(Calendar.MONTH) + 1; // Convert year and month to integer values try { cardExpirationYear = Integer.parseInt((String) card.get("expirationYear")); } catch (NumberFormatException exc) { throw new B2CProfileException(MSG_INVALID_CC, "The year of expiration must be specified", "expirationYear"); } try { cardExpirationMonth = Integer.parseInt((String) card.get("expirationMonth")); } catch (NumberFormatException exc) { throw new B2CProfileException(MSG_INVALID_CC, "The month of expiration must be specified", "expirationMonth"); }

// 3. Check that the card has not expired if (cardExpirationYear < year) { throw new B2CProfileException(MSG_INVALID_CC, "The card you entered is past its expiration date", "expirationYear"); } if (cardExpirationYear == year) { if (cardExpirationMonth < month) { throw new B2CProfileException(MSG_INVALID_CC, "The card you entered is past its expiration date", "expirationMonth"); } } return true; } catch (B2CProfileException exc) { String rawErrorStr = bundle.getString(exc.getCode()); String formattedErrorStr = exc.getMessage(); (new MessageFormat(rawErrorStr)).format(new String[] {exc.getMessage()}); addFormException(new DropletFormException(formattedErrorStr,

Page 50: At g Cons Comm Ref Guide

44 3 Profile Management

new String(getAbsoluteName() + "editValue." + exc.getDescription()), MSG_INVALID_CC)); return false; }}

You could easily override this method for more rigorous validation. You could check that certain credit card

types have a specific number of digits; for example, Visa cards have sixteen digits. In addition, all credit cards are

validated during the checkout process by the commerce pipeline. See the section on Additional Order Capture

Information (page 164) in the Order Processing (page 135) chapter for more information.

Viewing and Searching History Page

The customer can click on the things I recently looked at and for hyperlink in the My Commerce Profile section

of the My Profile page for a record of categories and products that she recently viewed and her recent searches

on the Pioneer Cycling site.

Page 51: At g Cons Comm Ref Guide

3 Profile Management 45

pages_viewed.jsp

Categories and Products Recently Browsed

In order to record the categories and products browsed by a customer in her profile during a given visit

to Pioneer Cycling, we added two transient properties to the visitor’s profile: categoriesViewed and

productsViewed. We defined these properties as transient to avoid collecting and storing extraneous

information in the database. They can be seen in the following XML excerpt from PioneerCycling/config/

atg/userprofiling/userProfile.xml.

Note: The “. . .” markers in the following code sample indicate a place where code has been removed to clarify

the sample.

<item-descriptor name="user"> . . . <!-- These transient properties used to record categories & pages viewed --> <property name="categoriesViewed" display-name="Categories viewed recently" data-type="set" component-data-type="String" />

Page 52: At g Cons Comm Ref Guide

46 3 Profile Management

<property name="productsViewed" display-name="Products viewed recently" data-type="set" component-data-type="String" /> . . .</item-descriptor>

To record the categories and products viewed by a member during a visit to the Pioneer Store site, we needed

to generate a DMS (Dynamo Messaging System) message each time a category or product is viewed, and

record that message in the member’s profile. We used the Commerce component /atg/commerce/catalog/

CategoryBrowsed, as shown below, to generate the DMS message when a category is viewed.

…<dsp:droplet name="/atg/commerce/catalog/CategoryLookup"> <dsp:param bean="/OriginatingRequest.requestLocale.locale" name="repositoryKey"/> <dsp:oparam name="output"> <%/* Send a Views Item event that we browsed this category */%> <dsp:droplet name="/atg/commerce/catalog/CategoryBrowsed"> <dsp:param name="eventobject" param="element"/> </dsp:droplet></dsp:droplet>…

CategoryBrowsed is invoked by the following pages:

catalog/category_all_bikes.jspcatalog/category_bikes.jspcatalog/category_bundle.jspcatalog/category_clothing.jspcatalog/category_generic.jspcatalog/category_parts.jsp

A DMS message is sent by the component /atg/commerce/catalog/ProductBrowsed when the site visitor

views a product. It is invoked as follows:

. . .<dsp:droplet name="/atg/commerce/catalog/ProductLookup"> <dsp:param bean="/OriginatingRequest.requestLocale.locale" name="repositoryKey"/> <dsp:param name="elementName" value="Product"/> <dsp:oparam name="output"> <%/* Add this product to the user's list of products browsed. */%> <dsp:droplet name="/atg/commerce/catalog/ProductBrowsed"> <dsp:param name="eventobject" param="Product"/> </dsp:droplet></dsp:droplet>. . .

ProductBrowsed is invoked by the following pages:

catalog/product_bike.jspcatalog/product_bundle.jspcatalog/product_generic.jspcatalog/product_gift_certificate.jsp

Page 53: At g Cons Comm Ref Guide

3 Profile Management 47

catalog/product_part.jsp

The messages generated by these components are received by the Scenario Server. We created a Pioneer

Cycling Store scenario named Browsing to accept ItemViewed messages. It acts on incoming messages by

updating the user’s profile accordingly as shown below:

The Browsing scenario shown in the ACC.

The DMS messages generated by CategoryBrowsed and ProductBrowsed are of type atg.dps.ViewItem.

The Patch Bay definition file, /atg/dynamo/messaging/dynamoMessagingSystem.xml is used to deliver

these messages to the Scenario Server. The components are defined as message sources in the Commerce

configuration layer, and the Scenario Server is defined as a message sink in the User Profiling configuration layer.

For more about scenarios, see the Creating Scenarios chapter in the ATG Personalization Guide for Business Users.

Items Recently Searched For

The Pioneer Store also keeps track of and displays recent member searches. The word or words of each search

are displayed as hyperlinks that bring the customer to the search results page, where a list of matching products

and categories is displayed. For example, if the customer searched for a helmet and a water bottle, the list of

hyperlinks would look like the screenshot below.

Page 54: At g Cons Comm Ref Guide

48 3 Profile Management

As in the case of recently viewed categories and products, we added a transient set property to the visitor’s

profile: recentSearches.

<item-descriptor name="user"> . . . <!-- This transient properties used to record recent user searches --> <property name="recentSearches" display-name="Recent user searches" data-type="set" component-data-type="String" /> . . .

This feature is supported by making modifications to the component /atg/commerce/catalog/

CatalogSearch. It is defined as an instance of atg.commerce.catalog.SearchFormHandler; in the Pioneer

Store, we extended this class with atg.projects.b2cstore.B2CSearchFormHandler, which:

• Adds a one-step search facility.

• Generates a DMS message of type atg.projects.b2cstore.search whenever a search is performed,

passing the search text as the body of the message.

One-step Search

The hyperlink brings the site visitor back to the Simple Search page (in the search subdirectory of the Pioneer

Store site); clicking on this link also causes the search to be carried out. We did this by adding a property to the

CatalogSearch component: oneStepSearch. The hyperlink sets this property to the recorded search string,

and the search is carried out by the handleOneStepSearch() handler prior to displaying the Simple Search

page.

Page 55: At g Cons Comm Ref Guide

3 Profile Management 49

SimpleSearch.jsp

Send DMS Message to Scenario Server

In the Pioneer Store, the CatalogSearch component, which performs all user searches, also sends the

search string to the Scenario Server as a DMS message. Sending the message is delegated to a component

that implements the atg.dms.patchbay.MessageSource interface. The Pioneer StoreCatalogSearch

component uses the /atg/commerce/catalog/SearchEventSender component to send its message. This

component is identified by the SearchEventSender property of the CatalogSearch.

The SearchEventSender component, an instance of atg.projects.b2cstore.SearchEventSender,

implements the MessageSource methods, as well as the fireSearchEvent() method, which issues a

message to the Scenario Server.

public boolean fireSearchEvent(String searchString){ // Send message notifying that a search has been performed

Page 56: At g Cons Comm Ref Guide

50 3 Profile Management

if (mStarted) { // if message source was successfully started try { SearchMessage search = new SearchMessage(); search.setSearchString(searchString); ObjectMessage msg = getMessageSourceContext().createObjectMessage(); msg.setObject(search); msg.setJMSType("atg.projects.b2cstore.search"); getMessageSourceContext().sendMessage(msg); } catch (JMSException jmse) { System.out.println("Error sending message recording search: " + jmse); } } return true;}

Tying all this together involves configuring the Patch Bay to deliver a message from the SearchEventSender

to the Scenario Server. A message type must be defined, SearchEventSender identified as a message source

for this kind of message, and the Scenario Server identified as a message sink. For more information on the Patch

Bay, refer to the chapter on the Dynamo Message System in the ATG Programming Guide. Here is an overview of

the Patch Bay configuration file provided by the Pioneer Store:

<ATG9dir>/atg/dynamo/messaging/dynamoMessagingSystem.xml

<dynamo-message-system> <patchbay> <message-source> <nucleus-name> /atg/commerce/catalog/SearchEventSender </nucleus-name> <output-port> <port-name> DEFAULT </port-name> <output-destination> <provider-name> local </provider-name> <destination-name> localdms:/local/DPSTopic/CatalogSearchEvents </destination-name> <destination-type> Topic </destination-type> </output-destination> </output-port> </message-source> <message-sink> <nucleus-name> /atg/scenario/ScenarioManager </nucleus-name> <input-port> <port-name> IndividualEvents </port-name> <input-destination> <provider-name> local </provider-name> <destination-name> localdms:/local/DPSTopic/CatalogSearchEvents </destination-name> <destination-type> Topic </destination-type> </input-destination> </input-port> </message-sink> </patchbay> <!-- local JMS definitions -->

Page 57: At g Cons Comm Ref Guide

3 Profile Management 51

<local-jms> <jndi-prefix>/local</jndi-prefix> <topic-name>/DPSTopic/CatalogSearchEvents</topic-name> </local-jms> <!-- message registry definitions --> <message-registry> <message-family> <message-family-name> atg </message-family-name> <message-type> <jms-type> atg.projects.b2cstore.search </jms-type> <message-class> atg.projects.b2cstore.SearchMessage </message-class> <message-context> request </message-context> <display-name> Searches the catalog </display-name> <description> Generated when user searches a catalog </description> </message-type> </message-family> </message-registry></dynamo-message-system>

Profile Repository Extensions

This section describes extensions made to ATG Consumer Commerce and DPS for the Pioneer Cycling store site.

DPS performs the collection, storage, and retrieval of data specific to individual users of the Web application. A

profile is the data stored for an individual customer based on forms she fills out or her actions on the site. Once

this data is collected, it provides Web site developers the ability to show the customer personalized products

and content based on this profile.

In the Pioneer Cycling site, we used personalization in a variety of ways to make a compelling shopping

experience. DPS provides basic personalization capability, but you can easily extend this functionality to fit the

needs of your Web application. The Pioneer Cycling site demonstrates several ways to extend this functionality.

Please refer to the Setting up a Profile Repository chapter in the ATG Personalization Programming Guide and

the SQL Content Repositories chapter in the ATG Repository Guide for more detailed information about the SQL

Repository definitions in DPS and Consumer Commerce.

DPS stores customer data in a repository, which consists of user item types and several supporting item types.

The default DPS repository contains item types defined with the set of properties that are needed by most

personalized Web applications. In ATG Consumer Commerce, some additional properties have been added to

the user item type to make possible many commerce features. Pioneer Cycling adds yet more item types and

properties to provide functionality that is specific to a bike store.

These extensions are defined in the XML file <ATG9dir>/atg/userprofiling/userProfile.xml. This file

is combined with the files of the same name from ATG Consumer Commerce, DSS, and DPS. These files are

combined per the rules of XML combination to produce one XML file that is then parsed and used by ATG to

describe the item types in the repository. (See the Nucleus: Organizing JavaBean Components chapter of the for

more information on XML file combination.) The combined file is reparsed each time the repository starts and it

is never written out to disk, so we only need to maintain the separate files, not the combined one.

Page 58: At g Cons Comm Ref Guide

52 3 Profile Management

The underlying storage of the user profile repository in Pioneer Cycling is a relational database. The SQL Profile

Repository is used to expose that data via the Repository API. In the XML file, we describe the mapping of

repository items to the relational database tables and columns.

Here are the properties we added to the user item type:

Property Description

size The customer’s clothing size. We use this property for targeting size-

appropriate clothing to the user.

weightPreference This integer value is incremented or decremented according to the weight

class of the items purchased by the customer. If the customer purchases

many lightweight items, he has a low value for weightPreference and

we would use that low value to target additional lightweight items to the

customer.

pricePreference This property works much like weightPreference. Products have, in

addition to the actual price, a price class that indicates if the product is

cheap, moderate, expensive, or luxury, as compared to similar products. If a

customer tends to buy cheap items, we can target our best bargains to that

customer. A customer that tends to purchase luxury items can be shown

more expensive items.

numOrders A count of the number of orders that this user has placed with the store. This

can be used as a measure of customer loyalty.

cumulativeOrderAmount A running total of the amount of money spent on the site. This can be used

for targeting promotions or identifying trends in the customer base. This

number represents the amount spent in the currency indicated by the

customer’s selected locale. If the user changes his locale from ja_JP to

en_US between purchases, then the total, a sum of yen and dollars, will not

be meaningful. We assume that a customer chooses a locale and sticks to it.

bikesOwned We use a scenario to keep track of the types of bikes a user owns so that we

can target compatible products to the user.

userKeywords A list of keywords the customer picks up based on his activities in the store.

This property’s value is populated by DSS. When a user purchases a product,

that product’s keywords are appended to the userKeywords. When a user

conducts a product search, the search term is added to the userKeywords.

In general, when a user expresses interest in an item, we store the keywords

for the item of interest so that we might use that information later to

personalize the user’s experience.

itemsBought A list of the IDs of items purchased by the user.

categoriesViewed This transient property keeps track of a list of the catalog categories recently

visited by the user.

productsViewed This transient property keeps track of a list of the products recently visited

by the user.

Page 59: At g Cons Comm Ref Guide

3 Profile Management 53

Property Description

recentSearches This transient property keeps track of a list of the strings that the user has

searched on recently.

The following example shows the XML file for Pioneer Cycling. Please refer to the SQL Repository Architecture

chapter in the ATG Repository Guide for the proper definition file syntax.

<gsa-template xml-combine="append">

<header> <name>Bikestore Related Profile Changes</name> <author>DCS Team</author> <version>$Change: 224215 $$DateTime: 2001/12/27 14:00:56 $$Author: bbarber $</version> </header>

<item-descriptor name="user">

<table name="b2c_user" type="auxiliary" id-column-name="id"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/>

<property category-resource="categoryPioneerCycling" name="size" data-type="enumerated" default="clothingSizeUnknown" column-name="clothing_size" display-name-resource="clothingSize"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> <attribute name="useCodeForValue" value="false"/> <option resource="clothingSizeUnknown" code="0"/> <option resource="clothingSizeExtraSmall" code="1"/> <option resource="clothingSizeSmall" code="2"/> <option resource="clothingSizeMedium" code="3"/> <option resource="clothingSizeLarge" code="4"/> <option resource="clothingSizeExtraLarge" code="5"/> </property> <property category-resource="categoryPioneerCycling" name="weightPreference" data-type="int" column-name="weight_preference" display-name-resource="weightPreference"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property> <property category-resource="categoryPioneerCycling" name="pricePreference" data-type="int" column-name="price_preference" display-name-resource="pricePreference"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property> <property category-resource="categoryPioneerCycling" name="numOrders" display-name-resource="numOrders" data-type="int" column-name="num_orders" default="0"> <attribute name="resourceBundle"

Page 60: At g Cons Comm Ref Guide

54 3 Profile Management

value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property> <property category-resource="categoryPioneerCycling" name="cumulativeOrderAmount" display-name-resource="cumulativeOrderAmount" data-type="double" column-name="cum_order_amt" default="0"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property> </table>

<table name="b2c_bike_owned" type="multi" id-column-name="id" multi-column-name="sequence_num"> <property category-resource="categoryPioneerCycling" name="bikesYouOwn" data-type="list" component-data-type="String" column-name="bike" display-name-resource="bikesYouOwn"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property> </table> <table name="b2c_user_keyword" type="multi" id-column-name="id" multi-column-name="sequence_num"> <property category-resource="categoryPioneerCycling" name="userKeywords" data-type="list" component-data-type="String" column-name="keyword" display-name-resource="userKeywords"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property> </table> <table name="b2c_item_bought" type="multi" id-column-name="id" multi-column-name="sequence_num"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> <property category-resource="categoryPioneerCycling" name="itemsBought" data-type="list" component-item-type="sku" repository="/atg/commerce/catalog/ProductCatalog" column-name="item" display-name-resource="itemsBought"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property> </table>

<!-- The following transient properties used to record categories & pages viewed, and recent searches --> <property category-resource="categoryPioneerCycling" name="categoriesViewed" display-name-resource="categoriesViewed" data-type="set" component-data-type="String"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property>

<property category-resource="categoryPioneerCycling"

Page 61: At g Cons Comm Ref Guide

3 Profile Management 55

name="productsViewed" display-name-resource="productsViewed" data-type="set" component-data-type="String"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property>

<property category-resource="categoryPioneerCycling" name="recentSearches" display-name-resource="recentSearches" data-type="set" component-data-type="String"> <attribute name="resourceBundle" value="atg.projects.b2cstore.UserProfileTemplateResources"/> </property>

</item-descriptor>

</gsa-template>

We used the following SQL script to create the database schema to store the user profile data. Note that the

tables declared in the XML as type="auxiliary" have one row per user. The tables declared as type="multi"

have one row per user per value, so if there are1000 users and each customer purchases 5 items, there will be

5000 rows in the b2c_item_bought table.

CREATE TABLE b2c_user ( id VARCHAR(40) NOT NULL, clothing_size INT NULL, wants_prod_reviews TINYINT NULL, weight_preference INTEGER NULL, price_preference INTEGER NULL, num_orders INTEGER NULL, cum_order_amt DOUBLE PRECISION NULL, PRIMARY KEY(id));

CREATE TABLE b2c_user_keyword ( id VARCHAR(40) NOT NULL, sequence_num INTEGER NOT NULL, keyword VARCHAR(50) NOT NULL, PRIMARY KEY(id, sequence_num));

CREATE TABLE b2c_item_bought ( id VARCHAR(40) NOT NULL, sequence_num INTEGER NOT NULL, item VARCHAR(40) NOT NULL, PRIMARY KEY(id, sequence_num));

CREATE TABLE b2c_bike_owned ( id VARCHAR(40) NOT NULL, sequence_num INTEGER NOT NULL, bike VARCHAR(40) NOT NULL, PRIMARY KEY(id, sequence_num));

Page 62: At g Cons Comm Ref Guide

56 3 Profile Management

Page 63: At g Cons Comm Ref Guide

4 Extending the Product Catalog 57

4 Extending the Product Catalog

ATG Consumer Commerce comes with a default set of product catalog item types that most stores will find

useful. These include product, category, and SKU. However, you may find that you need to extend the standard

product catalog definition to meet the needs of your site. You may want to add additional properties to the

item types included in ATG Consumer Commerce or add new item types that are specific to your needs. ATG

Consumer Commerce is very flexible and makes these types of customizations to the product catalog easy to do.

To implement the specialized features of the Pioneer Cycling site, we extended the basic ATG Consumer

Commerce product catalog definition in three main ways. This chapter includes the following sections:

Background (page 57)

Explains what you need to know in order to extend the catalog.

Adding Properties to Product Definition (page 58)

We added simple properties (or attributes) to item types already defined in ATG Consumer

Commerce. For example, we added the property style to the product item type.

Adding New Item Types (page 61)We added new item types to hold product and SKU

property values that can’t be stored in a single, simple property. Adding a new item type

is necessary if a product or SKU property value cannot be represented as a simple string or

number.

Creating Subtypes (page 63)We created new item types by subclassing the product and

SKU item types. This allowed us to create new attributes for just one subtype if desired

Background

This section assumes you have read and understood the SQL Repository Architecture chapter in the ATG

Repository Guide. ATG Commerce stores its product catalog data in a SQL database and accesses that data

via the SQL Repository Adapter. In order for the SQL Repository Adapter to be able to access your product

catalog information, all of the information regarding the definition of your item types must be defined in an

XML repository template definition file. The XML definition file contains information about the item types in

your product catalog, the properties of each item type, how the item types and their properties are stored in

the database, and other important “meta” data. Any changes you want to make to the base item types have

to be made inside the XML definition file. Because all data is stored in a database, any additions have to be

accompanied by SQL DDL scripts for creating new tables for the new information. In summary, to add any new

item types or properties you need to write XML scripts and SQL DDL scripts.

The XML definition files reside in the configuration path. The entire definition of your product catalog could

reside in a single XML file, or the definition may be spread out among multiple files. In addition, these definition

Page 64: At g Cons Comm Ref Guide

58 4 Extending the Product Catalog

files may be layered among several configuration layers similar to the way properties files are. XML files from

different configuration layers are combined according to highly structured rules. For more information, see the

XML File Combination section in the Nucleus: Organizing JavaBean Components chapter of the ATG Programming

Guide.

ATG Consumer Commerce comes with an XML definition file that contains the information for the standard

product catalog. A reference to this XML file is found in the /atg/commerce/catalog/ProductCatalog

component. The location of this XML file is /atg/commerce/catalog/productCatalog.xml.

There are a number of ways you can add an attribute to an existing item type. Our approach was to leave

all of the base XML files intact. We created a new XML definition file called /atg/commerce/catalog/

productCatalog.xml, which can be found in PioneerCycling/config/config.jar. This file is later

merged with the base productCatalog.xml file included in ATG Consumer Commerce by the XML combiner.

ATG Consumer Commerce also comes with SQL DDL scripts that create the SQL tables in the SQL database.

Because any new attributes must be stored in a database table, we had to add SQL DDL scripts for each

new attribute. Again, our approach was to leave the ATG Consumer Commerce SQL DDL scripts intact. We

created new Pioneer Cycling SQL DDL scripts that can be found in <ATG9dir>/PioneerCycling/sql/

db_components/database/b2c_product_catalog_ddl.sql.

Adding Properties to Product Definition

For the Pioneer Cycling Catalog, we extended the basic ATG Consumer Commerce product item type with the

following new properties:

Property Description

available This property indicates whether this product is available in the current locale.

highlightPriority This int property makes it possible to highlight some products in the catalog

user interface via a targeter configured to look for high highlightPriority

values. We created this value to push items that are overstocked.

priceRange This enumerated property classifies items into cheap, normal, expensive, and

very expensive price categories. It helps customers locate items in a specified

price class. We also use it to track the kinds of items customers buy by storing

the priceRange of items they purchase. We can use this information to predict

future interests and to target content and products to the customer.

(A product’s actual price is stored in a SKU as an absolute number. Thus, we

have no way to see how prices compare within a category. For example, a $200

bike could be considered cheap, whereas a $200 helmet would be considered

expensive.)

weightRange This enumerated property classifies the relative weight of a product into heavy,

normal, light, and extralight so that customers of Pioneer Cycling can look for

bikes in a specific weight range. The exact weight of a product is stored on the

SKU.

Page 65: At g Cons Comm Ref Guide

4 Extending the Product Catalog 59

Property Description

style This property is a reference to an item of type “Style” which indicates the sort of

style a product is, such as sporty, funky, retro, or feminine. These values are used

for finding similarities between products.

manufacturer This property is a reference to an item of type “Manufacturer” that indicates the

company that makes the product.

The standard ATG Consumer Commerce product catalog stores the product item type attributes in the

dcs_product table. We didn’t want to modify the standard ATG Consumer Commerce dcs_product table,

so we created a new SQL table to hold these new attributes. The new table is called b2c_product. (All the

tables we created for the Pioneer Cycling Site have names that start with b2c.) The b2c_product table contains

columns for all of the new attributes. In order for these properties to be accessible on all products the two tables

(the original dcs_product and the new b2c_product tables) are “linked” together with the product_id

attribute.

Because these tables are linked together by a join at the database level, performance is impacted. Another

option is to add new columns to the dcs_product table. We elected to use a separate table that joins to

dcs_product because we wanted users to be able to uninstall the Pioneer Cycling site, an example application,

and keep other ATG products. When designing a production quality application, developers should use all the

techniques they ordinarily would to optimize database performance.

CREATE TABLE b2c_product (product_id VARCHAR(40) NOT NULLREFERENCES dcs_product(product_id),available TINYINT NULL,manufacturer VARCHAR(40) NULL REFERENCES b2c_manufacturer(manufacturer_id),highlight_priority INTEGER NULL,price_range INTEGER NULL,weight_range INTEGER NULL,style VARCHAR(40) NULL REFERENCES b2c_style(style_id),PRIMARY KEY(product_id));

Here is the XML file for the new product item type attributes. All of the code is within <item-descriptor

name="product"> tags. Because the product item-descriptor already exists in the standard ATG Consumer

Commerce XML definition file, this new information is merged with the existing product item-descriptor via

the XML File Combination algorithm mentioned earlier. Note that all of the new attributes are between the

"<table name="b2c_product">. . .</table>" tags so that the Repository API knows this data is stored in

the new b2c_product database table. Also note that two of the attributes have enumerated values (priceRange

and weightRange). This means that when product property values are entered in the ACC, the ACC provides a

dropdown list of possible values.

<item-descriptor name="product"> <table name="b2c_product" type="auxiliary" id-column-name="product_id"> <property name="available" data-type="boolean" column-name="available"/> <property name="highlightPriority" data-type="int" column name="highlight_priority"/> <property name="priceRange" data-type="enumerated" default="NORMAL" column name="price_range"> <attribute name="useCodeForValue" value="false"/>

Page 66: At g Cons Comm Ref Guide

60 4 Extending the Product Catalog

<option value="CHEAP" code="0"/> <option value="NORMAL" code="1"/> <option value="EXPENSIVE" code="2"/> <option value="VERY EXPENSIVE" code="3"/> </property> <property name="weightRange" data-type="enumerated" default="NORMAL" column name="weight_range"> <attribute name="useCodeForValue" value="false"/> <option value="HEAVY" code="0"/> <option value="NORMAL" code="1"/> <option value="LIGHT" code="2"/> <option value="EXTRA LIGHT" code="3"/> </property> </table></item-descriptor>

Adding simple properties to SKU item type

For the Pioneer Cycling Catalog, we extended the basic ATG Consumer Commerce SKU item type with the

following new properties:

Property Description

color The color of the SKU.

exactWeight The weight, in ounces, grams, or other units, of the SKU.

Here is the SQL DDL for adding the new attributes to the SKU item type. Again, we created a new table

(b2c_sku) to hold the new attributes instead of editing the SQL DDL that comes with ATG Consumer Commerce:

CREATE TABLE b2c_sku ( sku_id VARCHAR(40) NOT NULL REFERENCES dcs_sku(sku_id), color VARCHAR(100) NULL, exact_weight DOUBLE PRECISION NULL, PRIMARY KEY(sku_id));

Here is the code sample from the XML file that defines these additions to the SKU item-type definition:

<item-descriptor name="sku" sub-type-property="type">… <table name="b2c_sku" type="auxiliary" id-column-name="sku_id"> <property name="color" data-type="string" column-name="color"/> <property name="exactWeight" data-type="double" column-name="exact_weight"/> </table></item-descriptor>

Page 67: At g Cons Comm Ref Guide

4 Extending the Product Catalog 61

Adding New Item Types

If you want to add a simple attribute, such as an integer or string, you can simply add the attribute to the

existing product or SKU item type as shown above. However, some item types are more complicated; for

example, they may be multi-valued or have several associated attributes. In these cases, you may need to create

a brand new item type to contain the values, and then add a reference to this new item type in the product or

SKU. We needed to do this for the Pioneer Cycling store.

We added a couple of new item types to the Pioneer Cycling product catalog: manufacturer and style. These

item types hold information that product objects can reference. If it was simple information, we could have

represented it as a new attribute on the product item type. Because it was more complicated, we pulled it out

into a separate new item type. In order to tie the information together, each product contains a reference to

one of these new item types. These changes required two steps: creating the new item type, and updating the

product to contain a reference to the new information. The steps for adding manufacturer and style were

very similar so we only explain one of them here.

Manufacturer

The manufacturer item type defines the manufacturer of a product and has the following attributes:

Item Description

id Unique identifier

displayName Name of manufacturer

description Description of the manufacturer

longDescription Detailed description of the manufacturer

image Manufacturer logo

keywords List of keywords for this manufacturer (multivalued attribute)

We used SQL DDL scripts for adding the b2c_manufacter database tables. Here is the SQL DDL script we used

to add the main manufacturer table to the database:

CREATE TABLE b2c_manufacturer ( manufacturer_id VARCHAR(40) NOT NULL, manufacturer_name VARCHAR(200) NULL, description VARCHAR(200) NULL, long_description VARCHAR(40) NULL REFERENCES dcs_media_txt (media_id), image VARCHAR(40) NULL REFERENCES dcs_media_bin (media_id), PRIMARY KEY(manufacturer_id));

This is the SQL DDL script we used for the table that holds the multi-valued keyword attribute. (Multi-valued

attributes must be stored in a separate table).

Page 68: At g Cons Comm Ref Guide

62 4 Extending the Product Catalog

CREATE TABLE b2c_mnfr_keywrd ( manufacturer_id VARCHAR(40) NOT NULL REFERENCES b2c_manufacturer(manufacturer_id), sequence_num INTEGER NOT NULL, keyword VARCHAR(254) NOT NULL, PRIMARY KEY(manufacturer_id, sequence_num));

Then, we added an attribute to the b2c_product table so that the product can contain a reference to the new

manufacturer object:

CREATE TABLE b2c_product (… manufacturer VARCHAR(40) NULL REFERENCES b2c_manufacturer(manufacturer_id)…);

We had to add a new item-descriptor to the XML file for the manufacturer:

<item-descriptor name="manufacturer"> <table name="b2c_manufacturer" type="primary" id-column-name="manufacturer_id"> <property name="id" column-name="manufacturer_id"/> <property name="displayName" data-type="string" column-name="manufacturer_name"/> <property name="description" data-type="string" column-name="description"/> <property name="longDescription" item-type="media" column-name="long_description"/> <property name="image" item-type="media" column-name="image"/> </table> <table name="b2c_mnfr_keywrd" type="multi" id-column-name="manufacturer_id" multi-column-name="sequence_num"> <property name="keywords" data-type="list" component-data-type="string" column-name="keyword"/> </table></item-descriptor>

Finally, we updated the product item-descriptor to include a reference to the new manufacturer property.

Here is a portion of that XML script:

<item-descriptor name="product">… <property name="manufacturer" item-type="manufacturer" column name="manufacturer"/>…</item-descriptor>

After all of these changes were made, the displayName property of a manufacturer of a particular product can

be displayed in a JSP file using the following tag:

Page 69: At g Cons Comm Ref Guide

4 Extending the Product Catalog 63

<dsp:valueof param="childProduct.manufacturer.displayName"/>

The displayName property associates an item with a text string that users will see through the ACC interface.

For example, a product item refers to a Manufacturer item. In the ACC, the manufacturer field of a product

shows a pick list of the displayNames of all manufacturer items in the repository. If the manufacturer has no

display name, then the id property is used.

Style

We added the new style item-type in exactly the same way as manufacturer.

Here is the portion of productCatalog.xml that describes the item type “style”.

<item-descriptor name="style" display-name="Style"> <table name="b2c_style" type="primary" id-column-name="style_id"> <property name="id" column-name="style_id"/> <property name="displayName" data-type="string" column-name="style_name"/> </table></item-descriptor>

It has just two properties, id and displayName. Functionally, style is similar to an enumerated type property,

which has a code and a value. However, with this implementation, a new style can be added via the ACC while

ATG Consumer Commerce is running. If style were an enumerated property of Product, a developer would

have to add new styles when ATG Consumer Commerce was not running.

Creating Subtypes

In the Pioneering Cycling site, we wanted to subclass both the product and SKU item types. Subtypes

automatically inherit all of the attributes from a parent item type, but can have new properties that apply only to

that one subtype. Creating subtypes is useful when one subtype needs a property another subtype doesn’t. If all

possible attributes were added to the base item-type, users might enter values for certain properties that were

not relevant for all items.

For example, we created three new SKU item types: clothing-sku, bike-sku and part-and-accessory-

sku. All three of these new SKU items types are subtypes of SKU. This means that the new item types inherit

all of the attributes that a SKU has, but each can have some new attributes. When you create a new SKU in the

ACC, you can specify that it be of item type bike-sku for example. The ACC then allows you to specify all of the

properties that are associated with a bike-sku.

Product subtypes

We created two new subtypes for products: frame-product and frame-fit-product.

When we created new products with frames (such as a bike), we made the product of type frame-product.

This frame-product subtype has a new property called frame-type that is only relevant for frame-

products.

If the product was a part that fits on a particular bike frame (such as a crankset), we made it of type frame-fit-

product. This subtype has a new multi-valued property called compatibleframes that contains a list of all of

the frame types this part is compatible with. All other products, such as helmets, are simply of type product.

Page 70: At g Cons Comm Ref Guide

64 4 Extending the Product Catalog

We wanted to capture this information on our products to provide customers with the following types of

functionality:

• Show all the parts with a particular bike.

• Show all the bikes compatible with a particular part (for example, a brake).

• Warn customers when they add products that are incompatible to their shopping carts.

This is how we updated the XML script for the product item-descriptor to specify the two new subtypes of

product (frame-product and frame-fit-product):

<item-descriptor name="product">… <table name="dcs_product" type="primary" id-column-name="product_id"> <property name="type"> <attribute name="useCodeForValue" value="false"/> <option value="product" code="0"/> <option value="frame-product" code="1"/> <option value="frame-fit-product" code="2"/> </property> </table>…</item-descriptor>

Because frame-product subtypes have a new attribute called frame-type, we created a table to hold this

information with this SQL DDL:

CREATE TABLE b2c_frame_product ( product_id VARCHAR(40) NOT NULL REFERENCES dcs_product(product_id), frame_type INTEGER NULL, PRIMARY KEY(product_id));

We also had to add a new item-descriptor for the new frame-product subtype. Here is the XML definition for it.

Notice that by specifying super-type="product" this item type becomes a subclass of the existing product

item type, and then inherits all properties from it. The frame-type property is enumerated. When you create a

product of type frame-product, ACC provides you with a dropdown list of possible values for the frameType

value (A, B, C, etc.). Also, the frame-type property value is stored in the b2c_frame_product table created

above.

<item-descriptor name="frame-product" super-type="product" sub-type-value="frame-product" id-space-name="product"> <table name="b2c_frame_product" type="auxiliary" id-column-name="product_id"> <property name="frameType" data-type="enumerated" column-name="frame_type"> <option value="A" code="1"/> <option value="B" code="2"/> <option value="C" code="3"/> <option value="D" code="4"/> <option value="E" code="5"/> <option value="F" code="6"/> <option value="G" code="7"/>

Page 71: At g Cons Comm Ref Guide

4 Extending the Product Catalog 65

<option value="H" code="8"/> <option value="I" code="9"/> </property </table></item-descriptor>

Because frame-fit-product subtypes have a new multi-valued attribute called compatibleframes, we

created a table to hold this information. Here is the SQL DDL to do that. Notice that the column name (frame)

does not necessarily need to match the property name (compatibleframes).

CREATE TABLE b2c_compat_frame ( product_id VARCHAR(40) NOT NULL REFERENCES dcs_product(product_id), sequence_num INTEGER NOT NULL, frame INTEGER NOT NULL, PRIMARY KEY(product_id, sequence_num));

We also added a new item-descriptor for the new frame-fit-product subtype. Here is the XML

definition for it. Again, note that this subtype inherits from the product super type. It has one attribute

(compatibleframes), a multi-valued attribute whose data is stored in the b2c_compat_frame table.

<item-descriptor name="frame-fit-product" super-type="product" sub-type-value="frame-fit-product" id-space-name="product"> <table name="b2c_compat_frame" type="multi" id-column-name="product_id" multi-column-name="sequence_num"> <property name="compatibleframes" data-type="list" component-data-type="string" column-name="frame"/> </table></item-descriptor>

SKU subtypes

For the Pioneering Cycling site, we created three new SKU item types: clothing-sku, bike-sku and part-

and-accessory-sku. All three of these new SKU items types are subtypes of SKU.

Here is the XML for adding the three new subtypes. It was added to the SKU item-descriptor:

<item-descriptor name="sku" sub-type-property="type"> <table name="dcs_sku" type="primary" id-column-name="sku_id"> <property name="type"> <option value="sku" code="0"/> <option value="clothing-sku" code="1"/> <option value="bike-sku" code="2"/> <option value="part-and-accessory-sku" code="3"/> </property> </table>… </item-descriptor>

Clothing-SKU

The clothing-sku item type has the following three new enumerated properties:

Page 72: At g Cons Comm Ref Guide

66 4 Extending the Product Catalog

Property Description

size Indicates the size of a garment (S,M, L, XL)

proof Used for storing what a garment is impervious to (for example, waterproof or windproof ).

material Indicates the product material (for example, cotton, spandex).

These new attributes are stored in a new table in the database called b2c_clothing_sku. Here is the SQL DDL

for creating this new table.

CREATE TABLE b2c_clothing_sku ( sku_id VARCHAR(40) NOT NULLREFERENCES dcs_sku(sku_id), clothing_size INTEGER NULL, proof INTEGER NULL, material INTEGER NULL, PRIMARY KEY(sku_id));

Here is the XML definition for the clothing-sku item-descriptor. Notice that by specifying super-

type="sku" this item type is a subclass of the exiting SKU item type, and inherits all properties from it. The

properties are all enumerated so the ACC provides dropdown lists when entering SKU values:

<item-descriptor name="clothing-sku" super-type="sku" sub-type-value="clothing-sku" id-space-name="sku" display-name="Clothing SKU"> <table name="b2c_clothing_sku" type="auxiliary" id-column-name="sku_id"> <property category="Pioneer Cycling - Clothing" name="size" data-type="enumerated" column-name="clothing_size" display-name="Size"> <option value="S" code="0"/> <option value="M" code="1"/> <option value="L" code="2"/> <option value="XL" code="3"/> </property> <property category="Pioneer Cycling - Clothing" name="proof" data-type="enumerated" column-name="proof" display-name="Proof"> <attribute name="useCodeForValue" value="false"/> <option value="None" code="0"/> <option value="Water" code="1"/> <option value="Wind" code="2"/> <option value="Child" code="3"/> <option value="Crash" code="4"/> <option value="Tear" code="5"/> <option value="Dent" code="6"/> <option value="Fire" code="7"/> <option value="Monkey" code="8"/> </property> <property category="Pioneer Cycling - Clothing" name="material" data-type="enumerated" column-name="material" display-name="Material"> <attribute name="useCodeForValue" value="false"/> <option value="Unknown" code="0"/> <option value="Cotton" code="1"/> <option value="Spandex" code="2"/>

Page 73: At g Cons Comm Ref Guide

4 Extending the Product Catalog 67

<option value="Kevlar" code="3"/> <option value="Nylon" code="4"/> <option value="Polyester Blend" code="5"/> <option value="Silk" code="6"/> <option value="Wool" code="7"/> <option value="Goretex" code="8"/> </property> </table> </item-descriptor>

Bike-SKU and Part-and-Accessory-SKU

The new bike-sku and the part-and-accessory-sku item types have the following attributes:

Property Description

metal The type of metal used to make the bike.

dimensions A map where the keys are the names of the dimensions measured and the values are the

measurements.

The bike-sku and part-and-accessory-sku item types have exactly the same properties; they could be

combined into a single item type. The reason for keeping them separate is that if we want to know if a particular

SKU is a bike or a part, we can test the item type and have the answer easily.

Here is the SQL DDL script for the new b2c_bike_sku table created to store the new metal attribute:

CREATE TABLE b2c_bike_sku ( sku_id VARCHAR(40) NOT NULL REFERENCES dcs_sku(sku_id), metal VARCHAR(40) NULL REFERENCES PRIMARY KEY(sku_id));

Because the dimensions attribute is multi-valued, the values are stored in a separate table (b2c_dimensions).

The dimensions attribute is accessed as a Map. The Map type provides a flexible way of storing multiple key/

value pairs. The list of possible keys is not hard coded or predetermined. When fetching a value from a Map

attribute, we pass in a key and the Map returns the associated value. So, if the dimensions Map value for a

particular bike was {tubeLength=5.0,pedalDiameter=6.0} and we passed in the key: tubeLength, it would

return the value 5.0.

Here is the SQL DDL creating the table to store the dimensions property. Note that the tag column holds the

key, and the measurement column holds the value.

CREATE TABLE b2c_dimensions ( sku_id VARCHAR(40) NOT NULL REFERENCES dcs_sku(sku_id), tag VARCHAR(100) NOT NULL, measurement DOUBLE PRECISION NULL, PRIMARY KEY(sku_id, tag));

Here is the XML file for creating the bike-sku item type:

Page 74: At g Cons Comm Ref Guide

68 4 Extending the Product Catalog

<item-descriptor name="bike-sku" super-type="sku" sub-type-value="bike-sku" id-space-name="sku"> <table name="b2c_bike_sku" type="auxiliary" id-column-name="sku_id"> <property name="metal" data-type="enumerated" column-name="metal"> <attribute name="useCodeForValue" value="false"/> <option value="Unknown" code="0"/> <option value="Aluminium" code="1"/> <option value="Chromoly" code="2"/> <option value="Steel" code="3"/> <option value="Carbon" code="4"/> <option value="Titanium" code="5"/> </property> </table> <table name="b2c_dimensions" type="multi" id-column-name="sku_id" multi-column-name="tag"> <property name="dimensions" data-type="map" component-data-type="double" column-name="measurement"/> </table></item-descriptor>

After all of these changes were made, a particular SKU’s tubeLength value (stored in the dimensions Map

property) could be displayed in the following simple (yet very dynamic) way. Remember that the Map keys are

not hard-coded or predetermined, so any key stored in the Map can be displayed in this way:

<dsp:valueof param="Sku.dimensions.tubelength"/>

In summary, we have shown you how we customized the Pioneer Cycling product catalog to meet our needs by

adding SQL DDL scripts, and XML item-descriptor files. Just about any customization your site needs is possible

with the flexible ATG architecture.

Page 75: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 69

5 Displaying and Accessing the

Product Catalog

This chapter describes how the catalog pages are displayed to customers and how customers

navigate the catalog.

Product Template Pages (page 69)

Describes how to create product template pages for displaying the products and SKUs in

the product catalog. Detailed JSP code examples are shown to help you understand how we

implemented this for the Pioneer Cycling store.

Category Template Pages (page 88)Describes how to create category template pages for

displaying the categories in the product catalog.

Adding Catalog Navigation (page 95)Describes how to create a navigational structure for

your site.

Searching and Filtering the Catalog (page 101)Describes how to add search features to

your pages and how users can filter the results.

Comparing SKUs (page 120)Describes how to add side-by-side comparison to your pages.

Product Template Pages

In order to allow users to browse through your catalog of products, you need to create pages that display

information about the various categories and products you sell. You could create a separate Java Server Page

for every single product, but clearly this would be extremely inefficient. Each time a new product was created or

removed from your catalog, modifications would have to be made.

For Pioneer Cycling, we created generic template pages for products and product categories. These template

pages are simply Java Server Pages that display general information about one category or one product.

These pages are general and data-driven; they are not hard-coded for a specific product or category. All of

the information displayed on them comes from the repository. These general, dynamic pages are convenient

because once written, they can typically be used to display any category or any product in a product catalog.

For example, a very simple site could have one category template page file and one product template page file.

Because these pages are dynamic, they are easy to maintain. As you add more products to your repository, you

don’t necessarily have to change the product catalog template pages.

Page 76: At g Cons Comm Ref Guide

70 5 Displaying and Accessing the Product Catalog

However, you may decide to display different information about one category of products than another.

For example, in the Pioneer Cycling Store, we display slightly different information for bikes than for all

other products. On the bike template page, we included a Compare to Other Products button that brings

up a form that allows users to compare the features of one bike to another. We did not want this button

to appear for other products. For this reason, we created several template pages in the Pioneer Cycling

code: product_generic.jsp, product_bike.jsp, product_part.jsp, product_bundle_jsp, and

product_gift_certificate.jsp. Most products use the product_generic.jsp template file; only bikes

use the product_bike.jsp template file. However, because much of the template page content is similar for

the two template pages, lots of the supporting code is pulled out into various JSP code fragment files, which can

be inserted in the body of a JSP file (using the dsp:include tag or JSP’s include directive). This makes your

template pages easier to maintain.

By creating page fragments and using the dsp:include tag or JSP’s include directive to embed them in

other pages, you can reuse JSP code and simplify the page development process. However, extensive use of this

technique slows down site performance slightly. See Appendix B: Optimizing Performance for more information

on reusing code. In this case, we used the dsp:include tag syntax in moderation, but did not avoid it entirely.

Template Page File Name in database

Every category and product in the repository has a template.url database value that defines the template

page to use to display this product or category.

The template page (that is, the template.url value) for all bike products is:

<ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/web-app/en/catalog/product_bike.jsp.

The template page for all other products is:

<ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/web-app/en/catalog/product_generic.jsp.

Later, if we decided to display certain products (such as bike helmets) using a totally different template page,

we could create a third template page, and modify the template.url value for all helmets in the database to

reference the new template page name.

In Pioneer Cycling, all the catalog media files (templates and images) are stored on the file system and the URLs

for accessing those files are stored in the repository. It is also possible to use the repository item types media-

internal-binary and media-internal-text to store images and text in the repository.

Creating the Bike Template Pages

The following sections describe pieces of the product_bike.jsp product catalog template page in the Pioneer

Cycling store. You should be able to use this product catalog template page as a stepping-stone for building

your own product template page. The code in the following sections can be found in:

<ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/web-app/en/catalog/product_bike.jsp.

This file is discussed in the sequence most useful for explanation, rather than from beginning to end.

Page 77: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 71

A rendered template page for one bike product

JSP Comments versus HTML Comments

We used JSP comments <%--My Comment--%> rather then in HTML comment tags (<!—My Comment-->) to

enclose many of the comments in our pages. JSP comments do not end up in the page source code, which

keeps the source code as small and light as possible. (HTML comments end up in page source code, which the

page source code longer, and can be seen by users.)

Page 78: At g Cons Comm Ref Guide

72 5 Displaying and Accessing the Product Catalog

Using ProductLookup

We used ProductLookup, a component that is an instance of ItemLookup, to fetch product attributes from the

repository on the template pages. This is the beginning of the code from a template page where we used the

ProductLookup component:

Note: The “. . .” markers in the following code sample indicate a place where code has been removed to clarify

the sample.

<dsp:droplet name="/atg/commerce/catalog/ProductLookup">

<%-- *ProductLookup expects an "id" param, but because we already *have a page level "id" param, we don't need to pass it in *here. --%>

<dsp:param bean="/OriginatingRequest.requestLocale.locale" name="repositoryKey"/> <dsp:param name="elementName" value="Product"/> <dsp:oparam name="output">

. . . <dsp:valueof param="Product.displayName"/>

. . . </dsp:oparam></dsp:droplet>

ProductLookup takes an id parameter as input. It then binds the Product parameter to the product with the

id that was passed in. In this code example, we didn’t explicitly pass the id parameter to the ProductLookup

component. The page-level id parameter was already defined at the top of the page as an input parameter and

thus implicitly passed to ProductLookup. The code inside the output parameter section of ProductLookup

simply refers to the Product parameter when it needs to access the product’s property values.

The following code shows how we displayed the product’s longDescription property, or, a default value if the

product does not have a longDescription property value:

<!-- Display the Product description here: --><dsp:valueof param="Product.longDescription"> This product doesn't have a longDescription yet...</dsp:valueof>

The following code example shows how we displayed the product’s image. Note that we used the getvalueof

tag of the DSP tag library expose a scripting variable to use for the product image:

<dsp:getvalueof id="smallImageUrl" param="Product.smallImage.url" idtype="java.lang.String"> <dsp:img src="<%=smallImageUrl%>"/></dsp:getvalueof>

Page 79: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 73

Displaying Links to Products or Categories

Because there is extensive linking between products and categories in the Pioneer Cycling store, we created

a code fragment to produce hyperlinks to products or categories in ItemLink.jsp. This code fragment was

created for convenience and to make our template pages easier to read.

<%/* ------------------------------------------------------- * Display a link to the Item (Product or Category). * The link will take you to the jsp page which displays * this Item. The jsp page is fetched from the * "template.url" attribute on the Item. * If an Image is passed in, both the Image, and a text * link is displayed - clicking either the Image or the * text link brings the user to the above described jsp page. * ------------------------------------------------------- */%><dsp:importbean bean="/atg/dynamo/droplet/IsNull"/><DECLAREPARAM NAME="Item" CLASS="java.lang.Object" DESCRIPTION="A Repository Item to display a link to - typically a Product or Category"><DECLAREPARAM NAME="Image" CLASS="java.lang.String" DESCRIPTION="The optional param is used to display an image along with the link." OPTIONAL><DECLAREPARAM NAME="navAction" CLASS="java.lang.String" DESCRIPTION="How to change the navigation history. Choices are push, pop and jump. Blank is treated as push." OPTIONAL><DECLAREPARAM NAME="DisplayText" CLASS="java.lang.String" DESCRIPTION="This optional string can be passed in to display different text (than what is default). The default text to display is the Item's DisplayName" OPTIONAL><% /* Display link in bold: */ %><b><%/* ------------------------------------------------------- * Display a clickable link to the Item (Product or Category). * The link will take you to the jsp page which displays * this Item. The jsp page to go to for this Item is * specified in the "template.url" attribute on the Item. * ------------------------------------------------------- */%>

<dsp:getvalueof id="templateUrl" idtype="String" param="Item.template.url"><dsp:a page="<%=templateUrl%>"> <dsp:param name="id" param="Item.repositoryId"/> <dsp:param name="navCount" bean="/atg/commerce/catalog/CatalogNavHistory.navCount"/>

<% /* These set for breadcrumb navigation: */ %> <dsp:param name="navAction" param="navAction"/>

<dsp:getvalueof id="imageInp" param="Image">

Page 80: At g Cons Comm Ref Guide

74 5 Displaying and Accessing the Product Catalog

<core:ifNotNull value="<%=imageInp%>"> <font color=000000> <dsp:getvalueof id="imageURL" param="Image.url" idtype="java.lang.String"> <core:ifNotNull value="<%=imageURL%>"> <img border="1" src="<%=imageURL%>"> </core:ifNotNull> </dsp:getvalueof> </font> <br> </core:ifNotNull> </dsp:getvalueof>

<%-- Show DisplayText if set, otherwise show item's display name --%> <dsp:valueof param="DisplayText"><dsp:valueof param="Item.displayName"/></dsp:valueof>

</dsp:a></dsp:getvalueof>

<% /* end link in bold: */ %></b>

The ItemLink.jsp code fragment takes at least one parameter: Item. The item must be a repository item

(for example, a product or a category) that has a template.url value in the database. The value of the

template.url property is the JSP template file that should be used to display this repository item. The code

creates a hyper link to whatever JSP file is stored in the item’s template.url value.

<%/* Display a link to the Product: */%><dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="Product"/> <dsp:param name="Image" param="Product.thumbnailImage"/> <dsp:param name="DisplayText" param="Product.displayName"/> <dsp:param name="navAction" value="jump"/></dsp:include>

The above call to the ItemLink.jsp code fragment is turned into the a HTML code similar to the following after

it is rendered:

<b><a href="/PioneerCyclingJSP/en/catalog/product_generic.jsp?id=prod10023&navAction=jump&navCount=5"> <font color=000000> <img src="/MEDIA/ProductCatalog/m10027_helmets_red_sm.gif" border=1> </font> <br> Shatterproof Helmet</a></b>

Displaying Product Prices

The Pioneer Cycling site uses two code fragments to display prices on the product template pages. These

fragments can be found in the following directories:

• <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/

Page 81: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 75

web-app/en/Common/DisplaySKUPrice.jsp

• <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/web-app/en/DisplayProductPrice.jsp.

List Price versus User’s Price

In the Pioneer Cycling store, we made a distinction between the list price for a product and the user’s price:

• list price: the full, undiscounted price of a product.

• user’s price: the price of a product for a certain user, after all discounts and promotions for which the user

is eligible have been calculated. It does not include order-specific promotions (promotions that depend on

other items on the order).

For example, on Mother’s Day the Pioneer Cycling store offers all women 20% off. The list price of a helmet is

$100; the user’s price (for women) would be $80. The store also offers 50% off the price of a helmet with the

purchase of a bike. However, the user’s price displayed on the product template page does not show this 50%

discount, even if she is purchasing a bike. This order-level discount is displayed on the Shopping Cart page. Order-

level discounts and promotions are calculated with the pricing calculators and are discussed in the Promotions

section of the Merchandising (page 193) chapter.

SKU Price

The DisplaySkuPrice.jsp code fragment displays the price of a single SKU. The code fragment below only

addresses US currency. Code for different locales and currencies is addressed later in the manual.

<DECLAREPARAM NAME="Sku" CLASS="java.lang.Object" DESCRIPTION="A Sku repository Item - REQUIRED">

<dsp:importbean bean="/atg/commerce/pricing/PriceItem"/><dsp:importbean bean="/atg/dynamo/droplet/Compare"/>

<%/* The PriceItem droplet will fetch SKU * Price Info for this user. Any general * promotions this user is eligible for WILL * be reflected in the price. Any order * dependent promotions (ie buy one x, get * y 50% off) will NOT be reflected in the * price info returned by this droplet. */%><dsp:droplet name="/atg/commerce/pricing/PriceItem"> <dsp:param name="item" param="Sku"/> <dsp:param name="elementName" value="PricedSku"/>

<dsp:oparam name="output"> <dsp:droplet name="/atg/dynamo/droplet/Compare"> <dsp:param name="obj1" param="PricedSku.priceInfo.ListPrice"/> <dsp:param name="obj2" param="PricedSku.priceInfo.amount"/>

<%/*ListPrice is greater, so display it first in strikout mode: */%> <dsp:oparam name="greaterthan"> <span class="strikeout"> <dsp:valueof converter="currency" param="PricedSku.priceInfo.ListPrice"/> </span> </dsp:oparam> </dsp:droplet>

Page 82: At g Cons Comm Ref Guide

76 5 Displaying and Accessing the Product Catalog

<%/*Display their price: */%> <dsp:valueof converter="currency" param="PricedSku.priceInfo.amount"/> </dsp:oparam></dsp:droplet> <%/*PriceItem droplet*/%>

We used the ATG Consumer Commerce PriceItem servlet bean to calculate the price for this SKU for this

user. It is passed the SKU that needs to be priced, <dsp:param name="item" value="Sku"/>, and it

passes back the SKU in a parameter called PricedSku with user-specific prices filled into it (<dsp:param

name="elementName" value="PricedSku"/>).

The Compare servlet compares the product’s list price to the customer’s price. If the ListPrice is higher, it is

displayed first, but with a “strike-out” font so the customer can see the discount she is getting. The user’s price is

always displayed.

Inside PriceItem, the list price is displayed like this:

<dsp:valueof converter="currency" param="PricedSku.priceInfo.ListPrice"/>

The user’s price is displayed like this:

<dsp:valueof converter="currency" param="PricedSku.priceInfo.amount"/>

This is the call to the DisplaySkuPrice code fragment:

<dsp:include page="../../common/DisplaySkuPrice.jsp" flush="true"> <dsp:param name="Sku" param="Sku"/></dsp:include>

Page 83: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 77

The list price and user’s price for each SKU is displayed on the product page.

Displaying Product Price

We used the DisplayProductPrice.jsp code fragment to display the price of a product versus the price

of an individual SKU. It simply fetches the first SKU on the product and calls its DisplaySkuPrice.jsp code

fragment. If your site has products with differently priced SKUs, you need to expand this functionality.

Adding Items to the Shopping Cart from the Catalog

In the Pioneer Cycling store, customers can look at products in the catalog and add them directly to their

shopping carts or wish lists. The JSP snippet used to add items to a cart can be found in <ATG9dir>/

PioneerCyclingJSP/j2ee-apps/pioneer/web-app/en/catalog/common/AddToCart.jsp

This is the code example we used to include the code fragment in our template page. The AddToCart.jsp code

fragment takes a product repository item, so it is passed the Product parameter that was initialized inside the

ProductLookupDroplet.

<dsp:getvalueof id="pval0" param="Product"> <dsp:include page="common/AddToCart.jsp" flush="true"> <dsp:param name="product" value="<%=pval0%>"/> </dsp:include>

Page 84: At g Cons Comm Ref Guide

78 5 Displaying and Accessing the Product Catalog

</dsp:getvalueof>

This is the body of the AddToCart.jsp code fragment:

<DECLAREPARAM NAME="Product" CLASS="java.lang.Object" DESCRIPTION="A Product repository Item - REQUIRED.">

<DECLAREPARAM NAME="SkuDisplayFormat" CLASS="java.lang.String" DESCRIPTION="How the list of SKUs should be displayed. Either DropDownList or RadioButtons - default is a dropdown list">

<dsp:importbean bean="/atg/commerce/order/ShoppingCartModifier"/><dsp:importbean bean="/atg/dynamo/droplet/IsNull"/>

<%/*Create a form for user to select a SKU and add it to their cart:*/%><dsp:getvalueof id="form16" bean="/OriginatingRequest.requestURI" idtype="java.lang.String"><dsp:form action="<%=form16%>" method="post">

<%/*Store this Product's ID in the Form Handler: */%> <dsp:input bean="ShoppingCartModifier.ProductId" paramvalue="Product.repositoryId" type="hidden"/>

<%/*set id param so that the Navigator won't get messed up in case of an error that makes us return to this page.*/%> <input name="id" type="hidden" value='<dsp:valueof param="Product.repositoryId"/>'>

<table cellpadding=0 cellspacing=0 border=0> <tr> <td class=box-top-store>Add to Cart</td> </tr>

<tr> <td class=box>

<%/*Display any errors that have been generated during Cart operations: */%> <dsp:include page="../../common/DisplayShoppingCartModifierErrors.jsp" flush="true"></dsp:include>

Add

<%/*Textbox with QTY the user wants to order: */%> <dsp:input bean="ShoppingCartModifier.quantity" size="4" type="text" value="1"/>

<dsp:droplet name="/atg/dynamo/droplet/Switch"> <dsp:param name="value" param="SkuDisplayFormat"/>

<%/*------------RadioButtons format ------------------*/%> <dsp:oparam name="RadioButtons">

<%/*For each of the SKUs in this Product, add a radio button:*/%> <dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="Product.childSKUs"/> <dsp:param name="elementName" value="Sku"/>

Page 85: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 79

<dsp:oparam name="output">

<br>

<%/*If they select this SKU, store its ID in the form handler:*/%> <dsp:input bean="ShoppingCartModifier.catalogRefIds" paramvalue="Sku.repositoryID" name="SkuGroup" type="radio"/>

<%/*Display the SKU's display name for the radio button:*/%> <dsp:valueof param="Sku.displayName"/>

</dsp:oparam> </dsp:droplet> <%/*ForEach SKU droplet*/%>

<br>

</dsp:oparam>

<%/*------------DropDownList format (default) ------------------*/%> <dsp:oparam name="default">

<%/*Create a dropdown list will all Sku in the Product. Store the selected SKU's id in the Form Handler: */%> <dsp:select bean="ShoppingCartModifier.catalogRefIds">

<%/*For each of the SKUs in this Product, add the SKU to the dropdown list:*/%> <dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="Product.childSKUs"/> <dsp:param name="elementName" value="Sku"/> <dsp:param name="indexName" value="skuIndex"/> <dsp:oparam name="output">

<%/*This is the ID to store, if this SKU is selected in dropdown:*/%> <dsp:getvalueof id="option136" param="Sku.repositoryID" idtype="java.lang.String"><dsp:option value="<%=option136%>"/></dsp:getvalueof>

<%/*Display the SKU's display name in the dropdown list:*/%> <dsp:valueof param="Sku.displayName"/>

</dsp:oparam> </dsp:droplet> <%/*ForEach SKU droplet*/%>

</dsp:select> <br>

</dsp:oparam> </dsp:droplet> <%/*Switch on SkuDisplayFormat*/%>

<%/* URL to go to if user's session expires while he is filling out this form */%> <dsp:input bean="ShoppingCartModifier.sessionExpirationURL" type="hidden" value="../../common/SessionExpired.jsp"/>

<%/* ADD TO CART BUTTON: Adds this SKU to the Order*/%> <dsp:input bean="ShoppingCartModifier.addItemToOrder" type="submit" value=" Add to Cart "/>

Page 86: At g Cons Comm Ref Guide

80 5 Displaying and Accessing the Product Catalog

<%/* Goto this URL if NO errors are found during the ADD TO CART button processing:*/%> <dsp:input bean="ShoppingCartModifier.addItemToOrderSuccessURL" type="hidden" value="../checkout/cart.jsp"/>

</td> </tr> </table></dsp:form></dsp:getvalueof>

As with other code fragments, the code takes a Product parameter:

<DECLAREPARAM NAME="Product" CLASS="java.lang.Object" DESCRIPTION="A Product repository Item - REQUIRED.">

The AddToCart.jsp code also takes an optional parameter that indicates how the list of child SKUs should

be displayed. The code can display the SKUs in a dropdown list or as a list of option buttons. The default is

DropDownList.

ShoppingCartModifier form handler

The AddToCart code uses the ShoppingCartModifier component to add items to the shopping cart. This

ATG Consumer Commerce form handler (or a subclass you create) has many methods and properties that

perform most of the tasks required for manipulating a shopping cart. We imported it at the top of the page to

access its properties and methods. Here is the code that includes the form handler on our template page:

<dsp:importbean bean="/atg/commerce/order/ShoppingCartModifier"/>

In order to add a SKU to the user’s shopping cart we needed to pass the ShoppingCartModifier

the repositoryId of the product and the repositoryId of the SKU. These values are passed to the

ShoppingCartModifier when the user pushes the Add To Cart button.

The ProductId is stored in the ShopingCartModifier form handler via a hidden text box, a common

technique for setting properties on a form handler. Here is the code to do that:

<dsp:input bean="ShoppingCartModifier.ProductId" paramvalue="Product.repositoryId" type="hidden"/>

Creating a Dropdown List of All Child SKUs

The repositoryId of the SKU the user wants to add is stored in the form handler’s catalogRefIds property.

The form handler is passed the IDs of the SKUs the user selects from a dropdown list. HTML allows the creation

of a dropdown list using the <select ... > tag. Within this tag is a list of zero or more <option> tags for each

item in the dropdown list.

In the following code, we used the ForEach component to cycle through all of the child SKUs of the product

and to create a dropdown list selection for each one. If there are five SKUs for a product, it creates five items in

the dropdown list. When the user selects one of these SKUs in the dropdown list, the repositoryID of that SKU

is stored in the catalogRefIds property of the ShoppingCartModifier. Here is the code:

<%/*Create a dropdown list will all Sku in the Product.

Page 87: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 81

Store the selected SKU's id in the Form Handler: */%> <dsp:select bean="ShoppingCartModifier.catalogRefIds">

<%/*For each of the SKUs in this Product, add the SKU to the dropdown list:*/%> <dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="Product.childSKUs"/> <dsp:param name="elementName" value="Sku"/> <dsp:param name="indexName" value="skuIndex"/> <dsp:oparam name="output">

<%/*This is the ID to store, if this SKU is selected in dropdown:*/%> <dsp:getvalueof id="option136" param="Sku.repositoryID" idtype="java.lang.String"> <dsp:option value="<%=option136%>"/> </dsp:getvalueof>

<%/*Display the SKU's display name in the dropdown list:*/%> <dsp:valueof param="Sku.displayName"/>

</dsp:oparam> </dsp:droplet> <%/*ForEach SKU droplet*/%>

</dsp:select>

After page compilation, the above code is converted into HTML that looks something like this:

<select name="/atg/commerce/order/ShoppingCartModifier.catalogRefIds"> <option value="sku20014">1.5 Liter Bottle Red <option value="sku20015">1.5 Liter Bottle Blue</select>

For example, if the user chooses “1.5 Liter Bottle Red,” the value “sku20014” (the selected SKU’s repository ID) is

in the ShoppingCartModifier.catalogRefIds property.

Add to Cart button

When the Add To Cart button is pushed, the handleAddItemToOrder method on the

ShoppingCartModifier form handler is called. This method does all of the work necessary for adding this item

to the order. Here is the code for the Add To Cart button:

<%/* ADD TO CART BUTTON: Adds this SKU to the Order*/%><dsp:input bean="ShoppingCartModifier.addItemToOrder" type="submit" value=" Add to Cart "/>

If the addItemToOrder operation succeeds or fails, you may want to redirect the

customer to another page. The ShoppingCartModifier has two properties that can be

set on a page to indicate where the user should be redirected if the addItemToOrder

operation succeeds or fails: ShoppingCartModifier.addItemToOrderSuccessURL and

ShoppingCartModifier.addItemToOrderErrorURL

Here is an example of how we set the SuccessURL property using a hidden text box.

<%/* Goto this URL if NO errors are found during the ADD TO CART button processing:*/%><dsp:input bean="ShoppingCartModifier.addItemToOrderSuccessURL" type="hidden"

Page 88: At g Cons Comm Ref Guide

82 5 Displaying and Accessing the Product Catalog

value="../checkout/cart.jsp"/>

Displaying Form Errors

If any errors are found during method calls on ShoppingCartModifier, the form handler stores all of these

errors. They can be displayed with the following code fragment:

<dsp:include page="../../common/DisplayShoppingCartModifierErrors.jsp" flush="true"></dsp:include>

The ShoppingCartModifier is described in detail in the Order Processing (page 135) chapter.

Displaying Related Products

The product template pages in the Pioneer Cycling store display related products. The relatedProducts

property on a product returns a list of zero or more products that are categorized as being related to this

product. For a given product, we wanted the template page to display these related products as image

links to the products. We needed this functionality in several places, so we created a code fragment called

RelatedProducts.jsp to display this information. Here is the code fragment:

<dsp:importbean bean="/atg/dynamo/droplet/IsNull"/><p> <!-- Display a table of related products. --> <dsp:droplet name="/atg/dynamo/droplet/TableForEach"> <dsp:param name="array" param="Product.relatedProducts"/> <dsp:param name="elementName" value="relatedProduct"/> <dsp:param name="numColumns" value="2"/>

<dsp:oparam name="outputStart">

<!-- Display this header only if there are some related products --> <b>Related products</b><P>

<table cellspacing=4 cellpadding=0 border=0> </dsp:oparam>

<dsp:oparam name="outputRowStart"> <tr valign=top> </dsp:oparam>

<dsp:oparam name="output"> <td> <dsp:droplet name="IsNull"> <dsp:param name="value" param="relatedProduct"/> <dsp:oparam name="false"> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="relatedProduct"/> <dsp:param name="Image" param="relatedProduct.thumbnailImage"/> <dsp:param name="navAction" value="jump"/> <dsp:param name="DisplayText" param="relatedProduct.displayName"/> </dsp:include> <dsp:include page="../common/DisplayProductPrice.jsp" flush="true"> <dsp:param name="Product" param="relatedProduct"/>

Page 89: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 83

</dsp:include>

<%/* Create an add to cart button to add 1 of sku #1 to the cart */%> <dsp:importbean bean="/atg/commerce/order/ShoppingCartModifier"/> <dsp:getvalueof id="form67" bean="/OriginatingRequest.requestURI" idtype="java.lang.String"> <dsp:form action="<%=form67%>" method="post"> <dsp:input bean="ShoppingCartModifier.ProductId" paramvalue="relatedProduct.repositoryId" type="hidden"/> <input name="id" type="hidden" value='<dsp:valueof param="relatedProduct.repositoryId"/>'> <dsp:input bean="ShoppingCartModifier.quantity" type="hidden" value="1"/> <dsp:input bean="ShoppingCartModifier.catalogRefIds" paramvalue="relatedProduct.childSKUs[0].repositoryID" type="hidden"/> <dsp:input bean="ShoppingCartModifier.addItemToOrderSuccessURL" type="hidden" value="../checkout/cart.jsp"/> <dsp:input bean="ShoppingCartModifier.addItemToOrder" type="submit" value="Get It"/> </dsp:form></dsp:getvalueof>

</dsp:oparam> </dsp:droplet> </td> </dsp:oparam>

<dsp:oparam name="outputRowsEnd"> </tr> </dsp:oparam>

<dsp:oparam name="outputEnd"> </table> </dsp:oparam>

<dsp:oparam name="empty"> </dsp:oparam>

</dsp:droplet>

Page 90: At g Cons Comm Ref Guide

84 5 Displaying and Accessing the Product Catalog

Related Products

We wanted to display the related products in a table, so we used the TableForEach component.

It displays the data in two columns (<dsp:param name="numColumns" value="2"/>). Each

of the products is bound to the parameter relatedProduct as the droplet cycles through the

TableForEach (<dsp:param name="elementName" value="relatedProduct"/>). Because each

relatedProduct is of type product, product properties are accessible (<dsp:param name="Image"

param="relatedProduct.thumbnailImage"/>). In the “output” parameter section of the TableForEach,

we used ItemLink.jsp (described earlier) to display a link (with image) to the related product. Note that

nothing is hard-coded on this page; everything is data driven.

<dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="relatedProduct"/> <dsp:param name="Image" param="relatedProduct.thumbnailImage"/> <dsp:param name="navAction" value="jump"/> <dsp:param name="DisplayText" param="relatedProduct.displayName"/></dsp:include>

Displaying Inventory Information

For each SKU of the product on the template page, we wanted to display some inventory information: SKU

display name, price, and inventory level.

Page 91: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 85

Inventory Information

We needed this functionality on all of our template pages so we put the code into a separate code fragment

called DisplaySkuInfo.jsp.

Here is an example of how this code fragment is included from the template page. The code fragment is passed

the Product parameter whose SKUs need to be displayed:

<dsp:getvalueof id="pval0" param="Product"><dsp:include page="common/DisplaySkuInfo.jsp" flush="true"><dsp:param name="Product" value="<%=pval0%>"/></dsp:include></dsp:getvalueof>

This is the actual code for the DisplaySkuInfo.jsp code fragment:

<DECLAREPARAM NAME="Product" CLASS="java.lang.Object" DESCRIPTION="A Product repository Item - REQUIRED.">

<dsp:importbean bean="/atg/commerce/pricing/PriceItem"/>

Page 92: At g Cons Comm Ref Guide

86 5 Displaying and Accessing the Product Catalog

<dsp:importbean bean="/atg/commerce/inventory/InventoryLookup"/>

<dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="Product.childSKUs"/> <dsp:param name="elementName" value="sku"/>

<dsp:oparam name="outputStart"> <table cellspacing=4 cellpadding=0 border=0> <tr> <td> <b>Models available</b> </td> <td> Price </td> <td>&nbsp;&nbsp;</td> <td> Inventory </td> </tr> <tr><td colspan=4><hr size=0></td></tr> </dsp:oparam>

<dsp:oparam name="outputEnd"> </table> </dsp:oparam>

<dsp:oparam name="output">

<dsp:droplet name="InventoryLookup"> <dsp:param name="itemId" param="sku.repositoryId"/> <dsp:param name="useCache" value="true"/> <dsp:oparam name="output"> <tr valign=top> <td><dsp:valueof param="sku.displayName"/></td> <td> <dsp:getvalueof id="pval0" param="sku"> <dsp:include page="../../common/DisplaySkuPrice.jsp" flush="true"> <dsp:param name="Sku" value="<%=pval0%>"/> </dsp:include></dsp:getvalueof><br> </td> <td></td> <td> <dsp:valueof param="inventoryInfo.availabilityStatusMsg">Unknown </dsp:valueof></td> </tr> </dsp:oparam> </dsp:droplet> </dsp:oparam></dsp:droplet> <%/*ForEach sku droplet*/%>

To display information about each of the child SKUs of a product, we used the ForEach component. ForEach is

passed an array that is the list of SKUs associated with the product (the childSkus property value). As the code

cycles through each of the SKUs in the product, it binds the Sku parameter to the current SKU. Here is the start

of this code section:

<dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="Product.childSKUs"/> <dsp:param name="elementName" value="sku"/>. . .

Page 93: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 87

The Sku parameter that was bound in the ForEach component is used to access property values on the SKU.

Any information to be displayed on the page is put in the output parameter sections of ForEach (for example,

output, outputStart, or outputEnd). To display the SKU’s displayName attribute, we did the following:

<dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="Product.childSKUs"/> <dsp:param name="elementName" value="sku"/>. . . <dsp:oparam name="output">. . . <dsp:valueof param="sku.displayName"/>. . . </dsp:oparam></dsp:droplet>

We used the DisplaySkuPrice.jsp code fragment to display the SKU’s price.

<dsp:getvalueof id=""pval10" param="sku"><dsp:include page="../../common/DisplaySkuPrice.jsp" flush="true"> <dsp:param name="Sku" value="<%=pval0%>"/></dsp:include></dsp:getvalueof>

We used the InventoryLookup component (which is explained in more detail later) to display the SKU’s

inventory level:

<dsp:droplet name="InventoryLookup"> <dsp:param name="itemId" param="sku.repositoryId"/> <dsp:param name="useCache" value="true"/> <dsp:oparam name="output"> <dsp:valueof param="sku.displayName"/> <dsp:getvalueof id="pval0" param="sku"> <dsp:include page="../../common/DisplaySkuPrice.jsp" flush="true"> <dsp:param name="Sku" value="<%=pval0%>"/> </dsp:include> </dsp:getvalueof><br> <dsp:valueof param="inventoryInfo.availabilityStatusMsg">Unknown </dsp:valueof> </dsp:oparam></dsp:droplet>

Adding Compare Functionality

We included a Compare with Other Products button on the bike template pages that allows customers to

compare two bikes side by side.

Adding Historical Navigation

The top of each product template page includes historical navigation information that makes it easy for users to

jump back to previously viewed pages.

Page 94: At g Cons Comm Ref Guide

88 5 Displaying and Accessing the Product Catalog

These features are covered in detail below in another section so we can continue to focus on the process of

creating template pages.

Category Template Pages

Just as template pages are created to display products, template pages are also created to display categories.

The Pioneer Cycling store has six different category template pages. These templates can be found in the

<ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/web-app/en/catalog/ directory. The templates

are:

• category_clothing.jsp

• category_parts.jsp

• category_bundle.jsp

• category_bikes.jsp

• category_all_bikes.jsp

• category_generic.jsp

Page 95: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 89

category_generic.jsp

The category_generic.jsp template page displays products of type Accessory. The following example is

the code for this JSP fragment:

<% /*This template displays all subcategories or products of any categoryin a four column format.*/ %>

<DECLAREPARAM NAME="id" CLASS="java.lang.String" DESCRIPTION="The id of the category to display"><DECLAREPARAM NAME="navAction" CLASS="java.lang.String" DESCRIPTION="How to change the navigation history. Choices are push, pop and jump. Blank is treated as push.">

<dsp:setvalue bean="/atg/userprofiling/Profile.currentLocation" value="catalog_category"/>

Page 96: At g Cons Comm Ref Guide

90 5 Displaying and Accessing the Product Catalog

<dsp:droplet name="/atg/commerce/catalog/CategoryLookup"> <dsp:param bean="/OriginatingRequest.requestLocale.locale" name="repositoryKey"/> <dsp:oparam name="output">

<%/* Send a Views Item event that we browsed this category */%> <dsp:droplet name="/atg/commerce/catalog/CategoryBrowsed"> <dsp:param name="eventobject" param="element"/> </dsp:droplet>

<dsp:include page="../common/HeadBody.jsp" flush="true"> <dsp:param name="pagetitle" param="element.displayName"/> </dsp:include>

<dsp:include page="../common/StoreBrand.jsp" flush="true"></dsp:include>

<span class=storelittle> <dsp:include page="common/breadcrumbs.jsp" flush="true"><dsp:param name="displaybreadcrumbs" value="true"/></dsp:include> </span> <p>

<!-- 2 column table --> <table cellspacing=0 cellpadding=0 border=0>

<!-- 1st column: --> <tr valign=top> <td> <dsp:img src="../images/cat7.jpg"/> </td> <td>&nbsp;&nbsp;</td> <td> <!-- Show promotions --> <dsp:include page="../common/DisplayMediaSlot.jsp" flush="true"><dsp:param name="NumToDisplay" value="1"/></dsp:include> <p> <!-- Search --> <dsp:include page="../search/SimpleSearchFragment.jsp" flush="true"><dsp:param name="FormAction" value="SimpleSearch.jsp"/></dsp:include> </td> </tr> </table>

<span class=storebig> Browse <dsp:valueof param="element.displayName"/> </span> <p> <% /* Note that we are displaying both child categories and products here. In our catalog setup, any category that uses this template will have only childProducts or childCategories but not both. Therefore we can DisplayChildren of both kinds like this and one will be empty and thus render invisible. */ %>

<dsp:include page="DisplayChildrenGeneric.jsp" flush="true"> <dsp:param name="ChildArray" param="element.childCategories"/>

Page 97: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 91

<dsp:param name="Columns" value="4"/> </dsp:include>

<dsp:include page="DisplayChildrenGeneric.jsp" flush="true"> <dsp:param name="ChildArray" param="element.childProducts"/> <dsp:param name="Columns" value="4"/> </dsp:include>

<p>

<dsp:include page="../common/CatalogFooter.jsp" flush="true"></dsp:include> <dsp:include page="../common/Copyright.jsp" flush="true"></dsp:include>

</dsp:oparam></dsp:droplet>

JSP Fragment Parts

CurrentLocation

The user profile has a property called currentLocation that may be used by complex targeting systems to

enable the targeter to know what kind of targeting information is available. It is an enumerated property type

whose choices are unknown, home, store_home, shopping_cart, catalog_category, catalog_product,

catalog_search, checkout, and profile. Because this is a category page, we set the property like this:

<dsp:setvalue bean="/atg/userprofiling/Profile.currentLocation" value="catalog_category"/>

HTML Header and Title

The HeadBody.jsp fragment is invoked on every page in the site to render the HTML header and the opening

<BODY> tag. This is a good example of separating fragments that are used on every page into reusable droplets.

This fragment takes a pagetitle parameter and uses it as the HTML <TITLE>. In passing this parameter

to HeadBody.jsp we used the single quotation mark ( ' ) syntax to evaluate '"Pioneer Cycling - "

+request.getParameter("element.displayName")' as a Java statement. When '"Pioneer Cycling

- " +request.getParameter("element.displayName")' is compiled, executed, and rendered on the

browser, the user sees “Pioneer Cycling – Accessories.”

CategoryLookup Component

We used the CategoryLookup component to get a Category object for a particular category ID. The id

parameter required by CategoryLookup is passed to this page as a URL parameter, so it doesn’t have to

be passed again here. The user’s locale is passed as the repositoryKey parameter so that the category is

generated in the proper language. (See the section on Adding Support for Multi Locales for more information).

In response to these parameters, CategoryLookup binds the element parameter to the Category repository

item that it found. The element parameter is in scope between the <dsp:oparam name="output"> and </

dsp:oparam> tags.

CategoryBrowsed Droplet

We used the CategoryBrowsed component to generate an ItemViewed message for this category. The

message can have any number of listeners. The listener for the ItemViewed event is a ATG Consumer Commerce

Page 98: At g Cons Comm Ref Guide

92 5 Displaying and Accessing the Product Catalog

scenario that logs the message to a data set that can later produce reports on the categories viewed, how often

they were viewed, and by which users. Pioneer Cycling has an additional listener that is a scenario that records

the category in the user’s categoriesViewed property.

Product display pages use the ProductBrowsed component, which is analogous to CategoryBrowsed.

Navigation History

We used breadcrumbs.jsp to collect and display navigation history information. It is also invoked by product

pages. For more information, please refer to the section on Adding Catalog Navigation (page 95).

Displaying Child Products and Categories

Repository items of type Category have the properties childProducts and childCategories. These

properties describe the hierarchical relationships between categories and between categories and products.

DisplayChildrenGeneric.jsp is invoked to display the products and categories in the hierarchy contained

by the current category. It displays an array of repository items in a table. The parameter is supplied to specify

the number of columns in the table and the array to display.

Displaying Promotions

We used DisplayMediaSlot.jsp to display promotions. A “slot” is an ATG Relationship Management Platform

term for a generic place to hold objects. Slots can be populated in many ways but the most common way is by

using a scenario. DisplayMediaSlot.jsp can display any number of objects from the slot. In this page, it is

invoked with the numToDisplay parameter set to “1”.

Search

We used SimpleSearchFragment.jsp to display the Search form on each catalog page. There are two kinds

of searching in Pioneer Cycling: simple search and advanced search. The formAction parameter indicates

which search method to use.

Using the Cache Component to Improve Performance of Java Server Pages

The Cache component allows ATG products to serve content straight from an in-memory cache rather than

recomputing the content each time. When used properly, the Cache component can help the performance of

your site.

We chose to use the Cache component on the page that displays the whole bike categories,

category_all_bikes.jsp. We recommend that you use the Cache component on a small number of popular

pages. The pages you choose should have content that is not personalized, or, if it is personalized, it should not

have many options. Most of the content on cateogry_all_bikes.jsp is the same for every person who looks

at it.

Cache was used for the portion of the page that shows the title “Browse Whole Bikes” and the listing of the

categories of bikes, with a link for each bike, a picture, and a description.

Page 99: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 93

If ATG is rewriting URLs then the Cache component must disable itself unless it is told that there are no URLs

in the content being cached. In this example, the Cache component is used on those sections of the page

that include no URLs. We also used the key parameter because there are many possible values for the output

parameter of this invocation of the Cache component. The following version is actually included in Pioneer

Cycling.

Note: The “. . .” markers in the following code sample indicate a place where code has been removed to clarify

the sample.

<span class=storebig> Browse <dsp:valueof param="element.displayName"/></span>

. . .

<dsp:droplet name="ForEach"> <dsp:param name="array" param="element.childCategories"/> <dsp:param name="elementName" value="childCategory"/> <dsp:param name="indexName" value="CategoryIndex"/>

Page 100: At g Cons Comm Ref Guide

94 5 Displaying and Accessing the Product Catalog

<dsp:oparam name="outputStart"> <table cellspacing=5 cellpadding=0 border=0> </dsp:oparam>

<dsp:oparam name="outputEnd"> </table> </dsp:oparam>

<dsp:oparam name="output">

<tr valign=top> <td> <dsp:droplet name="/atg/dynamo/droplet/IsNull"> <dsp:param name="value" param="childCategory.thumbnailImage.url"/> <dsp:oparam name="false"> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="childCategory"/> <dsp:param name="Image" param="childCategory.thumbnailImage"/> <dsp:param name="DisplayText" value=""/> <dsp:param name="navAction" value="push"/> </dsp:include> </dsp:oparam> </dsp:droplet>

</td> <td></td>

<td> <!-- Display a link to the page which displays the Category: --> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="childCategory"/> <dsp:param name="navAction" value="push"/> </dsp:include><br>

<dsp:droplet name="/atg/dynamo/droplet/Cache"> <dsp:param name="hasNoURLs" value="true"/> <dsp:param name="key" param="childCategory.repositoryId"/> <dsp:oparam name="output"> <dsp:valueof param="childCategory.longDescription"> No description.</dsp:valueof> </dsp:oparam> </dsp:droplet> </td>

</tr>

</dsp:oparam>

</dsp:droplet>

Page 101: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 95

Adding Catalog Navigation

Hierarchical Navigation

To make it easy to browse the product catalog in the Pioneer Cycling site, we organized the product catalog into

a hierarchy of categories, with several levels of subcategories and products. To impose this hierarchy, we defined

properties of category called childProducts and childCategories that are lists of categories or products as

appropriate. There is no limit to the number of places that a single item (product or category) can appear as a

child.

Navigation down the hierarchy in any category can be shown in a Java Server Page by rendering a link to each of

the childProducts and childCategories. There are many ways to use the hierarchy to display the content in

the catalog. This manual describes one way to navigate.

On the parts category page of Pioneer Cycling that shows bike parts, the left panel contains a display of all

the categories of parts and their subcategories, using an iterative approach. This implementation requires

knowing the depth of the tree, or at least knowing how deep you want to traverse. We used the JSP snippet

below, CategoryTreeTwoLevel.jsp, to generate the content of the panel. It retrieves the parts category

with the CategoryLookup component. The itemId parameter required by CategoryLookup is supplied by

the Java Server Page that invokes this fragment. Then, a ForEach component renders a link to each of the

Page 102: At g Cons Comm Ref Guide

96 5 Displaying and Accessing the Product Catalog

childCategories of parts. For each child category of parts, a nested ForEach component renders a link to

each of its child categories.

<dsp:droplet name="/atg/commerce/catalog/CategoryLookup"><dsp:param bean="/atg/dynamo/servlet/RequestLocale.locale" name="repositoryKey"/>

<dsp:oparam name="output">

<dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="element.childCategories"/> <dsp:param name="elementName" value="child"/> <dsp:param bean="/OriginatingRequest.requestLocale.locale" name="repositoryKey"/>

<dsp:oparam name="output"> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="child"/> <dsp:param name="navAction" value="jump"/> <dsp:param name="Image" param="child.thumbnailImage"/> <dsp:param name="DisplayText" param="child.displayName"/> </dsp:include> <br>

<dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="child.childCategories"/> <dsp:param name="elementName" value="grandChild"/>

<dsp:oparam name="output"> &nbsp; &nbsp; <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="grandChild"/> <dsp:param name="navAction" value="jump"/> <dsp:param name="Image" param="grandChild.thumbnailImage"/> <dsp:param name="DisplayText" param="grandChild.displayName"/> </dsp:include>

<br> </dsp:oparam> </dsp:droplet>

</dsp:oparam> </dsp:droplet>

</dsp:oparam></dsp:droplet>

You could also use a recursive algorithm to display the whole tree. The JSP code sample below retrieves the

category on the top-most level with the CategoryLookup component and invokes itself to recurse through

the child categories. A category ID is passed in as the ID parameter in the URL. The major drawback of this

implementation is that if your tree has any circular references (for example, a category containing an ancestor

category as a child category), it will recurse forever.

<dsp:importbean bean="/atg/dynamo/droplet/ForEach"/><dsp:importbean bean="/atg/commerce/catalog/CategoryLookup"/><!-- display all products under this category and subcategories.

Page 103: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 97

-- the id parameter specifies the repository id of the top-most -- category. --><dsp:droplet name="CategoryLookup"> <dsp:param bean="/OriginatingRequest.requestLocale.locale" name="repositoryKey"/> <dsp:param name="id" param="id"/> <dsp:oparam name="output"> <!-- display a link to each product in a category --> <!-- the param element is a Category object passed in to this droplet --> <dsp:droplet name="ForEach"> <dsp:param name="array" param="element.childProducts"/> <dsp:oparam name="output"> <dsp:getvalueof id="templateUrl" param="element.template.url" idtype="java.lang.String"> <dsp:a href="<%=templateUrl%>"><dsp:valueof param="element.displayName"/></dsp:a> </dsp:getvalueof><br> </dsp:oparam> </dsp:droplet> <!-- recursively call this droplet to display products for all childCategories --> <dsp:droplet name="ForEach"> <dsp:param name="array" param="element.childCategories"/> <dsp:oparam name="output"> <dsp:getvalueof id="repId" param="element.repositoryId"> <dsp:include page="test.jsp" flush="true"> <dsp:param name="id" value="<%=repId%>"/> </dsp:include> </dsp:getvalueof> </dsp:oparam> </dsp:droplet> </dsp:oparam></dsp:droplet>

Historical Navigation

The top of the template pages displays historical navigation information by listing the hierarchy of categories

that a customer has already visited on his or her way to the current page.

For example, if a customer navigates from the top of the Pioneer Cycling catalog to the Clothing page, to the

Men’s Jersey’s page, and then to the product page for a men’s Black Arrow Jersey, the navigation history (at the

top of the page) looks like this:

Page 104: At g Cons Comm Ref Guide

98 5 Displaying and Accessing the Product Catalog

Each of the items in the hierarchical navigation is a link. For example, if the user clicks on Clothing she is

brought to the category template page for clothing.

If a customer goes directly from the home page to the product page of the Black Arrow Jersey (for example,

if it was a featured product on the home page) the historical navigation also reflects the catalog’s hierarchical

structure. We wanted the navigation path to be intuitive to the customer and therefore, the historical navigation

list reflects the category pages so that the user can have a sense of how this item fits into the catalog. To do this,

we invoke the CatalogNavHistoryCollector component with the navAction parameter set to “jump”.

There are two parts to using historical navigation: collecting the information and displaying it.

Collecting the Navigation History

We used the component /atg/commerce/catalog/CatalogNavHistoryCollector to collect the locations

visited and to add them to the array of visited locations. In Pioneer Cycling, the visited locations are the

repository items that represent the products and categories of the product catalog. This snippet, taken from

breadcrumbs.jsp, invokes the CatalogNavHistoryCollector:

<dsp:droplet name="/atg/dynamo/droplet/Switch"> <dsp:param name="value" param="no_new_crumb"/>

Page 105: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 99

<dsp:oparam name="true"> </dsp:oparam> <dsp:oparam name="default"> <dsp:droplet name="CatalogNavHistoryCollector"> <dsp:param name="navAction" param="navAction"/> <dsp:param name="item" param="element"/> </dsp:droplet> </dsp:oparam></dsp:droplet>

There are two things to note in this JSP snippet. Although both these techniques are specific to the Pioneer

Cycling implementation, they can be applied to any site. First, notice that the required parameter navCount is

not passed to CatalogNavHistoryCollector. The navCount parameter is set in the Java Server Page that

invokes this JSP snippet. Since JSP files invoked as servlets from other JSP files are in a sub-scope of their callers,

they have access to the same parameters as the caller. Second, we used the no_new_crumb parameter to decide

whether to invoke the snippet. This is just a switch on a parameter passed to the page to determine whether

to add the current location to the NavHistory or not. However, it demonstrates how we decided to address

navigation for pages that do not represent catalog items. For example, the search page, the shopping cart, and

the user profile page are not added to the NavHistory like regular catalog pages.

Displaying the Navigation History

The property /atg/commerce/catalog/CatalogNavHistory.navHistory is a LinkedList of locations.

The CatalogNavHistoryCollector populates this list as described in the preceding section. The following

snippet demonstrates how the navigation history is displayed in Pioneer Cycling. A ForEach component

iterates over the NavHistory list and a link is created for each item in the list. Comments in the JSP describe the

variations from that behavior.

<dsp:droplet name="/atg/dynamo/droplet/Switch"> <dsp:param name="value" param="displaybreadcrumbs"/> <dsp:oparam name="true">

<dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param bean="CatalogNavHistory.navHistory" name="array"/> <dsp:param name="elementName" value="crumb"/> <dsp:oparam name="output">

<dsp:droplet name="/atg/dynamo/droplet/Switch"> <% /* ------------------------------------------------- * We want to put a separator between the items in the navHistory. In * this example we put a greater than sign between them. We use a * switch droplet to identify the first item in the array because we * don't want to render a separator before the first item. * ------------------------------------------------- */ %> <dsp:param name="value" param="count"/> <dsp:oparam name="1"> </dsp:oparam>

<dsp:oparam name="default"> &gt; </dsp:oparam>

</dsp:droplet>

<dsp:getvalueof id="countStr" param="count" idtype="Integer">

Page 106: At g Cons Comm Ref Guide

100 5 Displaying and Accessing the Product Catalog

<dsp:getvalueof id="catNavCount" bean="/atg/commerce/catalog/CatalogNavHistory.navCount"> <dsp:getvalueof id="templateUrl" idtype="String" param="crumb.template.url">

<dsp:droplet name="/atg/dynamo/droplet/Switch"> <% /* ------------------------------------------------- * Use a switch droplet to compare size to count. When * they are the same, then we are on the last item in * array iterated by the ForEach. * ------------------------------------------------- */ %> <dsp:param name="value" param="size"/> <dsp:oparam name="<%=countStr.toString()%>"> <dsp:droplet name="/atg/dynamo/droplet/Switch"> <% /* ------------------------------------------------- * The last item in the list is generally the item we are * currently visiting and should therefore not be a link. * In some cases, when we do not want to add a new breadcrumb, * we want the last item to be a link. We do this on the * shopping cart page, search page, and others. This is * indicated by the "no_new_crumb" parameter. * ------------------------------------------------- */ %> <dsp:param name="value" param="no_new_crumb"/> <dsp:oparam name="true"> <dsp:a page="<%=templateUrl%>"> <dsp:valueof param="crumb.displayName"/> <%-- These set for breadcrumb navigation: --%> <dsp:param name="navAction" value="pop"/> <dsp:param name="id" param="crumb.repositoryId"/> <dsp:param name="navCount" value="<%=catNavCount%>"/> </dsp:a> </dsp:oparam> <dsp:oparam name="default"> <dsp:valueof param="crumb.displayName"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> <dsp:oparam name="default"> <dsp:a page="<%=templateUrl%>"> <dsp:valueof param="crumb.displayName"/> <% /* These set for breadcrumb navigation: */ %> <dsp:param name="navAction" value="pop"/> <dsp:param name="id" param="crumb.repositoryId"/> <dsp:param name="navCount" value="<%=catNavCount%>"/> </dsp:a> </dsp:oparam> </dsp:droplet> </dsp:getvalueof> </dsp:getvalueof> </dsp:getvalueof>

</dsp:oparam> </dsp:droplet>

</dsp:oparam>

Page 107: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 101

</dsp:droplet> <%/* end ForEach */%>

Searching and Filtering the Catalog

Product Searching

Searching allows customers to find certain products that satisfy a set of criteria. In the Pioneer Cycling store, we

implemented two types of searching—simple and advanced. Simple searches allow a customer to search the

catalog for keywords. In an advanced search, a customer can also search using additional product attributes

such as manufacturer, weight range, or part number.

Simple Search

In a simple search, customers search the catalog by entering text. For example, a customer could search

for “shatter-proof helmets.” The customer’s input is searched as a complete string (for example, the

above query would be based on “shatter-proof helmets” and not “shatter-proof” or “helmets”). We used

SearchFormHandler to build up the query to apply to the SQL Repository (Product Catalog) and to return

a result set (empty if no products are found). The result set(s) is a collection of repository items that can be

displayed in any format. Simple searches return both products and categories.

Page 108: At g Cons Comm Ref Guide

102 5 Displaying and Accessing the Product Catalog

We used properties or configuration files to define the action a page performs and Boolean arguments to

specify the type of searching to perform. Property names specify which catalog properties to search. The single

configurable form handler simplifies catalog searching and should support all searching requirements. If your

store requires custom searching beyond configuration changes, you can easily extend this form handler or write

another handler.

We implemented the simple search feature as a collection of Java Server Pages, page fragments, and a

Nucleus component configuration file (properties file). The configuration file creates and configures a Nucleus

component of type atg.commerce.catalog.SearchFormHandler to perform the search. The simple search

functionality allows the user to enter a string and get products or categories that match the keywords as results.

Products have multiple properties such as keywords, description, longDescription, and displayName.

Keywords are a one-to-many property and the others are one-to-one properties. A keyword search looks

through all keyword properties for a string input. A text search looks through all text properties for a string

input, but does not search keyword properties.

SimpleSearchFragment.jsp

We wanted customers to be able to search from other store pages besides the search page,

SimpleSearch.jsp. Therefore, we created the small search form fragment that defines the search text entry

field and the Search button to embed in any page that requires searching.

Page 109: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 103

This fragment imports the CatalogSearch component in order to configure its searchInput property and

invoke its seach handler.

Finally, the form redirects to the page name passed via the FormAction parameter. This page fragment declares

a page parameter that is used as the action for the search form. In other words, the FormAction parameter is

the file name of the page to which the user is redirected when the search form’s submit method is invoked.

The following example shows SimpleSearchFragment.jsp:

<!--This page fragment is intended to be imbedded on any page thatrequires a search capability. The FormAction parameter is the nameof the page to redirect to display the results of the search.-->

<DECLAREPARAM NAME="FormAction" CLASS="java.lang.String" DESCRIPTION="The name of the that will be the action of this form">

<dsp:importbean bean="/atg/userprofiling/Profile"/><dsp:importbean bean="/atg/commerce/catalog/CatalogSearch"/><dsp:importbean bean="/atg/commerce/catalog/CatalogNavHistory"/>

<!-- Title: Simple Search Form Fragment -->

<dsp:getvalueof id="successUrl" param="FormAction" idtype="java.lang.String"><dsp:form action="<%=successUrl%>" method="POST">

Search for <dsp:input bean="CatalogSearch.searchInput" size="20" type="text"/> <input name="repositoryKey" type="hidden" value='<dsp:valueof bean="/OriginatingRequest.requestLocale.locale"/>'> <!-- use this hidden form tag to make sure the search handler is invoked if someone does not hit the submit button --> <dsp:input bean="CatalogSearch.search" type="hidden" value="Search"/> <dsp:input bean="CatalogSearch.search" type="submit" value="Search"/>

</dsp:form></dsp:getvalueof>

The hidden input tag is an HTML (rather than ATG-specific) workaround for forms with a single input field. The

hidden input allows the user to invoke the submit method by typing the return key within the text input field.

Without this hidden tag, the user must click the Search button.

SimpleSearch.jsp

SimpleSearch.jsp is a page built of several JSP code fragments that provide their own specific functionality.

The SimpleSearch page uses two search-specific page fragments: SimpleSearchFragment.jsp accepts the

search input string and invokes the search itself and SearchResultsFragment.jsp displays the search results.

SimpleSearchFragment.jsp takes a parameter called FormAction to indicate the page to go to when the

form is submitted. In SimpleSearch.jsp, this value is set to SimpleSearch.jsp, causing the search page to

reload when the search completes.

SearchResultsFragment.jsp is passed the value of the CatalogSearch component’s

searchResultsByItemType parameter in order to format the results.

The following example shows SimpleSearch.jsp:

<%/* This JSP code demonstrates how to perform a simple search on the

Page 110: At g Cons Comm Ref Guide

104 5 Displaying and Accessing the Product Catalog

* product catalog repository. The CatalogSearch form handler component takes customer input and returns search results. This form handler is configured with the following prooperties: * * doKeywordSearch=true # search for input in keyword properties * keywordPropertyNames=keywords # which properties to search * itemTypes=category,product # which repository item types to search * $scope=session # the CatalogSearch component is session scoped * * The CatalogSearch form handler's handleSubmit() method will * generate a repository query based on the input. The results of * the query are retrieved via the * CatalogSearch.searchResultsByItemType property and passed to the * SearchResultsFragment JSP droplet to be displayed. */%><dsp:importbean bean="/atg/dynamo/droplet/ForEach"/><dsp:importbean bean="/atg/commerce/catalog/CatalogSearch"/><dsp:importbean bean="/atg/commerce/catalog/Navigator"/><dsp:setvalue bean="/atg/userprofiling/Profile.currentLocation" value="catalog_search"/>

<dsp:include page="../common/HeadBody.jsp" flush="true"></dsp:include>

<dsp:include page="../common/StoreBrand.jsp" flush="true"></dsp:include>

<span class=storelittle> <% /* Display Navigation information (list of all parent * categories as links users can jump to) */ %> <dsp:include page="../catalog/common/breadcrumbs.jsp" flush="true"> <dsp:param name="displaybreadcrumbs" value="true"/> <dsp:param name="no_new_crumb" value="true"/> </dsp:include></span><p><span class=storebig>Shop by Searching</span><br><p><table cellpadding=0 cellspacing=2 border=0> <tr> <td class=box-top-store>Simple Search</td> </tr> <tr> <td class=box> <% /* Use the SimpleSearchFragment JSP droplet, which contains * a simple form, to get the search input and to invoke the * CatalogSearch component's handleSubmit() method. */ %> <dsp:include page="SimpleSearchFragment.jsp" flush="true"><dsp:param name="FormAction" value="SimpleSearch.jsp"/></dsp:include> <p> <dsp:a href="AdvancedSearch.jsp">Use the advanced search form

Page 111: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 105

instead</dsp:a> <br><font size=-1><i>Simple search result set size limited to 50.</i></font><br> </td> </tr></table><p><%/* Display the results with the SearchResultsFragment JSP droplet */%><dsp:getvalueof id="pval0" bean="CatalogSearch.searchResultsByItemType"><dsp:include page="SearchResultsFragment.jsp" flush="true"><dsp:param name="ResultArray" value="<%=pval0%>"/></dsp:include></dsp:getvalueof>

<p>

<dsp:include page="../common/CatalogFooter.jsp" flush="true"></dsp:include><dsp:include page="../common/Copyright.jsp" flush="true"></dsp:include>

</BODY></HTML>

SearchResultsFragment.jsp

We used the SearchResultsFragment page fragment to iterate and display search results from both

SimpleSearch.jsp and AdvancedSearch.jsp. ForEach is sent the parameter array, which can take

many kinds of multi-valued objects. The type in this case is a java.util.Map that is the product of the

SearchFormHandler component.

The Map has one entry for each value specified in the itemTypes value of the component’s configuration file. In

both SimpleSearch and AdvancedSearch the itemTypes are category and product.

The Map is a collection of search result arrays and their associated itemType identifiers. The value associated

with category and product in the Map is an array of category or product items, or null if no results are found

for that key.

The category and product Map elements are extracted by iterating through the Map with the standard

ForEach component in the outermost ForEach droplet tag. For each element of the Map, the Switch

component is then invoked to distinguish the category and product elements, and to affect the order in

which they’re rendered on the page.

Once the category and product search result array is obtained, it is necessary to iterate through each element

of that array to display the results. In order to detect an empty result array, we used another Switch component,

this time for the unsetOPARAM feature of Switch that identifies an empty input array and a message is rendered

indicating that no items of the given type were found. When the array is non-null, the default OPARAM tag

of the Switch is rendered. In this case, another call to the ForEach component iterates through the array and

ItemLink.jsp is invoked for each element to properly format and display the repository item.

<%/* ---------------------------------------------------------------This jsp droplet demonstrates how to display the contents of searchthat potentially returns both category and product repository items.The one paramater, ResultArray, accepts a HashMap that containselements with the keys "category" and "product". The values of thesekeys are collections of category or product repository items found inthe search.

Page 112: At g Cons Comm Ref Guide

106 5 Displaying and Accessing the Product Catalog

----------------------------------------------------------------*/%>

<DECLAREPARAM NAME="ResultArray" CLASS="java.util.Map" DESCRIPTION="Array of Search Results">

<dsp:importbean bean="/atg/dynamo/droplet/Switch"/><dsp:importbean bean="/atg/dynamo/droplet/ForEach"/>

<dsp:droplet name="ForEach"> <dsp:param name="array" param="ResultArray"/>

<%/*Each item in this array is a Collection of Categories or Products...*/%> <dsp:param name="elementName" value="ResultCollection"/>

<dsp:oparam name="output"> <dsp:droplet name="Switch">

<%/*The key tells us if this is a Collection of Products or Categories: */%> <dsp:param name="value" param="key"/>

<%/*For the list of CATEGORIES: */%> <dsp:oparam name="category"> <b>We found these categories matching your search</b> <blockquote>

<dsp:droplet name="Switch"> <dsp:param name="value" param="ResultCollection"/> <dsp:oparam name="default"> <p> <%/*For each Category in the Collection: */%> <dsp:droplet name="ForEach"> <dsp:param name="array" param="ResultCollection"/> <dsp:param name="elementName" value="Category"/> <dsp:oparam name="output">

<%-- Display a link to the Category: --%> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="Category"/> <dsp:param name="Image" param="Category.thumbnailImage"/> <dsp:param name="DisplayText" param="Category.displayName"/> <dsp:param name="navAction" value="jump"/> </dsp:include>

<br> </dsp:oparam> </dsp:droplet> </dsp:oparam> <%-- If NO Categories returned by the search: --%> <dsp:oparam name="unset"> No category items in the catalog could be found that match your query </dsp:oparam> </dsp:droplet><%/*ForEach Category*/%>

</blockquote> <P> </dsp:oparam>

Page 113: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 107

<%/*For the list of PRODUCTS: */%> <dsp:oparam name="product"> <p> <b>We found these products matching your search</b> <blockquote><p>

<dsp:droplet name="Switch"> <dsp:param name="value" param="ResultCollection"/>

<dsp:oparam name="default">

<%/*For each Product in the Collection: */%> <dsp:droplet name="ForEach"> <dsp:param name="array" param="ResultCollection"/> <dsp:param name="elementName" value="Product"/>

<dsp:oparam name="output"> <dsp:valueof param="element.manufacturer.displayName"/>

<%/* Display a link to the Product: */%> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="Product"/> <dsp:param name="Image" param="Product.thumbnailImage"/> <dsp:param name="DisplayText" param="Product.displayName"/> <dsp:param name="navAction" value="jump"/> </dsp:include>

<br> </dsp:oparam> </dsp:droplet> <%/*ForEach Product*/%>

</dsp:oparam>

<%/*If NO Products returned by the search: */%> <dsp:oparam name="unset"> No product items in the catalog could be found that match your query<p> </dsp:oparam>

</dsp:droplet> </blockquote><P> </dsp:oparam> </dsp:droplet>

</dsp:oparam>

</dsp:droplet>

Advanced Search

In addition to the keyword and text search of the simple search functionality, the advanced search functionality

allows the customer to search for products using by part number, manufacturer, and weight range, which are

defined as product properties.

Page 114: At g Cons Comm Ref Guide

108 5 Displaying and Accessing the Product Catalog

AdvProductSearch.properties Nucleus Configuration File

The advanced search functionality uses the SearchFormHandler but requires a slightly more complex

properties file than the simple search Nucleus component. Advanced searches return only products because the

additional search criteria are based on products only.

advanced search

The following example shows the properties of the AdvProductSearch component:

$class=atg.commerce.catalog.SearchFormHandler$scope=sessiondoKeywordSearch=truekeywordsPropertyNames=keywordsdoTextSearch=truetextSearchPropertyNames=description,displayNamedoAdvancedSearch=trueadvancedSearchPropertyNames=weightRange,manufacturer,childSKUsminScore=1catalogTools=CatalogToolsitemTypes^=CatalogTools.productItemTypes

As with the simple search, the SearchFormHandler is session scoped. Keyword and text searching are

configured identically for both simple and advanced searches and they both use the same catalog. For the

enumerated types used in the search (manufacturer and weightRange), the possible values are inserted into

<dsp:select> input tags on the form. These values are not coded into the form, but instead are retrieved from

the catalog via the propertyValuesByType property of the SearchFormHandler.

Page 115: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 109

The propertyValuesByType property is a Map with property names for keys and arrays of all possible

values for the given property. By default, no enumerated property information is available from the

propertyValuesByType property of the SearchFormHandler. Instead, you must explicitly set the names of

required enumerated properties in the search component’s advancedSearchPropertyNames property. In this

case, the values are weightRange, manufacturer, and childSKUs.

The search form’s categories in the advanced search, manufacturer and weightRange, are populated with

valid choices for each item. These choices (<select> components) are loaded from the repository rather than

coded into the page so that the page need not be changed when changes are made to the repository.

To display the list of all repository items of type category we used the RepositoryValues servlet bean. It

takes an input parameter itemDescriptorName that in this case is set to category because that is the type of

repository item that we want to list. The servlet bean outputs an array of repository items that can be rendered

using the ForEach component.

For the manufacturer and weightRange query values, the product item descriptor is specified and product’s

manufacturer and weightRange properties are passed via the propertyName parameter. The definitions

of these two properties in the productCatalog.xml file differ. The manufacturer property is an item-type

property, a link to another item-descriptor, while the weightRange property is an enumerated data-type. The

RepositoryValues servlet bean is able to provide appropriate values for either type of property.

Note: The “. . .” marker in the following code sample indicate a place where code has been removed to clarify the

sample.

<item-descriptor name="product"> <table name="b2c_product" type="auxiliary"id-column-name="product_id"> <property name="manufacturer" item-type="manufacturer"column-name="manufacturer"/></item-descriptor>

<item-descriptor name="manufacturer"> . . .</item-descriptor>

AdvancedSearch.jsp:

<dsp:setvalue bean="/atg/userprofiling/Profile.currentLocation" value="catalog_search"/><dsp:importbean bean="/atg/userprofiling/Profile"/><dsp:importbean bean="/atg/dynamo/droplet/ForEach"/><dsp:importbean bean="/atg/dynamo/droplet/For"/><dsp:importbean bean="/atg/dynamo/droplet/Switch"/><dsp:importbean bean="/atg/dynamo/droplet/Compare"/><dsp:importbean bean="/atg/commerce/catalog/AdvProductSearch"/><dsp:importbean bean="/atg/commerce/catalog/RepositoryValues"/>

<HTML><HEAD><TITLE>Pioneer Cycling Store</TITLE><dsp:link rel="STYLESHEET" href="../common/Style.css" type="text/css" title="Prototype Stylesheet"/></HEAD><BODY>

<dsp:include page="../common/StoreBrand.jsp" flush="true"></dsp:include>

Page 116: At g Cons Comm Ref Guide

110 5 Displaying and Accessing the Product Catalog

<span class=storelittle><!-- Display Navigation information (list of all parent --><!-- categories as links users can jump to) --><dsp:include page="../catalog/common/breadcrumbs.jsp" flush="true"><dsp:param name="displaybreadcrumbs" value="true"/><dsp:param name="no_new_crumb" value="true"/></dsp:include></span><p><span class=storebig>Shop by Searching</span><br><p>

<dsp:form action="AdvancedSearch.jsp" method="POST"> <table cellpadding=0 cellspacing=0 border=0> <tr><td colspan=3 class=box-top-store>Advanced Search</td></tr> <tr valign=top><td class=box>

<!-- For each property specified in AdvProductSearch.advancedSearchPropertyNames, retrieve all possible property values. This allows the customer to pick one to search on for advanced searching. -->

<!-- Category --> Search in <dsp:select bean="AdvProductSearch.hierarchicalCategoryId"> <dsp:option value=""/>-- Entire Store -- <dsp:droplet name="RepositoryValues"> <dsp:param name="itemDescriptorName" value="category"/> <dsp:oparam name="output"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="values"/> <dsp:param name="sortProperties" value="+displayName"/> <dsp:oparam name="output"> <dsp:getvalueof id="option85" param="element.repositoryId" idtype="java.lang.String"> <dsp:option value="<%=option85%>"/> </dsp:getvalueof> <dsp:valueof param="element.displayName"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:select> <p>

<!-- Text Search --> Text Search for <dsp:input bean="AdvProductSearch.searchInput" size="30" type="text"/> <BR><br>

<!-- Part Number --> Search for Part Number (sku id)<br> <dsp:input bean="AdvProductSearch.propertyValues.childSKUs" size="15" type="text"/> </br>

<td class=box>&nbsp;</td>

<td class=box> Match these attributes

Page 117: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 111

<p>

<!-- Manufacturer --> Manufacturer <dsp:select bean="AdvProductSearch.propertyValues.manufacturer"> <dsp:option value=""/>-- Any -- <dsp:droplet name="RepositoryValues"> <dsp:param name="itemDescriptorName" value="product"/> <dsp:param name="propertyName" value="manufacturer"/> <dsp:oparam name="output"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="values"/> <dsp:param name="sortProperties" value="+id"/> <dsp:oparam name="output"> <dsp:getvalueof id="option140" param="element.id" idtype="java.lang.String"><dsp:option value="<%=option140%>"/></dsp:getvalueof><dsp:valueof param="element.id"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:select> <p>

Weight Range <dsp:select bean="AdvProductSearch.propertyValues.weightRange"> <dsp:option value=""/>-- Any -- <dsp:droplet name="RepositoryValues"> <dsp:param name="itemDescriptorName" value="product"/> <dsp:param name="propertyName" value="weightRange"/> <dsp:oparam name="output"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="values"/> <dsp:oparam name="output"> <dsp:getvalueof id="option174" param="element" idtype="java.lang.String"><dsp:option value="<%=option174%>"/></dsp:getvalueof><dsp:valueof param="element"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:select> <p>

<nobr> <dsp:input bean="AdvProductSearch.currentResultPageNum" type="hidden" value="1"/> <input name="repositoryKey" type="hidden" value='<dsp:valueof bean="/OriginatingRequest.requestLocale.locale"/>'> <dsp:input bean="AdvProductSearch.search" type="hidden" value="Search"/> <dsp:input bean="AdvProductSearch.search" type="submit" value=" Search "/> <p>

<dsp:a href="SimpleSearch.jsp">Search with the simple form instead</dsp:a></b> </td></tr> </table>

Page 118: At g Cons Comm Ref Guide

112 5 Displaying and Accessing the Product Catalog

</dsp:form><p>

<dsp:droplet name="Compare"> <dsp:param bean="AdvProductSearch.resultSetSize" name="obj1"/> <dsp:param bean="AdvProductSearch.maxResultsPerPage" name="obj2"/> <dsp:oparam name="greaterthan"> Now viewing results <dsp:valueof bean="AdvProductSearch.startIndex"/> to <dsp:valueof bean="AdvProductSearch.endIndex"/> out of <dsp:valueof bean="AdvProductSearch.resultSetSize"/> </dsp:oparam></dsp:droplet>

<!-- Display Results --><dsp:getvalueof id="pval0" bean="AdvProductSearch.searchResultsByItemType" idtype="java.util.Map"><dsp:include page="SearchResultsFragment.jsp" flush="true"><dsp:param name="ResultArray" value="<%=pval0%>"/></dsp:include></dsp:getvalueof>

<dsp:droplet name="Switch"> <dsp:param bean="AdvProductSearch.resultSetSize" name="value"/> <dsp:oparam name="0"> </dsp:oparam> <dsp:oparam name="default"> <dsp:droplet name="For"> <dsp:param bean="AdvProductSearch.resultPageCount" name="howMany"/> <dsp:oparam name="output"> <dsp:droplet name="Switch"> <dsp:param bean="AdvProductSearch.currentResultPageNum" name="value"/> <dsp:getvalueof id="nameval2" param="count" idtype="java.lang.Integer"> <dsp:oparam name="<%=nameval2.toString()%>"> <dsp:valueof param="count"/> </dsp:oparam> </dsp:getvalueof> <dsp:oparam name="default"> <dsp:a href="AdvancedSearch.jsp" bean="AdvProductSearch.currentResultPageNum" paramvalue="count"> <dsp:valueof param="count"/> </dsp:a> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:oparam></dsp:droplet>

<p>

<dsp:include page="../common/StoreFooter.jsp" flush="true"></dsp:include>

</BODY></HTML>

Page 119: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 113

Filtering

In the Pioneer Cycling store, we used filtering to allow the user to limit the types of parts that are displayed on

category_parts.jsp. The filtering functionality allows the user to filter parts by manufacturer, by frame types

that the parts should fit, and by the part’s general price category.

Filtering functionality is composed of three pieces:

• The Java Server Page where the filter’s criteria are specified (search/filtering.jsp).

• The PartsFilterFormHandler, which accepts the criteria and generates the

RuleBasedRepositoryItemGroup targeting object.

• The Parts Java Server Page that uses the RuleBasedRepositoryItemGroupFilter to display the filtered

parts.

The Filtering Page

The filtering page initializes the PartsFilterFormHandler with filtering criteria including a list of

manufacturers, types of frames the parts fit, and general price categories for the parts.

filtering.jsp:

<dsp:importbean bean="/atg/commerce/catalog/FilterCreator"/><dsp:importbean bean="/atg/commerce/catalog/RepositoryValues"/><dsp:importbean bean="/atg/dynamo/droplet/ForEach"/>

<dsp:include page="../common/HeadBody.jsp" flush="true"></dsp:include>

Page 120: At g Cons Comm Ref Guide

114 5 Displaying and Accessing the Product Catalog

<dsp:include page="../common/StoreBrand.jsp" flush="true"></dsp:include>

<dsp:include page="../catalog/common/breadcrumbs.jsp" flush="true"><dsp:param name="displaybreadcrumbs" value="true"/><dsp:param name="no_new_crumb" value="true"/></dsp:include></span><p>

<span class=storebig>Shop by Filtering</span><br><span class=help>Filtering allows you to see only parts in the catalog that meet the criteria that you indicate here.</span>

<p><dsp:form action="../catalog/category_parts.jsp" method="POST">

<table width=100% cellpadding=0 cellspacing=0 border=0> <tr> <td colspan=3 class=box-top-store>Filter Catalog</td> </tr>

<tr valign=top><td class=box> <dsp:input bean="FilterCreator.searchAllManufacturers" type="radio" value="true"/> Show products from all manufacturers<br> <dsp:input bean="FilterCreator.searchAllManufacturers" type="radio" value="false"/> Show products from only these manufacturers<br> <dsp:select multiple="<%=true%>" bean="FilterCreator.manufacturers" size="4"> <dsp:droplet name="RepositoryValues"> <dsp:param name="itemDescriptorName" value="product"/> <dsp:param name="propertyName" value="manufacturer"/> <dsp:oparam name="output"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="values"/> <dsp:param name="sortProperties" value="+id"/> <dsp:oparam name="output"> <dsp:getvalueof id="option76" param="element.id" idtype="java.lang.String"><dsp:option value="<%=option76%>"/></dsp:getvalueof><dsp:valueof param="element.id"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:select> <p>

<dsp:input bean="FilterCreator.anyFrame" type="radio" value="true"/> Show products that fit any frame<br> <dsp:input bean="FilterCreator.anyFrame" type="radio" value="false"/> Show only products that fit this frame<br> <dsp:select bean="FilterCreator.frameType"> <dsp:droplet name="RepositoryValues"> <dsp:param name="itemDescriptorName" value="frame-product"/> <dsp:param name="propertyName" value="frameType"/> <dsp:oparam name="output"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="values"/>

Page 121: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 115

<dsp:oparam name="output"> <dsp:getvalueof id="option116" param="element" idtype="java.lang.String"><dsp:option value="<%=option116%>"/></dsp:getvalueof><dsp:valueof param="element"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:select> <br>&nbsp;<br> </td>

<td class=box>&nbsp;&nbsp;</td>

<td class=box> <dsp:input bean="FilterCreator.priceSelectionType" type="radio" value="allPrices"/> Show products of all prices<br> <dsp:input bean="FilterCreator.priceSelectionType" type="radio" value="priceCategory"/> Show products that are priced<br> <dsp:select bean="FilterCreator.priceCategory" size="4"> <dsp:droplet name="RepositoryValues"> <dsp:param name="itemDescriptorName" value="product"/> <dsp:param name="propertyName" value="priceRange"/> <dsp:oparam name="output"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="values"/> <dsp:oparam name="output"> <dsp:getvalueof id="option166" param="element" idtype="java.lang.String"><dsp:option value="<%=option166%>"/></dsp:getvalueof><dsp:valueof param="element"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:select> <br> <P>

<%/* Set these hidden params to cause the action to return to the * previous page */%> <input name="navCount" type="hidden" value='<dsp:valueof bean="/atg/commerce/catalog/CatalogNavHistory.navCount"/>'> <input type="hidden" name="navAction" value="jump"> <input type="hidden" name="id" value="cat10005">

<dsp:input bean="FilterCreator.submit" type="submit" value=" Filter Catalog "/> </td> </tr></table>

<p>

</dsp:form>

</td>

Page 122: At g Cons Comm Ref Guide

116 5 Displaying and Accessing the Product Catalog

</tr></table>

<p>

<dsp:include page="../common/StoreFooter.jsp" flush="true"></dsp:include><dsp:include page="../common/Copyright.jsp" flush="true"></dsp:include>

</BODY></HTML>

filtering.jsp imports and uses two Nucleus components, FilterCreator and RepositoryValues. The

RepositoryValues configuration file sets a single property, the catalog for which products will be filtered.

RepositoryValues component:

$class=atg.repository.servlet.PossibleValuesrepository^=CatalogTools.catalog

The FilterCreator component configures the PartsFilterFormHandler class’s repository property with

the store catalog and sets the itemDescriptor to product, since parts are products.

FilterCreator component

$class=atg.projects.b2cstore.PartsFilterFormHandler$scope=sessionloggingIdentifier=Parts Filter Form Handlerrepository^=CatalogTools.catalogitemDescriptor=product

The PartsFilterFormHandler

The PartsFilterFormHandler accepts manufacturer names, frame types, and price categories and generates

an atg.targeting.RuleBasedRepositoryItemGroup, which is a targeting rule set made up of SGML

Content Targeting Rules.

Page 123: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 117

The PartsFilterFormHandler overrides the getRuleRepresentation() methods of its parent class. This

allows it to specify an SGML targeting rule.

Generating the Targeting Rules

The targeting rules are SGML rules that specify logical operations such as “and”, “or”, or “includes” to accept

or reject repository items. See the Creating Rules for Targeting Content chapter of the ATG Personalization

Programming Guide for more information on targeting rules.

If no filtering criteria have been entered by the user, getRuleRepresentation() returns a rule that accepts all

items:

<ruleset> <accepts> <rule op=any> </rule> </accepts>

Page 124: At g Cons Comm Ref Guide

118 5 Displaying and Accessing the Product Catalog

</ruleset>

When one or more conditions are specified via filtering.jsp, the PartsFilterFormHandler generates

an “and” rule within the <accepts> rule that contains one or more child rules that specify the actual filtering

criteria:

<ruleset> <accepts> <rule op=and> <rule op=isoneof> <valueof target="manufacturer.id"> <valueof constant="[BatGoose, DZ Supply]"> </rule> <rule op=eq> <valueof target="priceRange"> <valueof constant="EXPENSIVE"> </rule> </rule> </accepts></ruleset>

The top level <rule op=and> tag within the <accepts> tag is necessary because the filtering conditions must

be grouped together with an “and” rule. In the above case, the manufacturer.id should be one of the listed

constants and the price range should equal EXPENSIVE. Without the parent “and” rule, the filtering conditions

are grouped together with an “or” rule.

Creating the RuleBasedRepositoryItemGroup

The RuleBasedRepositoryItemGroup is a Java Targeting object created from the

SGML rules that were generated by the PartsFilterFormHandler. It is later used by the

RuleBasedRepositoryItemGroupFilter to filter an array of parts repository items.

We created the RuleBasedRepositoryItemGroup within FilterFormHandler. Within its

handleSubmit() method, the parent class, FilterFormHandler, retrieves the SGML rules from

PartsFilterFormHandler via the overridden getRuleRepresentation() method and generates a

RuleBasedRepositoryItemGroup object from those rules. The RuleBasedRepositoryItemGroup object

is available via the getRepositoryItemGroup() method of the FilterFormHandler or, in JSP, via the

repositoryItemGroup property.

You can duplicate the functionality of FilterFormHandler in your own code. The handleSubmit()

method of FilterFormHandler simply calls the TargeterUtils.createGroup() method to create the

RuleBasedRepositoryItemGroup.

Filtering and Displaying Parts Repository Items

The RuleBasedRepositoryItemGroupFilter component takes the RepositoryItemGroup created by

FilterCreator and an array of repository items and outputs a subset of the input array that contains only

those repository items that pass the filter.

ProductsLineItem.jsp Fragment

<dsp:importbean bean="/atg/dynamo/droplet/ForEach"/><dsp:importbean bean="/atg/commerce/catalog/FilterCreator"/><dsp:importbean bean="/atg/targeting/RuleBasedRepositoryItemGroupFilter"/>

<declareparam name="array" class="java.lang.Object[]"

Page 125: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 119

description="An array of repository items">

<%/* Filter the array of repository items in the page param, "array" */%><dsp:droplet name="RuleBasedRepositoryItemGroupFilter"> <dsp:param name="inputItems" param="array"/> <dsp:param bean="FilterCreator.repositoryItemGroup" name="repositoryItemGroup"/> <dsp:oparam name="output">

<%/* Display each filtered item */%>

<dsp:droplet name="ForEach"> <dsp:param name="array" param="filteredItems"/> <dsp:param name="elementName" value="childProduct"/> <dsp:oparam name="outputStart"> <table cellspacing=3 cellpadding=0 border=0> </dsp:oparam> <dsp:oparam name="output"> <tr valign=top> <td> <dsp:valueof param="childProduct.manufacturer.displayName"/> </td> <td>&nbsp;&nbsp;&nbsp;&nbsp;</td> <td colspan=3> <b> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="childProduct"/> <dsp:param name="Image" param="childProduct.thumbnailImage"/> <dsp:param name="DisplayText" param="childProduct.displayName"/> <dsp:param name="navAction" value="push"/> </dsp:include> </b> </td> </tr>

<%/* List all child SKUs of the product */%> <dsp:droplet name="ForEach"> <dsp:param name="array" param="childProduct.childSKUs"/> <dsp:param name="elementName" value="childSku"/> <dsp:oparam name="output"> <tr valign=top> <td colspan=2></td> <td> <dsp:valueof param="childSku.displayName"/> </td> <td>&nbsp;&nbsp;&nbsp;&nbsp;</td> <td align=right> <dsp:include page="../common/DisplaySkuPrice.jsp" flush="true"> <dsp:param name="Sku" param="childSku"/> <dsp:param name="Image" param="childSku.thumbnailImage"/> <dsp:param name="navAction" value="push"/> <dsp:param name="DisplayText" param="childSku.displayName"/> </dsp:include> </td> </tr> </dsp:oparam> </dsp:droplet> <tr> <td colspan=5><hr size=0></td> </tr> </dsp:oparam>

Page 126: At g Cons Comm Ref Guide

120 5 Displaying and Accessing the Product Catalog

<dsp:oparam name="outputEnd"> </table> </dsp:oparam> </dsp:droplet> </dsp:oparam></dsp:droplet>

The ProductsLineItem.jsp fragment imports the FilterCreator component to access

the parts RuleBasedRepositoryItemGroup from its repositoryItemGroup property, and

imports the RuleBasedRepositoryItemGroupFilter servlet bean in order to apply the

RuleBasedRepositoryItemGroup to each repository item. ProductsLineItem.jsp also declares a

page parameter called array that is set by the caller to the array of parts repository items to display. All the

actual filtering work is done up front in the first invocation, RuleBasedRepositoryItemGroupFilter. As

parameters, the array that’s been passed to this page fragment is passed on to the inputItems parameter in

the filter servlet bean, and the RuleBasedRepositoryItemGroup created by the PartsFilterFormHandler

(described above) is passed in as the repositoryItemGroup parameter. In the output open parameter,

the ForEach servlet bean iterates the RuleBasedRepositoryItemGroupFilter's output parameter

filteredItems that contains the list of repository items that have passed the filter.

Comparing SKUs

In the Pioneer Cycling store, we wanted users to be able to compare SKUs side by side. In this section we discuss

how we implemented this feature.

Adding Compare Functionality

We included a Compare with Other Products button on the bike template pages that allows customers to

compare two bikes side by side. When customers click this button, it adds all of the SKUs for this product to a

session-scoped SKU comparison list. Pushing the button also brings customers to the “Shop by Comparing”

page, which allows them to pick two different SKUs (typically for two different bike models) and do a side-by-

side comparison against attributes such as color, price, and weight.

Page 127: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 121

CompareSkusFormHandler

The Pioneering Cycling store uses the ATG Consumer Commerce CompareSkusFormHandler to implement this

comparison functionality. The CompareSkusFormHandler has several methods and properties that hold the

information needed to compare items. This section shows how we used these methods and properties to allow

users to compare two bikes.

First, we included the form handler component on the bike template page:

<dsp:importbean bean="/atg/commerce/catalog/CompareSkusFormHandler"/>

Then we added the button for adding SKUs to the SKU compare list. In the Pioneer Cycling store, the bike

template page includes a button that says Compare With Other Products. When the customer clicks this

button, all of the SKUs for the product are added to the SKU compare list. Here is the code for this:

Page 128: At g Cons Comm Ref Guide

122 5 Displaying and Accessing the Product Catalog

<dsp:getvalueof id="form179" bean="/OriginatingRequest.requestURI" idtype="java.lang.String"><dsp:form action="<%=form179%>" method="post">

<%/*Display any errors that have been generated during Compare *operations: */%> <dsp:include page="../common/DisplayCompareSkusFormHandlerErrors.jsp" flush="true"></dsp:include>

<input name="id" type="hidden" value='<dsp:valueof param="Product.repositoryId"/>'>

<!-- Add this ProductID to the Compare List: --> <dsp:input bean="CompareSkusFormHandler.ProductToCompare" paramvalue="id" type="hidden"/>

<%/*Goto this URL if no errors are found during Compare button *processing: */%> <dsp:input bean="CompareSkusFormHandler.AddToCompareListSuccessURL" type="hidden" value="compare_bikes.jsp"/>

<%/*COMPARE button: */%> <dsp:input bean="CompareSkusFormHandler.addToCompareList" type="submit" value="Compare With Other Products"/>

</dsp:form></dsp:getvalueof>

Adding Items to the Compare List

When the customer clicks the Compare With Other Products button on the bike product page, the page first

sets the form handler’s ProductToCompare property with the repository ID of the bike product. This is done via

a hidden text box. Here is the code that sets this property in the form handler:

<!-- Add this ProductID to the Compare List: --><dsp:input bean="CompareSkusFormHandler.ProductToCompare" paramvalue="id" type="hidden"/>

The CompareSkusFormHandler.addToCompareList method is called when the customer actually clicks the

button. It stores all of the SKUs associated with the ProductToCompare in the form handler’s SkuCompareList

property. Here is the code for the button:

<%/*COMPARE button: */%><dsp:input bean="CompareSkusFormHandler.addToCompareList" type="submit" value="Compare With Other Products"/>

The page also sets the SuccessURL property. This value tells the form handler where to redirect at the end

of the CompareSkusFormHandler.addToCompareList method. If this product is successfully added to the

compare list, the user is redirected to the compare_bikes.jsp page. Here is the code that stores this value in

the form handler:

<dsp:input bean="CompareSkusFormHandler.AddToCompareListSuccessURL" type="hidden" value="compare_bikes.jsp"/>

Page 129: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 123

Selecting two SKUs to Compare

After a user has added some SKUs to the compare list, there is a page that lets them select two specific SKUs and

do the side-by-side comparison. compare_bikes.jsp performs this functionality.

compare_bikes.jsp

The form handler’s SkuCompareList property contains the list of the SKUs that have been added thus far. This

list is used to populate two dropdown lists. The user selects one SKU from each of these dropdown lists.

Here is the code that displays a dropdown list with all of SKUs in the SkuCompareList property. The code

uses the DSP tag library’s <dsp:select> tag for creating a dropdown list. The indexes (not the repository IDs) of

the SKUs they select are stored in the form handler’s SelectedIndex1 and SelectedIndex2 properties. Here

is the code for the first SKU dropdown list:

<!-- First dropdown list: -->

Page 130: At g Cons Comm Ref Guide

124 5 Displaying and Accessing the Product Catalog

<!-- Display a drop down list of all Skus they can compare: --><!-- Store the index of their selection in the "SelectedIndex1" attribute --><!-- in the CompareSkusFormHandler. --><dsp:select bean="CompareSkusFormHandler.SelectedIndex1">

<!-- For each Sku they can select: --><dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param bean="CompareSkusFormHandler.SkuCompareList" name="array"/> <dsp:param name="elementName" value="Sku"/> <dsp:oparam name="output">

<!-- If this sku is selected, save its list INDEX in SelectedIndex1 --> <dsp:getvalueof id="option92" param="index" idtype="java.lang.Integer"> <dsp:option value="<%=option92.toString()%>"/> </dsp:getvalueof>

<!-- Display the name of the Sku in the dropdown: --> <dsp:valueof param="Sku.displayName"/>

</dsp:oparam> <!-- output param for each Sku --></dsp:droplet> <!-- ForEach Sku -->

</dsp:select>

The above dropdown code for selecting the first SKU and almost identical code for selecting the second SKU are

nested inside of a <dsp:form action> statement that includes the code for the Compare button. Here is the

remaining code:

<dsp:form action="compare_bikes.jsp" method="POST">

<!—Code for first dropdown list (see above) -->

<!—Code for second dropdown list (see above) -->

<!-- COMPARE button: -->

<dsp:input bean="CompareSkusFormHandler.compareSkus" type="submit" value=" Compare "/>

</dsp:form>

The first selected SKU for comparison can be referenced by indexing the skuCompareList. Likewise, the

product with which the SKU is associated can be referenced in the same manner.

Displaying the side-by-side values

After the SelectedIndex1 and SelectedIndex2 properties are set, the rest of compare_bikes.jsp displays

the values of the two SKUs side by side.

Because the code that displays the side-by-side values has to repeatedly get the selected SKUs and associated

products to fetch their values, we created some page parameters so the display code is easier to read and more

efficient. Here is the code that sets up the local parameters on the page:

<dsp:setvalue beanvalue="CompareSkusFormHandler.SelectedIndex1" param="Index1"/>

Page 131: At g Cons Comm Ref Guide

5 Displaying and Accessing the Product Catalog 125

<dsp:setvalue beanvalue="CompareSkusFormHandler.SkuCompareList[param:Index1]" param="Sku1"/><dsp:setvalue beanvalue="CompareSkusFormHandler.ProductCompareList[param:Index1]" param="Product1"/>

<dsp:setvalue beanvalue="CompareSkusFormHandler.SelectedIndex2" param="Index2"/><dsp:setvalue beanvalue="CompareSkusFormHandler.SkuCompareList[param:Index2]" param="Sku2"/><dsp:setvalue beanvalue="CompareSkusFormHandler.ProductCompareList[param:Index2]" param="Product2"/>

We added a link to each product at the top of the side-by-side comparison chart, in case the customer wants to

return to the product page (and, for example, add the item to her shopping cart).

A link to each product is displayed in the header of the comparison chart.

We used the ItemLink.jsp code fragment described earlier. It requires a product repository item and takes the

product page parameter that was just set in the previous code example.

<!-- Display the Product name as a link to the Product page: --><dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="Product1"/> <dsp:param name="navAction" value="push"/></dsp:include><br>

Various SKU properties are displayed in the following way:

<!-- Display the SKU display name: --><dsp:valueof param="Sku1.displayName"/>

Page 132: At g Cons Comm Ref Guide

126 5 Displaying and Accessing the Product Catalog

. . .

<!- Display the SKU display name: --><dsp:valueof param="Sku2.displayName"/>

Displaying Errors found by CompareSkusFormHandler

The pages that reference the CompareSkusFormHandler (product_bike.jsp and compare_bikes.jsp)

include the following code for displaying any form errors that are found by the CompareSkusFormHandler:

<%/*Display any errors that have been generated during Compare operations: */%><dsp:include page="../common/DisplayCompareSkusFormHandlerErrors.jsp" flush="true"></dsp:include>

This code fragment simply displays any form errors caught by the CompareSkusFormHandler.

<%/*Display any errors found by the CompareSkusFormHandler: */%><dsp:droplet name="/atg/dynamo/droplet/ErrorMessageForEach"> <dsp:param bean="CompareSkusFormHandler.formExceptions" name="exceptions"/>

<dsp:oparam name="outputStart"> <font color=cc0000><STRONG><UL> </dsp:oparam>

<dsp:oparam name="outputEnd"> </UL></STRONG></font> </dsp:oparam>

<dsp:oparam name="output"> <LI><dsp:valueof param="message"/> </dsp:oparam>

</dsp:droplet>

Page 133: At g Cons Comm Ref Guide

6 Pricing 127

6 Pricing

This chapter describes the pricing engine used in the Pioneer Cycling site and includes the following sections:

Setting Product Prices (page 127)

Describes how to set list and sale prices for each item in the repository.

Displaying Prices (page 128)

Describes how to use the PriceItem droplet to display prices.

Shipping Prices (page 130)

Describes how to create shipping prices.

Creating Your Own Pricing Models (page 132)

Describes how to further customize the pricing for your store.

Setting Product Prices

The Pioneer Cycling site demonstrates how you can use ATG Relationship Management Platform’s flexible

pricing to create both standard and promotional prices.

Standard Prices

The listPrice and salePrice are properties of SKU that are assigned by the merchant using the ACC and

stored in the catalog repository. The onSale flag determines the price to use as the base price for the item. The

catalog administrator can set these properties using the ACC.

Promotional Prices

In addition to listPrice and salePrice, it is possible to offer different prices depending on circumstances

such as timing, other products being purchased, or the customer’s profile. The actual prices displayed to the

customer are calculated dynamically using a combination of standard prices and promotions. You can use

the ACC to build the pricing models that determine promotional prices. See the section on Promotions (page

193) in the Merchandising (page 193) chapter of this guide or refer to the Using and Extending Pricing Services

chapter in the ATG Commerce Programming Guide for more information.

Page 134: At g Cons Comm Ref Guide

128 6 Pricing

Displaying Prices

To calculate or display a price in a Java Server Page, we used /atg/commerce/pricing/PriceItem.

Three different types of prices are available: listPrice, salePrice, and amount. The amount price is the

personalized price that is calculated dynamically by the pricing engine for a particular user at a particular time.

The PriceItem component calculates different prices for the same item depending on whether that item is

displayed alone (for example, in the product template page of the catalog) or in the context of the shopping

cart.

In the Pioneer Cycling store, we made a distinction between the list price for a product and the user’s price:

• list price: the full, undiscounted price of a product.

• user’s price: the price of a product for a certain user, after all discounts and promotions for which the user

is eligible have been calculated. It does not include order-specific promotions (promotions that depend on

other items on the order).

ATG Consumer Commerce enables you to set up pricing models in which a special price on one item depends on

the purchase of another item (for example, buy a bike, get a helmet at half price). When the helmet is displayed

alone, its full price (minus any single-item discounts) is displayed. Even if the customer has a bike in his shopping

cart, entitling him to the helmet at half price, the helmet still displays outside the cart at list price because there

is no way to know whether or not he will keep the bike in his cart. Therefore, promotional prices that depend on

other purchases are only displayed in the shopping cart.

Here’s another example: on Mother’s Day the Pioneer Cycling store offers all women 20% off. The list price of

a helmet is $100; the user’s price (for women) would be $80. The store also offers 50% off the price of a helmet

with the purchase of a bike. However, the user’s price displayed on the product template page does not show this

50% discount, even if the she is purchasing a bike. This order-level discount is displayed on the Shopping Cart

page. Order-level discounts and promotions are calculated with the pricing calculators and are discussed in the

Promotions (page 193) section of the Merchandising (page 193) chapter.

PriceItem takes a SKU repository item as its item parameter. It outputs an element parameter that is of type

CommerceItemImpl that we have renamed to SKU using the elementName parameter. This SKU parameter

contains a personalized PriceInfo object with an amount property that is the lowest price available to the user

at that time.

The Switch component invoked on the Format parameter allows many ways of displaying prices using a single

JSP fragment. Site developers who do not require this flexibility may want to remove this Switch for improved

performance.

The following example shows how we used the /atg/commerce/pricing/PriceItem component in the

Pioneer Cycling Store to display prices in an extremely personalized way.

<!-- Display the price of the SKU: --> <dsp:droplet name="PriceItem"> <dsp:param name="item" param="Product.childSkus[0]"/> <dsp:param name="elementName" value="Sku"/> <dsp:oparam name="output">

<!-- Check which Format caller wants: --> <dsp:droplet name="Switch"> <dsp:param name="value" param="Format"/>

<!-- Displays the List Price: --> <dsp:oparam name="ListPrice">

Page 135: At g Cons Comm Ref Guide

6 Pricing 129

<dsp:valueof converter="currency" param="Sku.priceInfo.ListPrice"/> </dsp:oparam>

<!-- Displays the SALE Price. Doesn't check if Item is on sale: --> <dsp:oparam name="SalePrice"> <dsp:valueof converter="currency" param="Sku.priceInfo.SalePrice"/> </dsp:oparam>

<!-- Displays the YOUR Price: --> <dsp:oparam name="YourOnly"> <dsp:valueof converter="currency" param="Sku.priceInfo.amount"/> </dsp:oparam>

<dsp:oparam name="All"> <br><b>All price info (for debug):</b> <br><b>OnSale: </b><dsp:valueof param="Sku.priceInfo.onSale"/> <br><b>ListPrice: </b><dsp:valueof converter="currency" param="Sku.priceInfo.ListPrice"/> <br><b>SalePrice: </b><dsp:valueof converter="currency" param="Sku.priceInfo.SalePrice"/> <br><b>Amount: </b><dsp:valueof converter="currency" param="Sku.priceInfo.amount"/> </dsp:oparam>

<dsp:oparam name="default"> <!-- Check if Product is on sale: --> <dsp:droplet name="Switch"> <dsp:param name="value" param="Sku.priceInfo.onSale"/> <dsp:oparam name="false"><dsp:valueof converter="currency" param="Sku.priceInfo.amount"> no price</dsp:valueof></dsp:oparam> <dsp:oparam name="true">On Sale for <dsp:valueof converter="currency" param="Sku.priceInfo.salePrice"> no price</dsp:valueof> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> <!-- PriceItem -->

The example above is an extremely resource-intensive way to display prices. If your site does not require

dynamic generation of personalized pricing, you can remove any invocations to the PriceItem component for

a large performance improvement.

The example below shows how you can use static pricing and still display listPrice or salePrice using the

onSale property.

<dsp:droplet name="Switch"> <dsp:param name="value" value="product.childSkus[0].onSale"/> <dsp:oparam name="false"> <dsp:valueof param="product.childSkus[0].listPrice"> </dsp:oparam> <dsp:oparam name="true"> <dsp:valueof param="product.childSkus[0].salePrice"> </dsp:oparam>

Page 136: At g Cons Comm Ref Guide

130 6 Pricing

</dsp:droplet>

Shipping Prices

ATG Consumer Commerce provides a robust framework in which multiple shipping methods and discounts can

be defined. There are two different types of shipping price calculations: calculating the base price of shipping

(for example, setting the price of any shipment via a Ground method at $5) and calculating a discount on the

existing price of shipping (for example, giving 10% off the existing shipping cost).

To calculate the base price of shipping ATG Consumer Commerce provides several different calculators:

atg.commerce.pricing.FixedPriceShippingCalculatoratg.commerce.pricing.PriceRangeShippingCalculatoratg.commerce.pricing.WeightRangeShippingCalculator

In addition to these calculators, there are several abstract calculators that can be easily extended to gain more

functionality. For more information, refer to the section on calculators in the Using and Extending Pricing Services

chapter in the ATG Commerce Programming Guide.

Shipping Methods

We chose to offer three different shipping methods to the customers of the Pioneer Cycling Store: Ground, Two

Day, and Next Day. During the checkout process, the customer elects one of these three shipping methods.

The base price of shipping for each method is calculated using a specific instance of one of the calculators

mentioned above.

Shipping Calculators

There are three steps to setting up the various shipping methods: configuring the specific calculator instances,

adding these calculator instances to our ShippingPricingEngine, and finally, displaying these shipping

methods to the customer via AvailableShippingMethods.

Configuring Shipping Calculators

In the Pioneer store, we used the atg.commerce.pricing.FixedPriceShippingCalculator for the Ground

and Two Day shipping methods and atg.commerce.pricing.PriceRangeShippingCalculator for Next

Day shipping. We created properties files to generate instances of these calculators. For example, for the Ground

shipping method, we created the Ground.properties file with the following configuration:

# Define a Ground shipping method$class=atg.commerce.pricing.FixedPriceShippingCalculator

# name of shipping methodshippingMethod=Ground# cost of shipping methodamount=5.0

Page 137: At g Cons Comm Ref Guide

6 Pricing 131

This properties file creates an instance of a shipping calculator and sets the properties of that new shipping

calculator. It gives it a method name (which is exposed to the user when they are selecting their shipping

method) and the fixed cost of shipping. In this case, when the calculator is exposed to the user as a shipping

method it has the name “Ground” associated with it and the cost is $5.

We also chose to use another instance of the FixedPriceShippingCalculator for the Pioneer store to set the

cost of all Two Day shipments to $20. This is what the properties file looks like:

# Define a Two day shipping method$class=atg.commerce.pricing.FixedPriceShippingCalculator

# name of this shipping methodshippingMethod=Two Day

# cost of shipping methodamount=20.0

Finally, we created a Next Day shipping method using an instance of the PriceRangeShippingCalculator.

The PriceRangeShippingCalculator sets the cost of shipping based upon a range of prices. Here is the

properties file that we created:

# Define a Next Day shipping method$class=atg.commerce.pricing.PriceRangeShippingCalculator

# name of shipping methodshippingMethod=Next Day

# range of costsranges=00.00:10.00:10.00,\ 10.01:30.00:15.00,\ 30.01:50.00:20.00,\ 50.01:70.00:25.00,\ 70.01:100.00:30.00,\ 100.01:MAX_VALUE:40.00

The first line of the ranges property states, “if the cost is between $0 and $10, set the price to $10.” The next line

states, “if the price is between $10.01 and $30 set the price to $15.” This allows the cost of shipping to vary based

upon the amount of the order.

We created these properties files at the Nucleus namespace at:

/atg/commerce/pricing/shipping/Ground.properties/atg/commerce/pricing/shipping/TwoDay.properties/atg/commerce/pricing/shipping/NextDay.properties

Adding Calculators to Pricing Engine

The ShippingPricingEngine is a structural component that does not actually calculate shipping prices itself.

The actual price calculations are performed by the Pricing Calculator components that are configured as the

preCalculators and postCalculators properties of the ShippingPricingEngine. This component is

located at /atg/commerce/pricing/ShippingPricingEngine.

# Calculators that ShippingPricingEngine will use to determine shippingpreCalculators=shipping/Ground,\

Page 138: At g Cons Comm Ref Guide

132 6 Pricing

shipping/NextDay,\ shipping/TwoDay

The values shipping/Ground and shipping/NextDay and shipping/TwoDay are Nucleus references to the

calculators that were defined above.

Enabling Customers to Choose Shipping Methods

During the checkout process, the customer must choose from a list of available shipping methods. Then, the

selected method is associated with the shipping group selected in the customer’s order.

To display the various shipping methods that a customer can choose in the Pioneer Cycling store, we used the

servlet bean located at /atg/commerce/pricing/AvailableShippingMethods. It renders an array of the

availableShippingMethods.

We used the AvailableShippingMethods servlet bean instead of accessing the availableMethods property

of the ShippingPricingEngine for several reasons. Most importantly, the AvailableShippingMethods

servlet bean determines which shipping methods are available for a particular shipping group. For example, an

order going to Alaska might not be able to be shipped overnight.

<droplet bean="AvailableShippingMethods"> <param name="shippingGroup" value="bean:ShoppingCartModifier.shippingGroup"> <param name="pricingModels" value="bean:UserPricingModels.shippingPricingModels"> <param name="profile" value="bean:Profile"> <oparam name="output"> <select bean="ShoppingCartModifier.shippingGroup.shippingMethod"> <droplet bean="ForEach"> <param name="array" value="param:availableShippingMethods"> <param name="elementName" value="method"> <oparam name="output"> <option value="param:method"><valueof param="method"></valueof> </oparam> </droplet> </select> </oparam></droplet>

This JSP does several things. First, it exposes the list of available shipping methods via the parameter

availableShippingMethods of the AvailableShippingMethods servlet bean. Second, it iterates

through this array using a ForEach component. Third, it sets the name of the shipping method that the

user selects in the shippingGroup. In this case, it is the single shippingGroup that is created in an Order.

If there was more than one shipping group, like on some of the Pioneer Cycling store checkout pages

(shipping_info_multiple.jsp) then the correct shippingGroup would need to be referenced by iterating

through the list of shipping groups in the order, and calling the AvailableShippingMethods servlet bean for

each of them.

Creating Your Own Pricing Models

A PricingModel is a RepositoryItem that can calculate the price of an item, given a method to calculate a

price and parameters to use. The pricing models available in ATG Consumer Commerce take care of the pricing

Page 139: At g Cons Comm Ref Guide

6 Pricing 133

needs for most store sites, but you may want to offer your own promotions. This section describes the pricing

models that exist in ATG Consumer Commerce and how to create your own pricing model.

ATG Consumer Commerce supports the following types of pricing calculators:

• Percent discount (for example, 5% off all helmets)

• Set price (for example, buy 2 jerseys, get 1 jersey for $20)

• Amount off (for example, $5 off all gloves).

Discounts can be applied to any of the following:

• Items (objects in shopping cart)

• Order (the order total)

• Tax (tax on an order)

• Shipping (cost of shipping).

A pricing model has two pieces of information, pricingCalculatorService and adjuster. The

pricingCalculatorService specifies which calculator to use to determine the discount. The adjuster

indicates how much to adjust. For example, in the case of a percent discount, if the adjuster is set to 5, the

calculator takes a 5% discount off an item, an order, tax, or shipping charges.

Page 140: At g Cons Comm Ref Guide

134 6 Pricing

Page 141: At g Cons Comm Ref Guide

7 Order Processing 135

7 Order Processing

This chapter describes the order capture functionality in the Pioneer Cycling site and includes the following

sections:

Reviewing and Editing Orders (page 135)

Describes how customers can review and modify production information in their orders.

Entering Information Necessary for Checkout (page 146)

Describes how to enter shipping and payment information.

Additional Order Capture Information (page 164)

Describes how to validate credit cards and display error messages.

Express Checkout (page 165)Describes how to set up express checkout capabilities.

Order capture can be broken down into two large parts: allowing the user to review and edit items chosen for

purchase, and capturing the information necessary for the payment and shipping of their purchased goods.

There are two different methods for the second part of the process in the Pioneer Cycling store: a basic process

and a more advanced process.

Note: The order processing pages for Pioneer Cycling (JSP version) use a mixture of tags from the DSP tag

libraries. It might seem more intuitive to write the pages using only DSP tags, but we replaced some of the DSP

tags with simpler core tags to work around the limitations of some application servers’ JSP execution engines.

Reviewing and Editing Orders

checkout/cart.jsp and checkout/full/cart_edit.jsp allow users to review and edit their product

selections and quantities.

Page 142: At g Cons Comm Ref Guide

136 7 Order Processing

snapshot of cart.jsp

Page 143: At g Cons Comm Ref Guide

7 Order Processing 137

snapshot of cart_edit.jsp

The Shopping Cart Page

The “Shopping Cart” page, cart.jsp, gives users the ability to

• View the line item pricing for a particular item.

• View an order subtotal.

• Change the quantity ordered of a particular item in their cart.

• Delete items from their shopping cart.

The following sections discuss how each of these pieces of functionality is performed.

Item Pricing

To view the current price of an item, all of the items in the Shopping Cart must be accessed and all of the correct

price information for each of these items must be accessed.

The component located at /atg/commerce/order/OrderHolder is responsible for maintaining all of

the customer’s orders. (ATG Consumer Commerce allows a customer to have more than one shopping cart

Page 144: At g Cons Comm Ref Guide

138 7 Order Processing

where she can store orders. See the Working With Purchase Process Objects chapter of the ATG Commerce

Programming Guide for more information on the OrderHolder component.) The Pioneer Cycling store

supports a single Shopping Cart that is referenced in the OrderHolder component at /atg/commerce/

order/OrderHolder.current. We accessed this component through the /atg/commerce/order/

ShoppingCartModifier component. The ShoppingCartModifier component has an order property that

is the current order from the OrderHolder component. We used the following JSP to reference the customer’s

current Order object:

<dsp:valueof bean="ShoppingCartModifier.Order"/>

On the Shopping Cart page, the contents of the customer’s order are displayed with a ForEach component

that iterates through each ShippingGroup in the order and then iterates through the items in each. It would

be possible to simply have the ForEach iterate through each CommerceItem in the order directly. However, we

decided that each ShippingGroup and its items should be displayed separately. The following code snippet,

taken from cart.jsp, shows the nested iteration.

<% atg.servlet.DynamoHttpServletRequest dRequest = atg.servlet.ServletUtil.getDynamoRequest(request);%>

. . .

<dsp:droplet name="ForEach"> <dsp:param bean="ShoppingCartModifier.Order.ShippingGroups" name="array"/> <dsp:param name="elementName" value="ShippingGroup"/> <dsp:param name="indexName" value="shippingGroupIndex"/> <dsp:oparam name="output"> <core:switch value='<%= dRequest.getParameter("ShippingGroup.shippingGroupClassType") %>'>

<core:case value="electronicShippingGroup">

<dsp:droplet name="ForEach"> <dsp:param name="array" param="ShippingGroup.CommerceItemRelationships"/> <dsp:param name="elementName" value="CiRelationship"/> <dsp:param name="indexName" value="index"/> <dsp:oparam name="outputStart"> <tr><td colspan=13>&nbsp;<Br>Electronic delivery to <dsp:valueof param="ShippingGroup.emailAddress"> unknown email address</dsp:valueof>: <hr size=0></td></tr> </dsp:oparam> <dsp:oparam name="output"> <dsp:setvalue param="itemName" value='<%= "item" + request.getParameter("shippingGroupIndex") + ":" +request.getParameter("index")%>'/> <%@ include file="CartLineItem.jspf" %> </dsp:oparam> </dsp:droplet> </core:case>

<core:defaultCase>

<dsp:droplet name="IsGiftShippingGroup"> <dsp:param name="sg" param="ShippingGroup"/> <dsp:oparam name="false"> <dsp:droplet name="ForEach">

Page 145: At g Cons Comm Ref Guide

7 Order Processing 139

<dsp:param name="array" param="ShippingGroup.CommerceItemRelationships"/> <dsp:param name="elementName" value="CiRelationship"/> <dsp:param name="indexName" value="index"/> <dsp:oparam name="outputStart"> <tr><td colspan=13>&nbsp;<br>Shipping to you:<hr size=0></td></tr> </dsp:oparam> <dsp:oparam name="output"> <dsp:setvalue param="itemName" value='<%="item" + request.getParameter("shippingGroupIndex") + ":" +request.getParameter("index")%>'/> <%@ include file="CartLineItem.jspf" %> </dsp:oparam> </dsp:droplet> </dsp:oparam> <dsp:oparam name="true">

<dsp:droplet name="ForEach"> <dsp:param name="array" param="ShippingGroup.CommerceItemRelationships"/> <dsp:param name="elementName" value="CiRelationship"/> <dsp:param name="indexName" value="index"/> <dsp:oparam name="outputStart"> <tr><td colspan=13>&nbsp;<br>Gifts <dsp:droplet name="GiftlistLookupDroplet"> <dsp:param name="id" param="giftlistId"/> <dsp:param name="elementName" value="giftlist"/> <dsp:oparam name="output"> for <dsp:valueof param="giftlist.owner.firstName"/> <dsp:valueof param="giftlist.owner.lastName"/> </dsp:oparam> </dsp:droplet>: <hr size=0></td></tr> </dsp:oparam> <dsp:oparam name="output"> <dsp:setvalue param="itemName" value='<%="item" + request.getParameter("shippingGroupIndex") + ":" +request.getParameter("index")%>'/> <%@ include file="CartLineItem.jspf" %> </dsp:oparam> </dsp:droplet>

</dsp:oparam> </dsp:droplet>

</core:defaultCase> </core:switch> </dsp:oparam></dsp:droplet>

Getting Correct Price Information

Each CommerceItem maintains price information in its priceInfo property, which then in turn has an amount

property that is the currently calculated cost of the commerce item.

The priceInfo object returned is an instance of ItemPriceInfo. It contains information on the current price

of a CommerceItem such as list price, a list of discounts applied to the item, and the item’s onSale status.

The following JSP code demonstrates the simplest case of displaying an item’s price, assuming that the

commerce item has a parameter named commerceItem.

Page 146: At g Cons Comm Ref Guide

140 7 Order Processing

<dsp:valueof converter="currency" param="commerceItem.priceInfo.amount"/>

Note that the currency TagConverter used here automatically formats the price so it is displayed in a particular

localized format.

The following JSP code displays the prices:

The price of the commerce item is:<dsp:valueof converter="currency" param="commerceItem.priceInfo.amount"/>

In the Pioneer store, we wanted to display both the sale price and the list price for an item. We used the compare

servlet bean to determine if the item has been discounted, and if so, to display both the list price as well as the

discounted price.

<%-- Figure out if there have been discounts applied to this item. If there have then print out both the discounted and undiscounted price. else just print out the current total price.--%><core:exclusiveIf>

<core:ifEqual object1='<%= drequest.getParameter("CiRelationship.commerceItem.priceInfo.amount") %>' object2='<%= drequest.getParameter("CiRelationship.commerceItem.priceInfo.rawTotalPrice") %>'> <td></td><td></td> <td></td> <td align=right> <dsp:valueof converter="currency" param="CiRelationship.amountByAverage"/> </td>

<td> </td> </core:ifEqual> <core:defaultCase> <td></td> <td align=right> <span class=strikeout><dsp:valueof converter="currency" param="CiRelationship.commerceItem.priceInfo.rawTotalPrice"/></span>

</td> <td></td> <td align=right> <dsp:valueof converter="currency" param="CiRelationship.commerceItem.priceInfo.amount"/> </td> </core:defaultCase> </core:exclusiveIf>

Viewing Order Subtotal

The cart.jsp also displays the subtotal of the order: the sum of all the commerce items minus any order-level

discounts. The ShoppingCartModifier accesses the subtotal. The priceInfo object of the Order object itself

holds price-level information.

Page 147: At g Cons Comm Ref Guide

7 Order Processing 141

<dsp:valueof converter="currency" bean="ShoppingCartModifier.order.priceInfo.amount"/>

Again in the Pioneer Cycling, store, we wanted to show customers their subtotals both before discounts and

after discounts.

We used the following JSP code to perform this functionality:

<%-- Obtain two subtotals. One that represents the subtotal after promotions and then the one from before promotions. This allows the end user to view the two different ones. --%>

<dsp:getvalueof id="subtotal" bean="ShoppingCartModifier.order.priceInfo.rawSubtotal">

Page 148: At g Cons Comm Ref Guide

142 7 Order Processing

<dsp:getvalueof id="amount" bean="ShoppingCartModifier.order.priceInfo.amount"><core:exclusiveIf> <core:ifEqual object1="<%=subtotal%>" object2="<%=amount%>"> <tr> <td colspan=9 align=right>Subtotal</td> <td></td> <td align=right> <b><dsp:valueof converter="currency" bean="ShoppingCartModifier.order.priceInfo.amount"/></b> </td> </tr> </core:ifEqual> <core:defaultCase> <tr> <td colspan=9 align=right>Subtotal with order discount</td> <td></td> <td align=right> <span class=strikeout> <dsp:valueof converter="currency" bean="ShoppingCartModifier.order.priceInfo.rawSubtotal"/> </span> </td> <td></td> <td align=right> <b><dsp:valueof converter="currency" bean="ShoppingCartModifier.order.priceInfo.amount"/></b> </td> </tr> </core:defaultCase></core:exclusiveIf></dsp:getvalueof></dsp:getvalueof>

We used the Compare servlet bean to check the actual amount of the order with the rawSubtotal of the order.

The rawSubtotal is the subtotal before any order level discounts are applied.

Changing the Quantity Ordered of an Item

The customer can change the quantity of an item in the Shopping Cart by entering a new number

into the quantity field and clicking Recalculate. cart.jsp then returns with the quantity reset. The

ShoppingCartModifier component performs this function.

The new quantity is passed to the server via the catalogRefId parameter and then the

ShoppingCartModifier method, handleSetOrder(), is invoked. If the quantity parameter passed in is

different than the current quantity of the commerce item, the new quantity is set.

Here is the JSP code to indicate the quantity of a particular item:

<dsp:input type="text" size="4" bean="item.catalogRefId" beanvalue="item.quantity"/>

This generates a text box that is set to the current quantity of the commerce item.

To pick up the changed quantity, it is necessary to invoke the handleSetOrder method of the

ShoppingCartModifier component by submitting the form to the handleSetOrder method. This is what the

submit JSP tags look like.

Page 149: At g Cons Comm Ref Guide

7 Order Processing 143

<!-- RECALCULATE Order button: --><!-------------------------------><dsp:input bean="ShoppingCartModifier.setOrderByRelationshipId" type="submit" value="Recalculate"/> &nbsp; &nbsp;

<!-- GoTo this URL if user pushes RECALCULATE button and there are no errors: --><dsp:input bean="ShoppingCartModifier.setOrderByRelationshipIdSuccessURL" type="hidden" value="cart.jsp"/> <!-- stay here -->

<!-- GoTo this URL if user pushes RECALCULATE button and there are errors: --><dsp:input bean="ShoppingCartModifier.setOrderByRelationshipIdErrorURL" type="hidden" value="cart.jsp"/> <!-- stay here -->

In the Pioneer Cycling store, we wanted a user to always return to the Shopping Cart page (cart.jsp) after he

has submitted a form to display either error messages or the new cart contents. (Displaying error messages is

discussed in detail later in this chapter.)

Deleting Items from the Cart

cart.jsp enables customers to delete items from their Shopping Cart either by checking the ‘X’ button or by

setting the quantity of an item to zero.

Using the ‘X’ Checkbox

To remove items from the Shopping Cart by checking the ‘X’ button, two things must happen. First, the Ids of

the items to be removed must be passed to the form handler. Second, the handleSetOrder() method in the

ShoppingCartModifier must be invoked.

The ShoppingCartModifier component has a property named removalCatalogRefIds, which is an

array property of the catalogRefIds that need to be removed from the cart. Thus, every item in this array is

removed from the cart. The JSP for each checkbox looks like:

<dsp:input bean="ShoppingCartModifier.removalCatalogRefIds" paramvalue="item.catalogRefId " type="checkbox" checked="<%=false%>"/>

As the cart.jsp iterates through each commerce item, it sets the parameter item to the current commerce

item and then references its catalogRefId. The catalogRefId is added to the array only if the box is checked.

For more information on using form elements in JSP, please consult the Creating Forms chapter in the ATG Page

Developer’s Guide.

When the form is submitted to the handleSetOrder method, the method iterates through the catalogRefId

objects in the removalCatalogRefIds property and removes them from the current order.

Invoking the handleSetOrder method is described in the section Changing the Quantity Ordered of An Item.

Setting Quantity to Zero

Removing an item by setting its quantity to zero is the same as changing an item order to any quantity. The

ShoppingCartModifier detects if the quantity equals zero. When this happens, the item is automatically

removed from the cart.

About cart_edit.jsp

The cart_edit.jsp gives the customer the ability to

Page 150: At g Cons Comm Ref Guide

144 7 Order Processing

• Delete an item from an order

• Change the quantity of an item

• Change the SKU instance of an item

Deleting items from the order is the simplest feature here and depends on the

handleRemoveItemFromOrder method of the ShoppingCartModifier. The other two features utilize the

handleRemoveAndAddItemToOrder method.

Deleting Items

The previous sections discussed one way to remove items from an order—setting the removalCatalogRefIds

property and then invoking the handleSetOrder method on the form submit. However, if it is only necessary

to remove items in this call, without also changing quantities, this can be accomplished by setting the

removalCommerceIds property and then invoking the handleRemoveItemFromOrder method.

The following JSP adds an item to the removalCatalogRefIds property.

<dsp:input bean="ShoppingCartModifier.removalCommerceIds" beanvalue="ShoppingCartModifier.commerceItemToEdit.Id" type="hidden"/>

This always adds the ID of the commerce itemToEdit to the removalCommerceIds property. If the form is

submitted to the handleRemoveItemFromOrder method using the following JSP code, the item is removed.

<!-- Delete the item from the cart --><dsp:input bean="ShoppingCartModifier.RemoveItemFromOrder" type="submit" value="Delete"/>

Changing the Quantity or SKU Instance of an Item

This page provides one place where a user can change the quantity or the SKU instance of a commerce item.

Since this functionality has to be provided by one form method, the handler needs to be general enough to

handle both situations. For more information on how the handleRemoveAndAddItemToOrder method works,

see the section on the ShoppingCartModifier component in the Configuring Merchandising Services chapter

in the ATG Commerce Programming Guide.

To change either the quantity or the SKU instance of an item, the item is actually deleted and a new one is

added. Therefore, to change either, the quantity and the catalogRefId are the minimum pieces of information

necessary to create a new commerce item. The removalCommerceId always is set as described in the previous

section. The quantity is set by setting a form element to the quantity property of the ShoppingCartModifier.

<!-- Display the current quantity of the item --><dsp:input bean="ShoppingCartModifier.quantity" beanvalue="ShoppingCartModifier.commerceItemToEdit.quantity" size="4" type="text"/>

The following JSP sets the catalogRefId that will be added:

<br>Model Selection</b><br>

<!-- Set the product id, so this item can be added to the cart if necessary --><dsp:input bean="ShoppingCartModifier.productId" beanvalue="ShoppingCartModifier.commerceItemToEdit.auxiliaryData

Page 151: At g Cons Comm Ref Guide

7 Order Processing 145

.productRef.repositoryId" type="hidden"/>

<!-- Display all the child SKUs for this particular SKUs parent product --><!-- When displaying info to the user, grab the displayableSKUAttributes--><!-- property from the product and iterate through the list getting--><!-- the SKU property for each one--><dsp:select bean="ShoppingCartModifier.catalogRefIds"><dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param bean="ShoppingCartModifier.commerceItemToEdit.auxiliaryData.productRef.childSKUs" name="array"/> <dsp:param name="elementName" value="sku"/> <dsp:param name="indexName" value="skuIndex"/> <dsp:oparam name="output"> <dsp:droplet name="/atg/commerce/catalog/DisplaySkuProperties"> <dsp:param name="delimiter" value=" | "/> <dsp:param name="sku" param="sku"/> <dsp:param bean="ShoppingCartModifier.commerceItemToEdit.auxiliaryData.productRef" name="product"/> <dsp:param name="displayElementName" value="outputString"/> <dsp:oparam name="output"> <dsp:droplet name="/atg/dynamo/droplet/Switch"> <dsp:param name="value" param="sku.repositoryId"/>

<!-- Current Sku object is the same as one in cart, make it default selected --> <dsp:getvalueof id="nameval2" bean="ShoppingCartModifier.commerceItemToEdit.auxiliaryData.catalogRef.repositoryId" idtype="java.lang.String"><dsp:oparam name="<%=nameval2%>"> <dsp:getvalueof id="option131" param="sku.repositoryId" idtype="java.lang.String"><dsp:option selected="<%=true%>" value="<%=option131%>"/></dsp:getvalueof><dsp:valueof param="sku.displayName"/> </dsp:oparam></dsp:getvalueof>

<!-- The current Sku object does NOT match one is the shopping cart, don't make selected --> <dsp:oparam name="default"> <dsp:getvalueof id="option139" param="sku.repositoryId" idtype="java.lang.String"><dsp:option value="<%=option139%>"/></dsp:getvalueof><dsp:valueof param="sku.displayName"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> <dsp:oparam name="empty"> <dsp:droplet name="/atg/dynamo/droplet/Switch"> <dsp:param name="value" param="sku.repositoryId"/>

<!-- Current Sku object is the same as one in cart, make it default selected --> <dsp:getvalueof id="nameval2" bean="ShoppingCartModifier.commerceItemToEdit.auxiliaryData.catalogRef.repositoryId" idtype="java.lang.String"><dsp:oparam name="<%=nameval2%>"> <dsp:getvalueof id="option157" param="sku.repositoryId" idtype="java.lang.String"><dsp:option selected="<%=true%>" value="<%=option157%>"/></dsp:getvalueof><dsp:valueof param="sku.displayName"/> </dsp:oparam>

Page 152: At g Cons Comm Ref Guide

146 7 Order Processing

</dsp:getvalueof>

<!-- Current Sku object does NOT match one is the shopping cart, don't make selected --> <dsp:oparam name="default"> <dsp:getvalueof id="option165" param="sku.repositoryId" idtype="java.lang.String"><dsp:option value="<%=option165%>"/></dsp:getvalueof><dsp:valueof param="sku.displayName"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> <dsp:oparam name="empty"> <dsp:getvalueof id="option177" param="sku.repositoryId" idtype="java.lang.String"><dsp:option selected="<%=true%>" value="<%=option177%>"/></dsp:getvalueof><dsp:valueof param="sku.displayName"/> </dsp:oparam> </dsp:droplet> </dsp:oparam></dsp:droplet>

</dsp:select>

The catalogRefId of the selected items is set to the catalogRefId property of the ShoppingCartModifier.

Inside the select tags, all the child SKUs of a particular product are listed out as a select box by the ForEach

component in conjunction with the productRef.childSkus property. For each of these child SKUs, the

DisplaySkuProperties servlet bean returns the displayable SKU properties or, if it returns nothing, shows the

sku.displayName.

The submit of this form then goes to the method handleRemoveAndAddItemToOrder method.

In Pioneer Cycling, the entire order is repriced on the Shopping Cart page because a customer may have

accumulated promotions while navigating the site. Repricing the order causes all the price changes to be

resaved and therefore SQL is sent to the database. Even if there are no new promotions, the order is repriced. As

a performance optimization, you may decide to remove the JSP code snippet that forces the order to be repriced

when the customer simply views the Shopping Cart page. If the customer changes something in the cart, or

moves to the confirmation page, the order is still repriced.

The line of JSP to remove is:

<dsp:setvalue bean="ShoppingCartModifier.repriceOrder" value="ORDER_SUBTOTAL"/>

If you remove this line, when a customer looks at the Shopping Cart page, the order is not repriced, and the

subsequent SQL is not be generated. Another option is to tie the repricing functionality to a submit button (such

as “recalculate”) to optimize performance.

Please note that SQL is only generated for registered users, not for anonymous users.

Entering Information Necessary for Checkout

The second part of the order capture is the checkout process. This process collects information such as shipping

information and payment information. In the Pioneer Cycling store, we implemented two different types of

checkout processes: basic and advanced.

Page 153: At g Cons Comm Ref Guide

7 Order Processing 147

The basic process allows a user to perform the following functions:

• Enter a single address that all purchased goods can be sent to

• Enter a single credit card number as a payment method

The advanced checkout process adds to this functionality by allowing:

• The ability to ship to multiple locations

• The ability to pay with multiple payment methods

We used the ShoppingCartModifier component to allow a user to perform these operations.

Secure Checkout

When customers enter credit card information in the checkout process or on the My Profile page, they

must be transferred from a non-secure server to a secure server. In order to configure this, you must

change the properties of the ProtocolChange component. You can edit these properties in the Pages and

Components> Components by path section of the ACC. The component pathname is /atg/dynamo/droplet/

ProtocolChange.

Set the values of the secure properties appropriate to your server. Note that the securePort (the port setup for

secure server) need not equal 443.

Set the enable property to true.

Page 154: At g Cons Comm Ref Guide

148 7 Order Processing

Changing the properties of atg/dynamo/droplet/ProtocolChange in the Component Editor

If you do not change these values, the ProtocolChange component stays in non-secure (http) protocol. This is

useful for development environments.

On cart.jsp, the ProtocolChange servlet bean is invoked to set the success URL for the checkout button to

the SSL equivalent of its former location.

Page 155: At g Cons Comm Ref Guide

7 Order Processing 149

On the co_confirm page, ProtocolChange is invoked again, but this time it sets the success URL of the

confirm button to the non-SSL equivalent of the former Thank You page.

Choosing Between Basic and Advanced Checkout

In the Pioneer Cycling store, we provided users with the ability to switch between basic and advanced checkout

processes. By default, the English locale uses the advanced checkout process and the French and Japanese

sections use the basic checkout. The moveToPurchaseInfoSuccessURL parameter in cart.jsp is set to

determine which checkout process is used. Here is the JSP to use basic checkout:

<!-- CHECKOUT Order button: -->

<dsp:input bean="ShoppingCartModifier.moveToPurchaseInfoByRelId" type="submit" value="Checkout"/>

<dsp:input bean="ShoppingCartModifier.moveToPurchaseInfoByRelIdSuccessURL" type="hidden" value="basic/order_info.jsp"/>

<dsp:input bean="ShoppingCartModifier.moveToPurchaseInfoByRelIdErrorURL" type="hidden" value="cart.jsp"/>

The basic process is used if the moveToPurchaseInfoSuccessURL parameter is set to basic/

order_info.jsp. Thus, if the above JSP is uncommented the basic checkout process is used. The following

section creates a button for advanced checkout.

<dsp:input bean="ShoppingCartModifier.moveToPurchaseInfoByRelId" type="submit" value=" Checkout"/>

<%--Use this snippet if you want regular http checkout:<dsp:input bean="ShoppingCartModifier.moveToPurchaseInfoByRelIdSuccessURL" type="hidden" value="full/order_info.jsp"/>

--%>

<%-- Use this snippet if you want to use SSL checkout:--%><dsp:droplet name="/atg/dynamo/droplet/ProtocolChange"> <dsp:param name="inUrl" value="full/order_info.jsp"/> <dsp:oparam name="output"> <dsp:input bean="ShoppingCartModifier.moveToPurchaseInfoByRelIdSuccessURL" paramvalue="secureUrl" type="hidden"/> </dsp:oparam></dsp:droplet>

<dsp:input bean="ShoppingCartModifier.moveToPurchaseInfoByRelIdErrorURL" type="hidden" value="cart.jsp"/>

Basic Checkout Process

The basic checkout process allows a customer to send an order to a single location using a single credit card as a

form of payment.

Page 156: At g Cons Comm Ref Guide

150 7 Order Processing

When an order is created, it has by default one shipping group and one payment group:

atg.commerce.order.HardgoodShippingGroup and atg.commerce.order.CreditCard. If these are

the only two groups that exist when a user checks out, then relationship objects are created between

the commerce items in the order and these groups. shipItemRel links all the commerce items to the single

shipping group; payItemRel links all the commerce items to the single payment group. The basic checkout

process populates these two groups.

Basic Shipping Information Input

Shipping information entered by the customer includes the address and method. The customer can input

information to the shipping group by entering the shipping address or copying the billing address to the

shipping address. The customer also can select from a choice of shipping methods.

This JSP enters shipping information into the order object:

<!-- Store the ProfileID in ownerId field of the new address. This tells us this address "belongs to" (and can be edited) by the user. --><dsp:input bean="ShoppingCartModifier.shippingGroup.shippingAddress.ownerId" beanvalue="Profile.id" type="hidden"/>

<dsp:getvalueof id="firstName" bean="ShoppingCartModifier.shippingGroup.shippingAddress.firstName">

<% if((firstName== null) || (firstName.equals(""))) { %> <dsp:input bean="ShoppingCartModifier.shippingGroup.shippingAddress.firstName" beanvalue="Profile.shippingAddress.firstName" size="15" type="text"/><%} else {%> <dsp:input bean="ShoppingCartModifier.shippingGroup.shippingAddress.firstName" size="15" type="text"/><%}%>

</dsp:getvalueof>

<dsp:getvalueof id="middleName" bean="ShoppingCartModifier.shippingGroup.shippingAddress.middleName">

<% if((middleName== null) || (middleName.equals(""))) { %> <dsp:input bean="ShoppingCartModifier.shippingGroup.shippingAddress.middleName" beanvalue="Profile.shippingAddress.middleName" size="10" type="text"/><%} else {%> <dsp:input bean="ShoppingCartModifier.shippingGroup.shippingAddress.middleName" size="10" type="text"/><%}%>

</dsp:getvalueof>

There are several things to notice about this JSP. First, note that each piece of information

is inserted somewhere in the default shipping group. The line <dsp:input type="text"

bean="ShoppingCartModifier.shippingGroup.shippingAddress"> refers to the default shipping group

Page 157: At g Cons Comm Ref Guide

7 Order Processing 151

address and then to each element of the shippingAddress. For example, shippingAddress.state refers to

the state to which the order is sent.

If the default shippingAddress is not empty, it displays the information in it. If it is empty, it tries to populate

those fields with information from the user’s Profile object. If a customer is registered and is going through the

checkout process for the first time, her shipping address information is prepopulated with her information from

her Profile. She can then choose to use this information or to change it. The customer can also go back to the

store to add other items to her cart.

This JSP sets the ownerId of the address to the id of the current user’s Profile. This indicates that the user is

the owner of this address and has the ability to edit that address if necessary.

Copy billing address to shipping address

In the basic checkout, an unregistered customer must enter a billing address first. She can then elect to copy the

billing address information to her shipping address or enter a new one. The ability to copy the billing address is

handled by the following JSP:

<!-- Indicate if the billing address should be copied to the shipping address --> <dsp:input bean="ShoppingCartModifier.CopyBillingAddrToShippingAddr" name="copyAddress" type="radio" checked="<%=true%>" value="true"/> Ship items to my billing address<br> <dsp:input bean="ShoppingCartModifier.CopyBillAddrToShippingAddr" name="copyAddress" type="radio" value="false"/> Ship items to the new address below <p>

If the customer decides to use her billing address as the shipping address, the Boolean property

ShoppingCartModifier.copyBillingAddrToShippingAddr remains true, and the billing address that she

entered is automatically copied to the shipping address. Otherwise, the Boolean property is set to false and

nothing is copied.

Choosing a shipping Method

Finally, the customer must select a shipping method such as ground or next day. The following JSP code handles

shipping method selection. Note: The “. . .” marker in the following code sample indicate a place where code has

been removed to clarify the sample.

<dsp:importbean bean="/atg/commerce/pricing/UserPricingModels"/><dsp:importbean bean="/atg/commerce/pricing/AvailableShippingMethods"/><dsp:importbean bean="/atg/userprofiling/Profile"/><dsp:importbean bean="/atg/commerce/order/ShoppingCartModifier"/><dsp:importbean bean="/atg/dynamo/droplet/ForEach"/><dsp:importbean bean="/atg/dynamo/droplet/Switch"/>

. . .

<!-- The droplet AvailableShippingMethods is able to give us a list of the shipping methods that should be available to a user. --> Shipping Method<br><dsp:droplet name="AvailableShippingMethods"><dsp:param bean="ShoppingCartModifier.shippingGroup" name="shippingGroup"/><dsp:param bean="UserPricingModels.shippingPricingModels" name="pricingModels"/><dsp:param bean="Profile" name="profile"/><dsp:oparam name="output"> <dsp:select bean="ShoppingCartModifier.shippingGroup.shippingMethod">

Page 158: At g Cons Comm Ref Guide

152 7 Order Processing

<dsp:droplet name="ForEach"> <dsp:param name="array" param="availableShippingMethods"/> <dsp:param name="elementName" value="method"/> <dsp:oparam name="output"> <dsp:getvalueof id="option199" param="method" idtype="java.lang.String"><dsp:option value="<%=option199%>"/></dsp:getvalueof><dsp:valueof param="method"/> </dsp:oparam> </dsp:droplet> </dsp:select></dsp:oparam></dsp:droplet>

The shipping method that the customer selects is inserted into the shippingMethod property of the

shippingGroup. For more information, consult the Shipping Prices (page 130) section of the Pricing chapter in

this document.

ATG Consumer Commerce also supports soft goods. For example, gift certificates are delivered via e-mail. See

the Gift Certificates (page 200) section in the Merchandising chapter of this guide for more information.

Billing information

The second part of the basic checkout process is when the customer enters the billing address and credit

information.

For more information about credit card properties, see the Working with Purchase Process Objects chapter of the

ATG Commerce Programming Guide.

Getting billing address information

The following JSP captures the billing address for the credit card:

Name<br><dsp:droplet name="IsEmpty"> <dsp:param bean="ShoppingCartModifier.paymentGroup.billingAddress.firstName" name="value"/> <dsp:oparam name="false"><dsp:input bean="ShoppingCartModifier.paymentGroup.billingAddress.firstName" size="15" type="text"/> </dsp:oparam> <dsp:oparam name="true"><dsp:input bean="ShoppingCartModifier.paymentGroup.billingAddress.firstName" beanvalue="Profile.billingAddress.firstName" size="15" type="text"/> </dsp:oparam></dsp:droplet>

<dsp:droplet name="IsEmpty"> <dsp:param bean="ShoppingCartModifier.paymentGroup.billingAddress.middleName" name="value"/> <dsp:oparam name="false"><dsp:input bean="ShoppingCartModifier.paymentGroup.billingAddress.middleName" size="10" type="text"/> </dsp:oparam> <dsp:oparam name="true"><dsp:input bean="ShoppingCartModifier.paymentGroup.billingAddress.middleName" beanvalue="Profile.billingAddress.middleName" size="10" type="text"/> </dsp:oparam>

Page 159: At g Cons Comm Ref Guide

7 Order Processing 153

</dsp:droplet>

Credit Card Information

In addition to the address associated with the paymentGroup, it is necessary to obtain credit card information

such as the credit card number and expiration date. The credit card object in the order is accessed through the

paymentGroup property of the ShoppingCartModifier.

New card type <dsp:select bean="ShoppingCartModifier.paymentGroup.creditCardType" required="<%=true%>"><dsp:option value="Visa"/>Visa<dsp:option value="MasterCard"/>Master Card<dsp:option value="AmericanExpress"/>American Express<dsp:option value="Discover"/>Discover</dsp:select><br>

New card number and expiration date<br><dsp:input bean="ShoppingCartModifier.paymentGroup.creditCardNumber" maxsize="20" size="20" type="text" required="<%=true%>"/><br>

<!-- Set the month that the card will expire on -->Month: <dsp:select bean="ShoppingCartModifier.paymentGroup.expirationMonth"><dsp:option value="1"/>January<dsp:option value="2"/>February<dsp:option value="3"/>March<dsp:option value="4"/>April<dsp:option value="5"/>May<dsp:option value="6"/>June<dsp:option value="7"/>July<dsp:option value="8"/>August<dsp:option value="9"/>September<dsp:option value="10"/>October<dsp:option value="11"/>November<dsp:option value="12"/>December</dsp:select>

<!-- Set the year that the card will expire on -->Year: <dsp:select bean="ShoppingCartModifier.paymentGroup.expirationYear"><dsp:option value="2000"/>2000<dsp:option value="2001"/>2001<dsp:option value="2002"/>2002<dsp:option value="2003"/>2003<dsp:option value="2004"/>2004<dsp:option value="2005"/>2005<dsp:option value="2006"/>2006<dsp:option value="2007"/>2007<dsp:option value="2008"/>2008<dsp:option value="2009"/>2009<dsp:option value="2010"/>2010</dsp:select>

Advanced Checkout Process

The advanced checkout process builds on the basic checkout process by giving the user the ability to ship to

multiple locations as well as to pay with multiple forms of payment. While the user can elect to ship/pay with

Page 160: At g Cons Comm Ref Guide

154 7 Order Processing

one method in the advanced checkout process, we do not discuss that here since it mirrors that of the basic

checkout process. The ability to ship and pay with multiple methods are examined.

Ability to Ship to Multiple Locations

The ability to ship items to multiple locations is defined as the ability to take an order and split the items up into

multiple shipping groups. For example, a customer with five bikes and five water bottles in his shopping cart can

choose to send the bikes to his house using over night delivery and the water bottles to his work address using

second day mail. Or, he could send one bike and two water bottles to his summer house and the remainder to

another address.

Two different pages compose provide this functionality: ship_to_multiple.jsp and

shipping_info_multiple.jsp. Both of these files can be found in <ATG9dir>/PioneerCyclingJSP/j2ee-

apps/pioneer/web-app/en/checkout/full.

ship_to_multiple.jsp creates new shipping groups in the order and assigns each commerceItem to them.

shipping_info_multiple.jsp populates any missing address information to the shipping groups/profile.

ship_to_multiple.jsp allows the user to do several things. Its primary function is to reassign

commerceItems from one shipping address to another. Its secondary function is the ability to create a new

nicknamed address in the user’s address book.

Reassigning Items to Shipping Addresses

Reassigning items from one shipping group to another first requires that the current division of commerce items

among shipping groups be displayed to the user. The JSP iterates through all the commerce items in the order.

Page 161: At g Cons Comm Ref Guide

7 Order Processing 155

Then, it determines how each commerce item is being shipped and displays its destination address and relevant

quantity. This is the JSP that displays the current information:

<!--For each CommerceItem in the Shopping Cart do the following: --><!-----------------------------------------------------------------><dsp:droplet name="ForEach"> <dsp:param bean="ShoppingCartModifier.order.commerceItems" name="array"/> <dsp:param name="elementName" value="CommerceItem"/> <dsp:oparam name="output">

<!-For each ShippingGroupRelationship for one CommerceItem do the following:-> <!---------------------------------------------------------------------------> <dsp:droplet name="ForEach"> <dsp:param name="array" param="CommerceItem.ShippingGroupRelationships"/> <dsp:param name="elementName" value="ShippingGroupRelationship"/> <dsp:oparam name="output"><dsp:droplet name="Switch"><dsp:param name="value" param="ShippingGroupRelationship.shippingGroup.shippingGroupClassType"/><dsp:oparam name="electronicShippingGroup"> <%/**----------------------------------- * Users can not move things to be shipped via * electronic shipping groups to another type * of shipping group, so don't display goods for * electronic shipping group. */ %></dsp:oparam><dsp:oparam name="hardgoodShippingGroup"> <tr valign=top>

<!-- Display the QTY value: --> <td align=center> <dsp:valueof param="ShippingGroupRelationship.Quantity"/> </td> <td></td>

<!-- Display PRODUCT SKU: --> <td> <dsp:valueof param="CommerceItem.auxiliaryData.catalogRef.displayName"/> </td> <td></td>

Notice that this JSP checks whether the shipping group is of type electronic or hard good. If it is electronic, no

information is displayed since electronic goods are delivered via e-mail unlike tangible goods.

After displaying the current delivery information, the site allows the user to make changes. For instance, if five

bikes are currently going to “My Shipping Address” the user might want to change the destination to be “My

Work Address”. Here is the JSP that does this:

<!-- Each MOVE button will be surrounded by its own "<dsp:form ... action </form>" so that only the values for this one MOVE are saved to the handler: --><dsp:form action="ship_to_multiple.jsp" method="post">

Ship

<!-- Display a textbox containing the current product quantity: -->

Page 162: At g Cons Comm Ref Guide

156 7 Order Processing

<!-- Save value they enter in QuantityToMove property on handler: --> <dsp:input bean="ShoppingCartModifier.QuantityToMove" paramvalue="ShippingGroupRelationship.Quantity" size="2" maxlength="2" type="text"/>

<!-- Store CommerceItem ID being modified: --> <dsp:input bean="ShoppingCartModifier.CommerceItemIdToEdit" paramvalue="CommerceItem.id" type="hidden"/>

<!-- Store OriginalShippingAddressName --> <dsp:input bean="ShoppingCartModifier.originalShippingAddressName" paramvalue="ShippingGroupRelationship.ShippingGroup.Description" name="3" type="hidden"/>

of these to

<!-- Create a drop-down list with all possible addresses. Save the value they selected in the NewShippingAddressName property on the Handler: --> <dsp:select bean="ShoppingCartModifier.newShippingAddressName"> <%-- <dsp:select bean="ShoppingCartModifier.newShippingAddressName" nodefault="<%=true%>"> --%>

<!-- Add each ProfileAddressName to the dropdown list: --><!-- We want to automatically select the address that the relationship --><!-- exists for. --><dsp:droplet name="ForEach"> <dsp:param bean="ShoppingCartModifier.profileAddressNames" name="array"/> <dsp:param name="elementName" value="ProfileAddressName"/> <dsp:oparam name="output">

<!-- SET THE DEFAULT IN THE DROP DOWN LIST --> <dsp:droplet name="Switch"> <dsp:param name="value" param="ProfileAddressName"/>

<!-- if this is the address this ShippingGroupRelationship is for, select this address in the drop down list: --> <dsp:getvalueof id="nameval4" param="ShippingGroupRelationship.ShippingGroup.Description" idtype="java.lang.String"> <dsp:oparam name="<%=nameval4%>"> <dsp:getvalueof id="option233" param="ProfileAddressName" idtype="java.lang.String"> <dsp:option selected="<%=true%>" value="<%=option233%>"/> </dsp:getvalueof> </dsp:oparam> </dsp:getvalueof>

<!- Otherwise, don't select this address in dropdown list: --> <dsp:oparam name="default"> <dsp:getvalueof id="option241" param="ProfileAddressName" idtype="java.lang.String"> <dsp:option selected="<%=false%>" value="<%=option241%>"/> </dsp:getvalueof> </dsp:oparam>

</dsp:droplet>

Page 163: At g Cons Comm Ref Guide

7 Order Processing 157

<dsp:valueof param="ProfileAddressName"/> </dsp:oparam></dsp:droplet> <!-- ForEach ProfileAddress -->

</dsp:select> <!-- Dropdown list of ProfileAddressNames -->

<!-- MOVE NOW button --><!-----------------><dsp:getvalueof id="itemId" param="CommerceItem.id" idtype="java.lang.String"> <dsp:input submitvalue="<%=itemId%>" bean="ShoppingCartModifier.MoveToNewShippingAddress" name="<%=itemId%>" type="submit" value="save this change"/> <!-- NOTE: Because we'll have m ultiple buttons on the same page, we need to give them each a unique NAME or correct values aren't stored in the handler: --></dsp:getvalueof>

<!-- Goto this URL if NO errors are found during the MOVE NOW button processing: --><dsp:input bean="ShoppingCartModifier.moveToNewShippingAddressSuccessURL" type="hidden" value="ship_to_multiple.jsp"/> <!--stay on this page-->

<!-- Goto this URL if errors are found during the MOVE NOW button processing: --><dsp:input bean="ShoppingCartModifier.moveToNewShippingAddressErrorURL" type="hidden" value="ship_to_multiple.jsp"/> <!-- stay on this page -->

This JSP shows all of the variables necessary for moving items among shipping groups as well as how the site

displays the address where items are currently being sent.

There are four properties in the ShoppingCartModifier that must be set in order to perform a move from one

shipping group to another: quantityToMove, commerceItemIdToEdit, originalShippingAddressName,

and newShippingAddressName. The JSP above sets all four of them. quantityToMove defaults to

the current quantity in the particular shipping group, although the user can change this number.

The originalShippingAddressName is automatically set to the current shipping group name. The

newShippingAddressName is set to the address selected by the user out of their list of addresses. Finally, the

commerceItemIdToEdit indicates the commerce item to be moved from one shipping group to another. The

description of the shipping group identifies each shipping group. It is set to the nickname of the address

associated with the shipping group.

Finally, we need to submit the form to the ShoppingCartModifier to instantiate the new shipping group.

ship_to_multiple.jsp has multiple form elements on one page. That is, each line listing a commerce item

and its associated shipping group is surrounded by its own form. If this is not done, then the properties of the

ShoppingCartModifier being set, such as quantityToMove, will be written over many times since each

listed commerce item would try to set this property.

The JSP submits the form to the handleMoveToNewShippingAddress() method of the

ShoppingCartModifier component. It also sets the success and error URLs.

Creating New Nicknamed Addresses

Users can create new nicknamed addresses in their address books on ship_to_multiple.jsp. If the user

creates a new nicknamed address, when the page reloads he has the option of sending his goods to this

nicknamed address. He then can associate an actual address with this nickname.

Page 164: At g Cons Comm Ref Guide

158 7 Order Processing

The handleCreateAddress() method of the ShoppingCartModifier handles creating a new nickname in

the address book. Here is the JSP to create a new address in the address book:

<!-- The user can enter the new address in this text box: --><dsp:input bean="ShoppingCartModifier.newShippingAddressName" size="20" type="text" value=""/><br>

<!-- Pushing the CREATE ADDRESS button will add the address to the user's Profile under the secondaryAddress attribute. The new nickname will also be added to the list of addresses in the drop-down list. --><dsp:input bean="ShoppingCartModifier.createAddress" type="submit" value="Create this address nickname"/>

<!-- GoTo this URL if NO errors are found during the CREATE ADDRESS button processing: --><dsp:input bean="ShoppingCartModifier.createAddressSuccessURL" type="hidden" value="ship_to_multiple.jsp"/> <!-- stay on same page -->

<!-- GoTo this URL if errors are found during the CREATE ADDRESS button processing: --><dsp:input bean="ShoppingCartModifier.createAddressErrorURL" type="hidden" value="ship_to_multiple.jsp"/> <!-- stay on same page -->

The newShippingAddressName property must be set for handleCreateNewAddress to work properly. Then,

when the user submits the form the address is automatically added to the user’s address book.

Populating Missing Address Information

Finally, users must enter any missing address information. For example, if a user creates a new nickname address

on the ship_to_multiple.jsp named “Summer House,” this address does not contain any information other

than the name “Summer House”. If the user elects to send a bike there, shipping_info_multiple.jsp needs

to obtain the actual address.

There are several things that are useful to note about the process for capturing this information. First, when a

user enters a new address, it needs to be inserted into two places: the shipping group that is part of the order

and the user’s address book. Second, if the address information is filled in, it is displayed; if not, a form appears

asking the user to provide it. Finally, to display the current arrangement of commerce items among shipping

groups, it is necessary to iterate through the shipping groups in the order and list the commerce items for each

one. This is done just as on ship_to_multiple.jsp.

First, the code determines if the address information is filled in, by testing for the presence of firstName. (We

decided that firstName is present in virtually any address so it is a good indicator that the address is filled in.)

<dsp:droplet name="IsEmpty"><dsp:param bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.firstName" name="value"/>

If the address is missing, the user must supply it. The address is copied into the shipping group of the order

object and into the user’s address book by adding the name of the address (held in the description property)

to the addressesToCopy array property of the ShoppingCartModifier. When the user submits the form, all

addresses in this array get copied from the shipping groups in the order object to the user’s address book.

<%-- Mark this address to be copied from the Order to the Profile so we

Page 165: At g Cons Comm Ref Guide

7 Order Processing 159

can persist it --%><dsp:input bean="ShoppingCartModifier.AddressesToCopy" paramvalue="ShippingGroup.Description" type="hidden"/><%-- Set the ownerId field of the new address so that we know that this address belongs to the profile. --%><dsp:input bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.ownerId" beanvalue="Profile.id" type="hidden"/>

Name<br>

<dsp:input bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.firstName" size="15" type="text"/>

<dsp:input bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.middleName" size="10" type="text"/>

<dsp:input bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.lastName" size="15" type="text"/><br>

Street address <br>

<dsp:input bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.address1" size="40" type="text"/><br>

<dsp:input bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.address2" size="40" type="text"/><br>

City, State, Postal Code<Br>

<dsp:input bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.city" size="20" type="text"/>

<dsp:select bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.state"> <%@ include file="../../user/StatePicker.jspf" %></dsp:select>

<dsp:input bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.postalCode" size="10" type="text"/><br>

Country<br>

<dsp:select bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress.country"> <%@ include file="../../user/CountryPicker.jspf" %></dsp:select><br>

If the address already exists, the page displays it unless it is private and owned by someone else. For example,

if the user is buying something for another person from a gift list, only the city and state of the address are

displayed. Please refer to the Gift Registry section of the Merchandising (page 193) chapter of this guide for

more information. The following JSP code determines whether or not the address is private.

<dsp:droplet name="Switch"> <dsp:param name="value" param="ShippingGroup.shippingAddress.ownerId"/> <dsp:getvalueof id="nameval4" bean="Profile.id" idtype="java.lang.String"> <dsp:oparam name="<%=nameval4%>">

Page 166: At g Cons Comm Ref Guide

160 7 Order Processing

<dsp:include page="../../user/DisplayAddress.jsp" flush="true"> <dsp:param name="address"bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress"/> <dsp:param name="private" value="false"/> </dsp:include> </dsp:oparam> </dsp:getvalueof> <dsp:oparam name="default"> <dsp:include page="../../user/DisplayAddress.jsp" flush="true"> <dsp:param name="address" bean="ShoppingCartModifier.Order.ShippingGroups[param:shippingGroupIndex].shippingAddress"/> <dsp:param name="private" value="true"/> </dsp:include> </dsp:oparam></dsp:droplet>

The last piece of information needed to create a shipping group is the shipping method. For example, customers

can choose Next Day delivery, Two Day delivery, etc. For instructions on setting the shipping method, see the

Basic Checkout section.

The final part of this process is the submission of the ship_info_multiple.jsp form and redirecting the

user to the payment screens. ship_info_multiple.jsp submits to handleShipToDone(), a method of the

ShoppingCartModifier that copies addresses from the order object to the profile. The submit fields look like

this:

<!-- CONTINUE button: --><!----------------------><!-- Pushing the CONTINUE button will move all commerce items to their new shipping groups, and move on to next page: --><dsp:input bean="ShoppingCartModifier.ShipToDone" type="submit" value="Continue---> "/>

<!-- Goto this URL if NO errors are found during the CONTINUE button processing: --><dsp:input bean="ShoppingCartModifier.ShipToDoneSuccessURL" type="hidden" value="payment_info_returning.jsp"/>

<!-- Goto this URL if errors are found during the CONTINUE button processing: --><dsp:input bean="ShoppingCartModifier.ShipToDoneErrorURL" type="hidden" value="shipping_info_multiple.jsp"/> <!-- stay on same page -->

Multiple Payment Methods

The advanced checkout process supports payment with multiple methods. For example, a customer can pay

with a combination of a credit card and a gift certificate. The payment screen allows a user to select from the

saved credit card list in his profile. The following section shows how users enter credit cards and gift certificates.

Entering a Credit Card

When a user enters a credit card he can either elect to use a credit card from the list of credit cards he saved

in his Profile or enter a new credit card. If he chooses to use an existing one, the ShoppingCartModifier

component copies all the fields for the credit card from the Profile to the payment group. The JSP to select an

existing credit card is:

Page 167: At g Cons Comm Ref Guide

7 Order Processing 161

<dsp:droplet name="ForEach"> <dsp:param bean="Profile.creditCards" name="array"/> <dsp:param name="elementName" value="creditCard"/> <dsp:oparam name="output"> <dsp:droplet name="Switch"> <dsp:param name="value" param="count"/> <dsp:oparam name="1"> <dsp:input bean="ShoppingCartModifier.selectedCreditCardName" paramvalue="key" name="creditCardToUse" type="radio" checked="<%=true%>"/> </dsp:oparam> <dsp:oparam name="default"> <dsp:input bean="ShoppingCartModifier.selectedCreditCardName" paramvalue="key" name="creditCardToUse" type="radio"/> </dsp:oparam> </dsp:droplet> <b><dsp:valueof converter="creditcard" param="key"/></b><br> </dsp:oparam></dsp:droplet>

This JSP sets the value of the selectedCreditCardName property of the ShoppingCartModifier to be

the selected credit card from the Profile. When the customer submits an order, the credit card information is

obtained from the Profile, using the nickname supplied in the selectedCreditCardName property.

When the user uses a new credit card, the information is inserted directly into the payment group

associated with the order and also copied to the Profile list of credit cards. There are two steps. First, the

selectedCreditCardName property of the ShoppingCartModifier must be set to the value new to flag the

ShoppingCartModifier that the card is a new one and should be copied to the user’s Profile. Second, the

credit card information is placed into the payment group. This includes the billing address, credit card number,

etc.

The following JSP snippet shows how we asked the user for payment information for each payment group in the

order. This code can be found in <ATG9dir>/PioneerCyclingJSP/

j2ee-apps/pioneer/web-app/en/checkout/full/payment_info_returning.jsp.

<dsp:droplet name="ForEach"><dsp:param bean="ShoppingCartModifier.creditCardPaymentGroups" name="array"/><dsp:oparam name="output"> <p><dsp:input bean="ShoppingCartModifier.selectedCreditCardName" name="creditCardToUse" type="radio" value="new"/> <b>New card below</b><p>

<b>Billing Address</b><p>

Name<br><dsp:input bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.firstName" beanvalue="Profile.billingAddress.firstName" size="20" type="text"/>

<dsp:input bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.middleName" beanvalue="Profile.billingAddress.middleName" size="10" type="text"/>

<dsp:input bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.lastName" beanvalue="Profile.billingAddress.lastName" size="20" type="text"/><br>

Page 168: At g Cons Comm Ref Guide

162 7 Order Processing

Street address <br><dsp:input bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.address1" beanvalue="Profile.billingAddress.address1" size="40" type="text"/><br>

<dsp:input bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.address2" beanvalue="Profile.billingAddress.address2" size="40" type="text"/><br>

City, State, Postal Code<br><dsp:input bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.city" beanvalue="Profile.billingAddress.city" size="20" type="text"/>

<%/* ------------------------------------------------------------ * The setvalue is a used to preload the value from * Profile.billingAddress into the address we are modifying. * After the form is submitted the actual value from the form * will be entered into the address. -------------------------------------------------------*/%> <dsp:setvalue bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.state" beanvalue="Profile.billingAddress.state"/> <dsp:select bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.state"> <%@ include file="../../user/StatePicker.jspf" %> </dsp:select>

<dsp:input bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.postalCode" beanvalue="Profile.billingAddress.postalCode" size="10" type="text"/><br>

Country<br> <dsp:setvalue bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.country" beanvalue="Profile.billingAddress.country"/> <dsp:select bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.country"> <%@ include file="../../user/CountryPicker.jspf" %> </dsp:select><br>

Phone Number<br> <dsp:input bean="ShoppingCartModifier.creditCardPaymentGroups[param:index].billingAddress.phoneNumber" beanvalue="Profile.billingAddress.phoneNumber" size="20" type="text"/><br>

<p><b>Credit Card</b><p>

<!-- Set the type of card -->New card type <dsp:selectbean="ShoppingCartModifier.creditCardPaymentGroups[0].creditCardType"required="<%=true%>"><dsp:option value="Visa"/>Visa<dsp:option value="MasterCard"/>Master Card<dsp:option value="AmericanExpress"/>American Express

Page 169: At g Cons Comm Ref Guide

7 Order Processing 163

<dsp:option value="Discover"/>Discover</dsp:select><br>

<!-- Set card type and expiration date --> New card number and expiration date<br><dsp:input bean="ShoppingCartModifier.creditCardPaymentGroups[0].creditCardNumber" maxsize="20" size="20" type="text"/><br>

<!-- Set the month that the card will expire on -->Month: <dsp:select bean="ShoppingCartModifier.creditCardPaymentGroups[0].expirationMonth"><dsp:option value="1"/>January<dsp:option value="2"/>February<dsp:option value="3"/>March<dsp:option value="4"/>April<dsp:option value="5"/>May<dsp:option value="6"/>June<dsp:option value="7"/>July<dsp:option value="8"/>August<dsp:option value="9"/>September<dsp:option value="10"/>October<dsp:option value="11"/>November<dsp:option value="12"/>December</dsp:select><br>

<!-- Set the year that the card will expire on -->Year: <dsp:select bean="ShoppingCartModifier.creditCardPaymentGroups[0].expirationYear"><dsp:option value="2002"/>2002<dsp:option value="2003"/>2003<dsp:option value="2004"/>2004<dsp:option value="2005"/>2005<dsp:option value="2006"/>2006<dsp:option value="2007"/>2007<dsp:option value="2008"/>2008<dsp:option value="2009"/>2009<dsp:option value="2010"/>2010<dsp:option value="2011"/>2011<dsp:option value="2012"/>2012</dsp:select></dsp:oparam></dsp:droplet>

Note that credit card information is not inserted directly into ShoppingCartModifier.paymentGroup

because customers may use a combination of payment methods. The ShoppingCartModifier has a

CreditCard payment group as one of its properties. After the values for the new credit card are copied

to this location, the ShoppingCartModifier merges this new payment group into the right place in

the order. The property of the ShoppingCartModifier that contains these payment groups is the

creditCardPaymentGroups. This is an array property of credit card payment groups that defaults to a size of

one. This credit card is copied to the order as well as placed into the user’s Profile property creditCards. The

nickname for the credit card in the creditCards property of the Profile is the credit card number.

There are two final points about adding credit cards to an order that should be discussed. First, when a credit

card is added to the order, it always is given a relationship to the Order object of OrderAmountRemaining. This

means that the credit card is used to pay for any part of the order that is not accounted for by other payment

methods. For a discussion of relationships between payment groups and orders, consult the section on Using

Relationship Objects in Working with Purchase Process Objects chapter in the ATG Commerce Programming Guide.

Page 170: At g Cons Comm Ref Guide

164 7 Order Processing

ATG Consumer Commerce allows you to implement multiple credit card payment. However, the Pioneer Cycling

store only supports one credit card per order.

Paying with Gift Certificates

Paying with gift certificates is a relatively straightforward process. The single string property

ShoppingCartModifier.giftCertificateNumbers can hold any number of gift certificates. That is, the user

can enter as many gift certificate numbers as desired as long as they are delimited by white space. These gift

certificates are incorporated into new payment groups and added to the order in the following manner:

If you have gift certificates, type their codes below<br><dsp:input bean="ShoppingCartModifier.giftCertificateNumbers" size="50" type="text" value=""/><br>

Users obtain gift certificate claim numbers by purchasing them in the store. Refer to the section on Gift

Certificates in the Merchandising (page 193) chapter in this guide for more information.

Form Submission

Finally, all of these new payment methods must be submitted to the ShoppingCartModifier. The JSP to do

this is.

<dsp:input bean="ShoppingCartModifier.MoveToConfirmation" type="submit" value=" Continue -->" />

This code submits the form and the handleMoveToConfirmation() method creates and inserts payment

methods in the order.

Additional Order Capture Information

There are two pieces to the checkout process that have not been covered elsewhere in this guide. They are:

entering a valid credit card and displaying error messages.

Entering a Valid Credit Card

The checkout process attempts to validate a credit card when it is first entered. This includes things like

expiration date as well as a mod check against the number. All credit card numbers pass a particular

mathematical algorithm that validates them as real numbers. So, in order to actually complete the checkout

process a credit card number that passes this test must be supplied. ATG Consumer Commerce does not accept

random 16-digit numbers. You can use one of the following credit card numbers for testing:

• Visa: 4111111111111111

• MasterCard: 5555555555554444

• American Express: 378282246310005

Page 171: At g Cons Comm Ref Guide

7 Order Processing 165

• Discover: 6011111111111117

The expiration date for all cards can be any date in the future.

Displaying Error Messages

Most pages in the checkout process have their errorURL set to their own address. For example, if an error is

encountered during processing because a user enters an expired credit card, the user is returned to that same

page. All of the error messages print on that page and the user can correct the mistake on that page. Displaying

error messages to a user follows a standard pattern:

<!-- Check to see if there are any errors on the form --><dsp:droplet name="/atg/dynamo/droplet/Switch"><dsp:param bean="ShoppingCartModifier.formError" name="value"/><dsp:oparam name="true"> <font color=cc0000><STRONG><UL> <dsp:droplet name="/atg/dynamo/droplet/ErrorMessageForEach"> <dsp:param bean="ShoppingCartModifier.formExceptions" name="exceptions"/> <dsp:oparam name="output"> <LI> <dsp:valueof param="message"/> </dsp:oparam> </dsp:droplet> </UL></STRONG></font></dsp:oparam></dsp:droplet>

The ShoppingCartModifier stores all error messages that it encounters in the formExceptions array

property. If there are errors, the code iterates through the array to display the relevant messages.

Express Checkout

In the Pioneer Cycling store, the customer can place items in the shopping cart and then complete the purchase

in two clicks. Customers specify default values for necessary order information (credit card, shipping address,

and shipping method) and then can use the streamlined express checkout functionality for all subsequent

orders.

Enabling Express Checkout

In order to use the express checkout feature, the customer must have registered as a member, logged in as a

member, and entered at least one valid credit card and one shipping address. Once these conditions are met, a

customer may enable the express checkout feature by clicking see my express checkout ordering preferences

under “My commerce profile” on the “My Profile” page and then clicking the Set Your Express Checkout

Preferences button.

Page 172: At g Cons Comm Ref Guide

166 7 Order Processing

The hidden field in the form sample in express_checkout.jsp below sets the form handler’s

updateRepositoryId property. This property is used by theB2CProfileFormHandler to ensure that the

profile being edited is in fact the profile that is associated with the current session (in case a session expires or

otherwise becomes invalid while a person is filling in a form). We recommend using this hidden field in all forms

that invoke the handleUpdate method of ProfileFormHandler or its subclasses.

<dsp:form action="express_checkout.jsp"> <dsp:input bean="B2CProfileFormHandler.updateRepositoryId" beanvalue="B2CProfileFormHandler.repositoryId" type="hidden"/>

After the user checks the Set Your Express Checkout Preferences button, she is presented with a form that

asks for default shipping and billing information. The express checkout feature requires that at least one credit

card already be entered. If the customer’s profile contains no credit cards, she is presented with a message and

a link to the credit card entry page. After a customer has entered at least one credit card, she is presented again

with the page above.

Page 173: At g Cons Comm Ref Guide

7 Order Processing 167

Users without a valid credit card see the message above

Users with valid credit cards see a form that lets them select a default credit card. Additionally, the “Save These

Express Checkout Preferences” button is rendered.

Page 174: At g Cons Comm Ref Guide

168 7 Order Processing

Setting Express Checkout preferences

When the customer pushes the Save These Express Checkout Preferences button, the expressCheckout

property of the user’s profile is set. This ensures that the address, credit card, and shipping methods have been

set and prevents invalid data from causing errors at checkout time.

Setting the Express Checkout preferences is specialized Pioneer Cycling Store functionality. This code

can be found in <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/web-app/en/user/

express_checkout_preferences.jsp.

Page 175: At g Cons Comm Ref Guide

7 Order Processing 169

The first form element on the page is the pull-down list for selecting the default shipping address.

The customer’s default shipping address is stored in the property Profile.shippingAddress.

The pull-down list in the screen shot above shows the list of all nicknames of addresses

stored in Profile.secondaryAddressess. The pull down list is linked to the property

ProfileFormHandler.editValue.defaultAddressNickname. When the form is submitted, the selected

value is set in the Profile.shippingAddress property. The following snippet demonstrates how the shipping

address is set.

Your current default shipping address is: <!-- <b><dsp:valueof param="key"/></b>.--><p><dsp:getvalueof id="pval0" bean="Profile.shippingAddress"><dsp:include page="DisplayAddress.jsp" flush="true"><dsp:param name="address" value="<%=pval0%>"/></dsp:include></dsp:getvalueof><p>

<dsp:droplet name="IsEmpty"> <dsp:param bean="Profile.secondaryAddresses" name="value"/> <dsp:oparam name="false">

Change it to:<dsp:select bean="B2CProfileFormHandler.editValue.defaultAddressNickname"> <dsp:droplet name="ForEach"> <dsp:param bean="Profile.secondaryAddresses" name="array"/> <dsp:oparam name="outputStart"> <dsp:option value=""/>&nbsp;Don't Change </dsp:oparam> <dsp:oparam name="output"> <dsp:droplet name="Switch"> <dsp:param name="value" param="key"/> <dsp:oparam name="editAddress"> </dsp:oparam> <dsp:oparam name="newBillingAddress"> </dsp:oparam> <dsp:oparam name="default"> <dsp:getvalueof id="option119" param="key" idtype="java.lang.String"><dsp:option value="<%=option119%>"/></dsp:getvalueof><dsp:valueof param="key"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet></dsp:select>

The concepts involved in setting the default credit card are similar to setting the shipping address. The default

credit card is stored in Profile.defaultCreditCard, which is a reference to a RepositoryItem of type

credit_card that has the properties creditCardType and creditCardNumber. These are displayed on the

page next to the word “Default:” to remind the user of the current setting, if any. Just below that is a pull-down

list for selecting a new default credit card that shows a list of all credit cards on the user’s profile.

To display the current default value as pre-selected, we used the <dsp:setvalue> to set the form handler’s

value for the default credit card from the profile. To actually render the <dsp:select> drop-down list, we used

an IsEmpty servlet bean to determine if there is anything to render at all. The ForEach servlet bean has an

empty oparam that one might expect to be able to use for the case when the credit cards are empty and an

oparam="outputStart" for the <dsp:select> tag and oparam="outputEnd" for the </dsp:select>. The

ForEach servlet bean has been tuned for top performance and it would be too inefficient for the page compiler

to figure out which <dsp:select> and </dsp:select> and <dsp:option> tags go together. If we used a

Page 176: At g Cons Comm Ref Guide

170 7 Order Processing

loosely coupled group, like radio buttons, the IsEmpty would not be necessary, but since we used select, we

must help the page compiler by using IsEmpty.

Default: <dsp:valueof bean="Profile.defaultCreditCard.creditCardType"/><dsp:valueof bean="Profile.defaultCreditCard.creditCardNumber" converter="creditcard"/><p>

<dsp:setvalue bean="B2CProfileFormHandler.defaultCreditCardID" beanvalue="Profile.defaultCreditCard.id"/>

<dsp:droplet name="IsEmpty"> <dsp:param bean="Profile.creditCards" name="value"/> <dsp:oparam name="true"> <dsp:a href="credit_cards.jsp"> <font color=#cc0000> Please enter one or more credit cards in order to enable this feature </font> </dsp:a> <br> </dsp:oparam> <dsp:oparam name="false"> Choose from one of your saved credit cards<p> <dsp:select bean="B2CProfileFormHandler.defaultCreditCardID"> <dsp:droplet name="ForEach"> <dsp:param bean="Profile.creditCards" name="array"/> <dsp:oparam name="output"> <dsp:getvalueof id="option226" param="element.id" idtype="java.lang.String"><dsp:option value="<%=option226%>"/></dsp:getvalueof> <dsp:valueof converter="creditcard" param="key"/> &nbsp; <dsp:valueof param="element.creditCardType"/> <dsp:valueof converter="creditcard" param="element.creditCardNumber"/> </dsp:oparam> </dsp:droplet> </dsp:select> </dsp:oparam></dsp:droplet>

While credit cards and shipping addresses that must be drawn from the user’s profile, setting the shipping

method is simpler because the list of possible shipping methods is common to all users. We used the same

methods as above to pre-set the selected item on the pull-down list of shipping methods. The actual pull

down list is generated by the AvailableShippingMethods servlet bean that is described in the Shipping

Prices (page 130) section of the Pricing (page 127) chapter.

Default: <dsp:valueof bean="Profile.defaultCarrier"/><p>

<dsp:setvalue bean="B2CProfileFormHandler.defaultCarrier" beanvalue="Profile.defaultCarrier"/>

Choose from one of our shipping methods<p><!-- The droplet AvailableShippingMethods is able to give us a list of the --><!-- shipping methods that should be available to a user. --><dsp:droplet name="AvailableShippingMethods"> <dsp:param bean="ShoppingCartModifier.shippingGroup" name="shippingGroup"/> <dsp:param bean="UserPricingModels.shippingPricingModels" name="pricingModels"/>

Page 177: At g Cons Comm Ref Guide

7 Order Processing 171

<dsp:param bean="Profile" name="profile"/> <dsp:oparam name="output"> <dsp:select bean="B2CProfileFormHandler.defaultCarrier"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="availableShippingMethods"/> <dsp:param name="elementName" value="method"/> <dsp:oparam name="output"> <dsp:getvalueof id="option313" param="method" idtype="java.lang.String"><dsp:option value="<%=option313%>"/></dsp:getvalueof> <dsp:valueof param="method"/> </dsp:oparam> </dsp:droplet> </dsp:select> </dsp:oparam></dsp:droplet>

The last form element on the page is the Save These Express Checkout Preferences submit button.

This button is rendered only if the list of credit cards in the profile is not empty. The form is submitted to

B2CProfileFormHandler.setExpressCheckoutPreferences, which takes the values from the form fields

and saves them to the profile.

<dsp:droplet name="IsEmpty"><dsp:param bean="Profile.creditCards" name="value"/><dsp:oparam name="false"> <dsp:input bean="B2CProfileFormHandler.setExpressCheckoutPreferences" type="submit" value="Save These Express Checkout Preferences"/> </dsp:oparam></dsp:droplet>

Deactivating Express Checkout

A customer can deactivate express checkout option by clicking the Deactivate Express Checkout button on

the “Express Checkout” page. The form below from express_checkout.jsp can be used to deactivate express

checkout by setting the value of B2CProfileFormHandler.value.expressCheckout to false.

<dsp:form action="express_checkout.jsp">

<dsp:input bean="B2CProfileFormHandler.updateRepositoryId" beanvalue="B2CProfileFormHandler.repositoryId" type="hidden"/> <dsp:input bean="B2CProfileFormHandler.editValue.expressCheckout" type="hidden" value="false"/> <dsp:input bean="B2CProfileFormHandler.setExpressCheckout" type="submit" value="Deactivate Express Checkout"/>

</dsp:form>

Modifying the Shopping Cart

We made two modifications to the cart.jsp page to enable express checkout. First, the Express Checkout

button is conditionally added to the form depending on the state of the Profile.expressCheckout property.

Conditional express checkout button incart.jsp:

Page 178: At g Cons Comm Ref Guide

172 7 Order Processing

<core:if value="Profile.expressCheckout"> <dsp:input bean="ShoppingCartModifier.expressCheckout" type="submit" value=" Express Checkout"/></core:if>

The Express Checkout button appears on cart.jsp when express checkout preferences have been set.

We also modified cart.jsp by adding a hidden input value containing the URL for a successful express

checkout:

<dsp:input bean="ShoppingCartModifier.expressCheckoutSuccessURL" type="hidden" value="full/co_confirm.jsp"/>

When express checkout is enabled, clicking the Express Checkout button creates an order using the items in

the user’s shopping cart and the pre-set default shipping address, credit card, and shipping method. The user is

then redirected to the final order confirmation page.

Page 179: At g Cons Comm Ref Guide

8 Order History 173

8 Order History

This chapter describes how the Pioneer Cycling store makes it easy for customers to view all the orders they

have placed on the Web site. They can see the details of what they ordered, where it was shipped, and the

order’s status; they can also use this section of the Web site to cancel orders that haven’t already been shipped.

This chapter includes the following sections:

Displaying a User’s Orders (page 173)

Describes how customers can review information from all the past and current orders.

Displaying a Single Order (page 176)

Describes how we display detailed information on a specific order.

Canceling Orders (page 179)

Describes how customers can cancel orders that haven’t already been shipped.

Displaying a User’s Orders

We used several servlet beans from ATG Consumer Commerce and one special servlet bean written especially for

the bike store to create the order history pages.

Page 180: At g Cons Comm Ref Guide

174 8 Order History

order_history.jsp, the Order History page in a customer’s profile

The following snippet of JSP, taken from order_history.jsp, demonstrates how the OrderLookup

servlet bean renders a customer’s list of open orders. OrderLookup is located at/atg/commerce/order/

OrderLookup. Please refer to the Configuring the Order Fulfillment Framework chapter in the ATG Commerce

Programming Guide for more information about OrderLookup.

The OrderLookup servlet bean can be used in several ways. In this case, it is passed the user’s profile ID and the

requested order state (“open”) and it outputs a list of orders in the result parameter. Then, a ForEach servlet

bean renders links to each of those orders in an ordered list.

The OrderStatesDetailed component, which is invoked for each order in the list, asks the fulfillment module

for detailed information about the state of an order.

<dsp:droplet name="OrderLookup"> <dsp:param bean="/atg/userprofiling/Profile.repositoryId" name="userId"/> <dsp:param name="state" value="open"/><dsp:param name="startIndex" param="openStartIndex"/><dsp:param name="numOrders" value="10"/>

<dsp:oparam name="output"> <dsp:droplet name="IsNull"> <dsp:param name="value" param="previousIndex"/> <dsp:oparam name="false"> <dsp:droplet name="Compare"> <dsp:param name="obj1" param="openStartIndex"/>

Page 181: At g Cons Comm Ref Guide

8 Order History 175

<dsp:param name="obj2" value="0"/> <dsp:oparam name="greaterthan"> <dsp:a href="order_history.jsp"><i>Previous orders</i> <dsp:param name="openStartIndex" param="previousIndex"/> <dsp:param name="closedStartIndex" param="closedStartIndex"/> <dsp:param name="cancelledStartIndex" param="cancelledStartIndex"/> </dsp:a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet>

<dsp:droplet name="ForEach"> <dsp:param name="array" param="result"/> <dsp:oparam name="outputStart"> <dsp:droplet name="IsNull"> <dsp:param name="value" param="nextIndex"/> <dsp:oparam name="false"> <dsp:a href="order_history.jsp"><i>More orders</i> <dsp:param name="openStartIndex" param="nextIndex"/> <dsp:param name="closedStartIndex" param="closedStartIndex"/> <dsp:param name="cancelledStartIndex" param="cancelledStartIndex"/> </dsp:a><br> </dsp:oparam> </dsp:droplet>

<OL> </dsp:oparam> <dsp:oparam name="outputEnd"> </OL> </dsp:oparam> <dsp:oparam name="empty"> No open orders. </dsp:oparam> <dsp:oparam name="output"> <LI> <dsp:a href="order.jsp"> <dsp:param name="orderId" param="element.id"/> #<dsp:valueof param="element.id">no order number</dsp:valueof></dsp:a> <dsp:valueof date="MMMMM d, yyyy" param="element.submittedDate"/> &nbsp;&nbsp; <dsp:droplet name="/atg/commerce/order/OrderStatesDetailed"> <dsp:param name="state" param="element.state"/> <dsp:oparam name="output"><dsp:valueof param="detailedState"/></dsp:oparam> </dsp:droplet>

</dsp:oparam> </dsp:droplet> </dsp:oparam>

<dsp:oparam name="error"> <span class=profilebig>ERROR: <dsp:valueof param="errorMsg">no error message</dsp:valueof> </span> </dsp:oparam></dsp:droplet>

Page 182: At g Cons Comm Ref Guide

176 8 Order History

Displaying a Single Order

When the customer chooses a link from the order history page to view a single particular order (such

as #o12345), he sees a page of detailed information about that order with a URL like this: http://

my.bikestore.com/path/order.jsp?orderId=o12345. The orderId parameter is generated by setting a

parameter between the <dsp:a> and </dsp:a> tags of the link on order_history.jsp like this:

<dsp:a href="order.jsp"> <dsp:param name="orderId" param="element.id"/> #<dsp:valueof param="element.id">no order number</dsp:valueof></dsp:a>

The customer is then shown the status and history of a single order (order.jsp with the information of order

number that corresponds to orderId). In the JSP code snippet from order.jsp below, instead of being passed

explicitly as a parameter to the OrderLookup servlet bean, orderId is set by the link from the order history

page.

<dsp:droplet name="/atg/commerce/order/OrderLookup">

<dsp:oparam name="error"> <p> <span class=profilebig>ERROR: <dsp:valueof param="errorMsg">no error message</dsp:valueof> </span> <p> </dsp:oparam>

<dsp:oparam name="output"> <dsp:setvalue paramvalue="result" param="order"/> <p> <span class=profilebig>order #<dsp:valueof param="order.id">no order id</dsp:valueof></span>

<p> <table cellspacing=0 cellpadding=0 border=0>

<!-- Setup gutter and make space --> <tr> <td><dsp:img height="1" width="100" src="../images/d.gif"/><br></td> <td>&nbsp;&nbsp;</td> <td><dsp:img height="1" width="400" src="../images/d.gif"/></td> </tr>

<dsp:droplet name="Switch"> <dsp:param name="value" param="order.stateAsString"/>

<dsp:oparam name="NO_PENDING_ACTION"> <tr valign=top> <td> <span class=help>Gosh. You should have this one by now.</span> </td>

<td></td>

<td> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-profile>Order status</td></tr></table>

Page 183: At g Cons Comm Ref Guide

8 Order History 177

<p> This order was placed on <dsp:valueof date="MMMMM d, yyyy" param="order.submittedDate"/> and shipped on <dsp:valueof date="MMMMM d, yyyy" param="order.completedDate"/>.

<p> If there is a problem with this order, please <dsp:a href="contact_customer_service.jsp">contact a customer service representative</dsp:a>. <p> &nbsp;<br> </td> </tr> </dsp:oparam>

<dsp:oparam name="default"> <tr valign=top> <td> <span class=help>Since this order has not yet shipped, you may still change it.</span> </td>

<td></td>

<td> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-profile>Order status</td></tr></table> <p> This order was placed on <dsp:valueof date="MMMMM d, yyyy" param="order.submittedDate"/>. <br> Its status is <dsp:valueof param="order.stateAsString">UNKNOWN STATUS</dsp:valueof>. <br> <p> > <dsp:a href="cancel_order.jsp"> <dsp:param name="orderId" param="order.id"/> cancel the order</dsp:a><br> <p> To make any other changes to this order, please <dsp:a href="contact_customer_service.jsp">contact a customer service representative</dsp:a>. </span> <p> &nbsp;<br> </td> </tr> </dsp:oparam> </dsp:droplet>

<tr valign=top> <td> <span class=help>This is the contents of the order.</span> </td> <td></td> <td> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-profile>Order details</td></tr></table> <p>

<table cellspacing=2 cellpadding=0 border=0>

Page 184: At g Cons Comm Ref Guide

178 8 Order History

<tr><td></td><td>&nbsp;&nbsp;</td><td></td><td>&nbsp;&nbsp;</td><td></td></tr>

<tr valign=top> <td colspan=3>

<b>Order # <dsp:valueof param="order.id">no order id</dsp:valueof></b><br> <dsp:valueof date="h:mma MMMMM d, yyyy" param="order.submittedDate"/><br> Sales: [email protected] </td> <td colspan=2></td> </tr> <tr><td colspan=5><hr size=0></td></tr>

<tr valign=top> <td colspan=3> <dsp:getvalueof id="pval0" param="order" idtype=" atg.commerce.order.Order"> <dsp:include page="OrderBillingInfo.jsp" flush="true"> <dsp:param name="order" value="<%=pval0%>"/></dsp:include></dsp:getvalueof> </td> <td colspan=2></td> </tr>

<tr><td colspan=5><hr size=0></td></tr>

<dsp:getvalueof id="pval0" param="order" idtype=" atg.commerce.order.Order"> <dsp:include page="OrderShippingInfo.jsp" flush="true"> <dsp:param name="order" value="<%=pval0%>"/></dsp:include></dsp:getvalueof>

<tr><td colspan=5><hr size=0></td></tr>

</table> </td> </tr> </table> <P>

</dsp:oparam></dsp:droplet>

The OrderLookup servlet bean puts the order into the result parameter. Because we thought it would be

clearer, we renamed the parameter order using the setvalue statement of <dsp:setvalue param="order"

paramvalue="result"/>. Then any of the order properties can be accessed using order.<propertyname>.

Because order.jsp contains so much information, the JSP can become difficult to read. For easier reading we

separated out the OrderBillingInfo.jsp and OrderShippingInfo.jsp fragments and included them in

order.jsp using:

<dsp:include page="filename.jsp" flush="true"></dsp:include>

These pages display details of the order’s shipping and billing information.

Page 185: At g Cons Comm Ref Guide

8 Order History 179

There is no technical reason for separating the JSP fragments OrderBillingInfo.jsp and

OrderShippingInfo.jsp rather than including them inline in order.jsp. However, it is often easier to

manage a large number of small JSP files than a small number of larger JSP files.

It is important to remember that the OrderLookup servlet bean has a security feature that enables a customer

to look up only his own orders. If you would like disable this security feature so that anyone can look at any

order, you can set the property OrderLookup.enableSecurity=false.

Canceling Orders

The ATG Consumer Commerce product provides the ability to cancel and edit existing orders through the

Customer Service Module. Because there are many different ways to present the cancel order functionality

to the customer and there are many different business rules governing order cancellation restriction, this

functionality is implemented outside the core product. The ability of a user to cancel his own orders is a Pioneer

Cycling specific piece of functionality.

The business rules for Pioneer Cycling dictate that any order that still has actions pending may be cancelled, so

in order.jsp we used a Switch servlet bean on the order’s state to determine if it may be cancelled:

<dsp:droplet name="Switch"> <dsp:param name="value" param="order.stateAsString"/>

<dsp:oparam name="NO_PENDING_ACTION">

<!-- Snip -->

This order has been completed. You cannot cancel it. </dsp:oparam>

<dsp:oparam name="default">

<!-- Snip -->

<dsp:a href="cancel_order.jsp"> <dsp:param name="orderId" param="order.id"/> cancel the order</dsp:a><br> </dsp:oparam></dsp:droplet>

The link to cancel_order.jsp contains an orderId parameter to keep track of which order to cancel.

cancel_order.jsp is a very simple page. The form used to cancel the order exists for the sake of the user

interface. It gives the customer the chance to change her mind about the cancellation.

Page 186: At g Cons Comm Ref Guide

180 8 Order History

The next page actually does the canceling. Note the hidden orderId parameter that gets passed to the next

page with the form submission. When displaying data like this on the web there is always a security concern. In

order to thwart malicious users who might hit the order URL and try out different orderId parameters to gain

access to private data, we allowed users to see only the orders that they have placed. By default this security

feature is turned on, but you can disable the security feature by setting the property /atg/commerce/order/

OrderLookup.enableSecurity=false.

<dsp:form action="order_cancelled.jsp"> Are you sure you wish to Cancel Order #<dsp:valueof param="orderId"><i>none</i></dsp:valueof> <p> If you do not wish to cancel your order, you may use your browser's back buttonto return to the order. <input name="orderId" type="hidden" value='<dsp:valueof param="orderId"/>'> <BR><br> <input type="submit" value="CANCEL ORDER"></dsp:form>

The action of the form, order_cancelled.jsp, is the next stop when the customer clicks the “Cancel Order”

button. This is the page that actually cancels the order using the CancelOrder servlet bean.

Page 187: At g Cons Comm Ref Guide

8 Order History 181

If the cancellation is initiated without any problem, the successoparam is rendered. Otherwise the

erroroparam is rendered and the error message is displayed. (Canceling an order is an asynchronous process

in which the form handler sends a Java message to the fulfillment system that then performs the cancel order

operation. Since it is asynchronous, there is no guarantee that it will complete by the time the “This Order Has

Been Cancelled” page displays to the user. Also, it is possible that the fulfillment process might not be running.

The cancel request could be sent and the order would not actually be cancelled until the fulfillment system was

brought back up.) Generally, in the Pioneer Cycling store it takes only tens of seconds. We included a message to

users so that they will understand if orders do not show up as cancelled right away on their Order History pages.

Here is the relevant part of order_cancelled.jsp:

<dsp:droplet name="/atg/commerce/order/CancelOrder"> <dsp:oparam name="success"> The Cancel Order instruction has been sent to the order processor. <P> It may take several minutes for the order to disappear from the list of orders on your <dsp:a href="order_history.jsp">order history</dsp:a> page. </dsp:oparam> <dsp:oparam name="error"> <dsp:valueof param="errorMsg"/> </dsp:oparam></dsp:droplet>

The CancelOrder servlet bean extends the OrderLookup servlet bean and therefore has many similarities

in its configuration. The main difference is that it has an as additional property, the OrderCanceller, that is

the component that communicates with the fulfillment system to cancel the order. Here is /atg/commerce/

order/CancelOrder.properties:

$class=atg.projects.b2cstore.CancelOrder$scope=global

orderCanceller=OrderCancellerorderManager=OrderManager

openStates=\ submitted,\ processing,\

Page 188: At g Cons Comm Ref Guide

182 8 Order History

pending_merchant_action,\ pending_customer_action

closedStates=\ no_pending_action

profilePath=/atg/userprofiling/ProfileenableSecurity=true

The OrderCanceller used by CancelOrder is configured as follows in /atg/commerce/order/

OrderCanceller.properties:

$class=atg.projects.b2cstore.OrderCanceller$scope=global

transactionManager=/atg/dynamo/transaction/TransactionManagermessageSourceName=OrderCancellermodifyPort=ModifyOrderPort

OrderCanceller.java:

package atg.projects.b2cstore;

// Commerceimport atg.commerce.order.*;import atg.commerce.fulfillment.*;import atg.commerce.*;import atg.commerce.states.*;import atg.commerce.messaging.*;

// Java classesimport javax.jms.*;import javax.transaction.*;import javax.transaction.xa.*;import java.util.ResourceBundle.*;import java.util.*;

/** * This class will contain methods needed to cancel an order by sending * messages to the fulfillment subsystem. * * @version $Change: 228696 $$DateTime: 2002/02/11 13:07:58 $$Author: releng $ */public class OrderCanceller extends SourceSinkTemplate{ //------------------------------------- /** Class version string */ public static final String CLASS_VERSION = "$Change: 228696 $$DateTime:2002/02/11 13:07:58 $$Author: releng $";

//--------------------------------------------------------------------------- // property:MessageSourceName //---------------------------------------------------------------------------

/** the name used by this class when it acts as a message source **/ private String mMessageSourceName = "OrderCanceller";

//---------------------------------------------------------------------------

Page 189: At g Cons Comm Ref Guide

8 Order History 183

/** * Sets the name used by this class when it acts as a message source * so that it's messages can be identified. **/ public void setMessageSourceName(String pMessageSourceName) { mMessageSourceName = pMessageSourceName; }

//--------------------------------------------------------------------------- /** * Gets the name used by this class when it acts as a message source * so that it's messages can be identified. **/ public String getMessageSourceName() { return mMessageSourceName; }

/** Port name for sending modify order messages */ String mModifyPort = null;

//------------------------------------- /** * Sets Port name for sending modify order messages **/ public void setModifyPort(String pModifyPort) { mModifyPort = pModifyPort; }

//------------------------------------- /** * Returns Port name for sending modify order messages **/ public String getModifyPort() { return mModifyPort; }

//------------------------------------- /** * Assemble and send a message to cancel the order * @param orderId the id of the order to cancel. **/ public void sendCancelOrder(String pOrderId) {

Modification[] mods = new Modification[1]; ModifyOrder message = new ModifyOrder();

message.setOrderId(pOrderId); message.setSource(getMessageSourceName()); message.setOriginalSource(getMessageSourceName()); message.setOriginalId(message.getId());

GenericRemove gr = new GenericRemove(); gr.setTargetType(Modification.TARGET_ORDER); gr.setTargetId(pOrderId); mods[0] = gr;

message.setModifications(mods);

try { sendCommerceMessage(message, getModifyPort());

Page 190: At g Cons Comm Ref Guide

184 8 Order History

} catch(JMSException j) { logError(j); } }

} // end of class

CancelOrder.java:

package atg.projects.b2cstore;

import javax.servlet.*;import javax.servlet.http.*;

import atg.commerce.CommerceException;import atg.commerce.order.*;import atg.commerce.states.*;import atg.nucleus.naming.ParameterName;import atg.nucleus.naming.ComponentName;import atg.servlet.*;import atg.repository.RepositoryException;import atg.repository.RepositoryItem;

import java.io.*;import java.util.List;import java.util.Locale;

/** * This servlet cancels the order for the orderId paramteter passed in. * It takes as parameters: * <dl> * * <dt>orderId * * <dd>the id of the order to cancel * * </dl> * * It renders the following oparams: * <dl> * * <dt>success * * <dd>The oparam success is rendered once if the cancel was successful * * <dt>error * * <dd>error will be rendered if an error occurred * * </dl> * * It sets the following output params: * <dl> * * <dt>errorMsg * * <dd>if an error occurred this will be the detailed error message * for the user. *

Page 191: At g Cons Comm Ref Guide

8 Order History 185

* </dl> * * This droplet has a security feature that allows only the current user to * cancel his own orders. This feature is enabled by default. To disable * it, set the property enableSecurity=false */public class CancelOrder extends OrderLookup { //------------------------------------- // Class version string public static final String CLASS_VERSION = "$Id: CancelOrder.java,v 1.2 2000/06/07 19:17:43 cynthia Exp $";

//------------------------------------- // Constants //------------------------------------- public final static String SUCCESS = "success";

//------------------------------------- // property: orderCanceller OrderCanceller mOrderCanceller; public void setOrderCanceller(OrderCanceller pOrderCanceller) { mOrderCanceller = pOrderCanceller; } public OrderCanceller getOrderCanceller() { return mOrderCanceller; }

//------------------------------------- /** * service the request */ public void service(DynamoHttpServletRequest pReq, DynamoHttpServletResponse pRes) throws ServletException, IOException { // Get the orderId parameter from the request. It was set by the // hidden form field on the cancel_order.jsp page. String orderId = pReq.getParameter(ORDERID); if (orderId != null) { try { if (isLoggingDebug()) logDebug("orderId = " + orderId);

// Make sure that an order for that orderId exists by asking the //OrderManager with the orderExists(orderId) method. We then make double //sure by doing orderManager.loadOrder(orderId) and making sure it is not //null, but that is optional. Order order = null; if (getOrderManager().orderExists(orderId)) order = getOrderManager().loadOrder(orderId);

if (order == null) { String errMsg = OrderUserMessage.format(MSG_NO_SUCH_ORDER, getUserLocale(pReq, pRes)); pReq.setParameter(ERRORMESSAGE, errMsg); pReq.serviceLocalParameter(ERROR, pReq, pRes); return; }

Page 192: At g Cons Comm Ref Guide

186 8 Order History

// Make sure that the current logged in user is the owner of this order by // resolving the current user's profile id and comparing it to the order's //owner. if (isEnableSecurity()) { if (isLoggingDebug()) logDebug("checking ownership. current user is " + getCurrentProfileId(pReq) + " order owner is " +order.getProfileId()); if (!order.getProfileId().equals(getCurrentProfileId(pReq))) {String errMsg = OrderUserMessage.format(MSG_NO_PERMISSION_FOR_ORDER, getUserLocale(pReq, pRes)); pReq.setParameter(ERRORMESSAGE, errMsg); pReq.serviceLocalParameter(ERROR, pReq, pRes); return; } }

// Now we actually cancel the order getOrderCanceller().sendCancelOrder(orderId);

// Everything went well, so we will send out the success oparam pReq.serviceLocalParameter(SUCCESS, pReq, pRes); } catch (CommerceException e) { if (isLoggingError()) logError(e);

// Something went wrong. This will cause the error oparam to be rendered //and the errorMsg parameter to be set for the jsp page. String errMsg = OrderUserMessage.format(MSG_GENERAL_ERROR, getUserLocale(pReq, pRes)); pReq.setParameter(ERRORMESSAGE, errMsg); pReq.serviceLocalParameter(ERROR, pReq, pRes); } return; }

// Oops! It looks like somebody forgot to pass in an orderId parameter. if (orderId == null) { String errMsg = OrderUserMessage.format(MSG_NO_PARAM_SPECIFIED, getUserLocale(pReq, pRes)); pReq.setParameter(ERRORMESSAGE, errMsg); pReq.serviceLocalParameter(ERROR, pReq, pRes); return; } }}

Page 193: At g Cons Comm Ref Guide

9 Inventory 187

9 Inventory

This chapter describes the Pioneer Cycling inventory system. It includes the following sections:

Overview of the Pioneer Cycling Inventory System (page 187)

Describes how we used the ATG Consumer Commerce inventory system.

Implementing an Inventory System (page 188)

Describes how we implemented the inventory system.

Accessing Inventory Information (page 188)

Describes how inventory is displayed on product pages and checkout pages.

Overview of the Pioneer Cycling Inventory System

We used the standard inventory system that ships with ATG Consumer Commerce for the Pioneer Cycling store.

Using the inventory system, the store can show users the following information on any particular item:

• Availability status message

• Availability status

• Availability date

• Stock level

• Backorder stock level

• Preorder stock level

The Pioneer Cycling site displays the inventory status of a product on the product page and on the shopping

cart. For example, before a user adds a bike to his cart, we want to indicate to him if the item is out of stock. This

way, he can make the educated decision to purchase the item on backorder or purchase an alternate product.

To implement working inventory in the Pioneer Cycling store, first we actually created inventory information in

an inventory subsystem. Second, we set up access to this information via the various components supplied by

ATG Consumer Commerce.

Page 194: At g Cons Comm Ref Guide

188 9 Inventory

Implementing an Inventory System

ATG Consumer Commerce includes an inventory system adapter that provides an API to determine if an item is

in stock, back-ordered, or pre-ordered. That is, ATG Consumer Commerce provides a set of Inventory interface

classes that can be implemented to provide inventory functionality. ATG Consumer Commerce ships with an

example that is a repository-based inventory system. See the Inventory Framework chapter in the ATG Commerce

Programming Guide for more information.

Since the repository is provided by ATG Consumer Commerce (as an instance of a GSA repository), we were able

to use the existing component: /atg/commerce/inventory/InventoryRepository and the inventory XML

schema: /atg/commerce/inventory/inventory.xml. We simply used the ACC to enter information into the

repository. We supplied inventory information for only a few examples of the products in the Pioneer Cycling

store; for other products, the inventory information displays as “unknown.”

Accessing Inventory Information

Inventory information is used on product pages and checkout pages. The next section examines the

components we used to access information at each of these places.

Product Pages

Each product page displays the current availability of a particular product.

Page 195: At g Cons Comm Ref Guide

9 Inventory 189

The availability of a product is one of the values that can be returned by the InventoryLookup component:

• INSTOCK

• OUTOFSTOCK

• PREORDERABLE

• BACKORDERABLE

“Unknown” is displayed if there is no information available for a particular item.

To access this information, we used the InventoryLookup component located at /atg/commerce/

inventory/InventoryLookup. We made some slight changes to the way that InventoryLookup is

configured. Specifically, we changed it so that errors are not logged if inventory information is not found on

a particular item. Since not all items are in the inventory, we didn’t want an error to result if inventory level

information was unavailable. To do this, we set the following property in our properties file:

logInventoryExceptionsAsError=false

Page 196: At g Cons Comm Ref Guide

190 9 Inventory

The InventoryLookup component takes a catalogRefId property and then queries the inventory system,

returning several parameters. We used the inventoryInfo parameter to get access to the availability status of

an item. The JSP that performs this is:

<dsp:droplet name="/atg/commerce/inventory/InventoryLookup"> <dsp:param name="itemId" param="link.item.repositoryId"/> <dsp:param name="useCache" value="true"/> <dsp:oparam name="output"> <dsp:valueof param="inventoryInfo.availabilityStatusMsg">Unknown</dsp:valueof> </dsp:oparam> </dsp:droplet>

One thing to note about this usage is that we set the parameter useCache to be true. By default, the

Inventory servlet bean uses the regular InventoryManager, but the useCache parameter directs it to use the

CachingInventoryManager. This improves the performance, but as with any cache, there is the possibility of

getting stale data.

Checkout Pages

The Shopping Cart page in the Pioneer Cycling store, cart.jsp provides more information than the catalog

pages. Specifically, it indicates if:

• An item is out of stock

• An item is on backorder

• An item is on preorder

• If an item is in stock, but the quantity that the user is ordering is greater than the quantity in stock, we indicate

how many are left in stock.

Page 197: At g Cons Comm Ref Guide

9 Inventory 191

To get this information, the InventoryLookup component was again used but this time with a different JSP

usage. This is what the JSP looks like:

<%-- Display inventory information if any skus are not available --%> <% String stockLevel = drequest.getParameter("inventoryInfo.stockLevel"); String quantity = drequest.getParameter("CiRelationship.commerceItem.quantity"); String availStatus = drequest.getParameter("inventoryInfo.availabilityStatus"); %>

<core:switch value="<%= availStatus %>"> <core:case value="1001"> (Out) </core:case> <core:case value="1002"> (Preorder) </core:case> <core:case value="1003"> (Backorder) </core:case> <core:case value="INSTOCK">

<core:IfNotNull value="<%=stockLevel%>"> <core:ifGreaterThan object1="<%=quantity%>" object2="<%=stockLevel%>">

<%-- -1 is the code for infinite stock, so display nothing here. Customer is ordering more than are in stock, so display a message --%>

<core:ifNotEqual object1="<%=stockLevel%>" object2="-1"> (Only <dsp:valueof param="inventoryInfo.stockLevel"/> left) </core:ifNotEqual>

</core:ifGreaterThan> </core:IfNotNull> </core:case>

</core:switch>

Page 198: At g Cons Comm Ref Guide

192 9 Inventory

Page 199: At g Cons Comm Ref Guide

10 Merchandising 193

10 Merchandising

This chapter describes the merchandising features implemented in the Pioneer Cycling site and includes the

following sections:

Promotions (page 193)

Describes how to use various types of promotions in your site.

Gift Certificates (page 200)

Describes how to create and use gift certificates.

Coupons (page 201)

Describes how to create and use coupons.

Gift Registry (page 203)

Describes how to create and use a gift registry.

Promotions

Promotions are repository items. When a user is “given” a promotion, it means that a reference to the promotion

repository item is added to the user’s profile property activePromotions.

Promotions are a way of encouraging a user to make a purchase by highlighting products and offering

discounts. ATG Consumer Commerce provides flexibility in the types of promotions and the ways that they can

be delivered to users.

One typical way to deliver a promotion is to embed a promotion ID in a URL, which automatically gives the user

that promotion when he clicks on the URL. Another way is to create a scenario that uses the “Give Promotions”

action, and shows the promotion’s media in a slot. For example:

"upon login of person whose purchase history=null give promo 'buy 2 apples, get 1

free' and show items in slot MediaSlot 'ApplesPromotionMedia'."

It is important to realize that, although these two actions are conceptually related in the eyes of the customer,

there are distinct differences. The first gives a customer a promotion that discounts a product while the second

shows the customer a media item.

When prices are calculated for items, orders, tax, or shipping, the pricing engine uses the user profile’s

activePromotion list to calculate a personalized price. Discounts are applied when the order is priced, which is

any time that the customer looks at his shopping cart or checks out.

Page 200: At g Cons Comm Ref Guide

194 10 Merchandising

If there are several applicable discounts, the “order of application” attribute of the promotion determines the

order in which they get applied.

Implementing Promotions

A promotion is basically a pricing model with a piece of media. It consists of a condition, a discount, and the

entity being discounted.

Conditions for discounts could include: Types of Discounts Entities Discounted

attributes of the product

the whole order

person purchasing

time of day

percentage off

fixed price off

fixed price

individual products

whole orders

shipping

tax

We used the following types of promotions in the Pioneer Cycling site:

• Percentage amount off a particular product

• Percentage amount off a whole order

• Specific amount off a particular product

• Specific amount off a whole order

• Specific amount or percentage off a product based on an attribute

• Free product or free order (100% off or fixed price = 0)

• Discount a product in an certain category

• Discount an item or order based upon order attributes (such as number of items in an order or total price of

an order)

Some examples of the discounts we used in the Pioneer Cycling store are:

• “Always <> Apply discount to all items”

• “When Order’s priceInfo’s amount is greater than 80 <> Apply discount to: Order Total”

• “When Order contains at least 3 ( product in category named Helmets ) <> Apply discount to: up to 1

( product in category named Helmets )”

Pioneer Cycling Promotions

We created the following promotions for the Pioneer Cycling store.

• $20 off an order over $80 – coupon (used for coupon COUP1234.)

Page 201: At g Cons Comm Ref Guide

10 Merchandising 195

• $5 off sixth part

• 10 Francs off sixth part

• 10% off everything in the store

• 20% off order

• 25% off BMX bikes

• 25% off Helmet with purchase of bike

• 30% off Helmet with purchase of bike

• 40% off Helmet with purchase of bike

• 5% off everything in the store

• 5% off everything in the store – coupon (Used for coupon COUP1000)

• 50 Yen off sixth part

• Buy 2 helmets get 1 free

• Buy 2 lights get 1 free

• Free shipping for orders over $400

• Free water bottle with jersey

Targeting Content for Promotions

This section describes the various ways to target content for promotions. There are different methods for

targeting content. You can:

• Create DSS-based promotions (scenarios)

• Highlight products and content

• Cross sell and upsell

Scenarios

Scenarios can update or track user profiles and offer promotions. Store pages can include slots dedicated to

DSS-based promotions. A DSS scenario is set up to watch for the following types of conditions or events:

• Buyers with certain profile traits

• Products or content viewed or products added to cart

• Attributes of products or content viewed or of products added to cart

• Order amount or quantity

• Time

Page 202: At g Cons Comm Ref Guide

196 10 Merchandising

The scenario responds by performing any of the following actions:

• Displaying a piece of promotional media in one of the slots

• Giving the buyer a promotion that changes the order price

• Sending the buyer an e-mail

• Changing a profile attribute

In the Pioneer Cycling store, the store home page and the shopping cart page display promotional information.

For example, the store offers a 10% discount to new members.

You may want to push specific products in specific locations on your site. For example, in the Pioneer Cycling

store, we chose to highlight a particular type of bike jersey for a period of time on the clothing page.

The Pioneer Cycling store highlights various products. For example, a specific bike is highlighted on the

Mountain Bikes category page. Or, particular helmets and water bottle cages are featured on each bicycle

product page in the “Related Products” section. For more information, see Appendix A, Pioneer Cycling

Scenarios (page 229).

Highlighting products and content

You can create and manage a list of specified products to recommend to all customers or a group of specifically

targeted customers. You can assign a discount to products on the list. Examples of promotional lists are a list of

the top ten products or a list of award-winning products. We did not implement any promotional lists in Pioneer

Cycling, but you could do this in the following manner:

1. Create a new category or categories in your store to hold these special product lists. For example: “Store ->

Special -> Award Winning Products”. One of the advantages of ATG Consumer Commerce’s flexible category

system is that products may be located in many different areas of the store.

2. Include the special products by adding them to the new category’s fixedChildProducts attribute.

3. Create a global promotion that discounts this category. Set its attribute "Automatically give to all

customers" = "true" to make it global. Then set its discount rule to "Always discount items in

category Award Winning Products". Now, these items are discounted, regardless of whether the

customer views them in the special category or their original category.

4. Deliver some media to the customer to announce your new category, or simply place a link to the category

somewhere noticeable.

Cross-selling

ATG Consumer Commerce offers cross-selling features. Cross-selling promotes an add-on or accessory product

or service that, when combined with the primary product, makes a better or complete solution. Business

managers can use the commerce repository editor to identify products that are related (for example, a jersey

and some matching gloves) and have the catalog display a set of links to those products.

In the Pioneer Cycling site, cross-selling is accomplished by a related accessory list on a product page. Each

product has a list of related products. These products are chosen by a merchandiser, and placed into the

fixedRelatedProducts attribute. These related products are shown on a product template, with links to more

product information. A Get It button is also created that allows the customer to add that product to the cart

from that page. For example, in the following screenshot, a helmet and a water bottle cage are shown on the

page of a specific bike. The customer could add the helmet to her cart directly from this page rather than having

to go to the product page of the helmet.

Page 203: At g Cons Comm Ref Guide

10 Merchandising 197

Cross-selling a helmet and water bottle cage with a bike.

Upselling

The key to upselling is to promote a more powerful or higher-level product than the one the customer is

considering. It is key to portray both the current product and the promoted product as desirable for the

customer.

We didn’t specifically implement any upselling in Pioneer Cycling but you could easily do it in your web

application. The concept of upselling is to sell products of higher quality or better price to customers. For

example, if a customer looks at $1500 bike, we could also show a $2000 bike under Related Products on that

product page. We could add the text, “Bike X has the same safety and styling features as Bike Y, plus the added

features of a specially designed comfort seat and a titanium frame.”

This sort of upselling is accomplished with the same functionality as cross selling: using the

fixedRelatedProducts attribute.

You could also use a scenario to upsell products. For example, you could create a scenario that watches for

orders that contain an expensive bike frame, but economy tires. The scenario could offer an incentive for a better

set of tires, and perhaps a small discount. This scenario could potentially only be targeted at customers who

prefer more expensive products.

Page 204: At g Cons Comm Ref Guide

198 10 Merchandising

Slots

A slot is a type of component that you can create to hold items. When creating a slot, you specify certain

attributes about the slot and the type of item you can put in the slot. A slot can contain only one type of item

—for example, Products or Promotions, but not both. Slots must contain repository items; however, that item

could be a media-external item that links to anything.

For information about how to create slots, refer to the ATG Personalization Programming Guide.

Slots are typically used in the following way: One or more scenario processes puts items into the slot, and

another process looks in the slot and either displays the item in the slot or performs some other action on it.

The Pioneer Cycling site has several scenarios that populate slots with items. Then, various pages (including

store_home.jsp and many catalog display pages) dynamically display pages based on the contents of these

slots.

We used slots on the home page and among top-level pages. Slots can be filled with content, lists of products, or

promotions.

Creating a Slot

Before you can populate a slot, you must first create it. A slot is a named component so it needs a .properties

file. The slots properties files must reside in specific slot directories. The Pioneer Cycling store site has three

different types of slots, so we created separate named components:

• atg/repository/Slots/MediaSlot for media items

• atg/repository/Slots/ProductSlot for Product items

Here is an example of the MediaSlot.properties file:

#/atg/registry/Slots/MediaSlot$class=atg.scenario.targeting.RepositoryItemSlot$scope=sessiongeneration=0itemDescriptorName=media-externalmaxRenderSize=1repositoryName=ProductCatalogretrieval=1

As mentioned before, a slot can contain only one type of item. The itemDescriptorName defines what

item type is put into this slot in this case media-external. The media-external type is defined in the

ProductCatalog, so ProductCatalog is stored in the repositoryName attribute. The maxRenderSize

indicates the maximum number of items that can be pulled out of the slot at one time. See the Using Slots

chapter in the ATG Personalization Programming Guide for specifics on slots properties files.

We needed to create two slots for displaying promotions to our French and Japanese users: ProductSlotFr

and ProductSlotJa. These are similar to the original product slot, except that they refer to the different

repositories for the different language catalogs because each slot can only receive items from a single

repository.

The MediaSlot was localized in a different fashion. The same slot is displayed to all three languages, but the

media item that is placed into the slot checks the current locale, and renders the media in the appropriate

language.

Page 205: At g Cons Comm Ref Guide

10 Merchandising 199

Populating a Slot

We created some scenarios that populate the Pioneer Cycling site’s MediaSlot with zero or more image files

that contain promotional images we want to display at the top of store_home.jsp.

ATG Relationship Management Platform is very flexible and allows many options. We chose to create a scenario

called “New Customers” that populates the Pioneer Cycling site’s MediaSlot with an HTML file containing an

image that tells users they are eligible for 20% off their first purchase.

It accomplishes this with the “Show items in slot” action.

Displaying Items in a Slot

Once a slot is populated, you can do several things with the item in the slot. This section shows a simple slot

item example.

store_home.jsp simply displays whatever image is in the media-external item (an HTML file with a

reference to an image file) in the MediaSlot. The image is not clickable — the user just sees the image, but

clicking on the image does not result in any special behavior.

store_home.jsp displays the contents of the MediaSlot using the DisplayMediaSlot.jsp fragment.

The following JSP fragment is used to display the contents of the MediaSlot:

<DECLAREPARAM NAME="NumToDisplay" CLASS="java.lang.String" DESCRIPTION="NumToDisplay">

<dsp:droplet name="/atg/targeting/TargetingFirst"> <dsp:param name="howMany" param="NumToDisplay"/> <dsp:param bean="/atg/registry/Slots/MediaSlot" name="targeter"/> <dsp:param name="fireContentEvent" value="false"/> <dsp:param name="fireContentTypeEvent" value="false"/> <dsp:oparam name="output"> <dsp:getvalueof id="media_url" param="element.url" idtype="String"> <dsp:include page="<%=media_url%>" flush="true"/> </dsp:getvalueof> </dsp:oparam> <dsp:oparam name="empty"> No special offers today! </dsp:oparam>

</dsp:droplet>

This fragment displays the first item in the MediaSlot. Because the items in the slot are of type media-

external, they have a “url” property that can be used to display the media item. In this case, the “url” value in

the repository item points to the name of a file that contains the desired image.

In addition to MediaSlot, the Pioneer Cycling site uses the ProductSlot to display various products to

customers. Scenarios are created to populate the ProductSlot with products. Site developers can create

scenarios to populate a ProductSlot with some of the site’s newest, coolest products.

You could also create a scenario to populate a ProductSlot with products that are overstocked and are being

discounted to reduce stock. The Pioneeer Cycling site populates the ProductSlot with two simple scenarios. One

is the “Show Products” scenario that adds the new QW-33 Bike and some related products to the ProductSlot.

Page 206: At g Cons Comm Ref Guide

200 10 Merchandising

If the user views that particular bike, several other related parts are added to the ProductSlot via the “Cross

Sell” scenario.

The ProductSlot display code is similar to the MediaSLot code. You can view the JSP at

<ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/

web-app/en/common/DisplayProductSLotHome.jsp.

Gift Certificates

The Pioneer Cycling store allows customers to purchase and redeem gift certificates. A customer can pay for an

order using a gift certificate, credit card, or a combination of the two. There are three steps in the gift certificate

process: the customer purchases the gift certificate; Pioneer Cycling delivers the gift certificate to the designated

recipient, and the recipient redeems the gift certificate.

Purchase of Gift Certificate

Gift certificates are created in the Claimable repository for customers to claim when they check out. This

repository entry is typically generated when a user purchases the gift certificate. Please refer to the ATG

Consumer Commerce Claimable repository for more information. The Claimable repository contains items that

can be claimed by a user; it currently includes gift certificates and coupons.

For the Pioneer Cycling store, we created gift certificates as a product of the store. When the product was

created, all the information necessary to create the Gift Certificate was captured by the base product item-

descriptor definition. The gift certificate was created just as any other product and data was inserted with one

exception: the fulfiller field. Gift certificates are “shipped” electronically, so we used the SoftgoodFulfiller

instead of the standardHardgoodFulfiller used for tangible goods.

After we created the gift certificate item in the product catalog so that customers could view it, we

generated a purchase page. The purchase page differs from the pages used to purchase hard goods

in that the type of shipping group used to ship the gift certificate is different than hard goods. The

two main differences are that when the gift certificate is purchased it requires an e-mail address,

and the form submits to ShoppingCartHandler.handleAddSoftGoodToOrder() instead

of .ShoppingCartHandler.handleAddItemToOrder(), which is used for hard goods.

The recipient’s e-mail address must be specified by the property softGoodRecipientEmailAddress of the

ShoppingCartModifier component. This e-mail address is then used by the handleAddSoftGoodToOrder()

method to populate the shipping group associated with the gift certificate.

The JSP that places the gift certificate into the cart looks like:

Recipient's email address <dsp:inputbean="ShoppingCartModifier.softGoodRecipientEmailAddress" size="30"/><p><%/* ADD TO CART BUTTON: Adds this SKU to the Order*/%><dsp:input bean="ShoppingCartModifier.addSoftGoodToOrder" type="submit" value=" Add Gift Certificate to Cart "/>

Note that the amount of the gift certificate is set as the face value of the item times the quantity. So, if a store

offers gift certificates with a SKU price of $20 and a customer purchases five of them for one address, the

amount of the gift certificate is set to $100.

Page 207: At g Cons Comm Ref Guide

10 Merchandising 201

E-mail Delivery of Gift Certificates

Gift certificates are delivered to the user via the SoftGoodFulfiller. This service is defined by the component

at /atg/commerce/fulfillment/SoftgoodFulfiller. The delivery mechanism is e-mail. The component

that lives at /atg/commerce/fulfillment/SoftgoodFulfiller has several properties that can be set

to personalize the message sent by the fulfiller to the user. For the Pioneer Cycling site, we changed the

values of some of these properties so that the message would be appropriate for the store. This is what the

SoftgoodFulfiller.properties file for the Pioneer Cycling store looks like:

# email properties for customized messagedefaultSubject=A Gift Certificate for [email protected]=Here is a gift certificate claim number for you. You can go toPioneer Cycling store to use it.giftCertificateEmailTemplate=/PioneerCycling/en/emails/GiftCertificate.jsp

This sets the properties that control the contents of the e-mail sent to the gift certificate recipient.

Using Gift Certificates

The ShoppingCartModifier component handles claiming gift certificates and placing them into the purchase

information for an order. It relies on two key pieces: having the property giftCertificateNumbers be

populated with a String that represents white space separated gift certificate claim numbers and calling the

moveToConfirmation method of the ShoppingCartModifier on form submission. This is how we set up the

use of gift certificates in the Pioneer Cycling store:

In the two pages that deal with payment in the Pioneer Cycling store, PaymentInfo.jsp and

PaymentInfo2.jsp, we generated an input field where customers can type in the claim codes for their gift

certificates. Then, the submit value of the form gets set to moveToConfirmation. This is what the JSP looks

like:

If you have gift certificates, type their codes below<br><dsp:input bean="ShoppingCartModifier.giftCertificateNumbers" size="50" type="text" value=""/><br>

<%/* submition handle */%><dsp:input bean="ShoppingCartModifier.MoveToConfirmation" type="submit" value=" Continue --> "/>

Coupons

Coupons are a mechanism for delivering promotions. A customer receives a claim code for a coupon via

e-mail; she enters it during the checkout process and then the promotion is added to her Profile’s list of

activePromotions. The promotion becomes immediately available to her. There are two parts to the coupon

system: getting coupons into the system so they can be claimed and then the action of claiming the coupon.

Page 208: At g Cons Comm Ref Guide

202 10 Merchandising

Creating Coupons

Coupons exist in the claimable repository. The claimable repository is located at <ATG9dir>/home/atg/

commerce/en/claimable/ClaimableRepository. One of the item-descriptors defined in the claimable

repository is coupon. A coupon is essentially a claim code attached to a promotion. There are several ways to put

information into the repository: using the /atg/commerce/promotions/CouponDroplet or using the ACC to

populate the repository with an item. For the Pioneer Cycling store, we used the ACC to populate the repository

with two coupons:

• COUP1234: gives the customer a promotion called “5% off everything in the store - coupon”

• COUP1000: gives the customer a promotion called “$20 off orders over $80 - coupon”

We clicked on the Commerce component in the ACC and then selected the repository for “Gift Certificates and

Coupons.” After selecting this repository, we selected “New Item” and then entered the necessary information—

the promotion that we wanted to associate with the coupon.

Claiming Coupons

Claiming coupons is done via the /atg/commerce/promotions/CouponFormHandler component. This

component needs several properties set on a Java Server Page:

• couponClaimCode - the code to claim the coupon

• claimCouponSuccessURL - the URL to go to on successful claim

• claimCouponErrorURL - the URL to go to on claim failure

After the user has typed in a coupon claim code, the FormHandler attempts to claim it and add it to the user’s

list of promotions. Here is the JSP that can be used to claim a coupon.

<dsp:form action="<%=form40%>" method="post">

<% /* Where to go to on success or failure */%>

<dsp:input bean="CouponFormHandler.claimCouponSuccessURL" beanvalue="/OriginatingRequest.requestURI" type="hidden"/> <dsp:input bean="CouponFormHandler.claimCouponErrorURL" beanvalue="/OriginatingRequest.requestURI" type="hidden"/>

<%/* Get the coupon claim code */%> Coupon code: <dsp:input bean="CouponFormHandler.couponClaimCode" type="text"/>

<dsp:input bean="CouponFormHandler.claimCoupon" type="submit" value="Claim it"/></dsp:form>

Page 209: At g Cons Comm Ref Guide

10 Merchandising 203

Gift Registry

Overview

The Pioneer Cycling Store uses ATG Consumer Commerce gift lists to let customers keep lists of items they would

like to purchase in the future or receive as gifts and register upcoming events (such as birthdays or weddings).

ATG Consumer Commerce gift lists are repository-based. The repository is configured in the /atg/commerce/

gifts/Giftlists.properties file. ATG Consumer Commerce provides all the components required to

interface with the repository. The gift lists in the Pioneer Cycling Store use standard ATG Consumer Commerce

functionality and components; no custom code was written.

ATG Consumer Commerce Components

The ATG Consumer Commerce component hierarchy for the gift list functionality used in the Pioneer Cycling

Store is in atg.commerce.gifts. This package includes formHandlers and droplets that support:

• creating, editing, and publishing gift lists.

• adding items to gift lists.

• moving items between gift lists and shopping carts.

• searching for and purchasing from published gift lists.

For a complete description of the gift list repository, database, components, and configuration files, see the

chapter, Configuring Merchandising Services, in the ATG Commerce Programming Guide.

GiftlistFormHandler

The GiftlistFormHandler provides the primary interface between the customer and the gift list repository.

This form handler accepts input from the customer to create, update, and delete gift lists using information

provided. It is also used to add items to and remove items from gift lists. Properties in the handler are used to

store user input for processing as well as to store success and failure URLs to which the user is redirected after

processing.

LookupDroplets

The GiftlistLookupDroplet and GiftitemLookupDroplet components are instances of the

atg.repository.servlet.ItemLookupDroplet. These components provide an easy way to find items in the

repository based on id.

GiftlistDroplet

The GiftlistDroplet is a servlet bean that allows customers to add and remove gift lists (of other customers)

to their profile.

GiftitemDroplet

The GiftitemDroplet is a configurable servlet bean that allows customers to remove or buy items

from their own personal gift lists. Two droplets are provided with ATG Consumer Commerce gift lists:

RemoveItemFromGiftlist and BuyItemFromGiftlist.

Page 210: At g Cons Comm Ref Guide

204 10 Merchandising

GiftlistSearch

The GiftlistSearch form handler is used to search the repository for gift lists based on properties other than

id. The handler uses input from the customer such as owner name, event name, event type, and state to find gift

lists that other customers have published and to return a list of gift lists that match the given criteria.

Pioneer Cycling Gift Lists

We used ATG Consumer Commerce gift list functionality to create two types of lists in the Pioneer Cycling Store

that allow customers to register products and other information.

We called the first type of registry a wish list. The wish list is a default private list given to all customers to record

products to purchase at a later date. As customers shop through the store, they can add items to the wish list or

move items from the wish list to their shopping cart.

We called the other type of registry a gift list. Registered customers can create an unlimited number of these

lists to register upcoming events and to record pertinent information such as the event name, date, or shipping

address. They can add products to the list as they shop. Customers can publish their gift lists so that other

customers can search for and purchase items from them.

Creating Wish Lists

All Pioneer Store customers are given a default wish list when they visit the store. ATG Consumer Commerce

extends the user definition in the standard ATG Relationship Management Platform Profile Repository by adding

a wishlist property to userProfile.xml.

Creating and Displaying Gift Lists

All Pioneer Store registered customers can create and update an unlimited number of gift lists that are added to

their profiles. We used the standard gift lists provided by ATG Consumer Commerce that extend the customer’s

profile in the Profile Repository. Again, we used userProfile.xml. Refer to the Configuring Merchandising

Services chapter in the ATG Commerce Programming Guide for more information on how gift lists are created for

each user.

The GiftlistFormHandler manages wish lists and gift lists. The following wish list and saved gift lists code

sample shows how the Pioneer Cycling store displays wish lists and gift lists and the items in each. lists.jsp.

This sample has been edited for clarity. See <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/web-

app/en/user/lists.jsp for the complete file.

<dsp:form action="lists.jsp" method="post"><p><span class=storebig>My Lists</span><p><span class=help>Lists allow you to save products you want for later, and shareyour giftlists with friends.</span><p>

<!-- First display the wishlist -->

<dsp:setvalue beanvalue="Profile.wishlist" param="wishlist"/><dsp:setvalue paramvalue="wishlist.giftlistItems" param="items"/><dsp:setvalue paramvalue="wishlist.id" param="giftlistId"/><dsp:input bean="GiftlistFormHandler.updateGiftlistItemsSuccessURL" type="hidden" value="./lists.jsp"/><dsp:input bean="GiftlistFormHandler.updateGiftlistItemsErrorURL" type="hidden"

Page 211: At g Cons Comm Ref Guide

10 Merchandising 205

value="./lists.jsp"/><dsp:input bean="GiftlistFormHandler.giftlistId" paramvalue="giftlistId" type="hidden"/><dsp:droplet name="IsEmpty"> <dsp:param name="value" param="items"/> <dsp:oparam name="false"> <dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="items"/>

<dsp:oparam name="output"> <tr valign=top> <td><input name='<dsp:valueof param="element.id"/>' size="2" type="text" value='<dsp:valueof param="element.quantityDesired"/>'></td> <td></td> <td> <dsp:droplet name="/atg/commerce/catalog/ProductLookup"> <dsp:param name="id" param="element.productId"/> <dsp:param name="elementName" value="product"/> <dsp:oparam name="output"> <b>

<dsp:getvalueof id="templateUrl" idtype="String" param="product.template.url"> <dsp:a page="<%=templateUrl%>"> <dsp:param name="id" param="id"/> <dsp:param name="navAction" value="jump"/> <dsp:droplet name="/atg/commerce/catalog/SKULookup"> <dsp:param name="id" param="element.catalogRefId"/> <dsp:param name="elementName" value="giftSku"/> <dsp:oparam name="output"><dsp:valueof param="giftSku.displayName"/></dsp:oparam> </dsp:droplet> </dsp:a> </dsp:getvalueof> </b> </dsp:oparam> </dsp:droplet> </td> </dsp:oparam> <dsp:oparam name="outputEnd"> <dsp:input bean="GiftlistFormHandler.updateGiftlistItems" type="submit" value="Update wishlist quantities"/> </dsp:oparam> </dsp:droplet> </dsp:oparam>

<dsp:oparam name="true"> <b>Your wishlist is empty</b> </dsp:oparam></dsp:droplet></dsp:form>

<!-- Next display each giftlist -->

<dsp:droplet name="IsEmpty"> <dsp:param bean="Profile.giftlists" name="value"/> <dsp:oparam name="false">

<dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param bean="Profile.giftlists" name="array"/>

Page 212: At g Cons Comm Ref Guide

206 10 Merchandising

<dsp:oparam name="output">

<blockquote> <dsp:form action="lists.jsp" method="post"> <table cellspacing=2 cellpadding=0 border=0 width=75%> <!-- Display giftlist name --> <tr><td colspan=10 class=box-top-viewmy> <dsp:valueof param="element.eventName"/> </td> </tr>

<!-- Display giftlist items if any --> <dsp:setvalue paramvalue="element.giftlistItems" param="items"/> <dsp:setvalue paramvalue="element.id" param="giftlistId"/> <dsp:setvalue paramvalue="element.eventName" param="listName"/>

<dsp:input bean="GiftlistFormHandler.updateGiftlistItemsSuccessURL" type="hidden" value="./lists.jsp"/> <dsp:input bean="GiftlistFormHandler.updateGiftlistItemsErrorURL" type="hidden" value="./lists.jsp"/> <dsp:input bean="GiftlistFormHandler.giftlistId" paramvalue="giftlistId" type="hidden"/>

<dsp:droplet name="IsEmpty"> <dsp:param name="value" param="items"/> <dsp:oparam name="false">

<!-- Display giftlist items if any --> </dsp:oparam> <dsp:oparam name="true"> </dsp:oparam> </dsp:droplet> </dsp:form><!--* Finally, check if customer is registered. If so allow them to create a * new giftlist using GiftlistFormHandler.createGiftlist. -->

The following screenshot shows how the above code sample displays the customer’s wish list, gift list(s) and

their contents.

Page 213: At g Cons Comm Ref Guide

10 Merchandising 207

Creating Gift Lists

In the above screenshot, one option for registered customers is to create and edit gift lists. The following code

sample shows how to test for registered customers and to display the box to create a new list:

lists.jsp

<dsp:droplet name="/atg/dynamo/droplet/Switch"> <dsp:param bean="Profile.transient" name="value"/> <dsp:oparam name="false"> <blockquote> <table cellspacing=0 cellpadding=0 border=0 width=75%> <tr><td colspan=6 class=box-top-viewmy>Make a new gift list</td></tr> <tr><td colspan=6 class=box>

Page 214: At g Cons Comm Ref Guide

208 10 Merchandising

<dsp:form action="lists_new.jsp" method="POST"> <dsp:input bean="GiftlistFormHandler.createGiftlistSuccessURL" type="hidden" value="./lists_new.jsp"/> <dsp:input bean="GiftlistFormHandler.createGiftlistErrorURL" type="hidden" value="./lists.jsp"/> Name of event <span class=help>(i.e. Harry's Wedding List)</span><br> <dsp:input bean="GiftlistFormHandler.eventName" size="25" type="text" value="New Event"/> <p> <dsp:input bean="GiftlistFormHandler.createGiftlist" type="submit" value="Make new gift list"/> </dsp:form> </td> </tr> </table> </blockquote> </dsp:oparam> <dsp:oparam name="default"> <blockquote> <b>Login or Register and create a gift list!</b><p> Registered customers at Pioneer Cycling are able to create giftlists and share them with their friends. Let everyone know what gear you want for your birthday. <p> > <dsp:a href="my_profile.jsp">Login or Register </dsp:a> now and start workingon your giftlist. </blockquote> </dsp:oparam></dsp:droplet>

When customers select the “Make New Gift list” button, they are directed to the “create gift list” page where

they enter information about the upcoming event such as date, type, and description. This form again uses the

GiftlistFormHandler to accept customer input and to call handlers to save the information in the repository.

The following code sample (from lists_create.jsp) shows how input is taken and handle methods are

called.

Note: The “. . .” markers in the following code sample indicate a place where code has been removed to clarify

the sample.

<%--This page allows a user to create a new giftlist--%>

<dsp:importbean bean="/atg/commerce/gifts/GiftlistFormHandler"/><dsp:importbean bean="/atg/dynamo/droplet/ForEach"/><dsp:importbean bean="/atg/dynamo/droplet/Switch"/><dsp:importbean bean="/atg/dynamo/droplet/IsEmpty"/><dsp:importbean bean="/atg/userprofiling/Profile"/>

<span class=storebig>Create new giftlist</span><dsp:form action="lists.jsp" method="POST"><dsp:input bean="GiftlistFormHandler.saveGiftlistSuccessURL" type="hidden" value="./lists.jsp"/><dsp:input bean="GiftlistFormHandler.saveGiftlistErrorURL" type="hidden" value="./lists_new.jsp"/>. . .

<b>Event Name</b><br>

Page 215: At g Cons Comm Ref Guide

10 Merchandising 209

<dsp:input bean="GiftlistFormHandler.eventName" size="40" type="text"/> <p> <b>Event Date</b> <dsp:select bean="GiftlistFormHandler.month"> <dsp:droplet name="ForEach"> <dsp:param bean="GiftlistFormHandler.months" name="array"/> <dsp:oparam name="output"> <dsp:getvalueof id="option564" param="index" idtype="java.lang.Integer"> <dsp:option value="<%=option564.toString()%>"/> </dsp:getvalueof> <dsp:valueof param="element">UNDEFINED</dsp:valueof> </dsp:oparam> </dsp:droplet> </dsp:select> <dsp:select bean="GiftlistFormHandler.date"> <dsp:droplet name="ForEach"> <dsp:param bean="GiftlistFormHandler.dates" name="array"/> <dsp:oparam name="output"> <dsp:getvalueof id="option583" param="element" idtype="java.lang.String"> <dsp:option value="<%=option583%>"/> </dsp:getvalueof> <dsp:valueof param="element">UNDEFINED</dsp:valueof> </dsp:oparam> </dsp:droplet> </dsp:select> <dsp:select bean="GiftlistFormHandler.year"> <dsp:droplet name="ForEach"> <dsp:param bean="GiftlistFormHandler.years" name="array"/> <dsp:oparam name="output"> <dsp:getvalueof id="option602" param="element" idtype="java.lang.String"> <dsp:option value="<%=option602%>"/> </dsp:getvalueof> <dsp:valueof param="element">UNDEFINED</dsp:valueof> </dsp:oparam> </dsp:droplet> </dsp:select> <p> <b>Event Type</b> <dsp:select bean="GiftlistFormHandler.eventType"> <dsp:droplet name="ForEach"> <dsp:param bean="GiftlistFormHandler.eventTypes" name="array"/> <dsp:oparam name="output"> <dsp:getvalueof id="option627" param="element" idtype="java.lang.String"> <dsp:option value="<%=option627%>"/> </dsp:getvalueof> <dsp:valueof param="element">UNDEFINED</dsp:valueof> </dsp:oparam> </dsp:droplet> </dsp:select><br>

<b>Comments</b><br> <dsp:setvalue bean="GiftlistFormHandler.comments" value=""/> <dsp:textarea bean="GiftlistFormHandler.comments" rows="4" cols="40"></dsp:textarea><%-- <dsp:textarea bean="GiftlistFormHandler.comments" maxlength="254" value="" rows="4" cols="40"></dsp:textarea></dsp:textarea> --%>

Page 216: At g Cons Comm Ref Guide

210 10 Merchandising

<p>

<b>Event Description</b><br> <dsp:setvalue bean="GiftlistFormHandler.description" value=""/><%-- <dsp:textarea bean="GiftlistFormHandler.description" maxlength="254" value="" rows="4" cols="40"></dsp:textarea> --%> <dsp:textarea bean="GiftlistFormHandler.description" rows="4" cols="40"></dsp:textarea> <p> <b>Extra information or special instructions</b><br> <%-- <dsp:textarea bean="GiftlistFormHandler.instructions" maxlength="254" value="" rows="4" cols="40"></dsp:textarea> --%> <dsp:textarea bean="GiftlistFormHandler.instructions" rows="4" cols="40"></dsp:textarea> <p> <b>Where should people ship the gifts?</b><p> <blockquote> <dsp:input bean="GiftlistFormHandler.isNewAddress" type="radio" checked="<%=true%>" value="false"/>Choose one of your saved shipping destinations.<br> <dsp:select bean="GiftlistFormHandler.shippingAddressId"> <dsp:droplet name="ForEach"> <dsp:param bean="GiftlistFormHandler.addresses" name="array"/> <dsp:oparam name="output"> <dsp:getvalueof id="option695" param="key" idtype="java.lang.String"> <dsp:option value="<%=option695%>"/> </dsp:getvalueof> <dsp:valueof param="element">UNDEFINED</dsp:valueof> </dsp:oparam> </dsp:droplet> </dsp:select><br> <p> <dsp:input bean="GiftlistFormHandler.isNewAddress" type="radio" value="true"/>New address below <p> If you want this address stored in your address book, please give it a nickname:<br> <dsp:input bean="GiftlistFormHandler.newAddressName" size="40"/><br> Name <br><dsp:input bean="GiftlistFormHandler.newAddress.firstName" name="firstName" size="15"/> <dsp:input bean="GiftlistFormHandler.newAddress.middleName" name="middleName" size="5"/> <dsp:input bean="GiftlistFormHandler.newAddress.lastName" name="lastName" size="15"/><br> Street address <br> <dsp:input bean="GiftlistFormHandler.newAddress.address1" name="address1" size="40"/><br> <dsp:input bean="GiftlistFormHandler.newAddress.address2" name="address2" size="40"/><br> <table border=0 cellspacing=0 cellpadding=0> <tr valign=top> <td>City<br> <dsp:input bean="GiftlistFormHandler.newAddress.city" name="city" size="15"/></td> <td>State<br> <dsp:select bean="GiftlistFormHandler.newAddress.state"> <%@ include file="StatePicker.jspf" %> </dsp:select></td> <td>Postal Code<br>

Page 217: At g Cons Comm Ref Guide

10 Merchandising 211

<dsp:input bean="GiftlistFormHandler.newAddress.postalCode" name="postalCode" size="15"/></td> </tr> </table>

Country <dsp:select bean="GiftlistFormHandler.newAddress.country"> <%@ include file="CountryPicker.jspf" %> </dsp:select> </blockquote>

<p>&nbsp;<br> </td> </tr>

<tr valign=top> <td width=30%> <span class=help> Decide if you want your list to be public yet. When you make your list public, your friends can find your list by searching. </span>

</td> <td></td> <td> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr><td class=box-top-store>Gift list public?</td></tr></table> <p>

<dsp:input bean="GiftlistFormHandler.isPublished" name="published" type="radio" value="true"/> Make my list public now<br> <dsp:input bean="GiftlistFormHandler.isPublished" name="published" type="radio" checked="<%=true%>" value="false"/> Don't make my list public yet

<p>&nbsp;<br>

<dsp:input bean="GiftlistFormHandler.saveGiftlist" type="submit" value="Save gift list"/>

Here is the screenshot for lists_new.jsp. It takes the input from customers and saves it to the repository.

Page 218: At g Cons Comm Ref Guide

212 10 Merchandising

lists_new.jsp

Editing and Deleting Gift Lists

The implementations of editing and deleting are similar to creating gift lists. On the “My Lists” page, there is an

edit this list link below each gift list. This link directs the customer to the edit gift list page and passes the gift

list id through. For the Pioneer Cycling store, we did this in lists_edit.jsp. It calls GiftlistLookupDroplet

to get the gift list item from the repository and checks the owner of the gift list against the profile id. If they

match, then the gift list information is displayed in the same format as create and options to save and delete are

Page 219: At g Cons Comm Ref Guide

10 Merchandising 213

provided. The following code sample demonstrates how to call GiftlistLookupDroplet and how to check for

permissions.

Note: The “. . .” markers in the following code sample indicate a place where code has been removed to clarify

the sample.

lists_edit.jsp:

<dsp:droplet name="/atg/commerce/gifts/GiftlistLookupDroplet"> <dsp:param name="id" param="giftlistId"/> <dsp:oparam name="output"> <dsp:droplet name="IsEmpty"> <dsp:param name="value" param="element"/> <dsp:oparam name="false"> <dsp:setvalue paramvalue="element" param="giftlist"/>

<dsp:droplet name="/atg/dynamo/droplet/Switch"> <dsp:param bean="Profile.id" name="value"/> <dsp:getvalueof id="nameval3" param="giftlist.owner.id" idtype="java.lang.String"><dsp:oparam name="<%=nameval3%>"> <dsp:form action="lists.jsp" method="POST">

. . .

<b>Event Name</b><br> <dsp:input bean="GiftlistFormHandler.eventName" paramvalue="giftlist.eventName" size="40" type="text"/>

. . . <p>&nbsp;<br> <dsp:input bean="GiftlistFormHandler.deleteGiftlistSuccessURL" type="hidden" value="./lists.jsp"/> <dsp:input bean="GiftlistFormHandler.deleteGiftlistErrorURL" type="hidden" value="<%=thisPage.getNewUrl()%>"/> <dsp:input bean="GiftlistFormHandler.deleteGiftlist" name="delete" type="submit" value="Delete gift list"/> <dsp:input bean="GiftlistFormHandler.updateGiftlistSuccessURL" type="hidden" value="./lists.jsp"/> <dsp:input bean="GiftlistFormHandler.updateGiftlistErrorURL" type="hidden" value="<%=thisPage.getNewUrl()%>"/> <dsp:input bean="GiftlistFormHandler.updateGiftlist" name="update" type="submit" value="Save gift list"/> </core:createUrl> </td> </tr> </table> </dsp:form> </dsp:oparam> <dsp:oparam name="default"> You do not have permission to access the specified giftlist </dsp:oparam> </dsp:droplet> </dsp:oparam> <dsp:oparam name="true"> <font color=cc0000><STRONG><UL> Either no giftlist found or you do not have permission to access

Page 220: At g Cons Comm Ref Guide

214 10 Merchandising

it. </dsp:oparam> </dsp:droplet> </dsp:oparam></dsp:droplet>

Adding Items to Gift Lists

Customers can add items to any of their lists while they shop. In the Pioneer Cycling Store, we used the /atg/

commerce/gifts/GiftlistFormHandler component in the product display page with an option for the

customer to pick a list for the selected item.

The following code sample (from GiftlistGetItBox.jsp) demonstrates how to set the formHandler

properties and how to add the selected product and quantity to a gift list:

<dsp:importbean bean="/atg/commerce/gifts/GiftlistFormHandler"/><dsp:importbean bean="/atg/userprofiling/Profile"/>

<table cellpadding=0 cellspacing=0 border=0> <tr> <td class=box-top-store>Add to Gift List</td> </tr> <tr> <td class=box>

<dsp:getvalueof id="form24" bean="/OriginatingRequest.requestURI" idtype="java.lang.String"><dsp:form action="<%=form24%>" method="post"> <input name="id" type="hidden" value='<dsp:valueof param="id"/>'> <input type="hidden" name="itemType" value="product"> <input name="itemId" type="hidden" value='<dsp:valueof param="Product.repositoryId"/>'> <dsp:input bean="GiftlistFormHandler.addItemToGiftlistSuccessURL" type="hidden" value="../user/lists.jsp"/> <dsp:input bean="GiftlistFormHandler.addItemToGiftlistErrorURL" type="hidden" value="../user/lists.jsp"/> <dsp:input bean="GiftlistFormHandler.productId" paramvalue="Product.repositoryId" type="hidden"/>

<%/*Display any errors that have been generated during Cart operations: */%> <dsp:include page="../../common/DisplayGiftlistFormHandlerErrors.jsp" flush="true"></dsp:include>

Add <dsp:input bean="GiftlistFormHandler.quantity" size="4" type="text" value="1"/>

<dsp:select bean="GiftlistFormHandler.catalogRefIds"> <dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="Product.childSKUs"/> <dsp:param name="elementName" value="sku"/> <dsp:param name="indexName" value="skuIndex"/> <dsp:oparam name="output"> <dsp:getvalueof id="option59" param="sku.repositoryId" idtype="java.lang.String"><dsp:option value="<%=option59%>"/></dsp:getvalueof><dsp:valueof param="sku.displayName"/>

Page 221: At g Cons Comm Ref Guide

10 Merchandising 215

</dsp:oparam> </dsp:droplet> </dsp:select> <BR> to <dsp:select bean="GiftlistFormHandler.giftlistId"> <dsp:getvalueof id="option73" bean="Profile.wishlist.id" idtype="java.lang.String"><dsp:option value="<%=option73%>"/></dsp:getvalueof>My Wishlist <dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param bean="Profile.giftlists" name="array"/> <dsp:param name="elementName" value="giftlist"/> <dsp:oparam name="output"> <dsp:getvalueof id="option83" param="giftlist.id" idtype="java.lang.String"><dsp:option value="<%=option83%>"/></dsp:getvalueof><dsp:valueof param="giftlist.eventName">Undefined</dsp:valueof> </dsp:oparam> </dsp:droplet> </dsp:select> <dsp:input bean="GiftlistFormHandler.addItemToGiftlist" type="submit" value="I Want It"/> </dsp:form></dsp:getvalueof>

</td> </tr></table>

The following screenshot is from a product page that demonstrates how the above code renders on the product

page.

Page 222: At g Cons Comm Ref Guide

216 10 Merchandising

Gift Purchasing

Customers can publish gift lists so that other customers can search for them and view and purchase the items

listed on them. (Wish lists are similar to gift lists, except they are private and can be seen only by their owners.

They are not searchable.) In order to implement gift list searching and gift purchasing for the Pioneer Cycling

Store, we used the GiftlistSearch form handler to search for gift lists, GiftListDroplet to add and

remove gift lists from a profile, and GiftitemLookupDroplet to view and purchase gift items.

Searching for Gift Lists

The GiftlistSearch component is a configurable component used for gift list searching. For the

Pioneer Cycling Store, we use the standard configuration to search for gift lists. The searching criteria

used are: owner name, owner state, event name, and event type. The following configuration file,

GiftlistSearch.properties, configures an instance of the atg.commerce.gifts.SearchFormHandler

class.

/atg/commerce/gifts/GiftlistSearch.properties

$class=atg.commerce.gifts.SearchFormHandler$scope=session

doNameSearch=truenameSearchPropertyNames=owner.firstName,owner.lastName

Page 223: At g Cons Comm Ref Guide

10 Merchandising 217

doAdvancedSearch=trueadvancedSearchPropertyNames=eventType,eventName,state

doPublishedSearch=truepublishedSearchPropertyNames=public,published

profileRepository^=/atg/userprofiling/ProfileTools.profileRepositoryprofileTools=/atg/userprofiling/ProfileToolsitemTypes=gift-list

The next code sample is from giftlist_search.jsp. It demonstrates how we used the GiftlistSearch

component to find published gift lists. This file has been split into two parts to show what each section does. The

first half of the code displays previously found (if any) gift lists that have been added to the customer profile.

The following code can be found <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/

web-app/en/catalog/giftlist_search.jsp:

<%/*Display the giftlists that customer is shopping for.*/%>

<dsp:droplet name="IsEmpty"> <dsp:param bean="Profile.otherGiftlists" name="value"/> <dsp:oparam name="false"> <b>You are shopping for these people</b><br> <hr size=0> <dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param bean="Profile.otherGiftlists" name="array"/> <dsp:oparam name="output"> <p> <b><dsp:valueof param="element.owner.firstName"/> <dsp:valueof param="element.owner.middleName"/> <dsp:valueof param="element.owner.lastName"/></b></br> <dsp:valueof param="element.eventName"/> <dsp:valueof date="d-MMM-yyyy" param="element.eventDate"/><br> <b>Event Description</b><br> <dsp:valueof param="element.description"/><br> <b>Extra Information</b><br> <dsp:valueof param="element.instructions"/><br> > <dsp:a href="./giftlist_view.jsp"> <dsp:param name="giftlistId" param="element.id"/>View the items in this gift list</dsp:a> <br> > <dsp:a href="./giftlist_search.jsp"> <dsp:param name="giftlistId" param="element.id"/> <dsp:param name="action" value="remove"/> Stop shopping for this person</dsp:a> </dsp:oparam> </dsp:droplet> <hr size=0></dsp:oparam>

<dsp:oparam name="true"></dsp:oparam>

</dsp:droplet>

When rendered, the page lists all previously found gift lists and provides customers with a form to perform

additional searches.

Page 224: At g Cons Comm Ref Guide

218 10 Merchandising

The second half of giftlist_search.jsp as shown here does the searching. It takes input from the customer

and, on submit, searches the repository for published gift lists that meet the criteria.

The following code can be found in <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/

web-app/en/catalog/giftlist_search.jsp:

<dsp:droplet name="ForEach"> <dsp:param bean="GiftlistSearch.searchResults" name="array"/> <dsp:oparam name="output"> <dsp:a href="./giftlist_search.jsp"> <dsp:param name="action" value="add"/> <dsp:param name="searching" value="false"/> <dsp:param name="giftlistId" param="element.id"/> <dsp:valueof param="element.owner.firstName"/> <dsp:valueof param="element.owner.middleName"/> <dsp:valueof param="element.owner.lastName"/> </dsp:a> <dsp:valueof param="element.eventName"/> <dsp:valueof param="element.eventDate"/> <br> </dsp:oparam></dsp:droplet>

Page 225: At g Cons Comm Ref Guide

10 Merchandising 219

</blockquote>

</dsp:oparam><dsp:oparam name="true">

Your search found no giftlists <p>

</dsp:oparam></dsp:droplet></tr></table>

</dsp:oparam></dsp:droplet>

<dsp:form action="giftlist_search.jsp"><p><b>Find someone's gift list</b><hr size=0>Name: <dsp:input bean="GiftlistSearch.searchInput" size="30" type="text"/><p>Optional criteria that may make it easier to find the right list:<p>

<dsp:droplet name="ForEach"><%-- For each property specified in GiftlistSearch.advancedSearchPropertyNames, retrieve all possible property values. This allows the customer to pick one to search on for advanced searching. --%><dsp:param bean="GiftlistSearch.propertyValuesByType" name="array"/><dsp:oparam name="output"><dsp:droplet name="Switch"> <dsp:param name="value" param="key"/> <%-- One property that a product in the store can have is weight range. In this case, if the property is weight range, we want to put all possible choices in a pulldown menu. --%> <dsp:oparam name="eventType"> Event Type <%-- property to store the customer's selection is propertyValues --%> <dsp:select bean="GiftlistSearch.propertyValues.eventType"> <dsp:option value=""/>Any <dsp:setvalue paramvalue="element" param="outerelem"/> <dsp:droplet name="ForEach"> <dsp:param name="array" param="outerelem"/> <dsp:oparam name="output">

<dsp:getvalueof id="eventTypeName" param="element" idtype="java.lang.String"> <dsp:option value="<%=eventTypeName%>"/> </dsp:getvalueof>

<dsp:valueof param="element">UNDEFINED</dsp:valueof> </dsp:oparam> </dsp:droplet> </dsp:select><br> </dsp:oparam>

<dsp:oparam name="eventName"> <b>Event Name <%-- property to store the customer's selection is propertyValues --%>

Page 226: At g Cons Comm Ref Guide

220 10 Merchandising

<dsp:input bean="GiftlistSearch.propertyValues.eventName" size="30" type="text" value=""/> <br> </dsp:oparam> <dsp:oparam name="state"> <b>State <%-- property to store the customer's selection is propertyValues --%> <dsp:input type="text" size="30" bean="GiftlistSearch.propertyValues.state" value=""/> <br> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet>

Viewing Someone Else’s Gift Lists

The View the items on this gift list link takes the customer to a giftlist_view.jsp page. For this feature, we

used the GiftlistLookupDroplet to retrieve the gift list from the repository by Id. The following code sample

shows how we used the component and displayed each item on the list and information about it.

The following code can be found in <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/

web-app/en/catalog/giftlist_view.jsp:

<%/* check if parameter giftlistId has been passed into page. if it has, then call GiftlistDroplet to do something */ %><dsp:droplet name="IsEmpty"><dsp:param name="value" param="giftlistId"/><dsp:oparam name="false"> <dsp:droplet name="/atg/commerce/gifts/GiftlistLookupDroplet"> <dsp:param name="id" param="giftlistId"/> <dsp:oparam name="output"> <dsp:droplet name="IsEmpty"> <dsp:oparam name="false"> <dsp:setvalue paramvalue="element" param="giftlist"/> <span class=storebig>Gift List for <dsp:valueof param="giftlist.owner.firstName"/> <dsp:valueof param="giftlist.owner.middleName"/> <dsp:valueof param="giftlist.owner.lastName"/> </span> <br> <p> <blockquote> <b>Event name:</b><dsp:valueof param="giftlist.eventName"/> <br> <b>Event date:</b><dsp:valueof date="d-MMM-yyyy" param="giftlist.eventDate"/> <p>

<table cellspacing=0 cellpadding=0 border=0> <tr valign=bottom> <td><b>Quantity<br>wanted</b></td> <td>&nbsp;</td> <td><b>Quantity<br>bought</b></td> <td>&nbsp;&nbsp;&nbsp;</td> <td><b>Product</b></td> </tr> <tr><td colspan=5><hr size=0></td></tr> <tr valign=top> <dsp:droplet name="/atg/dynamo/droplet/ForEach">

Page 227: At g Cons Comm Ref Guide

10 Merchandising 221

<dsp:param name="array" param="giftlist.giftlistItems"/> <dsp:oparam name="output"> <dsp:setvalue paramvalue="element" param="item"/> <dsp:droplet name="/atg/commerce/catalog/ProductLookup"> <dsp:param name="elementName" value="product"/> <dsp:param name="id" param="item.productId"/> <dsp:oparam name="output"> <td><dsp:valueof param="item.quantityDesired">quantity desired</dsp:valueof></td><td></td> <td><dsp:valueof param="item.quantityPurchased">quantity purchased</dsp:valueof></td><td></td> <td>

<dsp:droplet name="/atg/dynamo/droplet/IsNull"> <dsp:param name="value" param="product.template"/> <dsp:oparam name="true"> <dsp:valueof param="product.displayName">ERROR:no product name</dsp:valueof> </dsp:oparam> <dsp:oparam name="false"> <dsp:getvalueof id="templateUrl" idtype="String" param="product.template.url"> <dsp:a page="<%=templateUrl%>"> <dsp:param name="id" param="product.repositoryId"/> <dsp:param name="giftId" param="item.id"/> <dsp:param name="giftlistId" param="giftlistId"/> <dsp:param name="gift" value="true"/> <dsp:param name="navAction" value="jump"/> <dsp:valueof param="product.displayName">ERROR:no product name</dsp:valueof> </dsp:a> </dsp:getvalueof> </dsp:oparam> </dsp:droplet>

</td> </tr> </dsp:oparam> </dsp:droplet> </dsp:oparam> <dsp:oparam name="unset"> Your giftlist is empty </dsp:oparam> </dsp:droplet> </tr> </table> <p> <b>Event description:</b><br> <dsp:valueof param="giftlist.description"/><br> <p> <b>Ship to:</b> <blockquote> </blockquote> <p> </span> <% /* Where are the gifts going to be sent to? This person may be different from the owner of the giftlist, so we need to display the shipping address person, city and state. */ %>

Page 228: At g Cons Comm Ref Guide

222 10 Merchandising

<dsp:valueof param="giftlist.shippingAddress.firstName"/> <dsp:valueof param="giftlist.shippingAddress.middleName"/> <dsp:valueof param="giftlist.shippingAddress.lastName"/><br> <dsp:valueof param="giftlist.shippingAddress.city"/>, <dsp:valueof param="giftlist.shippingAddress.state"/><br> </blockquote> <p> </dsp:oparam> <dsp:oparam name="true"> Nothing to look at here. </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet></dsp:oparam></dsp:droplet>

The figure shows how the above code sample displays information about the event and items. For each item, it

shows quantity wanted and quantity purchased to date. A link to that product page displays the product and an

“add to cart” button.

giftlist_view.jsp

Address privacy

Security is always an important factor. When customers register a gift list on the Pioneer Cycling store, they

select one of their stored shipping addresses for the list. This way, friends know where to send their gifts. All

Page 229: At g Cons Comm Ref Guide

10 Merchandising 223

addresses in ATG Consumer Commerce have an owner ID. All pages where the address may appear (gift list

details, order summary) verify the owner ID before displaying complete address information. The current profile

ID is compared to the owner ID of the address. If they are not equal, only the city and state are displayed and

the street address is hidden. Because this code is at the page level, individual sites can select a security level

as required. The following code sample demonstrates how we used the switch servlet bean on the owner id

property of an address to show limited information.

The following code can be found in <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/

web-app/en/Checkout/full/co_confirm.jsp:

<!-- If address owner id = profile id, then display street address --> <dsp:droplet name="Switch"> <dsp:param name="value" param="ShippingGroup.shippingAddress.ownerId"/>

<!-- If this user "owns" this address, pass in private=false: --> <dsp:getvalueof id="nameval3" bean="Profile.id" idtype="java.lang.String"><dsp:oparam name="<%=nameval3%>"> <dsp:getvalueof id="pval0" param="ShippingGroup.shippingAddress"> <dsp:include page="../../user/DisplayAddress.jsp" flush="true"> <dsp:param name="address" value="<%=pval0%>"/> <dsp:param name="private" value="false"/></dsp:include></dsp:getvalueof> </dsp:oparam></dsp:getvalueof>

<!-- Else pass in private=true: --> <dsp:oparam name="default"> <dsp:getvalueof id="pval0" param="ShippingGroup.shippingAddress"> <dsp:include page="../../user/DisplayAddress.jsp" flush="true"> <dsp:param name="address" value="<%=pval0%>"/> <dsp:param name="private" value="true"/></dsp:include></dsp:getvalueof> </dsp:oparam> </dsp:droplet>

en\user\DisplayAddress.jsp:

<dsp:importbean bean="/atg/dynamo/droplet/IsEmpty"/><dsp:importbean bean="/atg/dynamo/droplet/Switch"/>

<DECLAREPARAM NAME="address" CLASS="java.lang.Object" DESCRIPTION="A ContactInfo Repository Item to display">

<DECLAREPARAM NAME="private" CLASS="boolean" DESCRIPTION="If true, we will hide the details of the address.">

<dsp:valueof param="address.firstName"/><dsp:valueof param="address.middleName"/><dsp:valueof param="address.lastName"/><br><dsp:droplet name="Switch"> <dsp:param name="value" param="private"/> <dsp:oparam name="true"> </dsp:oparam> <dsp:oparam name="default"> <dsp:valueof param="address.address1"/><br>

Page 230: At g Cons Comm Ref Guide

224 10 Merchandising

<dsp:droplet name="IsEmpty"> <dsp:param name="value" param="address.address2"/> <dsp:oparam name="false"> <dsp:valueof param="address.address2"/><br> </dsp:oparam> </dsp:droplet> </dsp:oparam></dsp:droplet><dsp:valueof param="address.city"/>,<dsp:valueof param="address.state"/><dsp:valueof param="address.postalCode"/><br><dsp:valueof param="address.country"/>

Purchasing Items from Gift Lists

Each page that shows a specific gift list has links to each product on the list that bring the customer to the

corresponding product pages. Then, on each product page, the customer has the ability to add that gift item to

a shopping cart. To do this, we used the addGiftItemToOrder in the ShoppingCartModifier component to

add the gift to the customer’s shopping cart. Special handling instructions are created for a gift in a shopping

cart. See the section on Extending the Purchase Process for Gift Lists in the Configuring Merchandising Services

chapter of the ATG Commerce Programming Guide for a description of the purchase process. Customers can

either continue shopping or proceed to checkout after they add gifts to their shopping carts.

The following sample is from en\catalog\GetItForGiftListBox.jsp:

<%/*This fragment is placed on a product template. It allows a customerto purchase something off someone else's gift list. It assumes thatparam:product, param:giftlistId, and param:giftId are passed.*/%>

<dsp:importbean bean="/atg/commerce/order/ShoppingCartModifier"/><dsp:importbean bean="/atg/commerce/catalog/SKULookup"/>

<dsp:droplet name="/atg/commerce/gifts/GiftlistLookupDroplet"> <dsp:param name="id" param="giftlistId"/> <dsp:param name="elementName" value="giftlist"/>

<dsp:oparam name="output"> <table cellpadding=0 cellspacing=0 border=0> <tr> <td class=box-top-store>Get this gift for <dsp:valueof param="giftlist.owner.firstName">someone</dsp:valueof></td> </tr>

<tr> <td class=box>

<dsp:getvalueof id="form33" bean="/OriginatingRequest.requestURI" idtype="java.lang.String"><dsp:form action="<%=form33%>" method="post"> <input name="id" type="hidden" value='<dsp:valueof param="Product.repositoryId"/>'> <input type="hidden" name="itemType" value="Product"> <input name="itemId" type="hidden" value='<dsp:valueof param="Product.repositoryId"/>'> <dsp:input bean="ShoppingCartModifier.addGiftItemToOrderSuccessURL"

Page 231: At g Cons Comm Ref Guide

10 Merchandising 225

type="hidden" value="../checkout/cart.jsp"/> <dsp:input bean="ShoppingCartModifier.productId" paramvalue="Product.repositoryId" type="hidden"/> <dsp:input bean="ShoppingCartModifier.giftlistId" paramvalue="giftlist.id" type="hidden"/> <dsp:droplet name="/atg/commerce/gifts/GiftitemLookupDroplet"> <dsp:param name="id" param="giftId"/> <dsp:param name="elementName" value="giftitem"/> <dsp:oparam name="output"> <dsp:input bean="ShoppingCartModifier.giftlistItemId" paramvalue="giftitem.id" type="hidden"/> <dsp:input bean="ShoppingCartModifier.catalogRefIds" paramvalue="giftitem.catalogRefId" type="hidden"/>

<dsp:valueof param="giftlist.owner.firstName">firstname</dsp:valueof> wants <dsp:valueof param="giftitem.quantityDesired">?</dsp:valueof> <dsp:droplet name="SKULookup"> <dsp:param name="id" param="giftitem.catalogRefId"/> <dsp:param name="elementName" value="skuItem"/> <dsp:oparam name="output"> <dsp:valueof param="skuItem.displayName">sku</dsp:valueof><br> </dsp:oparam> </dsp:droplet> and so far people have bought <dsp:valueof param="giftitem.quantityPurchased">?</dsp:valueof>. <p>

</dsp:oparam></dsp:droplet>

<%/* URL to go to if user's session expires while he is filling out this form */%><dsp:input bean="ShoppingCartModifier.sessionExpirationURL" type="hidden" value="../../common/SessionExpired.jsp"/>

Buy <dsp:input bean="ShoppingCartModifier.quantity" size="4" type="text" value="1"/><dsp:input bean="ShoppingCartModifier.addGiftItemToOrder" type="submit" value="Add to Cart"/></dsp:form></dsp:getvalueof>

</dsp:oparam></dsp:droplet>

This screenshot shows the product page for an item on the gift list and the option to purchase one for the

customer:

Page 232: At g Cons Comm Ref Guide

226 10 Merchandising

Checkout with Gifts

During checkout, a customer may edit the shipping instructions for the gift. By default, the item ships to

the address in the gift list; however, the purchaser may ship the gift to a different address. Once the order is

processed and item shipped, the gift list is updated to display the quantity purchased for that item.

Bundles

Overview

ATG Consumer Commerce allows you to bundle SKUs into a single product as a merchandising package. The

bundle of SKUs is then treated as any other product, and ordered as a single item.

Page 233: At g Cons Comm Ref Guide

10 Merchandising 227

Bundle building example

In the Pioneer Cycling store, we created several bundles. One of them, a bundle called the “Adventure Bundle”

contains a mountain bike, a car rack, a water bottle holder, and a helmet. All four products are available

separately in the store. However, when they are purchased together as a package, they have a special price.

First, we created the four SKUs we wanted to bundle. Then we created a SKUlink item for each SKU. We did that,

and set the quantity attribute of each SKUlink to one because there is one of each SKU in the bundle.

We then created a product called “Adventure Bundle”, and placed it in the “Bundles” category. We wrote its

descriptions and attached media just as we would for other products. We then created a SKU for it, and attached

all the SKU links that we made.

We added up the prices of all the component SKUs and decided upon an appropriate discount. We put the

full price into the bundle SKU’s listPrice attribute, the discounted price into its salePrice attribute, and

changed the onSale attribute to “TRUE”. This allowed us to show the bundle savings on its product template.

The Adventure Bundle has only one SKU, which includes a medium bike. However, we could also have easily

created several versions of each bundle, each represented by its own SKU. For example we could have had two

other different ‘Adventure Bundles’, one that came with a small bike and another with a large bike. Each SKU

would have a slightly different set of SKU links, and possibly a different price.

Displaying bundles

A bundle product can be treated exactly as a normal product. However, since bundles have some special

properties, we decided to create a special template to display them in the Pioneer Cycling store,. You can

view this template in <ATG9dir>/PioneerCyclingJSP/j2ee-apps/pioneer/web-app/en/catalog/

product_bundle.jsp. This template displays the names of all the component SKUs, and their inventory status.

It tells customers exactly what is in each bundle, and assures them that every part of it is in stock. It also displays

the list price of each component SKU, so customers notice their savings.

Page 234: At g Cons Comm Ref Guide

228 10 Merchandising

To print out the names and list prices of all the component SKUs in a bundle SKU, we simply iterated over the

SKU’s bundleLinks attribute. For example, the bundle SKU in the bundleSKU parameter:

<dsp:droplet name="/atg/dynamo/droplet/ForEach"> <dsp:param name="array" param="Product.childSKUs[0].bundleLinks"/> <dsp:param name="elementName" value="link"/> <dsp:oparam name="output"> <dsp:valueof param="link.item.displayName">Nothing</dsp:valueof> <dsp:valueof converter="currency" param="link.item.listPrice">no price</dsp:valueof> <br> </dsp:oparam> </dsp:oparam></dsp:droplet>

Page 235: At g Cons Comm Ref Guide

Appendix A. Pioneer Cycling Scenarios 229

Appendix A. Pioneer Cycling Scenarios

The following is a detailed description of how business strategies for the Pioneer Cycling store were

implemented as scenarios in DSS. You can look at these scenarios in the ACC to understand the connection

between each scenario’s business goals and its technical implementation.

The Pioneer scenarios are classified in four folders:

• Customer Services scenarios send helpful notifications to customers at various times.

• Marketing scenarios implement the site’s marketing strategies and research.

• Profiling scenarios track customer behavior and manipulate user profiles accordingly.

• Sales scenarios deliver incentives and special promotions to customers.

Pioneer Cycling Scenarios viewed in the ACC.

Page 236: At g Cons Comm Ref Guide

230 Appendix A. Pioneer Cycling Scenarios

Customer Services

NewGear

This scenario alerts owners of BMX bikes that there are new products that might interest them. (The BuildProfile

scenario tracks each BMX bike purchase and records it in the user profile.) When the NewGear scenario is

triggered, it immediately finds all the appropriate people who wish to receive e-mail. An e-mail template is then

personalized with a small amount of profile information, and sent to each person.

NotifyByArea

This scenario contains three segments that illustrate simple geographic personalization. The first segment

sends e-mail to anyone in a ZIP code starting with “02” who orders a certain product. The second segment

sends an e-mail to anyone from the West Coast (defined as a user with a billing address state of CA or WA) who

uses the promotion offered to new customers. In both cases, the scenario simply waits for the right event and

then filters people by the respective fields in their billing addresses. The third segment, “Event in Boston”, is an

announcement for a local trade show. It looks for people returning to the site at a certain time who are from the

Boston area. It shows those people an ad in the “MediaSlot” slot encouraging them to attend the trade show.

PleaseComeBack

This simple scenario sends e-mail to customers who have registered on the site, but who do not return within

two weeks. The store sends a friendly e-mail reminding them of its existence. Notice how this scenario waits for

them to log in, and then ends if they do. We designed our scenarios to always check if a customer has indicated

whether or not she wants to receive e-mail.

Marketing

Customer Survey

The two segments of this scenario implement a simple customer survey. The first segment solicits responses,

and then the second rewards those who answer it. This scenario also demonstrates how you can use audit trail

recording to monitor the success of your initiatives.

The first segment begins on the first day of July. We decided to survey customers who shopped in the last

month, so the scenario looks for everyone with a “Last activity” timestamp from June. First, it filters out all

the people who cannot be sent e-mail. Then, it randomly selects 10% of the remaining customers to receive

the survey solicitation. After each e-mail is sent, an audit trail is recorded to a special data set created for this

scenario, CustSurveyDataset. Each audit contains a timestamp, the profile ID of the customer, and a special

label, in this case SentSurveyInvite. In this way, we know exactly who we invited to answer the survey. The

scenario then waits for an event that indicates that the user has visited the page with the survey. Its final job is to

record that fact into the audit trail.

The second segment, “GiveReward”, gives a promotion to every customer that takes the time to complete the

survey. In this case, it’s “5% off everything in the store”.

Page 237: At g Cons Comm Ref Guide

Appendix A. Pioneer Cycling Scenarios 231

You can look at Analysis > Chart Templates in the ACC and see a chart for the month of June 2000 called

“customerSurvey” that shows the success of our survey scenario, by gender. The first set of bars shows us how

many male customers received the survey invitation, and how many responded. The second shows statistics for

female customers. You can easily adapt this chart to examine your response rate by any profile attribute, such as

home state. (The actual survey page that we built for this example is for demonstration only.)

Helmet No Discount

In this scenario, marketers are testing the effectiveness of three different promotional media items. When a

customer views a bike product page, one of three different media items (Helmets Safety First, Helmets Keep

Cool, or Helmets Attractive) is put into the MediaSlot. When the customer goes to a page with MediaSlot,

such as the bike category page (category_all_bikes.jsp), the media item in the slot is displayed. An audit

trail then tracks and records when customers view and/or order any helmets after being shown each specific

media item.

Helmet Promotional Offer

In this scenario, marketers are performing an experiment in which they hope to discover which of three similar

promotions works best in their store. The premise is to increase sales of bikes by offering a discounted helmet

with the order of any bike. We would like to determine which discount is most effective: 25%, 30%, or 40% off.

This scenario waits for a customer to view a bike template. When that happens, the first thing we do is put a

helmet into the ProductSlot slot that we want to suggest to the customer. Next the customer is randomly

given one of the three offers. First the offer is shown in the MediaSlot, then the promotion is given to the

customer, and then everything is recorded into the audit trail. The scenario then waits for the customer to

submit an order that uses that promotion, and records that fact into the audit trail. If the customer does not use

the promotion, we remove it from her profile.

By looking at several factors, our marketers then determine which promotion was most effective. They can

examine issues such as whether 40% off attracts more customers than 30%or whether reduced margins are

compensated by more sales.

Loyalty

One of the surest ways to build customer loyalty is to reward your repeat customers. This scenario shows a

small example of accomplishing this in the Pioneer Store. Part of the scenario is responsible for recording the

information necessary to know who those customers are, and the other part gives the reward.

Two simple segments “TrackNumber” and “TrackAmount” record the number of orders a customer has placed,

and their cumulative dollar amount. This information is stored in the order repository as well, but it is convenient

for many reasons to keep this information in the profile. For example, we could build a profile group called

“Loyal customers” who had placed four or more orders totaling more than $400.

The main segment “GivePromo” waits for these loyal customers to log in. In this case, we’ve decided to reward

those who have placed four or more orders adding up to at least $100. When those customers log in, they see

the media we put into the MediaSlot slot telling them that they have this special promotion. They also see all

the prices in the store discounted, as the type of promotion we have given them is a percent-off discount on all

items. As a further detail, we’ve decided to reward those customers whose orders total above $500 with 10% off,

and everyone else with 5% off.

Page 238: At g Cons Comm Ref Guide

232 Appendix A. Pioneer Cycling Scenarios

New Customers

This scenario offers anonymous customers an incentive to register in the form of a one time 20% discount. The

scenario works by displaying a graphic with the incentive on the homepage when the customer first comes to

the site. Again, it’s a piece of media in the MediaSlot slot.

The second segment then waits to see what the customer does next: registers or logs in. If the segment sees a

registration event, then it gives the customer the promotion. Either way, the media is then removed from the

slot, as the customer is now a member and no longer needs to see the incentive.

Profiling

Browsing

This scenario keeps track of what the customer has been browsing in the store. One segment records the ID of

each product a customer browses and another records each category that is browsed. A third segment records

each search string.

These values are stored in a transient profile attribute, so that the customer always has a record of each product

and category that has been browsed in each session. A Java Server Page in the My Profile area, displays this

information to the customer. You can view the code of this page in <ATG9dir>/PioneerCyclingJSP/j2ee-

apps/pioneer/web-app/en/user/pages_viewed.jsp.

BuildProfile

This scenario has many segments that illustrate several ways that a profile may be modified by a scenario

based upon a customer’s actions. In the Pioneer store, we built some profile attributes to track a customer’s

preferences for certain kinds of bikes. The attribute pricePreference tells whether a customer favors economy

or expensive products, and weightPreference tells whether a customer prefers sturdy or light products. These

are actually implemented as simple integer traits that begin at 0, and then indicate the customer’s bias by being

positive or negative.

Each segment catches a certain event, and then increments or decrements these values by an appropriate

amount. For example, browsing a cheap product may lower the pricePreference by one. However, adding a

very expensive item to the cart increases it by ten. In this way, we can make rough estimates about a customer’s

preferences.

The last segment records the types of bikes that a customer purchases. This enables the site to again roughly

classify its customers according to the types of bikes that they ride.

Page 239: At g Cons Comm Ref Guide

Appendix A. Pioneer Cycling Scenarios 233

Sales

Buy2Get1Free

Customers who browse the Accessories category page are shown an incentive to buy two accessories and get

the third free. However, a different incentive is shown to male customers than to female customers. Men are

urged to purchase lights and women to purchase helmets. Appropriate media was created to enhance this

targeted message. Note also that each gender group is given a promotion only redeemable for that category of

products.

Default

This scenario simply waits for an event called “Items requested”, which indicates that a page is requesting items

from a slot, but it is empty. This scenario sticks a generic advertisement into the slot in that case.

FreeGift

This scenario makes use of the “Add Item to Order” action, which enables a scenario to directly add items into a

customer’s shopping cart. The advantages of this are apparent when you consider the case when the site would

Page 240: At g Cons Comm Ref Guide

234 Appendix A. Pioneer Cycling Scenarios

like to offer a customer a free gift. In this case, Pioneer Cycling offers customers a free water bottle when they

purchase a jersey.

The first segment “ShowIncentive” waits for customers to browse the clothing category. When they do, they are

told about the free gift offer through media placed in the “MediaSlot” slot.

The next segment, “FreeGift”, begins when the customer places a jersey into the shopping cart. First, the

customer is given the promotion that discounts the water bottle to free. Next, the order is tested to see if it

already contains any other free water bottles. If it doesn’t, then the “Add Item to Order” action, puts one into the

cart. When the customer adds the jersey to the order, the cart shows both the jersey and the water bottle, with

the bottle marked as free. If the customer were to try to outwit our scenario by removing the jersey and taking

off with the free water bottle, he would notice that the water bottle without the jersey now costs $20.00 – its

regular price. This happens because of a safeguard in the promotion rule that states “Discount exactly 1 water

bottle when Order contains at least 1 jersey.”

FreeShipping

This scenario offers free shipping to customers in the USA who place orders over $400.

The first segment “GivePromo” waits for an item to be added to the order. First it checks to make sure that the

locale is en_US. Then, whenever the cart’s total exceeds $400.00, a message is displayed in the “MediaSlot” slot

informing the customer that shipping is now free. Notice that this segment fires every time that an item is added

to the order, but that the promotion is given only once and the slot holds only one copy of this media. This

works because one of the attributes of this promotion is that it can only be given to a customer once. The slot

also does not allow duplicate items. Recognizing tricks like this can help make the logic in your scenarios much

simpler.

The second segment in this scenario, “RemovePromo,” takes care of a special case. What if the customer removes

some items from the cart, and is no longer eligible for the shipping discount? The promotion is simply not

be applied because it knows to look for the order’s total price. However our slot would still be showing the

congratulatory message. To avoid this mix-up, this segment watches for the “Item Removed from Order” event,

and removes the media if the order’s total drops below $400.00.

IncreasePurchaseSize

This scenario offers some amount off a sixth product when you buy five others. It is interesting in that the

scenario delivers a different promotion for each locale, so that the discount is 5 dollars, 10 Francs, or 50 yen,

depending upon which locale the customer is shopping in.

ShowProducts

This scenario simply fills the “ProductSlot” slots of each locale with featured products using a targeter. These

products are displayed on the homepage.

The contents of the targeter are shown in the slot both when anonymous users visit the home page and at login

time. The reason for this is that the targeter may turn up some new items depending upon which user logs in,

but also has a pool of products to show to everybody.

Page 241: At g Cons Comm Ref Guide

Appendix A. Pioneer Cycling Scenarios 235

Scenario Templates

There are several prefabricated scenario templates that you can use as the beginnings of your own scenarios.

Scenario Template Description

Coupon Creates scenarios that give customers a promotion whenever their

orders reach a certain state

Cross-sell_promotion Cross-sells products against others

Free_gift Creates scenarios like the “FreeGift” one described above

Group_targeted_e-mail Sends e-mail to all members of a profile group

Group_targeted_promotion Delivers a promotion to all members of a group

Profile_targeted_e-mail Sends e-mail to all customers with a certain profile attribute

Targeter_based_promotion Shows the contents of a targeter in a slot

Changing URLs in E-mail Templates

The Pioneer Cycling store uses several scenarios with e-mail templates that get sent to users. The e-mail

templates in the following scenarios provide users with URLs:

user/NewBMXGear.jsp

user/PleaseComeBack.jsp

users/customer_survey.jsp

In the Pioneer Cycling store, the host and port are set to http://localhost:8840. You should change this

to reflect the host and port of the http server that is serving your site. For example, if your http server is named

webserver1 and is running on port 8080, then you should change:

http://localhost:8840/PioneerCyclingJSP

to the following:

http://webserver1:8080/PioneerCyclingJSP

Page 242: At g Cons Comm Ref Guide

236 Appendix A. Pioneer Cycling Scenarios

Page 243: At g Cons Comm Ref Guide

Appendix B. Optimizing Performance 237

Appendix B. Optimizing Performance

The performance of your web site depends on many factors, including the hardware on which it runs , the

performance of any back end systems, the proper tuning of caches, and the efficiency of the ATG services and

servlet beans. This appendix discusses the performance of Java Server Pages. For more tips and procedures for

testing your web site’s performance and how to improve it, see the performance-related chapters of the ATG

Installation and Configuration Guide.

JSPs are compiled into Java servlets at run time and are executed when a user requests a page that invokes

one of these servlets. The design and implementation of JSP code can make a huge difference in the overall

performance of a web site. We explain some guidelines that help you to tune your Java Server Pages to run as

accurately and as quickly as possible.

This appendix is divided into the following parts

Prioritizing Web Site Concerns (page 237)

Streamlining JSP Construction (page 238)

Using the Broadest Scope Possible (page 242)

Reloading the Catalog Repository Cache (page 242)

Prioritizing Web Site Concerns

A common problem for many developers is that they concentrate on efficiency issues too early in the

development process. We suggest that the first priority of any web site is that it functions. If people can’t access

your site or if the site is difficult to use and poorly designed, users aren’t going to care how fast it runs. ATG

products can help you to build a site that runs fast, but testing for accuracy and functionality is more important.

Since JSP is relatively easy to work with, performance optimizations can be considered after you stabilize basic

functionality.

Working on the most used pages in the site should be another high priority. Obviously a page that people only

visit if they sign up for a special promotion should get less attention than then site home page. Focusing on

tuning the most popular pages in the site so that they run smoothly and load quickly makes your web site more

attractive to users.

After your web site is up and running you can evaluate it and decide how to make it work even better. The rest

of this appendix offers some suggestions on how to write JSP code so that it provides optimal performance for

your site.

Page 244: At g Cons Comm Ref Guide

238 Appendix B. Optimizing Performance

Streamlining JSP Construction

There are several ways that you can improve your web site through writing “clean” JSP code. Some JSP functions

slow down the speed at which a page is compiled and served. Others make site development and maintenance

easier when they are used in certain ways. This section describes several methods that we recommend using to

make your site run as well as possible.

Limit Excess Droplet Invocations

To a certain degree, we recommend sectioning the Java Serve Pages so that often used fragments can be

separated into individual, reusable JSP files that can be included with <dsp:include>. While this sectioning

makes site development and maintenance easier, it also can make the site perform poorly if overused. However,

you don’t have to sacrifice all modularity in your Java Server Pages for better performance.

Each droplet invocation, either by <dsp:include page="filename.jsp" flush="true"/> or

<dsp:droplet name="/atg/dynamo/droplet/BeanName">, is compiled into a SubServlet invocation.

Another option for sectioning Java Server Pages is using JSP include directives, which work similarly to

droplet src. The syntax is as follows:

<dsp:include file="RelativeURLSpec.jsp">

This directive includes the JSP fragment at RelativeURLSpecJSP to be “in-lined” in the JSP in which this line

is written. The in-lining occurs before page compilation and the two files are treated as one fragment rather

than as the two fragment invocations you would get using the <dsp:inclide="RelativeURLSpec.jsp"></

dsp:include> syntax.

The use of the IsEmpty and IsNull components gives the developer security from NullPointerExceptions

but invoking each of these servlet beans takes time. To avoid this problem you can eliminate excess IsNull

invocations when you know that a value really can or cannot be null. For example, while iterating over a list of

SKUs for a product with a ForEach and displaying some properties for each SKU, it is a good idea to perform an

IsNull check on each of them. However, there are situations where you can be virtually certain that none of

the SKUs will be null. One of these situations is when each SKU on the list is retrieved as a product.childSKU.

If your database is in good shape and if you test to ensure that the list has no nulls in it, you can be virtually

assured that it never will have nulls. In cases like this, you can comfortably eliminate the IsNull check to

improve performance.

The following code sample is an example in which you would likely use ForEach to iterate over an array. If you

assume that it is possible, but not very likely, that the array has no elements, it is best to invoke ForEach without

checking ahead of time if the array is empty. Instead, use the <dsp:oparam name="empty"> of ForEach as

shown here:

The contents of someArray:<P><dsp:droplet name="ForEach"> <dsp:param name="array" param="param:someArray"/> <dsp:oparam name="empty"> <i>someArray has no elements!</i> </dsp:oparam> <dsp:oparam name="outputStart"> </OL>

Page 245: At g Cons Comm Ref Guide

Appendix B. Optimizing Performance 239

</dsp:oparam> <dsp:oparam name="outputEnd"> </OL> </dsp:oparam> <dsp:oparam name="output"> <LI><dsp:valueof param="element"></dsp:valueof> </dsp:oparam></dsp:droplet>

If you assume that most of the time the array is empty and only sometimes has elements, it may be more

efficient to check if the array is empty before invoking ForEach, as in this example:

The contents of someArray:<P><dsp:droplet name="IsEmpty"> <dsp:param name="value" param="param:someArray"/> <dsp:oparam name="empty"> <i>someArray has no elements!</i> </dsp:oparam> <dsp:oparam name="false"> <OL> <dsp:droplet name="ForEach"> <dsp:param name="array" param="param:someArray"/> <dsp:oparam name="output"> <LI><dsp:valueof param="element"></dsp:valueof> </dsp:oparam> </dsp:droplet> </OL> </dsp:oparam></dsp:droplet>

The efficiency gained here is the difference between the time it takes to invoke ForEach, to figure out what

kind of object someArray is, and to inquire if it is empty versus the time is takes to invoke IsEmpty, which goes

through roughly the same calculations. For both situations, the former code snippet performs approximately as

well or better than the latter.

Use the Cache Servlet Bean

Using the Cache servlet bean can save a lot of time when you use it properly. When used incorrectly, it can slow

rendering of a Java Sever Page and even cause the page to display incorrect data. It is important to understand

how Cache works in order to avoid mistakes that can slow your pages or make them wrong.

(If you are not familiar with Cache, you may want to read more about it in Appendix B: ATG Servlet Beans of the

ATG Page Developer’s Guide.

Let’s examine an example where Cache actually slows down your web site. The following example uses Cache

on a page called hello.jsp:

<dsp:droplet name="Cache"> <dsp:oparam name="empty"> Hello world! </dsp:oparam></dsp:droplet>

Page 246: At g Cons Comm Ref Guide

240 Appendix B. Optimizing Performance

Cache stores the value “Hello world!” in its cache so that future retrievals can come from that cache. This is

not a good place to use Cache from an efficiency point of view. Without Cache, the Java code generated to

print a static string is very fast and simple. The use of Cache in this example causes an unnecessary servlet

invocation and the unnecessary storage of more bytes. The Cache servlet bean is only useful when you are

caching dynamic content.

Here is another example in which Cache causes incorrect results. The dynamic pricing engine is among the most

“costly” pieces of functionality used in the Pioneer Cycling Store JSP code because it calculates pricing on the

fly and can run slowly. Determining a dynamic price for each SKU personalized for each user takes many more

computation resources than finding and displaying static prices. It is dynamic, so one way to make it run faster is

to cache it. Every SKU has a different price, so you can use the SKU ID as a key into the cache.

<dsp:droplet name="/atg/dynamo/droplet/Cache"> <dsp:param name="key" param="Sku.repositoryId "/> <dsp:oparam name="output">

<dsp:droplet name="/atg/commerce/pricing/PriceItem"> <dsp:param name="item" param="Sku"/> <dsp:param name="elementName" value="PricedSku"/>

<dsp:oparam name="output"> <dsp:droplet name="/atg/dynamo/droplet/Compare"> <dsp:param name="obj1" param="PricedSku.priceInfo.ListPrice"/> <dsp:param name="obj2" param="PricedSku.priceInfo.amount"/>

<%/*ListPrice is greater, so display it first in strikout mode: */%> <dsp:oparam name="greaterthan"> <span class="strikeout"> <dsp:valueof converter="currency" param="PricedSku.priceInfo.ListPrice"/> </span> </dsp:oparam> </dsp:droplet>

<%/*Display their price: */%> <dsp:valueof converter="currency" param="PricedSku.priceInfo.amount"/> </dsp:oparam></dsp:droplet> <%/*PriceItem droplet*/%>

</dsp:oparam></dsp:droplet>

In this code example, Cache caches a string representing the formatted price of the SKU, for example, “$42”. It

is much faster to let Cache render the string “$42” than to let the PriceItem servlet bean calculate a price, so

caching this string seems like a good idea. However, PriceItem calculates personalized prices, so if a user has a

promotion for 20% off everything and he is the first to invoke the cached PriceItem for SKU123, then his price

is cached and even users who don’t have the 20% off promotion see that price.

A solution might be to key the cache a combination of the profile ID and the SKU ID. The problem remains that

the string manipulation needed to construct a key out of those two pieces of data would cost time and that the

hit ratio on such a cache would likely be so low as to render the cache useless. Worst of all, if the user is granted a

promotion before the cache times out, the user sees the incorrect price.

Another situation to consider is caching content that contains links. In order to perform session tracking, the

session ID must be carried by every HTTP request either in a cookie or in the URL link, and inserted by on-the-

fly link rewriting. Cache cannot cache content that needs to have link rewriting performed on it. If you choose

Page 247: At g Cons Comm Ref Guide

Appendix B. Optimizing Performance 241

to use Cache to cache content that contains links and if a good portion of your user population does not accept

session cookies, then invoking Cache on content that contains link wastes time.

We have now explored in detail some situations where you should not use the Cache servlet bean:

1. When the content is static.

2. When the cached content would be incorrect for the situation.

3. When it takes more time to construct the key than to render the content.

4. When the cache hit ration would be low.

5. When link rewriting is required.

You should use Cache when the string you would use for a key is readily available and the content is always the

same for each key. You should also use Cache when the content either contains no links or the when the bulk of

the user population is expected to accept session cookies.

Avoid “Expensive” Functionality

Sometimes there is a piece of functionality that runs as efficiently as it possibly can but just isn’t fast enough

to use on every page of the site. For example, the PriceItem servlet bean performs a dynamic personalized

pricing calculation on a SKU based on its price attributes. The promotions granted to the user and any other

pricing models that might apply are calculated efficiently, but not quickly. On a page that displays a product

with four SKUs and the price of each SKU is displayed, PriceItem is invoked four times. If you find that the page

is unacceptably slow, one solution is to change the functionality so that the static price, list price, and sale price

are the only prices displayed on the product pages. Personalized pricing can be displayed on the Shopping Cart

page.

Writing your own Servlet Beans

Sometimes a site requires functionality that cannot be achieved from the servlet beans already included in ATG,

so as a developer you can write your own. To achieve desired performance, you should first ensure that the

servlet bean itself does its job efficiently; then, minimize the need for “helper” droplets.

For example, some servlet beans fail if given a null value in an input parameter. Make your servlet bean accept

null input parameters. If it takes input parameters, do the quick comparison to null or empty inside the service

method rather than using the Java Server Page to invoke IsNull or IsEmpty on the parameter value before

invoking your servlet bean.

Another example is if your servlet bean outputs an array that is then iterated by ForEach. This common pattern

works well because it is usually efficient and it is easily maintainable and readable by people who have used

ForEach before. However, if ForEach then requires a Switch servlet bean on each element and in some cases

invokes another servlet bean and so on and so on, there may be a point where it is better to do the iteration

within your own servlet bean.

To summarize, we do not recommend creating a whole site of customized servlet beans to gain performance.

Nor do we recommend that site developers begin by thinking about where they can and cannot use Cache

and how much code they can write inline. Instead, we recommend that site development begin with modular

JSP fragments used sensibly in an overall site structure driven by functionality and design. When your site is

somewhat functional, you should begin testing to see if it meets your performance goals and needs. If it does

not, then you should identify the slowest and most often used parts of the site and improve their performance.

Page 248: At g Cons Comm Ref Guide

242 Appendix B. Optimizing Performance

Using the Broadest Scope Possible

Three scopes are available to ATG components: global, session, and request. A global component is instantiated

once and is shared by all users. A session component is created once for a given session and is shared by all

requests of that session. Request scoped components are freshly instantiated for each request that uses them.

Resolving the path to an existing component takes a little time, but instantiating a new object is comparatively

more costly. To build a high performance web site one must be conscious of the number of objects that are

allocated on each request and the amount of memory each user consumes. Therefore it important to think

about the scope of a component when it is defined in the system.

In general it is fairly obvious when a component should be globally scoped versus session or request scoped. If

a component can be shared, without synchronization problems, by all users, then it should be globally scoped.

It is a somewhat more difficult decision to decide if a component should be session or request scoped. The

decision balances on minimizing the number of objects that are allocated on every request and reducing the

total amount of memory that is used per session. If a component is going to be used on many requests within

a session then in general it should be session scoped. However if an object is only going to be occasionally

used within the session then it should be declared request scoped. After the request the Java Virtual Machine is

allowed to garbage collect the object, versus holding onto it for the life of the session.

Form handlers are a special case of component; they can be used quite often, but in general are request scoped.

As a design pattern, messages relayed back to the user (such as error messages) are made available as properties

of the form component. It becomes increasingly difficult to manage this sort of component, with messages, as

session scoped, because your application needs to be cognizant of when to clear the messages. As a request

scoped component the error messages are automatically “cleared” after the request when the object is garbage

collected. Notice that the component /atg/commerce/order/ShoppingCartModifier, which manages the

shopping cart order, is by default declared to be a request scoped component. Its performance in request scope

is sufficient for most sites, but a site that needs an extra performance boost might benefit by changing that form

handler to session scoped.

Reloading the Catalog Repository Cache

The performance of the catalog can be improved by preloading the catalog repository when ATG starts up. The

cache is loaded by performing queries, just as during normal operation. Cache preloading differs from normal

operation because it normally involves querying for all items of a certain type and then doing nothing with the

results. This bulk query is easily achieved by addingquery-items tags to the repository definition XML file.

In Pioneer Cycling, we added these tags to a separate file:

PioneerCycling/config/atg/commerce/catalog/preloadCatalogCache.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<!DOCTYPE gsa-template PUBLIC "-//Art Technology Group, Inc.//DTD General SQL Adapter//EN" "http://www.atg.com/dtds/gsa/gsa_1.0.dtd">

<gsa-template>

<header> <name>Pioneer Cycling Default Catalog Cache Pre-loader</name> <author>The Pioneers</author>

Page 249: At g Cons Comm Ref Guide

Appendix B. Optimizing Performance 243

<version>$Id: $</version> </header>

<query-items item-descriptor="category">ALL</query-items> <query-items item-descriptor="product">ALL</query-items> <query-items item-descriptor="sku">ALL</query-items> <query-items item-descriptor="media">ALL</query-items> <query-items item-descriptor="manufacturer">ALL</query-items> <query-items item-descriptor="sku-link">ALL</query-items> <query-items item-descriptor="folder">ALL</query-items></gsa-template>

This file can be invoked from the product catalog definition file at /PioneerCycling/config/

atg/commerce/ProductCatalog.properties with the following property setting that adds

preloadCatalogCache.xml to the end of the list of definitonFiles.

definitionFiles+=\ /atg/commerce/catalog/preloadCatalogCache.xml

In Pioneer Cycling, we commented this setting out to improve the speed of startup. In a live site, it is often

desirable to trade off slower startup for better performance but in development and demonstration situations,

fast startup is generally preferable.

It should also be noted that in Pioneer Cycling, the cache preloading is provided for the default English language

catalog only. In an actual online store, you would provide query-items tags for all of the repositories and item-

types for which the performance boost is needed. In this implementation we did not provide query-items tags

for the French or Japanese catalogs.

Please see the section on SQL Repository Caching in the SQL Repository Architecture chapter of the ATG Repository

Guide for more information about reloading a repository cache.

Page 250: At g Cons Comm Ref Guide

244 Appendix B. Optimizing Performance

Page 251: At g Cons Comm Ref Guide

Index 245

Index

AACC

users, 14

Address Book page, 29

Address nicknames, 157

address_book.jsp, 37

Addresses

alternate shipping, 30

billing, 29

missing information, 158

privacy, 222

shipping, 30

user, 24

Advanced

checkout, 149

Advanced checkout, 153

Adventure bundle, 227

Anonymous users, 14

AvailableShippingMethods, 170

Bb2c_product table, 59

b2c_product_catalog_dll.sql, 58

B2CemailFormHandler class, 38

B2CProfileFormHandler, 16, 35, 166

B2CSearchFormHandler, 48

B2CUserResources, 40

Billing addresses, 29

in checkout, 152

Browsing scenario, 47

Bundles, 226

Business users’ overview, 5

CCache servlet bean, 239

cancel_order.jsp, 179

CancelOrder servlet bean, 181

cart.jsp, 137, 171

Catalog

browsing, 45

extending, 57

overview, 6

repository background, 57

searching, 48

Catalog repository

reloading cache, 242

CatalogSearch, 48, 49

CategoryBrowsed, 46

Checkout

advanced, 149, 153

entering information for, 146

express, 165

secure, 147

Checkout pages, 190

CommerceProfileFormHandler, 16

Cookies, 14, 240

Coupons, 201

claiming, 202

creating, 202

Credit card page, 39

Credit cards

collecting information, 39

in checkout, 153

selecting default, 167

validating, 42, 164

Cross-selling, 196

Customer service page, 37

DDatabase schema, 55

Databases

performance, 59

Discounts, 194

DMS messages, 46, 47, 49

Droplet invocation, 238

dynamoMessagingSystem.xml, 50

EEmail

templates, 235

EmailFormHandler component, 38

Error messages

displaying, 165

Express checkout

deactivating, 171

enabling, 165

express_checkout.jsp, 166

express_checkout_preferences.jsp, 168

FForm handlers, 242

Fragments, 238

Page 252: At g Cons Comm Ref Guide

246 Index

GGift certificates

email delivery of, 201

paying with, 164

purchasing, 200

using, 201

Gift lists, 204

adding items to, 214

creating and displaying, 204

editing and deleting, 212

purchasing items from, 224

searching for, 216

viewing someone else’s, 220

Gift Registry, 203

GiftListFormHandler, 203

Gifts

checkout with, 226

purchasing, 216

HhandleCreateNewCreditCard() method, 40

handleLogin() method, 16

History page, 44

IInventory, 187

accessing information, 188

caching information, 190

implementing a system, 188

InventoryLookup component, 189

IsEmpty component, 238

IsNull component, 238

item types

describing, 51

Item types

adding new, 61

JJSP include directives, 238

JSP pages

sectioning, 238

JSP Performance, 237

Kkeyword attribute, 61

LLogin page, 14

LoginFragment.jsp, 15

MManufacturer item type, 61

Media

delivering, 196

MediaSlot, 198

Merchandising, 193

overview, 10

My Profile page, 26

my_profile.jsp, 14

NNicknames

for addresses, 157

OOrder History, 173

Order processing

overview, 11

order_cancelled.jsp, 180

order_history.jsp, 174

OrderLookup, 174

Orders

cancelling, 179

changing quantities, 142

deleting items from, 143

displaying, 173

displaying a single, 176

modifying, 144

processing, 135

reviewing and editing, 135

viewing subtotal, 140

OrderStatesDetailed, 174

Overview, 5

PPage fragments, 238

Patch Bay, 47, 50

Payment methods, 160

Performance

optimizing, 237

Personalization, 51

Pricing

calculating, 139

displaying in checkout, 137

overview, 11

Pricing engine, 193

ProductBrowsed, 46

ProductCatalog, 58

Products

availability, 189

highlighting, 196

related accessories, 196

Page 253: At g Cons Comm Ref Guide

Index 247

slots, 198

Profile Management, 13

Profile Repository extensions, 51

Profile servlet bean, 14

Profile traits, 23

ProfileFormHandler, 16

ProfileFragment.jsp, 16

Profiles

overview, 8

Promotions, 193

implementing, 194

targeting content for, 195

Properties

adding, 58

displayName, 62

Qquery-item tags, 242

RRegistration, 14, 16

customized, 25

Repository API, 52

ResourceBundle, 40

SScenarios, 195

Browsing, 47

delivering promotions with, 193

Pioneer Cycling, 229

templates, 235

upselling with, 197

Scopes, 242

SearchEventSender, 49

SearchFormHandler, 48

Searching

keeping track of, 47

simple, 48

Servlet beans

writing your own, 241

Setting up email, 2

Shipping addresses, 30

multiple, 154

Shipping information

in checkout, 150

Shipping methods

choosing in checkout, 151

setting default, 170

Shopping cart

modifying, 171

Shopping cart page, 137

ShoppingCartModifier, 165

SKU

adding properties, 60

SKU Bundles, 226

Slots

creating, 198

populating, 199

SMTPEmail, 39

SoftGoodFulfiller, 200

Speed of loading, 237

SQL Profile Repository, 52

SQL tables, 59

Starting

Pioneer Cycling, 2

stock status, 190

Style item type

adding, 63

Subtypes

creating, 63

TTargeting, 195

overview, 10

UUpselling, 197

User item types, 51

User profile, 21

User profiles, 13

userProfile.xml, 45, 51

extending, 27

Users

anonymous, 14

VvalidateCreditCard, 42

WWish lists, 204

XXML files, 57

Page 254: At g Cons Comm Ref Guide

248 Index


Recommended