This report by Charles Williams was recently posted on the CRE Loaded forums. I thought I would share it with our users, and post my own comments here:
Order Total Modules – These are modules that are called to do the finial processing for an order. They are to be used after all items are added to the cart, shipping and payment method has been selected. The modules are intended to apply the whole of an order versus applying to one or more products in an order.
In short - for greatest accuracy, they must be applied after the contents of the order and any order modifiers are finalized by the customer.
Original osCommerce Order Total Modules
osCommerce 2.2 ships with the following Order Total Modules:
• ot_loworderfee.php – This module allows for an additional fee to be charged for orders under a total value. For example, orders under $25 will have a $5 handling surcharge added to it. This module will adjust the tax and total of the order if needed.
• ot_shipping.php – This module both captures and reports the shipping method selected and the cost of that method based on the Order Class.
• ot_subtotal.php – This module captures and reports the subtotal based on the Order Class.
• ot_tax.php – This module captures and reports the tax based on the Order Class.
• ot_total.php – This module captures and reports the total based on the Order Class.
Setting the Sort Order of the Order Total Modules
With the exception of the Low Order Fee modules, the sort order plays no role in the information to be reported. It does impact the order in which the information is presented. Placing tax or shipping before the subtotal will not change the subtotal value. The reason is that the important fields dealing with shipping, tax, subtotal and total are all set by the Order class when the content of the Shopping Cart class is moved to the Order class.
The Low Order Fee module is the exception. Once it has done its processing, it could adjust the values stored in the Order Class total and tax fields. This means that it does not matter whether it appears before or after the shipping and subtotal modules, their values will not change. It does need to appear BEFORE both the tax and total modules for the correct information to be reported. If it appears AFTER the tax or total modules, then the information reported is may not be the information being processed.
So, here we see the first sign of a major problem. Present in the core code, and implicit in the basic design....
CRE Loaded additional Order Total Modules
CRE Loaded ships with several additional Order Total Modules:
• ot_lev_discount.php – This module will apply a discount to the order if the total value of the order exceeds a certain value. The amount of discount can be set as percentage based on various total value levels (hence the name). For example, a $100 order gets a 1% discount, while a $200 order gets a 2% discount, etc. This module will adjust the tax and total of the order if needed. The subtotal field will be adjusted if this module is sorted before the subtotal module.
• ot_qty_discount.php – This module will apply a discount based on the total number of items in the ordered (A global order quantity discount). The discount can be a flat amount or a percentage. It is also based on ranges, so a count of 5 items gets one discount while a count of 100 items gets different discount. This module will adjust the tax and total of the order if needed. The subtotal field will be adjusted if this module is sorted before the subtotal module.
• ot_coupon.php – This module is used to support coupons. This module will adjust the tax and total of the order if needed.
• ot_gv.php – This module is used to support gift vouchers. This module will adjust the tax and total of the order if needed.
Additional Considerations
The quantity discount and price level discount modules are the only modules that are sort order aware. Since all the modules impact the total field and possibly the tax field, the tax and total should always be set to the highest sort order values. The Order Class treats shipping as a separate value which is never part of the subtotal. It treats the subtotal and tax in one of two ways. If display price with tax is false, then the subtotal is the sum of the products in the cart and the tax is the amount calculated based on it. Therefore subtotal + shipping + tax = total. If the display price with tax is true, then the subtotal is the sum of the displayed products prices which include tax and the tax field is a report only field. Therefore subtotal + shipping = total. It is important to know that the tax module does not calculate the tax based on the subtotal, but simply reports the tax that has been calculated.
Final Analysis
The sort order may appear to be a way to control things, but it is not effective. The recommended display order of the Order Total modules is as follows:
• The Quantity and Price Level Discount can appear before or after the Subtotal module.
• The Subtotal module should be the first modules presented, excepting the above.
• The Shipping, Coupon and Gift Voucher modules can appear in any order after the Subtotal module and before the Tax module.
• The Tax module should always appear next to last (since other modules could adjust this value).
• The Total module should always be presented last (since other modules could adjust this value).
_________________
Charles Williams
Chief Software Engineering Officer
Chain Reaction Ecommerce, Inc.
We see that the addition of more contribution code extends the visibility of the flaw.
The performance outlined above is an indicator that there is a deviance from the basic definition of a module: A set of encapsulated code which performs a well defined function without regard to external factors. If a module needs to be aware of the sort order - it is no longer a module.
Basically, there are certain points in the calculation which should be fixed in position. The subtotal should include all products, always. The total should be based on the subtotal, and all subtotal modifiers, always.
These methods should be core parts of the parent class, and always finalized in that class. There should not even be a sort order applied to these beginning and ending points.
The discount methods should be part of a separate class, with two methods used to manage communications between it and the checkout system.
The first should be a flag bundle, collated by the checkout class and delivered to the discount manager as part of collating the current module status. This will allow the management of payment method related discounts and fees. (This in fact, is why I often think of this as a "transaction" class, rather than a discount or credit class.)
The second should be a clear entry point into which the checkout/order class passes product total, shipping and payment data in order to acquire a bundled of credit/debit information. That information can then be applied in the checkout class to deliver final or interim totals. The responsibility for finalization needs to clearly rest on the checkout processing class - this would be where customer decisions and store policies would be mediated.
Along the way, additional module spaces need to be defined. This would allow for more granular calculations necessary to accommodate a wider range of legislative/jurisdictional and business requirements.
David