updated as of: April 1, 2015
last author: Andy Theuninck
The latest documentation can be found on the Project Wiki. The information below may be out of date.
The UPC parser module is currently hard to trace through and should probably be more flexible. I think it should [eventually] be modular in three ways: handle special store-specific UPCs so they don't have to be hard-coded, allow discounts to be calculated differently by location, and allow pricing methods to vary by location.

Special UPCs

This collection of classes handles UPCs that aren't actually products. For example, at WFC I use UPC prefix "4" on barcoded member cards and custom coupons; someone else may use the prefix differently or just not need that code enabled.

Note: Special UPCs cannot exist in the products table or they will be treated like normal items

All Special UPC object should subclass SpecialUPC (lib/Scanning/SpecialUPC.php) and implement two methods:


UPC 8005 switches to a member list at some store (or used to at some point and was still hardcoded in)
class WackyPLU extends SpecialUPC {

	function is_special($upc){
		if ($upc == "0000000008005")
			return true;
		return false;
	function handle($upc,$json){
		$json['main_frame'] = MiscLib::base_url().'gui-modules/memlist.php';
		return $json;

Discount Types

Calculating discounts and pricing is done based on opdata.products.discounttype. This column is an integer, and simply hard-coding checks for a specific value is bound to eventually conflict with another location's usage. Values zero through 63 are reserved for use with DiscountType modules provided in the default install. Values 64 through 127 may be customized on a per-coop basis to correspond to any available DiscountType.

All Discount Type objects should subclass DiscountType (lib/Scanning/DiscountType.php) and implement the following method:

There are four additional, optional methods that may be define as needed:


Here's a simple sale (see actual implementation for optional methods):
class EveryoneSale extends DiscountType {

	function priceInfo($row, $quantity){
		$ret = array(
			'regPrice'  => $row['normal_price'],
			'unitPrice' => $row['special_price'],
			'memDiscount'  => 0

		$ret['discount'] = ($ret['regPrice'] - $ret['unitPrice']) * $quantity;

		return $ret;

Price Methods

Price Methods also deal with calculating prices and discounts, but based on opdata.products.pricemethod (or opdata.products.specialpricemethod for sales). There's a bit of overlap with Discount Types in that respect. The key difference is that price method objects are responsible for adding the item to the transaction. In general, additional price methods beyond the basic case have to do with grouped sales and keep track of which items in a transaction are part of the group.

Like discount types, each store can specify an array of price method modules in their configuration and opdata.products.pricemethod maps into that array. The same reasons to avoid hard coding apply here, too.

All Price Method objects should subclass PriceMethod (lib/Scanning/PriceMethod.php) and implement the following method: