Implementing Tax and Shipping Charges

Một phần của tài liệu Beginning ASP.NET 2.0 E-Commerce in C# 2005 From Novice to Professional PHẦN 8 pptx (Trang 34 - 44)

As expected, you need to make several modifications to BalloonShop to enable the tax and shipping schemes outlined previously. You have two more database tables to add, Tax and Shipping, as well as modifications to make to the Orders table. You’ll need to add new stored procedures and make some modifications to existing ones. Some of the business tier classes need modifications to account for these changes, and the presentation tier must include a method for users to select a shipping method (the taxing scheme is selected automatically).

So, without further ado, let’s get started.

Database Modifications

In this section, you’ll add the new tables and modify the Orders table and stored procedures.

The Tax Table

The Tax table simply provides a number of tax options that are available, each of which has a name and a percentage tax rate. Table 13-2 shows the table structure that you’ll need to add.

These columns are not nullable. Figure 13-2 shows the data to add to this table.

Figure 13-2. Data for the Tax table

The Shipping Table

The Shipping table is also very simple. It provides a number of shipping options, each of which has a name, a cost, and an associated shipping region. Table 13-3 shows the table structure that you’ll need to add.

Table 13-2. The Tax Table

Column Name Column Type Description

TaxID int The ID of the tax option. This column is the primary key and should be configured as an identity so that it will be auto-numbered.

TaxType varchar(100) A text description of the tax option.

TaxPercentage float The percentage tax rate for this option.

C H A P T E R 1 3 ■ A D V A N C E D C U S T O M E R O R D E R S 503

These columns are not nullable. Figure 13-3 shows the data to add to this table.

Figure 13-3. Data for the Shipping table

Orders Table Modifications

The modifications to the Orders table are to associate an order with one entry each from the Tax and Shipping tables, as shown in Table 13-4.

CommerceLibOrderGetInfo Modifications

The existing CommerceLibOrderGetInfo stored procedure now needs to include the tax and shipping data for an order. The new stored procedure is as follows:

Table 13-3. The Shipping Table

Column Name Column Type Description

ShippingID int The ID of the shipping option. This column is the primary key and identity.

ShippingType varchar(100) A text description of the shipping option.

ShippingCost money The cost (to the customer) of the shipping option.

ShippingRegionID int The ID of the shipping region that this option applies to.

Table 13-4. Orders Table Modifications

Column Name Column Type Description

TaxID int The ID of the tax option to use for the order ShippingID int The ID of the shipping option to use for the order

8213592a117456a340854d18cee57603

ALTER PROCEDURE CommerceLibOrderGetInfo (@OrderID int)

AS

SELECT OrderID, DateCreated, DateShipped, Comments, Status, CustomerID, AuthCode, Reference,

Orders.ShippingID, ShippingType, ShippingCost, Orders.TaxID, TaxType, TaxPercentage FROM Orders

LEFT OUTER JOIN Tax ON Tax.TaxID = Orders.TaxID

LEFT OUTER JOIN Shipping ON Shipping.ShippingID = Orders.ShippingID WHERE OrderID = @OrderID

Here there are two joins to the Tax and Shipping tables, both of which are LEFT OUTER joins so that data will be retrieved from the Orders table regardless of a value of TaxID and ShippingID (to enable backward compatibility among other issues).

CreateCustomerOrder Modifications

You also need to modify CreateCustomerOrder so that a tax and a shipping option are added when an order is added. The modifications are as follows:

ALTER PROCEDURE CreateCustomerOrder (@CartID char(36),

@CustomerID uniqueidentifier, @ShippingID int,

@TaxID int) AS

/* Insert a new record into Orders */

DECLARE @OrderID int

INSERT INTO Orders (CustomerID, ShippingID, TaxID) VALUES (@CustomerID, @ShippingID, @TaxID)

/* Save the new Order ID */

SET @OrderID = @@IDENTITY

/* Add the order details to OrderDetail */

INSERT INTO OrderDetail

(OrderID, ProductID, ProductName, Quantity, UnitCost)

C H A P T E R 1 3 ■ A D V A N C E D C U S T O M E R O R D E R S 505

SELECT

@OrderID, Product.ProductID, Product.Name, ShoppingCart.Quantity, Product.Price FROM Product JOIN ShoppingCart

ON Product.ProductID = ShoppingCart.ProductID WHERE ShoppingCart.CartID = @CartID

/* Clear the shopping cart */

DELETE FROM ShoppingCart WHERE CartID = @CartID /* Return the Order ID */

SELECT @OrderID

The two new parameters to deal with are @ShippingID and @TaxID.

The CommerceLibShippingGetInfo Stored Procedure

You need to add a new stored procedure so that a list of shipping options associated with a shipping region can be obtained. The CommerceLibShippingGetInfo stored procedure achieves this:

CREATE PROCEDURE CommerceLibShippingGetInfo (@ShippingRegionID int)

AS

SELECT ShippingID, ShippingType, ShippingCost FROM Shipping

WHERE ShippingRegionID = @ShippingRegionID

Business Layer Modifications

To work with the new database tables and stored procedures, you need to make several changes to CommerceLibAccess.cs. You need to add two structs to represent tax and shipping options, TaxInfo and ShippingInfo. You also need to give access to shipping info based on shipping regions and modify CommerceLibOrderInfo to use the tax and shipping structs. You must modify CreateCommerceLibOrder in ShoppingCartAccess to configure tax and shipping for new orders as well.

The TaxInfo and ShippingInfo Structs

These structs use very simple code, which you can add to the top of CommerceLibAccess.cs:

/// <summary>

/// Wraps tax data /// </summary>

public struct TaxInfo {

public int TaxID;

public string TaxType;

public double TaxPercentage;

}

/// <summary>

/// Wraps shipping data /// </summary>

public struct ShippingInfo {

public int ShippingID;

public string ShippingType;

public double ShippingCost;

public int ShippingRegionId;

}

There’s not much to comment on here. The fields in the struct simply match up to the columns in the associated tables.

The GetShippingInfo Method

This method obtains a List<ShippingInfo> object containing shipping information for a shipping region. If it’s not there already, this code requires a reference to the System.Collections.Generic namespace in the file. Add this method to the CommerceLibAccess class:

public static List<ShippingInfo> GetShippingInfo(

int shippingRegionId) {

// get a configured DbCommand object

DbCommand comm = GenericDataAccess.CreateCommand();

// set the stored procedure name

comm.CommandText = "CommerceLibShippingGetInfo";

// create a new parameter

DbParameter param = comm.CreateParameter();

param.ParameterName = "@ShippingRegionId";

param.Value = shippingRegionId;

param.DbType = DbType.Int32;

comm.Parameters.Add(param);

// obtain the results

DataTable table = GenericDataAccess.ExecuteSelectCommand(comm);

List<ShippingInfo> result = new List<ShippingInfo>();

foreach (DataRow row in table.Rows) {

ShippingInfo rowData = new ShippingInfo();

rowData.ShippingID = int.Parse(row["ShippingId"].ToString());

rowData.ShippingType = row["ShippingType"].ToString();

rowData.ShippingCost =

double.Parse(row["ShippingCost"].ToString());

rowData.ShippingRegionId = shippingRegionId;

result.Add(rowData);

}

return result;

}

C H A P T E R 1 3 ■ A D V A N C E D C U S T O M E R O R D E R S 507

Here the ID of a shipping region is accepted as a parameter and used to access the CommerceLibShippingGetInfo stored procedure added earlier. The collection is assembled from row data.

CreateCommerceLibOrder Modifications

This method, in ShoppingCartAccess.cs, needs modifying as follows (again, a reference to System.Collections.Generic may be necessary):

public static string CreateCommerceLibOrder(int shippingId, int taxId)

{

// get a configured DbCommand object

DbCommand comm = GenericDataAccess.CreateCommand();

// set the stored procedure name

comm.CommandText = "CreateCustomerOrder";

// create parameters

DbParameter param = comm.CreateParameter();

param.ParameterName = "@CartID";

param.Value = shoppingCartId;

param.DbType = DbType.String;

param.Size = 36;

comm.Parameters.Add(param);

// create a new parameter param = comm.CreateParameter();

param.ParameterName = "@CustomerId";

param.Value =

Membership.GetUser(

HttpContext.Current.User.Identity.Name) .ProviderUserKey;

param.DbType = DbType.Guid;

param.Size = 16;

comm.Parameters.Add(param);

// create a new parameter param = comm.CreateParameter();

param.ParameterName = "@ShippingId";

param.Value = shippingId;

param.DbType = DbType.Int32;

comm.Parameters.Add(param);

// create a new parameter param = comm.CreateParameter();

param.ParameterName = "@TaxId";

param.Value = taxId;

param.DbType = DbType.Int32;

comm.Parameters.Add(param);

// return the result table

return GenericDataAccess.ExecuteScalar(comm);

}

Here two more parameters have been added to match up with the revised stored procedure CreateCustomerOrder.

CommerceLibOrderInfo Modifications

This class requires several modifications. First, you need to add two new fields for tax and shipping info:

public class CommerceLibOrderInfo {

...

public ShippingInfo Shipping;

public TaxInfo Tax;

Next, the constructor needs to be modified to extract this new data from the row returned by the CommerceLibOrderGetInfo stored procedure:

public CommerceLibOrderInfo(DataRow orderRow) {

...

CreditCard = new SecureCard(CustomerProfile.CreditCard);

OrderDetails =

CommerceLibAccess.GetOrderDetails(

orderRow["OrderID"].ToString());

// Get Shipping Data

if (orderRow["ShippingID"] != DBNull.Value && orderRow["ShippingType"] != DBNull.Value && orderRow["ShippingCost"] != DBNull.Value) {

Shipping.ShippingID =

Int32.Parse(orderRow["ShippingID"].ToString());

Shipping.ShippingType = orderRow["ShippingType"].ToString();

Shipping.ShippingCost =

double.Parse(orderRow["ShippingCost"].ToString());

} else {

Shipping.ShippingID = -1;

}

// Get Tax Data

if (orderRow["TaxID"] != DBNull.Value && orderRow["TaxType"] != DBNull.Value && orderRow["TaxPercentage"] != DBNull.Value)

C H A P T E R 1 3 ■ A D V A N C E D C U S T O M E R O R D E R S 509

{

Tax.TaxID = Int32.Parse(orderRow["TaxID"].ToString());

Tax.TaxType = orderRow["TaxType"].ToString();

Tax.TaxPercentage =

double.Parse(orderRow["TaxPercentage"].ToString());

} else {

Tax.TaxID = -1;

}

// set info properties Refresh();

}

Note here that checks are made for null values for tax and shipping information. If data isn’t found for tax information, TaxID will be set to -1. Similarly, no shipping data will result in ShippingID being -1. If all is well, these situations shouldn’t occur, but just in case they do (especially if you end up modifying the tax and shipping schemes), this will prevent an error from occurring.

Finally, the Refresh method needs to include tax and shipping costs in its calculation of total cost and in its creation of the OrderAsString field:

public void Refresh() {

// calculate total cost and set data StringBuilder sb = new StringBuilder();

TotalCost = 0.0;

foreach (CommerceLibOrderDetailInfo item in OrderDetails) {

sb.AppendLine(item.ItemAsString);

TotalCost += item.Subtotal;

}

// Add shipping cost

if (Shipping.ShippingID != -1) {

sb.AppendLine("Shipping: " + Shipping.ShippingType);

TotalCost += Shipping.ShippingCost;

}

// Add tax

if (Tax.TaxID != -1 && Tax.TaxPercentage != 0.0) {

double taxAmount = Math.Round(TotalCost * Tax.TaxPercentage, MidpointRounding.AwayFromZero) / 100.0;

sb.AppendLine("Tax: " + Tax.TaxType + ", $"

+ taxAmount.ToString());

TotalCost += taxAmount;

}

sb.AppendLine();

sb.Append("Total order cost: $");

sb.Append(TotalCost.ToString());

OrderAsString = sb.ToString();

...

}

The calculation of the tax amount involves some mathematical functionality from the System.Math class, but otherwise it’s all simple stuff.

Presentation Layer Modifications

Finally we come to the presentation layer. In fact, due to the changes we’ve made, the only changes to make here are to the checkout page.

Checkout.aspx Modifications

The .aspx page simply needs a means of selecting a shipping type prior to placing an order.

This can be achieved using a drop-down list:

<asp:Label ID="InfoLabel" runat="server" CssClass="InfoText" />

<br />

<br />

<span class="InfoText">Shipping type:

<asp:DropDownList ID="shippingSelection" runat="server" /></span>

<br />

<br />

<asp:Button ID="placeOrderButton" runat="server"

CssClass="ButtonText" Text="Place order"

OnClick="placeOrderButton_Click" />

</asp:Content>

Now you need to populate this list and/or hide it in the code behind.

Checkout.aspx.cs Modifications

The code behind for this page already checks to see whether an order can be placed in PopulateControls, based on whether a valid address and credit card have been entered. You can use this information to set the visibility of the new list control (shippingSelection) and populate the shipping option list accordingly. The code to modify is as follows:

8213592a117456a340854d18cee57603

C H A P T E R 1 3 ■ A D V A N C E D C U S T O M E R O R D E R S 511

private void PopulateControls() {

...

placeOrderButton.Visible = addressOK && cardOK;

shippingSelection.Visible = addressOK && cardOK;

// Populate shipping selection if (addressOK && cardOK) {

int shippingRegionId = int.Parse(Profile.ShippingRegion);

List<ShippingInfo> shippingInfoData =

CommerceLibAccess.GetShippingInfo(shippingRegionId);

foreach (ShippingInfo shippingInfo in shippingInfoData) {

shippingSelection.Items.Add(

new ListItem(shippingInfo.ShippingType, shippingInfo.ShippingID.ToString()));

}

shippingSelection.SelectedIndex = 0;

} }

This code uses the CommerceLibAccess.GetShippingInfo method added earlier, and creates ListItem controls dynamically for adding to the drop-down list. Note also that a valid selection in the list is ensured by setting the initially selected item in the drop-down list to the item with an index of 0, that is, the first entry in the list.

Next, you need to modify the placeOrderButton_Click event handler to create an order with tax and shipping option references. For the shipping option, you use the selected item in the drop-down list; for the tax option, you make an arbitrary selection based on the shipping region of the customer and the items you added earlier to the Tax table.

protected void placeOrderButton_Click(object sender, EventArgs e)

{

// Store the total amount because the cart // is emptied when creating the order

decimal amount = ShoppingCartAccess.GetTotalAmount();

// Get shipping ID or default to 0 int shippingId = 0;

try {

shippingId = int.Parse(shippingSelection.SelectedValue);

} catch { }

// Get tax ID or default to "No tax"

string shippingRegion =

(HttpContext.Current.Profile as ProfileCommon).ShippingRegion;

int taxId;

switch (shippingRegion) {

case "2":

taxId = 1;

break;

default:

taxId = 2;

break;

}

// Create the order and store the order ID string orderId =

ShoppingCartAccess.CreateCommerceLibOrder(shippingId, taxId);

// Redirect to the conformation page Response.Redirect("OrderPlaced.aspx");

}

Note that this is one of the most crucial pieces of code in this chapter. Here you’ll most likely make any modifications to the tax and shipping systems if you decide to add your own system, because choices are made on this page. The database and business layer changes are far more generic—although that’s not to say that modifications wouldn’t be necessary.

Một phần của tài liệu Beginning ASP.NET 2.0 E-Commerce in C# 2005 From Novice to Professional PHẦN 8 pptx (Trang 34 - 44)

Tải bản đầy đủ (PDF)

(70 trang)