| 
				   | 
				
| (2 intermediate revisions by the same user not shown) | 
| Line 3: | 
Line 3: | 
|   | * '''History''':  |   | * '''History''':  | 
|   | ** '''2011-12-18''' Saving current code just before making some API changes  |   | ** '''2011-12-18''' Saving current code just before making some API changes  | 
|   | + | ** '''2013-02-20''' About to rework part of the checkout -- combining shipping and payment pages; mostly works as-is, but iffy.  | 
|   | * '''Alternatives''':  |   | * '''Alternatives''':  | 
|   | ** [[/2011-12-18]] - got too ugly and complicated and unreliable  |   | ** [[/2011-12-18]] - got too ugly and complicated and unreliable  | 
| Line 9: | 
Line 10: | 
|   | <?php  |   | <?php  | 
|   | /*  |   | /*  | 
| − |    PURPOSE: vbz class library for handling dynamic data related to shopping (cart, mainly)  | + |    PURPOSE: vbz library for handling dynamic data related to shopping (cart, mainly)  | 
|   |    HISTORY:  |   |    HISTORY:  | 
|   |      2010-10-28 kluged the blank-order-email problem  |   |      2010-10-28 kluged the blank-order-email problem  | 
| Line 21: | 
Line 22: | 
|   | define('KWP_ICON_ALERT'		,'/tools/img/icons/button-red-X.20px.png');  |   | define('KWP_ICON_ALERT'		,'/tools/img/icons/button-red-X.20px.png');  | 
|   |  |   |  | 
| − | // TABLE NAMES:
  |   | 
| − | define('KST_CART_DATA'		,'shop_cart_data');
  |   | 
|   |  |   |  | 
|   | // TABLE ACTION KEYS  |   | // TABLE ACTION KEYS  | 
| Line 29: | 
Line 28: | 
|   | define('KS_URL_PAGE_ORDERS',	'orders');  |   | define('KS_URL_PAGE_ORDERS',	'orders');  | 
|   |  |   |  | 
| − | // FORM FIELD NAMES:
  | + | if (!defined('LIBMGR')) {  | 
| − | // -- cart/shipping
  | + |     require(KFP_LIB.'/libmgr.php');  | 
| − | define('KSF_SHIP_ZONE'		,'ship-zone');
  | + | }  | 
| − | // -- shipping
  |   | 
| − | define('KSF_SHIP_TO_SELF'	,'ship-to-self');	// TRUE = use shipping info for recipient too
  |   | 
| − | define('KSF_SHIP_IS_CARD'	,'ship-is-billing');	// TRUE = shipping address same as billing/card
  |   | 
| − | define('KSF_SHIP_MESSAGE'	,'ship-message');
  |   | 
| − | // address fields: prefix with "ship" or "card" as appropriate
  |   | 
| − | define('_KSF_ADDR_NAME'		,'addr-name');
  |   | 
| − | define('_KSF_ADDR_STREET'	,'addr-street');
  |   | 
| − | define('_KSF_ADDR_CITY'		,'addr-city');
  |   | 
| − | define('_KSF_ADDR_STATE'	,'addr-state');
  |   | 
| − | define('_KSF_ADDR_ZIP'		,'addr-zip');
  |   | 
| − | define('_KSF_ADDR_COUNTRY'	,'addr-country');
  |   | 
| − | define('_KSF_ADDR_EMAIL'	,'addr-email');
  |   | 
| − | define('_KSF_ADDR_PHONE'	,'addr-phone');
  |   | 
| − |    |   | 
| − | // for retrieving field data:
  |   | 
| − | define('KSF_PFX_SHIP',		'ship-');
  |   | 
| − | define('KSF_ADDR_SHIP_NAME'	,KSF_PFX_SHIP._KSF_ADDR_NAME);
  |   | 
| − | define('KSF_ADDR_SHIP_STREET'	,KSF_PFX_SHIP._KSF_ADDR_STREET);
  |   | 
| − | define('KSF_ADDR_SHIP_CITY'	,KSF_PFX_SHIP._KSF_ADDR_CITY);
  |   | 
| − | define('KSF_ADDR_SHIP_STATE'	,KSF_PFX_SHIP._KSF_ADDR_STATE);
  |   | 
| − | define('KSF_ADDR_SHIP_ZIP'	,KSF_PFX_SHIP._KSF_ADDR_ZIP);
  |   | 
| − | define('KSF_ADDR_SHIP_COUNTRY'	,KSF_PFX_SHIP._KSF_ADDR_COUNTRY);
  |   | 
| − | define('KSF_CUST_SHIP_EMAIL'	,KSF_PFX_SHIP._KSF_ADDR_EMAIL);
  |   | 
| − | define('KSF_CUST_SHIP_PHONE'	,KSF_PFX_SHIP._KSF_ADDR_PHONE);
  |   | 
| − |    |   | 
| − | // -- payment
  |   | 
| − | define('KSF_CUST_CARD_NUM'	,'cust-card-num');
  |   | 
| − | define('KSF_CUST_CARD_EXP'	,'cust-card-exp');
  |   | 
| − | define('KSF_CUST_CARD_NAME'	,'cust-card-name');
  |   | 
| − | define('KSF_CUST_CARD_STREET'	,'cust-card-street');
  |   | 
| − | define('KSF_CUST_CARD_CITY'	,'cust-card-city');
  |   | 
| − | define('KSF_CUST_CARD_STATE'	,'cust-card-state');
  |   | 
| − | define('KSF_CUST_CARD_ZIP'	,'cust-card-zip');
  |   | 
| − | define('KSF_CUST_CARD_COUNTRY'	,'cust-card-country');
  |   | 
| − | define('KSF_CUST_CHECK_NUM'	,'cust-check-num');
  |   | 
| − | define('KSF_CUST_PAY_EMAIL'	,'cust-pay-email');
  |   | 
| − | define('KSF_CUST_PAY_PHONE'	,'cust-pay-phone');
  |   | 
| − | // DATA STORAGE KEYS:
  |   | 
| − | // -- shipping
  |   | 
| − | define('KSI_SHIP_ZONE'		,100);
  |   | 
| − |    |   | 
| − | define('KSI_ADDR_SHIP_NAME'	,101);
  |   | 
| − | define('KSI_ADDR_SHIP_STREET'	,102);
  |   | 
| − | define('KSI_ADDR_SHIP_CITY'	,103);
  |   | 
| − | define('KSI_ADDR_SHIP_STATE'	,104);
  |   | 
| − | define('KSI_ADDR_SHIP_ZIP'	,105);
  |   | 
| − | define('KSI_ADDR_SHIP_COUNTRY'	,106);
  |   | 
| − | define('KSI_SHIP_MESSAGE'	,107);
  |   | 
| − | define('KSI_SHIP_TO_SELF'	,110);
  |   | 
| − | define('KSI_SHIP_IS_CARD'	,113);
  |   | 
| − |    |   | 
| − | define('KSI_CUST_SHIP_EMAIL'	,111);
  |   | 
| − | define('KSI_CUST_SHIP_PHONE'	,112);
  |   | 
| − | //define('KSI_SHIP_MISSING'	,120);
  |   | 
| − | // -- payment
  |   | 
| − | define('KSI_CUST_CARD_NUM'	,202);
  |   | 
| − | define('KSI_CUST_CARD_EXP'	,203);
  |   | 
| − |    |   | 
| − | define('KSI_ADDR_CARD_NAME'	,204);
  |   | 
| − | define('KSI_CUST_CARD_NAME'	,KSI_ADDR_CARD_NAME);	// alias
  |   | 
| − | define('KSI_ADDR_CARD_STREET'	,205);
  |   | 
| − | define('KSI_CUST_CARD_STREET'	,KSI_ADDR_CARD_STREET);	// alias
  |   | 
| − | define('KSI_ADDR_CARD_CITY'	,206);
  |   | 
| − | define('KSI_CUST_CARD_CITY'	,KSI_ADDR_CARD_CITY);
  |   | 
| − | define('KSI_ADDR_CARD_STATE'	,207);
  |   | 
| − | define('KSI_CUST_CARD_STATE'	,KSI_ADDR_CARD_STATE);
  |   | 
| − | define('KSI_ADDR_CARD_ZIP'	,208);
  |   | 
| − | define('KSI_CUST_CARD_ZIP'	,KSI_ADDR_CARD_ZIP);
  |   | 
| − | define('KSI_ADDR_CARD_COUNTRY'	,209);
  |   | 
| − | define('KSI_CUST_CARD_COUNTRY'	,KSI_ADDR_CARD_COUNTRY);
  |   | 
| − |    |   | 
| − | define('KSI_CUST_PAY_EMAIL'	,211);
  |   | 
| − | define('KSI_CUST_PAY_PHONE'	,212);
  |   | 
| − | //define('KSI_CUST_MISSING'	,220);
  |   | 
| − | define('KSI_CUST_CHECK_NUM'	,230);
  |   | 
|   |  |   |  | 
| − | // calculated data
  | + | clsLibMgr::Add('strings',		KFP_LIB.'/strings.php',__FILE__,__LINE__);  | 
| − | define('KSI_ITEM_TOTAL'		,301);
  | + | clsLibMgr::Add('string.tplt',	KFP_LIB.'/StringTemplate.php',__FILE__,__LINE__);  | 
| − | define('KSI_PER_ITEM_TOTAL'	,302);
  | + | clsLibMgr::Add('tree',		KFP_LIB.'/tree.php',__FILE__,__LINE__);  | 
| − | define('KSI_PER_PKG_TOTAL'	,303);
  | + | clsLibMgr::Add('vbz.store',	KFP_LIB_VBZ.'/store.php',__FILE__,__LINE__);  | 
| − |    | + |   clsLibMgr::AddClass('clsVbzPage', 'vbz.store');  | 
| − | // ORDER MESSAGE TYPES
  | + | clsLibMgr::Add('vbz.cart',	KFP_LIB_VBZ.'/cart.php',__FILE__,__LINE__);  | 
| − | // these reflect the values in the ord_msg_media table
  | + |   clsLibMgr::AddClass('clsPageCart', 'vbz.cart');  | 
| − | define('KSI_ORD_MSG_INSTRUC',	1);	// Instructions in submitted order
  | + |   clsLibMgr::AddClass('clsShopCarts','vbz.cart');  | 
| − | define('KSI_ORD_MSG_PKSLIP',	2);	// Packing slip
  | + | clsLibMgr::Add('vbz.order',	KFP_LIB_VBZ.'/orders.php',__FILE__,__LINE__);  | 
| − | define('KSI_ORD_MSG_EMAIL',	3);	// Email
  | + |   clsLibMgr::AddClass('clsOrders', 'vbz.order');  | 
| − | define('KSI_ORD_MSG_PHONE',	4);	// Phone call
  | + | clsLibMgr::Load('strings',__FILE__,__LINE__);  | 
| − | define('KSI_ORD_MSG_MAIL',	5);	// Snail mail
  | + | clsLibMgr::Load('string.tplt',__FILE__,__LINE__);  | 
| − | define('KSI_ORD_MSG_FAX',	6);	// Faxed message
  | + | clsLibMgr::Load('tree',__FILE__,__LINE__);  | 
| − | define('KSI_ORD_MSG_LABEL',	7);	// Shipping label (for delivery instructions)
  | + | clsLibMgr::Load('vbz.store',__FILE__,__LINE__);  | 
| − | define('KSI_ORD_MSG_INT',	8);	// internal use - stored, not sent
  | + | //    clsLibMgr::Load('vbz.cart',__FILE__,__LINE__);  | 
| − |    |   | 
| − | global $vgaCartDataType;	// documentation says this shouldn't be necessary
  |   | 
| − | $vgaCartDataType = array (
  |   | 
| − |     KSI_SHIP_ZONE		=> 'ship zone',
  |   | 
| − |     KSI_ADDR_SHIP_NAME		=> 'ship-to name',
  |   | 
| − |     KSI_ADDR_SHIP_STREET	=> 'ship-to street',
  |   | 
| − |     KSI_ADDR_SHIP_CITY		=> 'ship-to city',
  |   | 
| − |     KSI_ADDR_SHIP_STATE		=> 'ship-to state',
  |   | 
| − |     KSI_ADDR_SHIP_ZIP		=> 'ship-to zipcode',
  |   | 
| − |     KSI_ADDR_SHIP_COUNTRY	=> 'ship-to country',
  |   | 
| − |     KSI_SHIP_MESSAGE		=> 'ship-to message',
  |   | 
| − |     KSI_SHIP_TO_SELF		=> 'ship to self?',
  |   | 
| − |     KSI_SHIP_IS_CARD		=> 'ship to = card?',
  |   | 
| − |     KSI_CUST_SHIP_EMAIL		=> 'ship-to email',
  |   | 
| − |     KSI_CUST_SHIP_PHONE		=> 'ship-to phone',
  |   | 
| − | //    KSI_SHIP_MISSING		=> 'ship-to missing info',  |   | 
| − | // -- payment
  |   | 
| − |     KSI_CUST_CARD_NUM		=> 'card number',
  |   | 
| − |     KSI_CUST_CARD_EXP		=> 'card expiry',
  |   | 
| − |     KSI_ADDR_CARD_NAME		=> 'card owner',
  |   | 
| − |     KSI_ADDR_CARD_STREET	=> 'card street address',
  |   | 
| − |     KSI_ADDR_CARD_CITY		=> 'card address city',
  |   | 
| − |     KSI_ADDR_CARD_STATE		=> 'card address state',
  |   | 
| − |     KSI_ADDR_CARD_ZIP		=> 'card zipcode',
  |   | 
| − |     KSI_ADDR_CARD_COUNTRY	=> 'card country',
  |   | 
| − |     KSI_CUST_CHECK_NUM		=> 'check number',
  |   | 
| − |     KSI_CUST_PAY_EMAIL		=> 'customer email',
  |   | 
| − |     KSI_CUST_PAY_PHONE		=> 'customer phone',
  |   | 
| − |    |   | 
| − |     KSI_ITEM_TOTAL		=> 'item total',
  |   | 
| − |     KSI_PER_ITEM_TOTAL		=> 's/h per-item total',
  |   | 
| − |     KSI_PER_PKG_TOTAL		=> 's/h package total',
  |   | 
| − |     );
  |   | 
| − |    |   | 
| − | if (defined('LIBMGR')) {
  |   | 
| − |     clsLibMgr::Add('strings',		KFP_LIB.'/strings.php',__FILE__,__LINE__);
  |   | 
| − |     clsLibMgr::Add('string.tplt',	KFP_LIB.'/StringTemplate.php',__FILE__,__LINE__);
  |   | 
| − |     clsLibMgr::Add('tree',		KFP_LIB.'/tree.php',__FILE__,__LINE__);
  |   | 
| − |     clsLibMgr::Add('vbz.store',	KFP_LIB_VBZ.'/store.php',__FILE__,__LINE__);
  |   | 
| − |     clsLibMgr::Load('strings',__FILE__,__LINE__);
  |   | 
| − |     clsLibMgr::Load('string.tplt',__FILE__,__LINE__);
  |   | 
| − |     clsLibMgr::Load('tree',__FILE__,__LINE__);
  |   | 
| − |     clsLibMgr::Load('vbz.store',__FILE__,__LINE__);
  |   | 
| − | } else {
  |   | 
| − | // assume all libraries are on path  |   | 
| − |     require_once(KFP_LIB.'/strings.php');
  |   | 
| − |     require_once(KFP_LIB.'/StringTemplate.php');
  |   | 
| − |     require_once('store.php');
  |   | 
| − |     require_once(KFP_LIB.'/tree.php');
  |   | 
| − | }
  |   | 
|   |  |   |  | 
|   | define('KS_VBZCART_SESSION_KEY','vbzcart_key');  |   | define('KS_VBZCART_SESSION_KEY','vbzcart_key');  | 
| Line 184: | 
Line 58: | 
|   | define('KSQ_PAGE_SHIP','ship');	// shipping page  |   | define('KSQ_PAGE_SHIP','ship');	// shipping page  | 
|   | define('KSQ_PAGE_PAY','pay');	// payment page  |   | define('KSQ_PAGE_PAY','pay');	// payment page  | 
|   | + | define('KSQ_PAGE_CONT','cont');	// contact page -- shipping and payment  | 
|   | define('KSQ_PAGE_CONF','conf');	// customer confirmation of order  |   | define('KSQ_PAGE_CONF','conf');	// customer confirmation of order  | 
|   | define('KSQ_PAGE_RCPT','rcpt');	// order receipt  |   | define('KSQ_PAGE_RCPT','rcpt');	// order receipt  | 
| Line 192: | 
Line 67: | 
|   |   database class with creators for shop classes  |   |   database class with creators for shop classes  | 
|   | */  |   | */  | 
| − | abstract class clsPageShop extends clsPage {
  | + | class clsVbzData_Shop extends clsVbzData {  | 
| − |      public function Sessions() {  | + |      public function Sessions($id=NULL) {  | 
| − | 	return $this->Make('clsShopSessions');  | + | 	return $this->Make('clsSessions_StoreUI',$id);  | 
|   |      }  |   |      }  | 
| − |      public function Clients() {  | + |      public function Clients($id=NULL) {  | 
| − | 	return $this->Make('clsShopClients');  | + | 	return $this->Make('clsShopClients',$id);  | 
|   |      }  |   |      }  | 
| − |      public function Carts() {  | + |      public function Carts($id=NULL) {  | 
| − | 	return $this->Make('clsShopCarts');  | + | 	return $this->Make('clsShopCarts',$id);  | 
|   |      }  |   |      }  | 
| − |      public function CartLines() {  | + |      public function CartLines($id=NULL) {  | 
| − | 	return $this->Make('clsShopCartLines');  | + | 	return $this->Make('clsShopCartLines',$id);  | 
|   |      }  |   |      }  | 
|   |      public function CartLog() {  |   |      public function CartLog() {  | 
|   | 	return $this->Make('clsShopCartLog');  |   | 	return $this->Make('clsShopCartLog');  | 
|   |      }  |   |      }  | 
| − |      public function Orders() {  | + |      public function Orders($id=NULL) {  | 
| − | 	return $this->Make('clsOrders');  | + | 	return $this->Make('clsOrders',$id);  | 
|   |      }  |   |      }  | 
| − |      public function OrdLines() {  | + |      public function OrdLines($id=NULL) {  | 
| − | 	return $this->Make('clsOrderLines');  | + | 	return $this->Make('clsOrderLines',$id);  | 
|   |      }  |   |      }  | 
|   | /*  |   | /*  | 
| Line 219: | 
Line 94: | 
|   |      }  |   |      }  | 
|   | */  |   | */  | 
| − |      public function OrdMsgs() {  | + |      public function OrdMsgs($id=NULL) {  | 
| − | 	return $this->Make('clsOrderMsgs');  | + | 	return $this->Make('clsOrderMsgs',$id);  | 
|   |      }  |   |      }  | 
|   | /*  |   | /*  | 
| Line 244: | 
Line 119: | 
|   | }  |   | }  | 
|   |  |   |  | 
| − | class clsPageCart extends clsPageShop {
  | + | /*==================  | 
| − |     protected $objSess;
  | + |    CLASS: clsShipZone  | 
| − |     protected $objCart;
  | + |    PURPOSE: shipping zone functions  | 
| − |    | + |    USAGE: Customize the isDomestic() function if you're shipping from somewhere other than the US  | 
| − | // process functions
  |   | 
| − |     public function DoPreContent() {
  |   | 
| − | 	$this->inCkout = FALSE;	// (2011-04-01) not sure why this is sometimes not getting set
  |   | 
| − | 	parent::DoPreContent();
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       INPUT: $iCaller is for debugging and is discarded; caller should pass __METHOD__ as the argument.
  |   | 
| − |     */
  |   | 
| − |     public function GetObjects($iCaller) {
  |   | 
| − | 	$objSessions = $this->Sessions();
  |   | 
| − | 	$this->objSess = $objSessions->GetCurrent();	// get whatever session is currently applicable (existing or new)
  |   | 
| − | 	$this->objCart = $this->objSess->CartObj();
  |   | 
| − | 	$this->objCart->objSess = $this->objSess;	// used for logging
  |   | 
| − |     }
  |   | 
| − |     public function Cart($iObj=NULL) {
  |   | 
| − | 	if (!is_null($iObj)) {
  |   | 
| − | 	    $this->objCart = $iObj;
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objCart;
  |   | 
| − |     }
  |   | 
| − |     protected function HandleInput() {
  |   | 
| − | 	$this->GetObjects(__METHOD__);
  |   | 
| − | 	$this->objCart->CheckData();	// check for any form data (added items, recalculations, etc.)
  |   | 
| − |    |   | 
| − | 	$this->strSheet	= 'cart';	// cart stylesheet has a few different things in it
  |   | 
| − |    |   | 
| − | 	$this->strWikiPg	= '';
  |   | 
| − | 	$this->strTitle	= 'Shopping Cart';	// Displayed title (page header)
  |   | 
| − | 	$this->strName	= 'shopping cart';	// HTML title
  |   | 
| − | 	$this->strTitleContext	= 'this is your'; // 'Tomb of the...';
  |   | 
| − | 	$this->strHdrXtra	= '';
  |   | 
| − | 	$this->strSideXtra	= ''; //'<dt><b>Cat #</b>: '.$this->strReq;
  |   | 
| − | 	$this->strSheet	= KSQ_PAGE_CART;	// default
  |   | 
| − |     }
  |   | 
| − |     protected function DoContent() {
  |   | 
| − | 	echo $this->objCart->Render();
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − |    |   | 
| − | /* ==================== *\
  |   | 
| − | || -- HELPER CLASSES -- ||
  |   | 
| − | \* ==================== */
  |   | 
| − |    |   | 
| − | /*====
  |   | 
| − |    PURPOSE: parent class for all cart contact nodes  |   | 
| − |   HISTORY:
  |   | 
| − |     2011-11-26 started experimentally
  |   | 
| − | */
  |   | 
| − | abstract class clsContactNode extends clsTreeNode {
  |   | 
| − |    |   | 
| − |     protected $actSave;	// script created to record this data; NULL = not created yet
  |   | 
| − |    |   | 
| − |     public function __construct($iNodes=NULL) {
  |   | 
| − | 	parent::__construct($iNodes);
  |   | 
| − | 	$this->actSave = NULL;
  |   | 
| − |     }
  |   | 
| − |     public function ScriptMe() {
  |   | 
| − | 	return $this->actSave;
  |   | 
| − |     }
  |   | 
| − | /*
  |   | 
| − |     protected function ScriptUp() {
  |   | 
| − | 	if ($this->HasParent()) {
  |   | 
| − | 	    return $this->Parent()->ScriptMe();
  |   | 
| − | 	} else {
  |   | 
| − | 	    return NULL;
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − | */
  |   | 
| − |     public function ScriptRoot() {
  |   | 
| − | 	if (is_null($this->Parent())) {
  |   | 
| − | 	    throw new exception('Internal Error: Expecting parent, but it is NULL. CLASS: '.get_class($this).' CTRL: '.$this->CtrlName());
  |   | 
| − | 	}
  |   | 
| − | 	return $this->Parent()->ScriptRoot();
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: a "starter" script object to use for generating this object's scripts
  |   | 
| − | 	There's probably a better way to do this.
  |   | 
| − |     */
  |   | 
| − |     public function ScriptStart() {
  |   | 
| − | 	return new Script_Script();
  |   | 
| − |     }
  |   | 
| − |     public function Engine() {
  |   | 
| − | 	if (is_null($this->Parent())) {
  |   | 
| − | 	    throw new exception('Object (class '.get_class($this).') has no parent.');
  |   | 
| − | 	}
  |   | 
| − | 	return $this->Parent()->Engine();
  |   | 
| − |     }
  |   | 
| − |     public function Save() {
  |   | 
| − | 	$acts = NULL;
  |   | 
| − | 	if ($this->AccessCount() == 0) {
  |   | 
| − | 	    $acts = $this->ScriptStart();
  |   | 
| − |    |   | 
| − | 	    $acts->Add($this->SaveThis(),'this');
  |   | 
| − | 	    $acts->Add($this->SaveSubs(),'subs');
  |   | 
| − | 	    $acts->Add($this->SavePost($acts),'post');
  |   | 
| − |    |   | 
| − | 	    if ($acts->IsEmpty()) {
  |   | 
| − | 		$acts = NULL;
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	$this->IncCount();
  |   | 
| − | 	$this->actSave = $acts;
  |   | 
| − | 	return $acts;
  |   | 
| − |     }
  |   | 
| − |     abstract protected function SaveThis();
  |   | 
| − |     protected function SavePost(Script_Script $iScript) {}
  |   | 
| − |    |   | 
| − |     /*----
  |   | 
| − |       ACTION: Save sub-nodes
  |   | 
| − |     */
  |   | 
| − |     protected function SaveSubs() {
  |   | 
| − | 	if ($this->HasNodes()) {
  |   | 
| − | 	    $acts = new Script_Script();
  |   | 
| − | 	    $ar = $this->Nodes();
  |   | 
| − | 	    foreach ($ar as $name => $obj) {
  |   | 
| − | 		$acts->Add($obj->Save(),$name);
  |   | 
| − | 	    }
  |   | 
| − | 	} else {
  |   | 
| − | 	    $acts = NULL;
  |   | 
| − | 	}
  |   | 
| − | 	return $acts;
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | /*====
  |   | 
| − |    PURPOSE: a contact node that holds other nodes but has no data of its own  |   | 
| − |    HISTORY:  |   | 
| − |     2011-11-29 created
  |   | 
| − | */
  |   | 
| − | class clsContactFolder extends clsContactNode {
  |   | 
| − |     protected function SaveThis() {} 	// nothing to save here; only saves sub-nodes
  |   | 
| − | }
  |   | 
| − | class clsContactFolder_reference extends clsContactFolder {
  |   | 
| − |     protected function SaveSubs() {}	// do not save sub-nodes; they are for reference only
  |   | 
| − |    |   | 
| − |     /*----
  |   | 
| − |       OVERRIDE: does not set the nodes' parents, because they may be parented elsewhere
  |   | 
| − | 	Otherwise we might get recursion.
  |   | 
| − |     */
  |   | 
| − | /* not sure if this is actually necessary; the problem seems to be something else
  |   | 
| − |     protected function NodeAdd($iName,clsTreeNode $iNode) {
  |   | 
| − | 	$this->arSubs[$iName] = $iNode;
  |   | 
| − |     }
  |   | 
| − | */
  |   | 
| − | }
  |   | 
| − | class clsContactRoot extends clsContactFolder {
  |   | 
| − |     protected $objDB;
  |   | 
| − |     protected $actSave;
  |   | 
| − |    |   | 
| − |     public function __construct(clsPageShop $iDB) {
  |   | 
| − | 	$this->objDB = $iDB;
  |   | 
| − | 	$this->actSave = NULL;
  |   | 
| − |     }
  |   | 
| − |     public function Engine() {
  |   | 
| − | 	return $this->objDB;
  |   | 
| − |     }
  |   | 
| − |     public function ScriptRoot() {
  |   | 
| − | 	if (is_null($this->actSave)) {
  |   | 
| − | 	    $this->actSave = $this->ScriptStart();
  |   | 
| − | 	}
  |   | 
| − | 	return $this->actSave;
  |   | 
| − |     }
  |   | 
| − |     public function Save() {
  |   | 
| − | 	$this->ResetCount();
  |   | 
| − | 	return parent::Save();
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | /*====
  |   | 
| − |   PURPOSE: Generic cart data field, no special handling
  |   | 
| − |   HISTORY:
  |   | 
| − |     2011-11-29 changed ancestry from clsTreeNode to clsContactNode
  |   | 
| − | */
  |   | 
| − | class clsCartField extends clsContactNode {
  |   | 
| − |     protected $objCart;
  |   | 
| − |     protected $intIndex;	// index of field type
  |   | 
| − |     protected $strCtrlName;	// name of control (on form)
  |   | 
| − |    |   | 
| − |     public function __construct($iCart, $iIndex, $iCtrlName) {
  |   | 
| − | 	if (is_object($iIndex)) {
  |   | 
| − | 	    echo '<b>Internal error</b>: argument $iIndex is an object of class '.get_class($iIndex).', not a scalar.<br>';
  |   | 
| − | 	    throw new exception('Unexpected argument type.');
  |   | 
| − | 	}
  |   | 
| − | 	$this->objCart = $iCart;
  |   | 
| − | 	$this->intIndex = $iIndex;
  |   | 
| − | 	$this->strCtrlName = $iCtrlName;
  |   | 
| − |     }
  |   | 
| − |     protected function SaveThis() { } 	// define later
  |   | 
| − |     public function DataType() {
  |   | 
| − | 	return $this->intIndex;
  |   | 
| − |     }
  |   | 
| − |     public function Value($iVal=NULL) {
  |   | 
| − |     // overrides default action by loading data from database if needed
  |   | 
| − | 	if (!is_null($iVal)) {
  |   | 
| − | 	    die('writing to read-only clsTreeNode object');
  |   | 
| − | 	    // this should never happen, because we're not using this class to write these values
  |   | 
| − | 	}
  |   | 
| − | 	if (!$this->Loaded()) {
  |   | 
| − | 	    if (is_null($this->intIndex)) {
  |   | 
| − | 		$this->vVal = NULL;
  |   | 
| − | 	    } else {
  |   | 
| − | 		$this->vVal = $this->objCart->DataItem($this->intIndex);
  |   | 
| − | 	    }
  |   | 
| − | 	} else {
  |   | 
| − | 	}
  |   | 
| − | 	return $this->vVal;
  |   | 
| − |     }
  |   | 
| − |     public function CtrlName() {
  |   | 
| − | 	return $this->strCtrlName;
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − |    |   | 
| − | class clsCartAddr extends clsContactFolder {
  |   | 
| − | //class clsCartAddr extends clsTreeNode {
  |   | 
| − |     public $doFixed;
  |   | 
| − |     public $doFixedCountry;	// Country is sometimes determined by Zone
  |   | 
| − |     public $strStateLabel;	// label for "State" field
  |   | 
| − |     public $strStatePost;	// text for after "State" field
  |   | 
| − |     public $lenStateField;	// approximate width of "State" field
  |   | 
| − |     public $strZipLabel;	// label for "Zipcode"/"Postal code" field
  |   | 
| − |    |   | 
| − | //    protected function SaveThis() { } 	// stubbed off for customer-side pages
  |   | 
| − |     /*----
  |   | 
| − |       OVERRIDE: Name is saved normally, but other data is saved in aggregate by SaveThis()
  |   | 
| − |     */
  |   | 
| − |     protected function SaveSubs() {
  |   | 
| − | 	return $this->Name()->Save();
  |   | 
| − |     }
  |   | 
| − |    |   | 
| − |     public function Name() {
  |   | 
| − | 	return $this->Node('name');
  |   | 
| − |     }
  |   | 
| − |     public function Street() {
  |   | 
| − | 	return $this->Node('street');
  |   | 
| − |     }
  |   | 
| − |     public function City() {
  |   | 
| − | 	return $this->Node('city');
  |   | 
| − |     }
  |   | 
| − |     public function State() {
  |   | 
| − | 	return $this->Node('state');
  |   | 
| − |     }
  |   | 
| − |     public function Zip() {
  |   | 
| − | 	return $this->Node('zip');
  |   | 
| − |     }
  |   | 
| − |     public function Country() {
  |   | 
| − | 	return $this->Node('country');
  |   | 
| − |     }
  |   | 
| − |     public function HasCountry() {
  |   | 
| − | 	return $this->Exists('country');
  |   | 
| − |     }
  |   | 
| − |     public function Instruc() {
  |   | 
| − | 	return $this->Node('instruc');
  |   | 
| − |     }
  |   | 
| − |     public function Render($iCart) {
  |   | 
| − |    |   | 
| − | // copy calculated stuff over to variables to make it easier to insert in formatted output:
  |   | 
| − | 	$ksName		= $this->Name()->CtrlName();
  |   | 
| − | 	$ksStreet	= $this->Street()->CtrlName();
  |   | 
| − | 	$ksCity		= $this->City()->CtrlName();
  |   | 
| − | 	$ksState	= $this->State()->CtrlName();
  |   | 
| − | 	$ksZip		= $this->Zip()->CtrlName();
  |   | 
| − | 	$ksCountry	= $this->Country()->CtrlName();
  |   | 
| − |    |   | 
| − | 	$strName	= $this->Name()->Value();
  |   | 
| − | 	$strStreet	= $this->Street()->Value();
  |   | 
| − | 	$strCity	= $this->City()->Value();
  |   | 
| − | 	$strState	= $this->State()->Value();
  |   | 
| − | 	$strZip		= $this->Zip()->Value();
  |   | 
| − | 	$strCountry	= $this->Country()->Value();
  |   | 
| − |    |   | 
| − | 	$strStateLabel	= $this->strStateLabel;
  |   | 
| − | 	$strZipLabel	= $this->strZipLabel;
  |   | 
| − | 	$lenState	= $this->lenStateField;
  |   | 
| − | 	$strStateAfter	= $this->strStatePost;
  |   | 
| − |    |   | 
| − | 	if ($this->doFixedCountry) {
  |   | 
| − | 	    $htCountry = '<b>'.$strCountry.'</b>';
  |   | 
| − | 	    $htZone = '';
  |   | 
| − | 	} else {
  |   | 
| − | 	    $htCountry = '<input name="'.$ksCountry.'" value="'.$strCountry.'" size=20>';
  |   | 
| − | 	    $htShipCombo = $this->htShipCombo;
  |   | 
| − | 	    $htBtnRefresh = '<input type=submit name="update" value="Update Form">';
  |   | 
| − | 	    $htZone = " - change shippping zone: $htShipCombo $htBtnRefresh";
  |   | 
| − | 	}
  |   | 
| − |    |   | 
| − | 	if ($this->doFixedName) {
  |   | 
| − | 	    $out = <<<__END__
  |   | 
| − | <tr><td align=right valign=middle>Name:</td>
  |   | 
| − | 	<td><b>$strName</b></td>
  |   | 
| − | 	</tr>
  |   | 
| − | __END__;
  |   | 
| − | 	} else {
  |   | 
| − | 	    $out = <<<__END__
  |   | 
| − | <tr><td align=right valign=middle>Name: </td>
  |   | 
| − | 	<td><input name="$ksName" value="$strName" size=50></td>
  |   | 
| − | 	</tr>
  |   | 
| − | __END__;
  |   | 
| − | 	}
  |   | 
| − | 	$out .= $this->htmlBeforeAddress;
  |   | 
| − | 	if ($this->doFixed) {
  |   | 
| − | 	    $out .= <<<__END__
  |   | 
| − | <tr><td align=right valign=middle>Street Address<br>or P.O. Box:</td>
  |   | 
| − | 	<td><b>$strStreet</b></td>
  |   | 
| − | 	</tr>
  |   | 
| − | <tr><td align=right valign=middle>City:</td><td><b>$strCity</b></td></tr>
  |   | 
| − | <tr><td align=right valign=middle>$strStateLabel:</td><td><b>$strState</b></td></tr>
  |   | 
| − | <tr><td align=right valign=middle>$strZipLabel:</td><td><b>$strZip</b></td></tr>
  |   | 
| − | <tr><td align=right valign=middle>Country:</td><td><b>$htCountry<b>$htZone</td></tr>
  |   | 
| − | __END__;
  |   | 
| − | 	} else {
  |   | 
| − | 	    $out .= <<<__END__
  |   | 
| − | <tr><td align=right valign=middle>Street Address<br>or P.O. Box:</td>
  |   | 
| − | 	<td><textarea name="$ksStreet" cols=50 rows=3>$strStreet</textarea></td>
  |   | 
| − | 	</tr>
  |   | 
| − | <tr><td align=right valign=middle>City: </td><td><input name="$ksCity" value="$strCity" size=20></td></tr>
  |   | 
| − | <tr><td align=right valign=middle>$strStateLabel: </td><td><input name="$ksState" value="$strState" size=$lenState>$strStateAfter</td></tr>
  |   | 
| − | <tr><td align=right valign=middle>$strZipLabel: </td><td><input name="$ksZip" value="$strZip" size=11></td></tr>
  |   | 
| − | <tr><td align=right valign=middle>Country: </td><td>$htCountry - change shippping zone: $htShipCombo $htBtnRefresh</td></tr>
  |   | 
| − | __END__;
  |   | 
| − | 	}
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: Complete address as single string, in multiple lines
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-09-13 Added a line for Instruc()
  |   | 
| − |     */
  |   | 
| − |     public function AsText($iLineSep="\n") {
  |   | 
| − | 	$xts = new xtString($this->Street()->Value(),TRUE);
  |   | 
| − | 	$xts-> ReplaceSequence(chr(8).' ',' ',0);	// replace any blank sequences with single space
  |   | 
| − | 	$xts->ReplaceSequence(chr(10).chr(13),$iLineSep,0);	// replace any sequences of newlines with line sep string
  |   | 
| − |    |   | 
| − | 	$xts->Value .= $iLineSep.$this->City()->Value();
  |   | 
| − | 	if ($this->State()->Filled()) {
  |   | 
| − | 	    $xts->Value .= ', '.$this->State()->Value();
  |   | 
| − | 	}
  |   | 
| − | 	if ($this->Zip()->Filled()) {
  |   | 
| − | 	    $xts->Value .= ' '.$this->Zip()->Value();
  |   | 
| − | 	}
  |   | 
| − | 	if ($this->Country()->Filled()) {
  |   | 
| − | 		$xts->Value .= ' '.$this->Country()->Value();
  |   | 
| − | 	}
  |   | 
| − | 	if ($this->Instruc()->Filled()) {
  |   | 
| − | 		$xts->Value .= $iLineSep.$this->Instruc()->Value();
  |   | 
| − | 	}
  |   | 
| − | 	return $xts->Value;
  |   | 
| − |     }
  |   | 
| − |     public function AsSingleLine() {
  |   | 
| − | 	return $this->AsText(' / ');
  |   | 
| − |     }
  |   | 
| − |     public function AsSearchable() {
  |   | 
| − | 	$out = $this->Street()->Value();
  |   | 
| − | 	$out .= $this->City()->Value();
  |   | 
| − | 	$out .= $this->State()->Value();
  |   | 
| − | 	$out .= $this->Zip()->Value();
  |   | 
| − | /* including the country when creating the search string causes issues:
  |   | 
| − |  if the country is domestic, then we don't want to include it (because many people won't)...
  |   | 
| − |  so we need to have some way of determining whether this is so -- but that's currently locked inside
  |   | 
| − |  the ShipZone data, which isn't always available. Need to have a mapping of countries to zones, and
  |   | 
| − |  more rigorous handling of country inputs.
  |   | 
| − |    |   | 
| − |  For now, just leave it out (how likely is it that an address in another country will match, including postal code?)
  |   | 
| − | */	
  |   | 
| − | //	$out .= $this->Country()->Value();
  |   | 
| − | 	$out = clsCustAddrs::Searchable($out);
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | // contact fields
  |   | 
| − | class clsCartContact extends clsContactFolder {
  |   | 
| − | //    public $Addr;
  |   | 
| − |     public $doFixed;
  |   | 
| − |    |   | 
| − |     /*----
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-10-05 Apparently, when addr is the same for payment and shipping,
  |   | 
| − | 	  the default is to put it under payment rather than with the
  |   | 
| − | 	  other contact info (phone, email).
  |   | 
| − |    |   | 
| − | 	  If I can't figure out why this is, I'll probably switch it around later.
  |   | 
| − |    |   | 
| − | 	  In any case, this looks first locally, and then under (parent)->payment
  |   | 
| − |     */
  |   | 
| − |     public function Addr() {
  |   | 
| − | 	if ($this->Exists('addr')) {
  |   | 
| − | 	    return $this->Node('addr');
  |   | 
| − | 	} elseif ($this->Parent()->Exists('payment')) {
  |   | 
| − | 	    $objPay = $this->Parent()->Node('payment');
  |   | 
| − | 	    if ($objPay->Exists('addr')) {
  |   | 
| − | 		return $objPay->Node('addr');
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	return NULL;
  |   | 
| − |     }
  |   | 
| − |     public function HasEmail() {
  |   | 
| − | 	return $this->Exists('email');
  |   | 
| − |     }
  |   | 
| − |     public function Email() {
  |   | 
| − | 	return $this->Node('email');
  |   | 
| − |     }
  |   | 
| − |     public function HasPhone() {
  |   | 
| − | 	return $this->Exists('phone');
  |   | 
| − |     }
  |   | 
| − |     public function Phone() {
  |   | 
| − | 	return $this->Node('phone');
  |   | 
| − |     }
  |   | 
| − |     protected function Person() {
  |   | 
| − | 	return $this->Parent();
  |   | 
| − |     }
  |   | 
| − |     public function Render() {
  |   | 
| − | 	$hrefForSpam = '<a href="'.KWP_WIKI.'Anti-Spam_Policy">';
  |   | 
| − |    |   | 
| − | // copy any needed constants over to variables for parsing:
  |   | 
| − | 	$ksEmail	= $this->Email()->CtrlName();
  |   | 
| − | 	$ksPhone	= $this->Phone()->CtrlName();
  |   | 
| − |    |   | 
| − | 	$strEmail	= $this->Email()->Value();
  |   | 
| − | 	$strPhone	= $this->Phone()->Value();
  |   | 
| − |    |   | 
| − | 	if ($this->doFixed) {
  |   | 
| − | 	    $out = <<<__END__
  |   | 
| − | <tr><td align=right valign=middle>Email:</td><td><b>$strEmail</b></td></tr>
  |   | 
| − | <tr><td align=right valign=middle>Phone:</td><td><b>$strPhone</b></td></tr>
  |   | 
| − | __END__;
  |   | 
| − | 	} else {
  |   | 
| − | 	    $out = <<<__END__
  |   | 
| − | <tr><td align=right valign=middle>Email:</td>
  |   | 
| − | 	<td><input name="$ksEmail" value="$strEmail" size=30> {$hrefForSpam}anti-spam policy</a>
  |   | 
| − | </td>
  |   | 
| − | 	</tr>
  |   | 
| − | <tr><td align=right valign=middle>Phone:</td>
  |   | 
| − | 	<td><input name="$ksPhone" value="$strPhone" size=20> (optional)</td>
  |   | 
| − | 	</tr>
  |   | 
| − | __END__;
  |   | 
| − | 	}
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |    |   | 
| − | }
  |   | 
| − | // a Payment has an Address, a Number (/CVV/Exp) and a Name
  |   | 
| − | class clsPayment extends clsContactFolder {
  |   | 
| − |     //public $Name; // is this needed? Can be found at Addr->Name()
  |   | 
| − | //    public $Addr;
  |   | 
| − |    |   | 
| − | //    protected function SaveThis() { }	// stubbed for customer-side
  |   | 
| − |    |   | 
| − |     public function CustName() {
  |   | 
| − | 	return $this->Addr()->Node('name');
  |   | 
| − |     }
  |   | 
| − |     public function Addr() {
  |   | 
| − | 	return $this->Node('addr');
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |       USED BY: admin functions
  |   | 
| − |     */
  |   | 
| − |     public function MakeAddr($iVal) {
  |   | 
| − | 	$objNode = new clsTreeNode();
  |   | 
| − | 	$objNode->Value($iVal);
  |   | 
| − | 	$this->Node('addr',$objNode);
  |   | 
| − |     }
  |   | 
| − |     public function Num() {
  |   | 
| − | 	return $this->Node('num');
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |       USED BY: admin functions
  |   | 
| − |     */
  |   | 
| − |     public function MakeNum($iVal) {
  |   | 
| − | 	$objNode = new clsTreeNode();
  |   | 
| − | 	$objNode->Value($iVal);
  |   | 
| − | 	$this->Node('num',$objNode);
  |   | 
| − |     }
  |   | 
| − |     public function Exp() {
  |   | 
| − | 	return $this->Node('exp');
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |       USED BY: admin functions
  |   | 
| − |     */
  |   | 
| − |     public function MakeExp($iVal) {
  |   | 
| − | 	$objNode = new clsTreeNode();
  |   | 
| − | 	$objNode->Value($iVal);
  |   | 
| − | 	$this->Node('exp',$objNode);
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |       INPUT:
  |   | 
| − | 	iMaxFuture: if year is given as 2 digits, then this is the furthest in the future the year
  |   | 
| − | 	  is allowed to be (# of years from now). NOTE: Should be tested with current dates after 2050
  |   | 
| − | 	  (or between 1950 and 1999) to make sure it doesn't allow a year too far in the past.
  |   | 
| − |       OUTPUT: EXP as a DateTime object
  |   | 
| − |     */
  |   | 
| − |     public function ExpDate($iMaxFuture=50) {
  |   | 
| − | 	$strExp = $this->Exp()->Value();
  |   | 
| − | 	return clsCustCards::ExpDate($strExp,$iMaxFuture);
  |   | 
| − |     }
  |   | 
| − |     public function ExpDateSQL() {
  |   | 
| − | 	return clsCustCards::ExpDateSQL($this->Exp()->Value());
  |   | 
| − |     }
  |   | 
| − |     public function CVV() {
  |   | 
| − | 	if ($this->Exists('cvv')) {
  |   | 
| − | 	    return $this->Node('cvv')->Value();
  |   | 
| − | 	} else {
  |   | 
| − | 	    return NULL;
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       ACTION: Return a description of the payment in a safe format
  |   | 
| − | 	(incomplete credit card number)
  |   | 
| − |       TO DO: Allow for payment types other than credit card
  |   | 
| − |     */
  |   | 
| − |     public function SafeDisplay() {
  |   | 
| − | 	$out = clsCustCards::SafeDescr_Long($this->Num()->Value(),$this->Exp()->Value());
  |   | 
| − | 	$out .= '<br>'.$this->Addr()->AsText("\n<br>");
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | /*----
  |   | 
|   |    RULES:  |   |    RULES:  | 
| − |      * a Person must have two mailing addresses: one for shipping, one for payment  | + |      * If a country's code isn't found in arDesc, it defaults to International  | 
| − |     * these addresses may be the same
  | + |       ...there's got to be a better way to do this...  | 
| − |     * a Person must have an email address
  |   | 
| − |     * a Person may have a phone number
  |   | 
| − |     * email and phone are not tied to mailing address, just to Person
  |   | 
| − |     * a Person may (for now, must) have a card
  |   | 
| − |     * a card has one mailing address
  |   | 
|   | */  |   | */  | 
| − | class clsPerson extends clsContactFolder {  | + | class clsShipZone {  | 
| − | /*
  | + |      static private $arDesc = array(  | 
| − |     public $Contact;
  | + |       'CA' => 'Canada',  | 
| − |     public $Payment;
  | + |       'US' => 'United States',  | 
| − | */
  | + |       'INT' => 'International',  | 
| − |     protected $strName;	// for forms	
  | + |       );  | 
| − |      private $strDescr;	// for text messages  | + |      // per-item adjustment factors  | 
| − |    | + |      static private $arItmFactors = array(  | 
| − |     public function __construct($iName,$iDescr) {
  | + | 	'US' => 1.0,  | 
| − | 	$this->strName = $iName;
  | + | 	'CA' => 2.0,  | 
| − | 	$this->strDescr = $iDescr;
  | + | 	'INT' => 4.0,  | 
| − |     }
  | + |       );  | 
| − |     public function Descr($iDescr=NULL) {
  | + |      // per-package adjustment factors  | 
| − | 	if (!is_null($iDescr)) {
  | + |      static private $arPkgFactors = array(	// there's got to be a better way to do this...  | 
| − | 	    $this->strDescr = $iDescr;
  | + | 	'US' => 1.0,  | 
| − | 	}
  | + | 	'CA' => 2.0,  | 
| − | 	return $this->strDescr;
  | + | 	'INT' => 4.0,  | 
| − |     }
  | + |        );  | 
| − |     public function CtrlName() {
  | + |     static private $arCountryCodes = array(  | 
| − | 	return $this->strName;
  | + | 	'united states'	=> 'US',  | 
| − |      }  | + | 	'canada'	=> 'CA',  | 
| − |      public function HasContact() {  | + | 	'australia'	=> 'AU',  | 
| − | 	return $this->Exists('contact');  | + |        );  | 
| − |     }
  |   | 
| − |     public function Contact() {
  |   | 
| − | 	return $this->Node('contact');  |   | 
| − |     }
  |   | 
| − |     public function Payment() {
  |   | 
| − | 	return $this->Node('payment');  |   | 
| − |     }
  |   | 
| − |      //=== EMAIL functions  |   | 
| − |      /*----  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-09-23 This totally wasn't working. Is now.
  |   | 
| − | 	  clsTreeNode could probably use some tidying.
  |   | 
| − |     */
  |   | 
| − |     public function HasEmail() {
  |   | 
| − | 	$out = FALSE;  |   | 
| − | 	if ($this->Exists('contact')) {
  |   | 
| − | 	    $objCont = $this->Contact();
  |   | 
| − | 	    if ($objCont->Exists('email')) {
  |   | 
| − | 		$objEmail = $objCont->Node('email');
  |   | 
| − | 		$out = $objEmail->Filled();
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |        HISTORY:  |   | 
| − | 	2011-09-23 written to simplify things
  |   | 
| − |     */
  |   | 
| − |     public function GetEmail() {
  |   | 
| − | 	if ($this->HasEmail()) {
  |   | 
| − | 	    $out = $this->Contact()->Email()->Value();
  |   | 
| − | 	} else {
  |   | 
| − | 	    $out = NULL;
  |   | 
| − | 	}
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |    |   | 
| − |     //=== PHONE functions
  |   | 
| − |     public function HasPhone() {
  |   | 
| − | 	$out = FALSE;  |   | 
| − | 	if ($this->Exists('contact')) {  |   | 
| − | 	    $objCont = $this->Contact();
  |   | 
| − | 	    if ($objCont->Exists('phone')) {
  |   | 
| − | 		$objPhone = $objCont->Node('phone');
  |   | 
| − | 		$out = $objPhone->Filled();
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |        HISTORY:  |   | 
| − | 	2011-09-23 written to simplify things
  |   | 
| − |     */
  |   | 
| − |     public function GetPhone() {
  |   | 
| − | 	if ($this->HasPhone()) {
  |   | 
| − | 	    $out = $this->Contact()->Phone()->Value();
  |   | 
| − | 	} else {
  |   | 
| − | 	    $out = NULL;
  |   | 
| − | 	}
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
|   |  |   |  | 
| − |      //=== CCARD functions  | + |      private $strAbbr;  | 
| − |     public function HasCCard() {
  |   | 
| − | 	$out = $this->Exists('payment');
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-09-23 written to simplify things
  |   | 
| − |       NOTE: slightly different from email/phone;
  |   | 
| − | 	returns object, not string
  |   | 
| − |     */
  |   | 
| − |     public function GetCCard() {
  |   | 
| − | 	if ($this->HasCCard()) {
  |   | 
| − | 	    return $this->Payment();
  |   | 
| − | 	} else {
  |   | 
| − | 	    return NULL;
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
|   |  |   |  | 
| − |     //===
  |   | 
| − |     /*----
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-11-21 fixed a bug where $objAddr was being called as a function,
  |   | 
| − | 	  so presumably this method was not working before now.
  |   | 
| − |     */
  |   | 
| − |     public function Addrs() {
  |   | 
| − | 	$arOut = NULL;
  |   | 
| − | 	if ($this->HasCCard()) {
  |   | 
| − | 	    $arOut['card'] = $this->Payment()->Addr();
  |   | 
| − | 	}
  |   | 
| − | 	$objAddr = $this->Contact()->Addr();
  |   | 
| − | 	if (!is_null($objAddr)) {
  |   | 
| − | 	    $arOut['ship'] = $objAddr;
  |   | 
| − | 	}
  |   | 
| − | 	return $arOut;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       PURPOSE: The scripted version of DoResolve. EXPERIMENTAL/DEBUGGING
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-09-21 started
  |   | 
| − | 	2011-10-08 mostly working
  |   | 
| − | 	2011-11-21 Script_DataRow was being called as a function instead of new object; fixed.
  |   | 
| − | 	  Completed other identifier name changes.
  |   | 
| − |       INPUT:
  |   | 
| − | 	<form> data:
  |   | 
| − | 	  depends on which contact data is being resolved; there can be 1 or 2 different sets
  |   | 
| − | 	$iOrder - changes to be made to the order record
  |   | 
| − | 	  This function does not actually execute this, but only modifies it.
  |   | 
| − | 	  It is up to the caller to execute it *after* calling this.
  |   | 
| − | 	$iContact - ID of existing contact record to update, or NULL if a new one should be created
  |   | 
| − |     */
  |   | 
| − | /* 2011-11-29 currently trying to obsolete this
  |   | 
| − |     public function DoResolve_Script(Script_SQL_DataRow_Command $iOrder, $iShipSelf=FALSE) {
  |   | 
| − | 	global $wgRequest;
  |   | 
| − | 
  |   | 
| − | 	$objPerson = $this;
  |   | 
| − | 	$strStat = NULL;
  |   | 
| − | 
  |   | 
| − | 	$strFormName = $objPerson->FormName();
  |   | 
| − | 	$strChoice = $wgRequest->GetText($strFormName);
  |   | 
| − | 
  |   | 
| − | 	$doNew = FALSE;
  |   | 
| − | 	$doUpd = FALSE;
  |   | 
| − | 
  |   | 
| − | 	if ($strChoice == 'new') {
  |   | 
| − | 	    // creating completely new contact record
  |   | 
| − | 	    $doNew = TRUE;
  |   | 
| − | 	    $strStat = '<b>Creating</b> new contact record(s)';
  |   | 
| − | 	    $idContact = NULL;
  |   | 
| − | 	} elseif (is_numeric($strChoice)) {
  |   | 
| − | 	    // updating existing contact record (possibly creating new detail records)
  |   | 
| − | 	    $doUpd = TRUE;
  |   | 
| − | 	    $idContact = (int)$strChoice;
  |   | 
| − | 	    $strStat = '<b>Using</b> contact ID='.$idContact;
  |   | 
| − | 	} elseif (empty($strChoice)) {
  |   | 
| − | 	    $strStat = 'Please choose which contact to use.';
  |   | 
| − | 	} else {
  |   | 
| − | 	    throw new exception('Unexpected value for contact ID: ['.$strChoice.']');
  |   | 
| − | 	}
  |   | 
| − | 
  |   | 
| − | 	$acts = new Script_Script();
  |   | 
| − | 	  $acts->Add(new Script_Status($strStat));	// display the status message
  |   | 
| − | 
  |   | 
| − | 	if ($doNew || $doUpd) {
  |   | 
| − | 	    // get some data for later use
  |   | 
| − | 	    $strEmail = $objPerson->GetEmail();
  |   | 
| − | 	    $strPhone = $objPerson->GetPhone();
  |   | 
| − | 	    $objCCard = $objPerson->GetCCard();
  |   | 
| − | 
  |   | 
| − | 	    // get some objects for later use
  |   | 
| − | 	    $objDB = $iOrder->Engine();
  |   | 
| − | 	    $tblEmails = $objDB->CustEmails();
  |   | 
| − | 	    $tblPhones = $objDB->CustPhones();
  |   | 
| − | 	    $tblCCards = $objDB->CustCards();
  |   | 
| − | 
  |   | 
| − |     
  |   | 
| − | 	    if ($doNew) {
  |   | 
| − | 		$objContact = $objPerson->Contact();
  |   | 
| − | 		if (is_object($objContact)) {
  |   | 
| − | 		    // data integrity check successful
  |   | 
| − | 
  |   | 
| − | 		    $objAddr = $objContact->Addr();
  |   | 
| − | 		    if (is_null($objAddr)) {
  |   | 
| − | 			$acts->Add(new Script_Status('No address info'));
  |   | 
| − | 		    } else {
  |   | 
| − | 			// we have some address data to record, so record it
  |   | 
| − | 
  |   | 
| − | 			$actCust = $objDB->Custs()->Make_fromCartAddr_SQL($objAddr);
  |   | 
| − | 			$acts->Add(new Script_Status('ADDING CONTACT ADDRESS (check this!)'));
  |   | 
| − | 			$acts->Add($actCust,'cust');
  |   | 
| − | 
  |   | 
| − | 			// there needs to be a less script-structure-dependent way to do this:
  |   | 
| − | 			//$actCustIns = $actCust->Trial();	// get the insert action
  |   | 
| − | 			$actCustIns = $actCust->Get_byName('cust.ins',TRUE);
  |   | 
| − | 			$actCustXfer = $actCust->Get_byName('cust.id.xfer',TRUE);
  |   | 
| − | 
  |   | 
| − | 			// make sure email information is saved
  |   | 
| − | 			if (!is_null($strEmail)) {
  |   | 
| − | 
  |   | 
| − | 			    // CHANGE TO Make_Script()
  |   | 
| − | 
  |   | 
| − | 			    $actCustXfer->Add($tblEmails->Script_forAdd($strEmail, $actCustIns),'cust.email');
  |   | 
| − | 			}
  |   | 
| − | 			// make sure phone information is saved
  |   | 
| − | 			if (!is_null($strPhone)) {
  |   | 
| − | 
  |   | 
| − | 			    // CHANGE TO Make_Script()
  |   | 
| − | 
  |   | 
| − | 			    $actCustXfer->Add($tblPhones->Script_forAdd($strPhone, $actCustIns),'cust.phone');
  |   | 
| − | 			}
  |   | 
| − | 			// make sure credit card information is saved
  |   | 
| − | 			if (!is_null($objCCard)) {
  |   | 
| − | 			    $actAddr = $actCust->Get_byName('cust.addr',TRUE);
  |   | 
| − | 			    $actCCard = $tblCCards->Make_Script($idCust, $objCCard);
  |   | 
| − | 			    $actCustXfer->Add($actCCard,'cust.card');
  |   | 
| − | 			    //$actCustXfer->Add($tblCCards->Script_forAdd($objCCard, $actCustIns, $actAddr),'cust.card');
  |   | 
| − | 			    //$actFill_ID_toCard = new Script_SQL_Use_ID($actAddr,$arCardCreate,'ID_Cust');
  |   | 
| − | 			}
  |   | 
| − | 		    }
  |   | 
| − | 
  |   | 
| − | 		} else {
  |   | 
| − | 		    throw new exception('Person object has no Contact object.');
  |   | 
| − | 		}
  |   | 
| − | 	    }
  |   | 
| − | 
  |   | 
| − | 	    if ($doUpd) {
  |   | 
| − | 		$idCust = (int)$strChoice;
  |   | 
| − | 		$strStat .= 'resolved as contact ID='.$idCust;
  |   | 
| − | 
  |   | 
| − | 		$objAddrs = $objPerson->Addrs();	// list of addresses from cart data
  |   | 
| − | 
  |   | 
| − | echo $objPerson->DumpHTML(); die();
  |   | 
| − | 
  |   | 
| − | 		// create records for all names (will be either 1 or 2) associated with the order
  |   | 
| − | 		$tblNames = $objDB->CustNames();	// customer names table
  |   | 
| − | 		$tblAddrs = $objDB->CustAddrs();	// customer addresses table
  |   | 
| − | 		$out = NULL;
  |   | 
| − | 		if (!is_null($objAddrs)) {
  |   | 
| − | 
  |   | 
| − | 		    foreach ($objAddrs as $type => $obj) {
  |   | 
| − | 
  |   | 
| − | 			if (!is_object($obj)) {
  |   | 
| − | 			    throw new exception('value for '.$type.' is not an object.');
  |   | 
| − | 			}
  |   | 
| − | 
  |   | 
| − | 			$this->ImportContact($acts,$type,$obj);
  |   | 
| − | /*
  |   | 
| − | 			$acts->Add(new Script_Status('CHECKING '.$type));
  |   | 
| − | 
  |   | 
| − | 			$strName = $obj->Name()->Value();
  |   | 
| − | 			$strKey = strtolower($strName);
  |   | 
| − | 
  |   | 
| − | 			$acts->Add(new Script_Status('ADDING NAME: '.$strName));
  |   | 
| − | 
  |   | 
| − | 			$actOrdScratch = new Script_RowObj_scratch('scratch order');
  |   | 
| − | 			// ^ this will be copied to order update action ($iOrder) after name & address are set
  |   | 
| − | 
  |   | 
| − | 			// only add names that are different
  |   | 
| − | 			//if (array_key_exists($strKey,$arNames)) {
  |   | 
| − | 			$objName = $tblNames->Find($strKey,$idCust);
  |   | 
| − | 			if ($objName->HasRows()) {
  |   | 
| − | 			    $objName->FirstRow();
  |   | 
| − | 			    $id = $objName->KeyValue();
  |   | 
| − | 			    $acts->Add(new Script_Status('SAME as existing name ID='.$id));
  |   | 
| − | 			    $actOrdScratch->Value('name.'.$strFormName,$id);
  |   | 
| − | 			} else {
  |   | 
| − | 			    $arAct = $tblNames->Create_SQL($idCust,$strName);
  |   | 
| − | 			    $act = new Script_Tbl_Insert($tblNames,$arAct);
  |   | 
| − | 			    $acts->Add(new Script_SQL_Use_ID($act,$actOrdScratch,'name'));
  |   | 
| − | 			}
  |   | 
| − | 
  |   | 
| − | 			$strKey = $obj->AsSearchable();
  |   | 
| − | 			$acts->Add(new Script_Status('ADDING ADDRESS: '.$strKey));
  |   | 
| − | 
  |   | 
| − | 			$acts->Add($tblAddrs->Make_Script($obj,$idCust,$actOrdScratch));
  |   | 
| − | 
  |   | 
| − | 			switch ($type) {
  |   | 
| − | 			  case 'card':
  |   | 
| − | 			    $sqlOrdContField = 'ID_Buyer';
  |   | 
| − | 			    $sqlOrdNameField = 'ID_NameBuyer';
  |   | 
| − | 			    $sqlOrdAddrField = NULL;
  |   | 
| − | 			    break;
  |   | 
| − | 			  case 'ship':
  |   | 
| − | 			    $sqlOrdContField = 'ID_Recip';
  |   | 
| − | 			    $sqlOrdNameField = 'ID_NameRecip';
  |   | 
| − | 			    $sqlOrdAddrField = 'ID_ContactAddrRecip';
  |   | 
| − | 			    break;
  |   | 
| − | 			}
  |   | 
| − | 			$arXl = array(
  |   | 
| − | 			  $sqlOrdAddrField	=> 'addr',
  |   | 
| − | 			  $sqlOrdNameField	=> 'name'
  |   | 
| − | 			  );
  |   | 
| − | 			$iOrder->Value($sqlOrdContField,$idCust);
  |   | 
| − | echo 'xfer:<pre>'.print_r($arXl,TRUE).'</pre>';
  |   | 
| − | echo 'ord scratch:<pre>'.print_r($actOrdScratch->Values(),TRUE).'</pre>';
  |   | 
| − | 			$acts->Add(new Script_Copy_Named($actOrdScratch,$iOrder,$arXl),'order.update.'.$strFormName);
  |   | 
| − | //echo '#1:<pre>'.print_r($iOrder->Values(),TRUE).'</pre>';
  |   | 
| − | 			
  |   | 
| − | 			// the name is just for readability/debugging; it is not used by code
  |   | 
| − | */
  |   | 
| − | /*
  |   | 
| − | 		    }
  |   | 
| − | 		}
  |   | 
| − | 
  |   | 
| − | 		// handle email address
  |   | 
| − | 		if (is_null($strEmail)) {
  |   | 
| − | 		    $acts->Add(new Script_Status('No email address given.'));
  |   | 
| − | 		} else {
  |   | 
| − | 		    // make new email record, unless an identical one exists:
  |   | 
| − | 		    $acts->Add(new Script_Status('Adding email '.$strEmail));
  |   | 
| − | 		    $act = $tblEmails->Make_Script($idCust, $strEmail);
  |   | 
| − | 		    $acts->Add($act);
  |   | 
| − | 		}
  |   | 
| − | 
  |   | 
| − | 		// handle phone number
  |   | 
| − | 		if (is_null($strPhone)) {
  |   | 
| − | 		    $acts->Add(new Script_Status('No phone number given.'));
  |   | 
| − | 		} else {
  |   | 
| − | 		    // make new phone record, unless an identical one exists:
  |   | 
| − | 		    $acts->Add(new Script_Status('Adding phone '.$strPhone));
  |   | 
| − | 		    // THIS FUNCTION HAS NOT BEEN WRITTEN YET -- reuse code from email class
  |   | 
| − | 		    $act = $tblPhones->Make_Script($idCust, $strPhone);
  |   | 
| − | 		    $acts->Add($act);
  |   | 
| − | 		}
  |   | 
| − | 
  |   | 
| − | 		// handle credit card information
  |   | 
| − | 		if (is_null($objCCard)) {
  |   | 
| − | 		    $acts->Add(new Script_Status('No credit card information given.'));
  |   | 
| − | 		    // currently, we're not expecting this, as ccard is required
  |   | 
| − | 		} else {
  |   | 
| − | 		    $acts->Add(new Script_Status('Adding ccard '.$objCCard->SafeDisplay()));
  |   | 
| − | 		    $act = $tblCCards->Make_Script($idCust, $objCCard);
  |   | 
| − | 		    $acts->Add($act);
  |   | 
| − | 
  |   | 
| − | 		    $actIns = $act->Get_byName('ccard.make',TRUE);
  |   | 
| − | 		    $actCopy = new Script_SQL_Use_ID($actIns,$iOrder,'ID_ChargeCard');
  |   | 
| − | 		    $acts->Add($actCopy);
  |   | 
| − | 		}
  |   | 
| − | 	    }
  |   | 
| − | /*
  |   | 
| − |   By this time, $iOrder should have data for updating these fields:
  |   | 
| − |     * ID_Buyer
  |   | 
| − |     * ID_Recip
  |   | 
| − |     * ID_NameBuyer
  |   | 
| − |     * ID_NameRecip
  |   | 
| − |     * ID_ContactAddrRecip
  |   | 
| − |     * ID_ChargeCard
  |   | 
| − | 
  |   | 
| − |   Add one more thing:
  |   | 
| − | */
  |   | 
| − | /*
  |   | 
| − | 	    $iOrder->Value('WhenPrepped','NOW()');	// mark the order as "prepped"
  |   | 
| − | //echo '#2:<pre>'.print_r($iOrder->Values(),TRUE).'</pre>';
  |   | 
| − | 	    $acts->Add($iOrder,'ord.upd');
  |   | 
| − | 	}
  |   | 
| − | 
  |   | 
| − | 	return $acts;
  |   | 
| − |     }
  |   | 
| − | */
  |   | 
| − | /* 2011-11-29 this can't possibly work -- $strFormName is used but never defined
  |   | 
| − |     protected function ImportContact(Script_Script $acts, $type, $obj) {
  |   | 
| − | 	$acts->Add(new Script_Status('CHECKING '.$type));
  |   | 
| − | 
  |   | 
| − | 	$strName = $obj->Name()->Value();
  |   | 
| − | 	$strKey = strtolower($strName);
  |   | 
| − | 
  |   | 
| − | 	$acts->Add(new Script_Status('ADDING NAME: '.$strName));
  |   | 
| − | 
  |   | 
| − | 	$actOrdScratch = new Script_RowObj_scratch('scratch order');
  |   | 
| − | 	// ^ this will be copied to order update action ($iOrder) after name & address are set
  |   | 
| − | 
  |   | 
| − | 	// only add names that are different
  |   | 
| − | 	//if (array_key_exists($strKey,$arNames)) {
  |   | 
| − | 	$objName = $tblNames->Find($strKey,$idCust);
  |   | 
| − | 	if ($objName->HasRows()) {
  |   | 
| − | 	    $objName->FirstRow();
  |   | 
| − | 	    $id = $objName->KeyValue();
  |   | 
| − | 	    $acts->Add(new Script_Status('SAME as existing name ID='.$id));
  |   | 
| − | 	    $actOrdScratch->Value('name.'.$strFormName,$id);
  |   | 
| − | 	} else {
  |   | 
| − | 	    $arAct = $tblNames->Create_SQL($idCust,$strName);
  |   | 
| − | 	    $act = new Script_Tbl_Insert($tblNames,$arAct);
  |   | 
| − | 	    $acts->Add(new Script_SQL_Use_ID($act,$actOrdScratch,'name'));
  |   | 
| − | 	}
  |   | 
| − | 
  |   | 
| − | 	$strKey = $obj->AsSearchable();
  |   | 
| − | 	$acts->Add(new Script_Status('ADDING ADDRESS: '.$strKey));
  |   | 
| − | 
  |   | 
| − | 	$acts->Add($tblAddrs->Make_Script($obj,$idCust,$actOrdScratch));
  |   | 
| − | 
  |   | 
| − | 	switch ($type) {
  |   | 
| − | 	  case 'card':
  |   | 
| − | 	    $sqlOrdContField = 'ID_Buyer';
  |   | 
| − | 	    $sqlOrdNameField = 'ID_NameBuyer';
  |   | 
| − | 	    $sqlOrdAddrField = NULL;
  |   | 
| − | 	    break;
  |   | 
| − | 	  case 'ship':
  |   | 
| − | 	    $sqlOrdContField = 'ID_Recip';
  |   | 
| − | 	    $sqlOrdNameField = 'ID_NameRecip';
  |   | 
| − | 	    $sqlOrdAddrField = 'ID_ContactAddrRecip';
  |   | 
| − | 	    break;
  |   | 
| − | 	}
  |   | 
| − | 	$arXl = array(
  |   | 
| − | 	  $sqlOrdAddrField	=> 'addr',
  |   | 
| − | 	  $sqlOrdNameField	=> 'name'
  |   | 
| − | 	  );
  |   | 
| − | 	$iOrder->Value($sqlOrdContField,$idCust);
  |   | 
| − | echo 'xfer:<pre>'.print_r($arXl,TRUE).'</pre>';
  |   | 
| − | echo 'ord scratch:<pre>'.print_r($actOrdScratch->Values(),TRUE).'</pre>';
  |   | 
| − | 	$acts->Add(new Script_Copy_Named($actOrdScratch,$iOrder,$arXl),'order.update.'.$strFormName);
  |   | 
| − | //echo '#1:<pre>'.print_r($iOrder->Values(),TRUE).'</pre>';
  |   | 
| − | 	    
  |   | 
| − | 	// the name is just for readability/debugging; it is not used by code
  |   | 
| − |     }
  |   | 
| − | */
  |   | 
| − | // this is the OLD, non-scripted version (it didn't work very well either)
  |   | 
| − |     public function DoResolve($iDB,$iContactID) {
  |   | 
| − | 	$objDB = $iDB;
  |   | 
| − | 	$objPerson = $this;
  |   | 
| − | 
  |   | 
| − | 	$strChoice = $iContactID;
  |   | 
| − | 
  |   | 
| − | 	if (!is_null($strChoice)) {
  |   | 
| − | 	    $strStat = $objPerson->Descr().': ';
  |   | 
| − | 
  |   | 
| − | 	    if ($strChoice == 'new') {
  |   | 
| − | 		$doNew = TRUE;
  |   | 
| − | 		$doAdd = FALSE;
  |   | 
| − | 	    } else {
  |   | 
| − | 		$doNew = FALSE;
  |   | 
| − | 		if (is_numeric($strChoice)) {
  |   | 
| − | 		    $doAdd = TRUE;
  |   | 
| − | 		} else {
  |   | 
| − | 		    $doAdd = FALSE;
  |   | 
| − | 		}
  |   | 
| − | 	    }
  |   | 
| − | 
  |   | 
| − | 	    if ($doNew || $doAdd) {
  |   | 
| − | 		if ($doNew) {
  |   | 
| − | 		    $objContact = $objPerson->Contact();
  |   | 
| − | 		    if (is_object($objContact)) {
  |   | 
| − | 			// make new customer record, (shipping) address record, name record:
  |   | 
| − | 			$objCust = $objDB->Custs()->Make_fromCartAddr($objContact->Addr);
  |   | 
| − | 			$idCust = $objCust->ID;
  |   | 
| − | 			$strStat .= 'created new contact ID='.$idCust;
  |   | 
| − | 		    } else {
  |   | 
| − | 			throw new exception('Person object has no Contact object.');
  |   | 
| − | 		    }
  |   | 
| − | 		}
  |   | 
| − | 		if ($doAdd) {
  |   | 
| − | 		    $idCust = (int)$strChoice;
  |   | 
| − | 		    $strStat .= 'resolved as contact ID='.$idCust;
  |   | 
| − | 
  |   | 
| − | 		    $objAddrs = $objPerson->Addrs();
  |   | 
| − | 		    foreach ($objAddrs as $type => $obj) {
  |   | 
| − | 			$strStat .= ' / '.$type.':';
  |   | 
| − | 			$strName = $obj->Name()->Value();
  |   | 
| − | 			$arAction[] = 'making name record';
  |   | 
| − | 			$idName = $objDB->CustNames()->Create($idCust,$strName);
  |   | 
| − | 			$strStat .= ' ID_Name='.$idName;
  |   | 
| − | 			$arAction[] = 'making address record';
  |   | 
| − | 			$idAddr = $objDB->CustAddrs()->Create($idCust,$obj);
  |   | 
| − | 			$strStat .= ' ID_Addr='.$idAddr;
  |   | 
| − | 			$arAction[] =  'type=<b>'.$type.'</b> idName=<b>'.$idName.'</b> idAddr=<b>'.$idAddr.'</b>';
  |   | 
| − | 			switch ($type) {
  |   | 
| − | 			  case 'card':
  |   | 
| − | 			    $idNameBuyer = $idName;
  |   | 
| − | 			    $arOrdUpd['ID_NameBuyer'] = $idName;
  |   | 
| − | 			    break;
  |   | 
| − | 			  case 'ship':
  |   | 
| − | 			    $idNameRecip = $idName;
  |   | 
| − | 			    $idAddrRecip = $idAddr;
  |   | 
| − | 			    $arOrdUpd['ID_NameRecip'] = $idName;
  |   | 
| − | 			    $arOrdUpd['ID_ContactAddrRecip'] = $idAddr;
  |   | 
| − | 			    break;
  |   | 
| − | 			}
  |   | 
| − | 		    }
  |   | 
| − | 		    $strStat .= ' / ';
  |   | 
| − | 		}
  |   | 
| − | 		$this->SubValue('id',$idCust);	// 2011-09-22 what does this do? Where is it defined?
  |   | 
| − | 		//$arOrdUpd['ID_Cust'] = $idCust;
  |   | 
| − | 
  |   | 
| − | 		$arAction = NULL;
  |   | 
| − | 		$arUpdEv = NULL;
  |   | 
| − | 
  |   | 
| − | 		// make new email record, unless an identical one exists:
  |   | 
| − | 		if ($objPerson->HasEmail()) {
  |   | 
| − | 		    $strEmail = $objPerson->Contact()->Email()->Value();
  |   | 
| − | 		    $arAction[] = 'making email record for ['.$strEmail.']';
  |   | 
| − | 		    $idEmail = $objDB->CustEmails()->Create($idCust, $strEmail);
  |   | 
| − | /*
  |   | 
| − | 		    $objDB->LogEvent(
  |   | 
| − | 		      __METHOD__,
  |   | 
| − | 		      '|idCust='.$idCust.'|strEmail='.SQLValue($strEmail),
  |   | 
| − | 		      'Made email record; SQL='.SQLValue($sql),
  |   | 
| − | 		      '+EM',FALSE,FALSE);
  |   | 
| − | */
  |   | 
| − | 
  |   | 
| − | 		    $arEv = array(
  |   | 
| − | 		      'where'	=> __METHOD__,
  |   | 
| − | 		      'params'	=> '|idCust='.$idCust.'|strEmail='.SQLValue($strEmail),
  |   | 
| − | 		      'descr'	=> 'Made email record; SQL='.SQLValue($sql),
  |   | 
| − | 		      'code'	=> '+EM'
  |   | 
| − | 		      );
  |   | 
| − | 		    $arUpdEv[] = $arEv;
  |   | 
| − | 
  |   | 
| − | 		    /*
  |   | 
| − | 		    // 2009-11-28 at present, we don't attach specific email or phone IDs to orders or contacts
  |   | 
| − | 		    $arOrdUpd['ID_Email'] = $idEmail;
  |   | 
| − | 		    $strStat .= ' ID_Email='.$idEmail;
  |   | 
| − | 		    */
  |   | 
| − | 		}
  |   | 
| − | 		// make new phone record, unless an identical one exists:
  |   | 
| − | 		if ($objPerson->HasPhone()) {
  |   | 
| − | 		    $strPhone = $objPerson->Contact->Phone()->Value();
  |   | 
| − | 		    $arAction[] = 'making phone record for ['.$strPhone.']';
  |   | 
| − | 		    $idPhone = $objDB->CustPhones()->Create($idCust, $strPhone);
  |   | 
| − | /*
  |   | 
| − | 		    $objDB->LogEvent(
  |   | 
| − | 		      __METHOD__,
  |   | 
| − | 		      '|idCust='.$idCust.'|strPhone='.SQLValue($strPhone),
  |   | 
| − | 		      'Made phone record; SQL='.SQLValue($sql),
  |   | 
| − | 		      '+PH',FALSE,FALSE);
  |   | 
| − | // ($iWhere,$iParams,$iDescr,$iCode,$iIsError,$iIsSevere
  |   | 
| − | */
  |   | 
| − | 		    $arEv = array(
  |   | 
| − | 		      'where'	=> __METHOD__,
  |   | 
| − | 		      'params'	=> '|idCust='.$idCust.'|strPhone='.SQLValue($strPhone),
  |   | 
| − | 		      'descr'	=> 'Made phone record; SQL='.SQLValue($sql),
  |   | 
| − | 		      'code'	=> '+PH'
  |   | 
| − | 		      );
  |   | 
| − | 		    $arUpdEv[] = $arEv;
  |   | 
| − | 		    /*
  |   | 
| − | 		    // 2009-11-28 at present, we don't attach specific email or phone IDs to orders or contacts
  |   | 
| − | 		    $arOrdUpd['ID_Phone'] = $idPhone;
  |   | 
| − | 		    $strStat .= ' ID_Phone='.$idPhone;
  |   | 
| − | 		    */
  |   | 
| − | 		}
  |   | 
| − | 		// make new ccard record, unless an identical one exists:
  |   | 
| − | 		if ($objPerson->HasCCard()) {
  |   | 
| − | 		    $arAction[] = 'making ccard record';
  |   | 
| − | 		    $idCard = $objDB->CustCards()->Create($idCust, $objPerson->Payment());
  |   | 
| − | 		    $objDB->LogEvent(
  |   | 
| − | 		      __METHOD__,
  |   | 
| − | 		      '|idCust='.$idCust,
  |   | 
| − | 		      'Made ccard record; SQL='.SQLValue($sql),
  |   | 
| − | 		      '+CC',FALSE,FALSE);
  |   | 
| − | 		    assert('!empty($idCard) /* type='.get_class($objDB->CustCards()).' */');
  |   | 
| − | 		    $arOrdUpd['ID_ChargeCard'] = $idCard;
  |   | 
| − | 		    $strStat .= ' ID_ChargeCard='.$idCard;
  |   | 
| − | 		}
  |   | 
| − | // if customer is shipper, write ID_Name and ID_Addr back to customer record:
  |   | 
| − | 		if (isset($idNameRecip)) {
  |   | 
| − | 		    $arAction[] = 'updating contact with name/addr';
  |   | 
| − | 		    $arUpd['ID_Name'] = $idNameRecip;
  |   | 
| − | 		    $arUpd['ID_Addr'] = $idAddrRecip;
  |   | 
| − | 		    $objDB->Custs()->Update($arUpd,'ID='.$idCust);
  |   | 
| − | 		} else {
  |   | 
| − | 		    $arAction[] = 'recipient details not in this object';
  |   | 
| − | 		}
  |   | 
| − | 	    } else {
  |   | 
| − | 		// log invalid input
  |   | 
| − | 		$strStat = 'Could not resolve - invalid choice "'.$strChoice.'".';
  |   | 
| − | 	    }
  |   | 
| − | 	} else {
  |   | 
| − | 	    $strStat = 'No contact specified for resolution (how did we even get here?).';
  |   | 
| − | 	}
  |   | 
| − | 	
  |   | 
| − | 	$arUpdOrd['upd-ord'] = $arOrdUpd;
  |   | 
| − | 	$arUpdOrd['stat'] = $strStat;
  |   | 
| − | 
  |   | 
| − | 	$arOut['status'] = $strStat;
  |   | 
| − | 	$arOut['action'] = $arAction;
  |   | 
| − | 	$arOut['upd.ord'] = $arUpdOrd;
  |   | 
| − | 
  |   | 
| − | 	return $arOut;
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | /*==================
  |   | 
| − |  CLASS: clsShipZone
  |   | 
| − |  PURPOSE: shipping zone functions
  |   | 
| − | */
  |   | 
| − | class clsShipZone {
  |   | 
|   |      public function Abbr($iAbbr=NULL) {  |   |      public function Abbr($iAbbr=NULL) {  | 
| − | 
  |   | 
|   | 	if (!is_null($iAbbr)) {  |   | 	if (!is_null($iAbbr)) {  | 
|   | 	    $this->strAbbr = $iAbbr;  |   | 	    $this->strAbbr = $iAbbr;  | 
| Line 1,369: | 
Line 163: | 
|   | 	return $this->strAbbr;  |   | 	return $this->strAbbr;  | 
|   |      }  |   |      }  | 
| − |      public function Text() {  | + |      public function Set_fromName($iName) {  | 
| − | 	global $listShipListDesc;  | + | 	$strLC = strtolower($iName);  | 
| − |    | + | 	if (array_key_exists($strLC,self::$arCountryCodes)) {  | 
| − | 	return $listShipListDesc[$this->Abbr()];  | + | 	    $this->strAbbr = self::$arCountryCodes[$strLC];  | 
|   | + | 	} else {  | 
|   | + | 	    echo 'Country ['.$iName.'] not found in list.';  | 
|   | + | 	    throw new exception('Internal error: unknown country requested.');  | 
|   | + | 	}  | 
|   | + |     }  | 
|   | + |     public function Text() {	// should be Name()  | 
|   | + | 	return self::$arDesc[$this->Abbr()];  | 
|   |      }  |   |      }  | 
|   |         |   |         | 
|   |      public function hasState() {  |   |      public function hasState() {  | 
|   | 	switch ($this->Abbr()) {  |   | 	switch ($this->Abbr()) {  | 
|   | + | 	  case 'AU':	return TRUE;	break;  | 
|   | + | 	  case 'CA':	return TRUE;	break;  | 
|   | 	  case 'US':	return TRUE;	break;  |   | 	  case 'US':	return TRUE;	break;  | 
| − | 	  case 'CA':	return TRUE;	break;
  |   | 
|   | 	  default:	return FALSE;	break;  |   | 	  default:	return FALSE;	break;  | 
|   | 	}  |   | 	}  | 
| Line 1,384: | 
Line 186: | 
|   |      public function StateLabel() {  |   |      public function StateLabel() {  | 
|   | 	switch ($this->Abbr()) {  |   | 	switch ($this->Abbr()) {  | 
|   | + | 	  case 'AU':	return 'State/Territory'; break;  | 
|   | + | 	  case 'CA':	return 'Province';	break;  | 
|   | 	  case 'US':	return 'State';		break;  |   | 	  case 'US':	return 'State';		break;  | 
| − | 	  case 'CA':	return 'Province';	break;
  |   | 
|   | 	  default:	return 'County/Province'; break;  |   | 	  default:	return 'County/Province'; break;  | 
|   | 	}  |   | 	}  | 
| Line 1,406: | 
Line 209: | 
|   |      }  |   |      }  | 
|   |      public function ComboBox() {  |   |      public function ComboBox() {  | 
| − | 	global $listShipListDesc;
  |   | 
| − | 
  |   | 
|   | 	$strZoneCode = $this->Abbr();  |   | 	$strZoneCode = $this->Abbr();  | 
|   | 	$out = '<select name="ship-zone">';  |   | 	$out = '<select name="ship-zone">';  | 
| − | 	foreach ($listShipListDesc as $key => $descr) {  | + | 	foreach (self::$arDesc as $key => $descr) {  | 
|   | //$dest (keys(%listShipListDesc)) {  |   | //$dest (keys(%listShipListDesc)) {  | 
|   | 		$strZoneDesc = $descr;  |   | 		$strZoneDesc = $descr;  | 
| Line 1,422: | 
Line 223: | 
|   | 	}  |   | 	}  | 
|   | 	$out .= '</select>';  |   | 	$out .= '</select>';  | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | 
  |   | 
| − | 
  |   | 
| − | // ShopCart
  |   | 
| − | class clsShopCarts extends clsTable {
  |   | 
| − |     const TableName='shop_cart';
  |   | 
| − | 
  |   | 
| − |     public function __construct($iDB) {
  |   | 
| − | 	parent::__construct($iDB);
  |   | 
| − | 	  $this->Name(self::TableName);
  |   | 
| − | 	  $this->KeyName('ID');
  |   | 
| − | 	  $this->ClassSng('clsShopCart');
  |   | 
| − | 	  $this->ActionKey('cart');
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | class clsShopCart extends clsDataSet {
  |   | 
| − |     public $objShipZone;
  |   | 
| − |     private $arDataItem;
  |   | 
| − |     protected $objOrder;
  |   | 
| − | 
  |   | 
| − |     protected $hasDetails;	// customer details have been loaded?
  |   | 
| − |     protected $objDataTree;
  |   | 
| − |     protected $objAddrShip;
  |   | 
| − |     protected $objAddrCard;
  |   | 
| − |     protected $objContDest;
  |   | 
| − |     protected $objContCust;
  |   | 
| − |     protected $objShip;
  |   | 
| − |     protected $objCust;
  |   | 
| − | 
  |   | 
| − |     public function __construct(clsDatabase $iDB=NULL, $iRes=NULL, array $iRow=NULL) {
  |   | 
| − | 	parent::__construct($iDB,$iRes,$iRow);
  |   | 
| − | 	$this->objShipZone = new clsShipZone();
  |   | 
| − | 	$this->hasDetails = FALSE;
  |   | 
| − |     }
  |   | 
| − |     public function InitNew($iSess) {
  |   | 
| − | 	$this->ID = 0;
  |   | 
| − | 	$this->WhenCreated = NULL;	// not created until saved
  |   | 
| − | 	$this->WhenViewed = NULL;
  |   | 
| − | 	$this->WhenUpdated = NULL;
  |   | 
| − | 	$this->WhenOrdered = NULL;
  |   | 
| − | 	$this->ID_Sess = $iSess;
  |   | 
| − | 	$this->ID_Order = NULL;
  |   | 
| − | 	$this->ID_Cust = NULL;
  |   | 
| − |     }
  |   | 
| − |     /*====
  |   | 
| − |       BLOCK: EVENT HANDLING
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-03-27 copied from VbzAdminCustCard to VbzAdminCart
  |   | 
| − | 	  Then moved from VbzAdminCart to clsShopCart
  |   | 
| − |     */
  |   | 
| − |     protected function Log() {
  |   | 
| − | 	if (!is_object($this->logger)) {
  |   | 
| − | 	    $this->logger = new clsLogger_DataSet($this,$this->objDB->Events());
  |   | 
| − | 	}
  |   | 
| − | 	return $this->logger;
  |   | 
| − |     }
  |   | 
| − |     public function StartEvent(array $iArgs) {
  |   | 
| − | 	return $this->Log()->StartEvent($iArgs);
  |   | 
| − |     }
  |   | 
| − |     public function FinishEvent(array $iArgs=NULL) {
  |   | 
| − | 	return $this->Log()->FinishEvent($iArgs);
  |   | 
| − |     }
  |   | 
| − |     public function EventListing() {
  |   | 
| − | 	return $this->Log()->EventListing();
  |   | 
| − |     }
  |   | 
| − |     // specialized event logging (deprecated)
  |   | 
| − |     public function LogEvent($iCode,$iDescr,$iUser=NULL) {
  |   | 
| − | 	global $vgUserName;
  |   | 
| − | 
  |   | 
| − | 	$strUser = is_null($iUser)?$vgUserName:$iUser;
  |   | 
| − | 	$this->objDB->CartLog()->Add($this,$iCode,$iDescr,$strUser);
  |   | 
| − |     }
  |   | 
| − |     //====
  |   | 
| − |     /*----
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-12-31 Created so placed orders do not get "stuck" in user's browser
  |   | 
| − | 	2011-02-07 Doesn't work; same cart still comes up (though at least it generates a new order...
  |   | 
| − | 	  but it pulls up all the same contact info)
  |   | 
| − | 	2011-03-27 Changed flag from ID_Order to WhenOrdered OR WhenVoided, because we don't want to have to clear
  |   | 
| − | 	  ID_Order anymore. Carts should retain their order ID.
  |   | 
| − |     */
  |   | 
| − |     public function IsLocked() {
  |   | 
| − | 	return $this->IsOrdered() || $this->IsVoided();
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: TRUE if the cart has been converted to an order
  |   | 
| − |       USED BY: $this->IsLocked() and (something)->IsUsable()
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-03-27 written for improved handling of cart status at checkout
  |   | 
| − |     */
  |   | 
| − |     public function IsOrdered() {
  |   | 
| − | 	return !(is_null($this->WhenOrdered));
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: TRUE if the cart has been discarded (voided)
  |   | 
| − |       USED BY: $this->IsLocked() and (something)->IsUsable()
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-03-27 written for improved handling of cart status at checkout
  |   | 
| − |     */
  |   | 
| − |     public function IsVoided() {
  |   | 
| − | 	return !(is_null($this->WhenVoided));
  |   | 
| − |     }
  |   | 
| − | // == GENERAL DATA UTILITY FUNCTIONS
  |   | 
| − |     public function GetDataItem($iType) {
  |   | 
| − | 	global $sql;	// for debugging
  |   | 
| − | 
  |   | 
| − | 	if (isset($this->arDataItem[$iType])) {
  |   | 
| − | 	    return $this->arDataItem[$iType];
  |   | 
| − | 	} else {
  |   | 
| − | 	    $sqlType = $this->objDB->SafeParam($iType);
  |   | 
| − | 	    $sql = 'SELECT Val FROM '.KST_CART_DATA.' WHERE (ID_Cart='.$this->ID.') AND (Type="'.$sqlType.'");';
  |   | 
| − | 
  |   | 
| − | 	    $objItem = $this->objDB->DataSet($sql);
  |   | 
| − | 	    if ($objItem->HasRows()) {
  |   | 
| − | 		$objItem->FirstRow();
  |   | 
| − | 		return $objItem->Val;
  |   | 
| − | 	    } else {
  |   | 
| − | 		return NULL;
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     public function PutDataItem($iType,$iVal,$iForce=FALSE) {
  |   | 
| − | 	  global $sql;	// for debugging
  |   | 
| − | 
  |   | 
| − | 	  $sqlType = $this->objDB->SafeParam($iType);
  |   | 
| − | 	  $sqlFilt = '(ID_Cart='.$this->ID.') AND (Type="'.$iType.'")';
  |   | 
| − | 	  if ($iVal == '') {
  |   | 
| − | 	      if ($iForce) {
  |   | 
| − | 		  // delete the entry
  |   | 
| − | 		  $sql = 'DELETE FROM '.KST_CART_DATA.' WHERE '.$sqlFilt;
  |   | 
| − | 		  $ok = $this->objDB->Exec($sql);
  |   | 
| − | 		  return $ok;
  |   | 
| − | 	      } else {
  |   | 
| − | 		  return FALSE;
  |   | 
| − | 	      }
  |   | 
| − | 	  } else {
  |   | 
| − | 	      $sqlVal = $this->objDB->SafeParam($iVal);
  |   | 
| − | 	      // check to see if item exists (UPDATE) or not (INSERT):
  |   | 
| − | 	      $objRows = $this->objDB->DataSet('SELECT Val FROM '.KST_CART_DATA.' WHERE '.$sqlFilt);
  |   | 
| − | 	      $qrows = $objRows->RowCount();
  |   | 
| − | 	      if ($qrows == 0) {
  |   | 
| − | 		  $sql = 'INSERT INTO '.KST_CART_DATA.' (ID_Cart,Type,Val) VALUES('.$this->ID.','.$iType.',"'.$sqlVal.'");';
  |   | 
| − | 		  $ok = $this->objDB->Exec($sql);
  |   | 
| − | 	      } elseif ($qrows == 1) {
  |   | 
| − | 		  $sql = 'UPDATE '.KST_CART_DATA.' SET Val="'.$sqlVal.'" WHERE '.$sqlFilt.';';
  |   | 
| − | 		  $ok = $this->objDB->Exec($sql);
  |   | 
| − | 	      } else {
  |   | 
| − | 		  // the db should never let this happen since ID_Cart+Type is the primary key, but leaving in as a sanity check for now.
  |   | 
| − | 		  $this->objDB->LogEvent('cart.data.write','type='.$iType.' val='.$sqlVal,$qrows.' rows match: too many','DUP',TRUE,TRUE);
  |   | 
| − | 		  $ok = FALSE;
  |   | 
| − | 	      }
  |   | 
| − | 	      return $ok;
  |   | 
| − | 	  }
  |   | 
| − |     }
  |   | 
| − |     public function DataItem($iType,$iVal=NULL,$iForce=FALSE) {
  |   | 
| − | 	if ($this->HasCart()) {
  |   | 
| − | 	    if (is_null($iVal)) {
  |   | 
| − | 		return $this->GetDataItem($iType);
  |   | 
| − | 	    } else {
  |   | 
| − | 		return $this->PutDataItem($iType,$iVal,$iForce);
  |   | 
| − | 	    }
  |   | 
| − | 	} else {
  |   | 
| − | 	    return NULL;
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − | // == STATUS
  |   | 
| − |     public function HasCart() {
  |   | 
| − | 	return $this->IsCreated();	// may use different criteria later on
  |   | 
| − |     }
  |   | 
| − |     public function HasSession() {
  |   | 
| − | 	$ok = FALSE;
  |   | 
| − | 	if ($this->HasField('ID_Sess')) {
  |   | 
| − | 	    if ($this->ID_Sess>0) {
  |   | 
| − | 		$ok = TRUE;
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	return $ok;
  |   | 
| − |     }
  |   | 
| − |     public function Session() {
  |   | 
| − | 	if ($this->HasSession()) {
  |   | 
| − | 	    $objSessions = $this->objDB->Sessions();
  |   | 
| − | 	    $objSess = $objSessions->GetItem($this->ID_Sess);
  |   | 
| − | 	    return $objSess;
  |   | 
| − | 	} else {
  |   | 
| − | 	    return NULL;
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     // DEPRECATED - use OrderObj()
  |   | 
| − |     public function Order() {
  |   | 
| − | 	return $this->OrderObj();
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: Order object
  |   | 
| − |     */
  |   | 
| − |     public function OrderObj() {
  |   | 
| − | 	$doGet = TRUE;
  |   | 
| − | 	if (isset($this->objOrder)) {
  |   | 
| − | 	    if ($this->objOrder->ID == $this->ID_Order) {
  |   | 
| − | 		$doGet = FALSE;
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	if ($doGet) {
  |   | 
| − | 	    $this->objOrder = $this->objDB->Orders()->GetItem($this->ID_Order);
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objOrder;
  |   | 
| − |     }
  |   | 
| − |     public function HasLines() {
  |   | 
| − | 	$objLines = $this->GetLines();
  |   | 
| − | 	if (is_null($objLines)) {
  |   | 
| − | 	    return FALSE;
  |   | 
| − | 	} else {
  |   | 
| − | 	    return $objLines->hasRows();
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     public function LineCount() {
  |   | 
| − | 	if ($this->HasLines()) {
  |   | 
| − | 	    return $this->objLines->RowCount();
  |   | 
| − | 	} else {
  |   | 
| − | 	    return 0;
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     public function GetLines($iRefresh=TRUE) {
  |   | 
| − | 	if ($iRefresh || (!isset($this->objLines))) {
  |   | 
| − | 	    if ($this->IsCreated()) {
  |   | 
| − | 		//$this->objLines = $this->objDB->CartLines()->GetData('(ID_Cart='.$this->ID.') AND (Qty>0)','clsShopCartLine');
  |   | 
| − | 		$this->objLines = $this->objDB->CartLines()->GetData('(ID_Cart='.$this->ID.') AND (Qty>0)');
  |   | 
| − | 	    } else {
  |   | 
| − | 		$this->objLines = NULL;
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objLines;
  |   | 
| − |     }
  |   | 
| − |     public function IsCreated() {
  |   | 
| − | 	return ($this->ID > 0);
  |   | 
| − | 	//return !is_null($this->ID);
  |   | 
| − | 	//return $this->hasField('ID') || isset($this->ID)
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: TRUE iff customer has any known email addresses
  |   | 
| − |     public function HasEmail() {
  |   | 
| − | 	$isShipCard = $this->DataItem(KSI_SHIP_IS_CARD);
  |   | 
| − | 	$isShipSelf = $this->DataItem(KSI_SHIP_TO_SELF);
  |   | 
| − | 	// $objCart->ContCustObj()->Email()->Value()
  |   | 
| − | 	if ($isShipSelf) {
  |   | 
| − | 	return $this->ContCustObj()->HasEmail();
  |   | 
| − |     }
  |   | 
| − |     public function EmailObj() {
  |   | 
| − |     }
  |   | 
| − | */
  |   | 
| − | // == FORM HANDLING STUFF
  |   | 
| − |     public function CheckData() {
  |   | 
| − | // check for buttons
  |   | 
| − | 	$doCheckout = isset($_POST['finish']);
  |   | 
| − | 	$isCart = (isset($_POST['recalc']) || $doCheckout);
  |   | 
| − | 	$isZoneSet = FALSE;
  |   | 
| − | // check for specific actions
  |   | 
| − | 	if (isset($_GET['action'])) {
  |   | 
| − | 	    $strDo = $_GET['action'];
  |   | 
| − | 	    switch ($strDo) {
  |   | 
| − | 	      case 'del':
  |   | 
| − | 		$intItem = 0+$_GET['item'];
  |   | 
| − | 		$this->GetLines();
  |   | 
| − | 		$this->objLines->Update(array('Qty'=>0),'ID_Item='.$intItem);
  |   | 
| − | 		$this->LogEvent('del','deleting from cart: ID '.$intItem);
  |   | 
| − | 		break;
  |   | 
| − | 	      case 'delcart';
  |   | 
| − | 		$this->LogEvent('clr','voiding cart');
  |   | 
| − | 		$this->ID = -1;
  |   | 
| − | 		$this->objSess->DropCart();
  |   | 
| − | 		break;
  |   | 
| − | 	    }
  |   | 
| − | 	} else {
  |   | 
| − | 	    foreach ($_POST as $key => $val) {
  |   | 
| − |     // check for added items:
  |   | 
| − | 		if (substr($key,0,4) == 'qty-') {
  |   | 
| − | 		    if (($val != '') && ($val != 0)) {
  |   | 
| − | 			$sqlCatNum = $this->objDB->SafeParam(substr($key,4));
  |   | 
| − | 			if ($isCart) {
  |   | 
| − | 			    // zero out all items, so only items in visible cart will be retained:
  |   | 
| − | 			    $this->ZeroAll();
  |   | 
| − | 			}
  |   | 
| − | 			$this->AddItem($sqlCatNum,$val);
  |   | 
| − | 		    }
  |   | 
| − | 		} elseif ($key == KSF_SHIP_ZONE) {
  |   | 
| − | //		    $custShipZone	= $this->GetFormItem(KSF_SHIP_ZONE);
  |   | 
| − | 		    $custShipZone	= $val;
  |   | 
| − | 		    $this->DataItem(KSI_SHIP_ZONE,$custShipZone);
  |   | 
| − | 		    $this->objShipZone->Abbr($custShipZone);
  |   | 
| − | 		    $isZoneSet = TRUE;
  |   | 
| − | 		}
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	if (!$isZoneSet) {
  |   | 
| − | 	    $this->objShipZone->Abbr($this->DataItem(KSI_SHIP_ZONE));
  |   | 
| − | 	}
  |   | 
| − | 	if ($doCheckout) {
  |   | 
| − | 	    $this->LogEvent('ck1','going to checkout');
  |   | 
| − | 	    $objSess = $this->Session();
  |   | 
| − | 	    //http_redirect(KWP_CHKOUT,array(KS_VBZCART_SESSION_KEY => $objSess->SessKey()));
  |   | 
| − | 	    //http_redirect(KWP_CHKOUT.'?'.KS_VBZCART_SESSION_KEY.'='.$objSess->SessKey());
  |   | 
| − | 	    http_redirect(KWP_CHKOUT);
  |   | 
| − | //	    http_redirect('https://ssl.vbz.net/phpinfo.php');
  |   | 
| − | 	    $this->LogEvent('ck2','sent redirect to checkout');
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     public function ZeroAll() {
  |   | 
| − | 	$this->Update(array('Qty'=>0),'ID_Cart='.$this->ID);
  |   | 
| − |     }
  |   | 
| − |     public function AddItem($iCatNum,$iQty) {
  |   | 
| − | 	$this->Build();	// make sure there's a record for the cart, get ID
  |   | 
| − | 	$objCartLines = $this->objDB->CartLines();
  |   | 
| − | 	$objCartLines->Add($this->ID,$iCatNum,$iQty);
  |   | 
| − | 	$this->LogEvent('add','adding to cart: cat# '.$iCatNum.' qty '.$iQty);
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |       ACTION:
  |   | 
| − | 	* make sure there is a cart record
  |   | 
| − | 	* update the quantity, if there is one
  |   | 
| − |     */
  |   | 
| − |     public function Build() {
  |   | 
| − | 	$id = $this->ID;
  |   | 
| − | 	if (empty($id)) {
  |   | 
| − | 	    $this->Create();
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     public function Create() {
  |   | 
| − | 	$sql =
  |   | 
| − | 	  'INSERT INTO `'.clsShopCarts::TableName.'` (WhenCreated,ID_Sess)'.
  |   | 
| − | 	  'VALUES(NOW(),'.$this->ID_Sess.');';
  |   | 
| − | 	$this->objDB->Exec($sql);
  |   | 
| − | 	$this->ID = $this->objDB->NewID('carts.create');
  |   | 
| − | 	$objSess = $this->objDB->Sessions()->GetCurrent();
  |   | 
| − | 	if (!is_object($objSess->Table)) {
  |   | 
| − | 	    throw new exception('Session object has no table for Cart ID='.$this->Value('ID'));
  |   | 
| − | 	}
  |   | 
| − | 	$objSess->SetCart($this->ID);
  |   | 
| − |     }
  |   | 
| − |     public function RenderHdr() {
  |   | 
| − | 	$out = "\n".'<!-- Cart ID='.$this->KeyValue().' | Session ID='.$this->ID_Sess.' -->';
  |   | 
| − | 	$out .= "\n<center><table class=border><tr><td><table class=cart><tr><td align=center valign=middle>";
  |   | 
| − | 	$out .= "\n<form method=post action='./'>";
  |   | 
| − | 	$out .= "\n<table class=cart-data>\n";
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     public function RenderFtr() {
  |   | 
| − | 	return KHT_CART_FTR;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       ACTION: Render the receipt in HTML
  |   | 
| − | 	Shows the order as generated from *cart* data, not what's in the order record.
  |   | 
| − | 	...except for the order number.
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-03-27 adapting this from clsOrder::RenderReceipt()
  |   | 
| − |     */
  |   | 
| − |     public function RenderReceipt() {
  |   | 
| − | 	$out = NULL;
  |   | 
| − | 
  |   | 
| − | 	$objCart = $this;
  |   | 
| − | 	$objOrd = $this->OrderObj();
  |   | 
| − | 
  |   | 
| − | 	// load contact data
  |   | 
| − | 	assert('is_object($objOrd);');
  |   | 
| − | 	if (($objOrd->ID == 0) || ($objCart->ID == 0)) {
  |   | 
| − | 	    throw new exception('Receipt has missing object: Order ID='.$this->Value('ID_Order').', Cart ID='.$objCart->KeyValue());
  |   | 
| − | 	}
  |   | 
| − | 	$objCart->GetDetailObjs();
  |   | 
| − | 	$objPay = $objCart->PersonCustObj()->Payment();
  |   | 
| − | 	$objAddrCard = $objCart->AddrCardObj();
  |   | 
| − | 	// the next line is a kluge which only works as long as payment is always ccard
  |   | 
| − | 	// it's also not clear why GetDetailObjs() isn't loading it properly
  |   | 
| − | 	$objPay->Node('addr', $objAddrCard);
  |   | 
| − | 
  |   | 
| − | 	$arVars = array(
  |   | 
| − | 	  'ord.num'	=> $objOrd->Number,
  |   | 
| − | 	  'timestamp'	=> date(KF_RCPT_TIMESTAMP),
  |   | 
| − | 	  'cart.id'	=> $objCart->ID,
  |   | 
| − | 	  'cart.detail'	=> $objCart->RenderConfirm(),
  |   | 
| − | 	  'ship.name'	=> $objCart->AddrShipObj()->Name()->Value(),
  |   | 
| − | 	  'ship.addr'	=> $objCart->AddrShipObj()->AsText("\n<br>"),
  |   | 
| − | 	  'pay.name'	=> $objPay->Addr()->Name()->Value(),
  |   | 
| − | 	  'pay.spec'	=> $objPay->SafeDisplay(),
  |   | 
| − | 	  'email.short'	=> 'orders-'.date('Y').'@vbz.net'
  |   | 
| − | 	  );
  |   | 
| − | 	$objStrTplt = new clsStringTemplate_array(NULL,NULL,$arVars);
  |   | 
| − | 	$objStrTplt->MarkedValue(KHT_RCPT_TPLT);
  |   | 
| − | 	$out = '<!-- ORDER ID: '.$objOrd->ID.' / CART ID from order: '.$objCart->ID.' -->';
  |   | 
| − | 	$out .= $objStrTplt->Replace();
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |       ACTION: Renders the order contents as plaintext, suitable for emailing
  |   | 
| − |     */
  |   | 
| − |     public function RenderOrder_Text() {
  |   | 
| − | /* NOT USED HERE
  |   | 
| − | 	$isShipCard = $this->DataItem(KSI_SHIP_IS_CARD);
  |   | 
| − | 	$isShipSelf = $this->DataItem(KSI_SHIP_TO_SELF);
  |   | 
| − | */
  |   | 
| − | // copy any needed constants over to variables for parsing:
  |   | 
| − | 	$ksShipMsg	= KSF_SHIP_MESSAGE;
  |   | 
| − | 	$ksfCustCardNum = KSF_CUST_CARD_NUM;
  |   | 
| − | 	$ksfCustCardExp = KSF_CUST_CARD_EXP;
  |   | 
| − | 
  |   | 
| − | // get non-address field data:
  |   | 
| − | 	$strCardNum = $this->DataItem(KSI_CUST_CARD_NUM);
  |   | 
| − | 	$strCardExp = $this->DataItem(KSI_CUST_CARD_EXP);
  |   | 
| − | 	$strCustShipMsg = $this->DataItem(KSI_SHIP_MESSAGE);
  |   | 
| − | 	$ftCustShipMsg = wordwrap($strCustShipMsg);
  |   | 
| − | 
  |   | 
| − | 	$out = '';
  |   | 
| − | 	$out .= "ITEMS ORDERED:\n";
  |   | 
| − | 
  |   | 
| − | 	$out .= $this->RenderCore_Text();
  |   | 
| − | 
  |   | 
| − | 	$this->doFixedCard = TRUE;
  |   | 
| − | 	$this->doFixedSelf = TRUE;
  |   | 
| − | 	$this->doFixedName = TRUE;
  |   | 
| − | 	$this->htmlBeforeAddress = '';
  |   | 
| − | 	$this->htmlBeforeContact = '';
  |   | 
| − | 
  |   | 
| − | 	$out .= "\n\nSHIP TO:\n";
  |   | 
| − | 	$out .= '  '.$this->AddrShipObj()->Name()->Value()."\n";
  |   | 
| − | 	$out .= '  '.$this->AddrShipObj()->AsText("\n  ");
  |   | 
| − | 	$out .= "\n";
  |   | 
| − | 	$out .= "\n  Email: ".$this->ContDestObj()->Email()->Value();
  |   | 
| − | 	$out .= "\n  Phone: ".$this->ContDestObj()->Phone()->Value();
  |   | 
| − | 	
  |   | 
| − | 	$out .= "\n\n  ";
  |   | 
| − | 	if (empty($strCustShipMsg)) {
  |   | 
| − | 	    $out .= "(No special instructions)";
  |   | 
| − | 	} else {
  |   | 
| − | 	    $out .= "Special Instructions:\n$ftCustShipMsg";
  |   | 
| − | 	}
  |   | 
| − | 	$out .= "\n\nPAYMENT:\n  ".clsCustCards::SafeDescr_Long($strCardNum,$strCardExp)."\n";
  |   | 
| − | 	$out .= '  '.$this->AddrCardObj()->Name()->Value()."\n";
  |   | 
| − | 	$out .= '  '.$this->AddrCardObj()->AsText("\n  ");
  |   | 
| − | 	$out .= "\n";
  |   | 
| − | 	$out .= "\n  Email: ".$this->ContCustObj()->Email()->Value();
  |   | 
| − | 	$out .= "\n  Phone: ".$this->ContCustObj()->Phone()->Value();
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     public function RenderCore($iAsForm) {
  |   | 
| − | 	$strZone = $this->objShipZone->Abbr();
  |   | 
| − | 	$shipMinCost = 0;
  |   | 
| − | 
  |   | 
| − | 	$out = '<tr>'
  |   | 
| − | 	  .'<th><big>cat #</big></th>'
  |   | 
| − | 	  .'<th><big>description</big></th>'
  |   | 
| − | 	  .'<th>price<br>each</th>'
  |   | 
| − | 	  .'<th><small>per-item<br>s/h ea.</small></th>'
  |   | 
| − | 	  .'<th>qty.</th>'
  |   | 
| − | 	  .'<th><small>purchase<br>line total</small></th>'
  |   | 
| − | 	  .'<th><small>per-item<br>s/h line total</small></th>'
  |   | 
| − | 	  .'<th>totals</th>'
  |   | 
| − | 	  .'<th><small>pkg s/h<br>min.</small></th>'
  |   | 
| − | 	  .'</tr>';
  |   | 
| − | 
  |   | 
| − | 	$rsLine = $this->objLines;
  |   | 
| − | 	while ($rsLine->NextRow()) {
  |   | 
| − | 	    if ($iAsForm) {
  |   | 
| − | 		$out .= $rsLine->RenderForm($this);
  |   | 
| − | 	    } else {
  |   | 
| − | 		$out .= $rsLine->RenderHtml($this);
  |   | 
| − | 	    }
  |   | 
| − | 	    if ($shipMinCost < $rsLine->ShipPkgDest) {
  |   | 
| − | 		    $shipMinCost = $rsLine->ShipPkgDest;
  |   | 
| − | 	    }
  |   | 
| − | 	    $intQty = $rsLine->Qty;
  |   | 
| − | 	    $this->CostTotalItem += $rsLine->CostItemQty;
  |   | 
| − | 	    $this->CostTotalShip += $rsLine->CostShipQty;
  |   | 
| − | 	}
  |   | 
| − | // save official totals for order creation:
  |   | 
| − | // TO DO: are CostTotalItem and CostTotalShip referenced anywhere else? Make them local if not.
  |   | 
| − | //	But if they are, then why isn't shipMinCost also a field?
  |   | 
| − | 	$this->DataItem(KSI_ITEM_TOTAL,$this->CostTotalItem);
  |   | 
| − | 	$this->DataItem(KSI_PER_ITEM_TOTAL,$this->CostTotalShip);
  |   | 
| − | 	$this->DataItem(KSI_PER_PKG_TOTAL,$shipMinCost);
  |   | 
| − | 
  |   | 
| − | 	$strTotalMerch = FormatMoney($this->CostTotalItem);
  |   | 
| − | 	$strItemsShip = FormatMoney($this->CostTotalShip);
  |   | 
| − | 	$strTotalItems = FormatMoney($this->CostTotalItem + $this->CostTotalShip);
  |   | 
| − | 	$strShipZone = $this->objShipZone->Text();
  |   | 
| − | 	$strShipDesc = $strShipZone.' s/h package cost:';
  |   | 
| − | 	$strShipPkg = FormatMoney($shipMinCost);
  |   | 
| − | 	$strTotalDesc = 'order total if shipping to '.$strShipZone.':';
  |   | 
| − | 	$strOrdTotal = FormatMoney($this->CostTotalItem + $this->CostTotalShip + $shipMinCost);
  |   | 
| − | 
  |   | 
| − | 	$objSess = $this->Session();
  |   | 
| − | 	
  |   | 
| − | 	if ($iAsForm) {
  |   | 
| − | 	    $htDelAll = '<span class=text-btn>[<a href="?action=delcart" title="remove all items from cart">remove all</a>]</span>';
  |   | 
| − | 	    $htFirstTot = "<td align=left>$htDelAll</td><td align=right class=total-desc colspan=4>totals:</td>";
  |   | 
| − | 	    $htZoneCombo = 'Shipping destination: '.$this->objShipZone->ComboBox();
  |   | 
| − | 	} else {
  |   | 
| − | 	    $htFirstTot = '<td align=right class=total-desc colspan=5>totals:</td>';
  |   | 
| − | 	    $htZoneCombo = 'Shipping costs shown assume shipment to <b>'.$this->objShipZone->Text().'</b> address.';
  |   | 
| − | 	}
  |   | 
| − | 	$out .= <<<__END__
  |   | 
| − | <tr>$htFirstTot
  |   | 
| − | <td align=right class=total-amount>$strTotalMerch</td>
  |   | 
| − | <td align=right class=total-amount>$strItemsShip</td>
  |   | 
| − | <td align=right class=total-amount>$strTotalItems</td>
  |   | 
| − | <td align=right>⇓</td>
  |   | 
| − | </tr>
  |   | 
| − | <tr>
  |   | 
| − | <td align=right  class=total-desc colspan=7>$strShipDesc</td>
  |   | 
| − | <td align=right  class=total-amount>$strShipPkg</td>
  |   | 
| − | <td align=right>↵</td>
  |   | 
| − | </tr>
  |   | 
| − | <tr>
  |   | 
| − | <td align=right  class=total-desc colspan=7>$strTotalDesc</td>
  |   | 
| − | <td align=right  class=total-final>$strOrdTotal</td>
  |   | 
| − | </tr>
  |   | 
| − | <tr><td colspan=6>$htZoneCombo</td></tr>
  |   | 
| − | __END__;
  |   | 
| − | 	$this->LogEvent('disp','displaying cart, zone '.$this->objShipZone->Abbr().' total $'.$strOrdTotal);
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |       RETURNS: The contents of the cart as text. Includes column headers and totals.
  |   | 
| − |       USED BY: does anything actually use this, or was it intended for the email confirmation?
  |   | 
| − |     */
  |   | 
| − |     public function RenderCore_Text() {
  |   | 
| − | 	$abbrShipZone = $this->DataItem(KSI_SHIP_ZONE);
  |   | 
| − | 	$this->objShipZone->Abbr($abbrShipZone);	
  |   | 
| − | 
  |   | 
| − | 	$shipMinCost = 0;
  |   | 
| − | 
  |   | 
| − | 	$strLineFmt = '%-16s |%6.2f |%6.2f |%4d |%7.2f |%10.2f |%13.2f';
  |   | 
| − | 
  |   | 
| − | 	if ($this->HasRows()) {
  |   | 
| − | 	    $hdr = "\n".sprintf('%-17s|%6s|%5s|%5s|%7s|%10s|%13s'
  |   | 
| − | 	      ,'cat #'
  |   | 
| − | 	      ,' $ ea. '
  |   | 
| − | 	      ,' $ s/h '
  |   | 
| − | 	      ,' qty '
  |   | 
| − | 	      ,' $ sale '
  |   | 
| − | 	      ,' $ s/h tot '
  |   | 
| − | 	      ,' $ LINE TOTAL');
  |   | 
| − | 	    $out = $hdr;
  |   | 
| − | 	    $out .= "\n".str_repeat('-',strlen($hdr));
  |   | 
| − | 	    $objLines = $this->GetLines();
  |   | 
| − | 	    $dlrSaleTot = 0;	// total sale before shipping
  |   | 
| − | 	    $dlrPItmTot = 0;	// per-item shipping total
  |   | 
| − | 	    $dlrPPkgMax = 0;	// per-pkg shipping total
  |   | 
| − | 	    while ($objLines->NextRow()) {
  |   | 
| − | 		$objItem = $objLines->Item();
  |   | 
| − | 		$dlrShipItm = $objItem->ShipPriceItem($abbrShipZone);
  |   | 
| − | 		$dlrShipPkg = $objItem->ShipPricePkg($abbrShipZone);
  |   | 
| − | 
  |   | 
| − | 		$out .= $objLines->RenderText($this,$strLineFmt);
  |   | 
| − | 		if ($dlrPPkgMax < $dlrShipPkg) {
  |   | 
| − | 		    $dlrPPkgMax = $dlrShipPkg;
  |   | 
| − | 		}
  |   | 
| − | 		$intQty = $objLines->Qty;
  |   | 
| − | 		$dlrSaleTot += $objLines->PriceItem;
  |   | 
| − | 		$dlrPItmTot += $dlrShipItm*$intQty;
  |   | 
| − | 	    }
  |   | 
| − | 	    $out .= "\n".str_repeat('=',strlen($hdr));
  |   | 
| − | 
  |   | 
| − | 	    $ftTotalMerch = sprintf('%6.2f',FormatMoney($dlrSaleTot));
  |   | 
| − | 	    $ftItemsShip = sprintf('%6.2f',FormatMoney($dlrPItmTot));
  |   | 
| − | 	    $ftTotalItems = sprintf('%6.2f',FormatMoney($dlrSaleTot + $dlrPItmTot));
  |   | 
| − | 	    $ftShipPkg = sprintf('%6.2f',FormatMoney($dlrPPkgMax));
  |   | 
| − | 	    //$ftTotalDesc = 'order total for shipping to '.$ftShipZone.':';
  |   | 
| − | 	    $ftOrdTotal = sprintf('%6.2f',FormatMoney($dlrSaleTot + $dlrPItmTot + $dlrPPkgMax));
  |   | 
| − | 
  |   | 
| − | // these items don't depend on cart contents, but there's no point in calculating them if the cart is empty:
  |   | 
| − | 	$ftShipZone = $this->objShipZone->Text();
  |   | 
| − | 	$ftShipDesc = $ftShipZone.' s/h package cost:';
  |   | 
| − | 
  |   | 
| − | 	//$objSess = $this->Session();
  |   | 
| − | 
  |   | 
| − | 	$ftZone = $this->objShipZone->Text();
  |   | 
| − | 	//$ftZone = $this->objShipZone->Abbr();
  |   | 
| − | 	//$ftZone = $abbrShipZone;
  |   | 
| − | 
  |   | 
| − | 	$out .= "\n"
  |   | 
| − | 	  ."\n *          Sale: $ftTotalMerch"
  |   | 
| − | 	  ."\n * S/H -"
  |   | 
| − | 	  ."\n  * per item sum: $ftItemsShip"
  |   | 
| − | 	  ."\n  * per  package: $ftShipPkg"
  |   | 
| − | 	  ."\n========================"
  |   | 
| − | 	  ."\n==== FINAL TOTAL: $ftOrdTotal"
  |   | 
| − | 	  ."\n\nShipping Zone: $ftZone";
  |   | 
| − | 	} else {
  |   | 
| − | 	    $out = "\nSorry, we seem to have goofed: this cart appears to have no items in it.";
  |   | 
| − | 	    $out .= "\nThe webmaster is being alerted to the problem.";
  |   | 
| − | 	}
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     public function Render() {
  |   | 
| − | // return rendering of current contents of cart
  |   | 
| − | 	$ok = FALSE;
  |   | 
| − | 	if ($this->ID) {
  |   | 
| − | 	    if ($this->HasLines()) {
  |   | 
| − | 		$ok = TRUE;
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 
  |   | 
| − | 	if ($ok) {
  |   | 
| − | # get information for that destination type:
  |   | 
| − | 	    $out = $this->RenderHdr();
  |   | 
| − | 	    $out .= $this->RenderCore(TRUE);
  |   | 
| − | 	    $out .= $this->RenderFtr();
  |   | 
| − | 	} else {
  |   | 
| − | 	    if ($this->IsCreated()) {
  |   | 
| − | 		if (is_null($this->objLines)) {
  |   | 
| − | 		    $out = "<font size=4>Internal error - cart data not available!</font>";
  |   | 
| − | 		    $this->LogEvent('disp',"can't display cart - no data!");
  |   | 
| − | 		    $this->objDB->Events()->LogEvent('cart.render','','cart data unavailable','cdna',TRUE,TRUE);
  |   | 
| − | 		} else {
  |   | 
| − | 		    $out = "<font size=4>Your cart is empty.</font>";
  |   | 
| − | 		    $this->LogEvent('disp','displaying cart - empty; zone '.$this->objShipZone->Abbr());
  |   | 
| − | 		}
  |   | 
| − | 	    } else {
  |   | 
| − | 		$out = "<font size=4>You have not put anything in your cart yet.</font>";
  |   | 
| − | 		$this->LogEvent('disp','displaying cart - nothing yet; zone '.$this->objShipZone->Abbr());
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
|   | 	return $out;  |   | 	return $out;  | 
|   |      }  |   |      }  | 
|   |      /*----  |   |      /*----  | 
| − |     PURPOSE: Render cart for order confirmation page (read-only, no form controls)
  | + |       RETURNS: per-item price factor for the current shipping zone  | 
|   |      */  |   |      */  | 
| − |      public function RenderConfirm() {  | + |      protected function PerItemFactor() {  | 
| − | 	if ($this->HasLines()) {
  | + | echo 'CODE=['.$this->Abbr().'] ITEM FACTOR=['.self::$arItmFactors[$this->Abbr()].']<br>';  | 
| − | 	    $out = $this->RenderCore(FALSE);
  | + | 	return self::$arItmFactors[$this->Abbr()];  | 
| − | 	} else {
  |   | 
| − | 	    // log error - you shouldn't be able to get to this point with an empty cart
  |   | 
| − | 	    $txtParams = 'Cart ID='.$this->ID.' Order ID='.$this->ID_Order;
  |   | 
| − | 	    $this->objDB->LogEvent('cart.renderconf',$txtParams,'cart empty at confirmation','cec',TRUE,TRUE);	// also sends email alert
  |   | 
| − | 	    $out = '<font color=red>INTERNAL ERROR</font>: cart data has become separated from browser. The webmaster has been notified.';
  |   | 
| − | 	}
  |   | 
| − | 	return $out;  |   | 
| − |     }
  |   | 
| − | /*==========
  |   | 
| − |   CUSTOMER DATA AMALGAMATION
  |   | 
| − | */
  |   | 
| − |     public function AddrShipObj() {
  |   | 
| − | 	if (empty($this->objAddrShip)) {
  |   | 
| − | 	    $arFields = array(
  |   | 
| − | 	      'name'	=> $this->SpawnName(KSI_ADDR_SHIP_NAME	,KSF_ADDR_SHIP_NAME),
  |   | 
| − | 	      'street'	=> new clsCartField($this, KSI_ADDR_SHIP_STREET, KSF_ADDR_SHIP_STREET),
  |   | 
| − | 	      'city'	=> new clsCartField($this, KSI_ADDR_SHIP_CITY	,KSF_ADDR_SHIP_CITY),
  |   | 
| − | 	      'state'	=> new clsCartField($this, KSI_ADDR_SHIP_STATE	,KSF_ADDR_SHIP_STATE),
  |   | 
| − | 	      'zip'	=> new clsCartField($this, KSI_ADDR_SHIP_ZIP	,KSF_ADDR_SHIP_ZIP),
  |   | 
| − | 	      'country'	=> new clsCartField($this, KSI_ADDR_SHIP_COUNTRY,KSF_ADDR_SHIP_COUNTRY),
  |   | 
| − | 	      'instruc'	=> new clsCartField($this, KSI_SHIP_MESSAGE	,KSF_SHIP_MESSAGE)
  |   | 
| − | 	      );
  |   | 
| − | 	    $this->objAddrShip = $this->SpawnAddress($arFields);
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objAddrShip;
  |   | 
| − |     }
  |   | 
| − |     public function AddrCardObj() {
  |   | 
| − | 	if (empty($this->objAddrCard)) {
  |   | 
| − | 	    $arFields = array(
  |   | 
| − | 	      'name'	=> $this->SpawnName(KSI_ADDR_CARD_NAME	,KSF_CUST_CARD_NAME),
  |   | 
| − | 	      'street'	=> new clsCartField($this, KSI_ADDR_CARD_STREET	,KSF_CUST_CARD_STREET),
  |   | 
| − | 	      'city'	=> new clsCartField($this, KSI_ADDR_CARD_CITY	,KSF_CUST_CARD_CITY),
  |   | 
| − | 	      'state'	=> new clsCartField($this, KSI_ADDR_CARD_STATE	,KSF_CUST_CARD_STATE),
  |   | 
| − | 	      'zip'	=> new clsCartField($this, KSI_ADDR_CARD_ZIP	,KSF_CUST_CARD_ZIP),
  |   | 
| − | 	      'country'	=> new clsCartField($this, KSI_ADDR_CARD_COUNTRY,KSF_CUST_CARD_COUNTRY),
  |   | 
| − | 	      'instruc'	=> new clsCartField($this, NULL			,NULL)
  |   | 
| − | 	      );
  |   | 
| − | 	    $this->objAddrCard = $this->SpawnAddress($arFields);
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objAddrCard;
  |   | 
| − |     }
  |   | 
| − |     public function ContDestObj() {
  |   | 
| − | 	if (empty($this->objContDest)) {
  |   | 
| − | 	    $arFields = array(
  |   | 
| − | 	      'email'	=> $this->SpawnEmail(KSI_CUST_SHIP_EMAIL	,KSF_CUST_SHIP_EMAIL),
  |   | 
| − | 	      'phone'	=> $this->SpawnPhone(KSI_CUST_SHIP_PHONE	,KSF_CUST_SHIP_PHONE)
  |   | 
| − | 	      );
  |   | 
| − | 	    $this->objContDest = $this->SpawnContact($arFields);	// blank object for rendering
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objContDest;
  |   | 
| − |     }
  |   | 
| − |     public function ContCustObj_raw() {
  |   | 
| − | 	if (empty($this->objContCust)) {
  |   | 
| − | 	    $arFields = array(
  |   | 
| − | 	      'email'	=> $this->SpawnEmail(KSI_CUST_PAY_EMAIL	,KSF_CUST_PAY_EMAIL),
  |   | 
| − | 	      'phone'	=> $this->SpawnPhone(KSI_CUST_PAY_PHONE	,KSF_CUST_PAY_PHONE)
  |   | 
| − | 	      );
  |   | 
| − | 	    $this->objContCust = $this->SpawnContact($arFields);
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objContCust;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: Contact information for the customer. If the "shipping to myself" box was checked,
  |   | 
| − | 	then it returns the shipping contact information instead, since this is presumed to be the
  |   | 
| − | 	customer's contact information as well.
  |   | 
| − |     */
  |   | 
| − |     public function ContCustObj() {
  |   | 
| − | 	//$isShipCard = $this->DataItem(KSI_SHIP_IS_CARD);
  |   | 
| − | 	$isShipSelf = $this->DataItem(KSI_SHIP_TO_SELF);
  |   | 
| − | 	if ($isShipSelf) {
  |   | 
| − | 	    return $this->ContDestObj();
  |   | 
| − | 	} else {
  |   | 
| − | 	    return $this->ContCustObj_raw();
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       METHOD: SpawnPerson()
  |   | 
| − |       ACTION: creates clsPerson object or descendant
  |   | 
| − | 	This lets us stub off admin functionality here and extend it later.
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-11-29 created
  |   | 
| − |     */
  |   | 
| − |     public function SpawnPerson($iName,$iDescr) {
  |   | 
| − | 	return new clsPerson($iName,$iDescr);
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       METHOD: SpawnPayment()
  |   | 
| − |       ACTION: creates clsPayment object or descendant
  |   | 
| − | 	This lets us stub off admin functionality here and extend it later.
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-11-29 created
  |   | 
| − |     */
  |   | 
| − |     public function SpawnPayment($iNodes=NULL) {
  |   | 
| − | 	return new clsPayment($iNodes);
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       METHOD: SpawnAddress()
  |   | 
| − |       ACTION: creates clsCartAddr object or descendant
  |   | 
| − | 	This lets us stub off admin functionality here and extend it later.
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-11-29 created
  |   | 
| − |     */
  |   | 
| − |     public function SpawnAddress($iNodes=NULL) {
  |   | 
| − | 	return new clsCartAddr($iNodes);
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       METHOD: SpawnContact()
  |   | 
| − |       ACTION: creates clsCartContact object or descendant
  |   | 
| − | 	This lets us stub off admin functionality here and extend it later.
  |   | 
| − | 	The extra functionality may not actually be needed for this, but I went and created it anyway.
  |   | 
| − | 	  (2011-12-15: yes, it was needed.)
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-11-29 created
  |   | 
| − |     */
  |   | 
| − |     public function SpawnContact($iNodes=NULL) {
  |   | 
| − | 	return new clsCartContact($iNodes);
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       METHOD: SpawnEmail()
  |   | 
| − |       ACTION: creates object for handling cart email address data
  |   | 
| − | 	This lets us stub off admin functionality here and extend it later.
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-11-29 created
  |   | 
| − |     */
  |   | 
| − |     public function SpawnEmail($iIndex, $iCtrlName) {
  |   | 
| − | 	return new clsCartField($this, $iIndex, $iCtrlName);
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       METHOD: SpawnPhone()
  |   | 
| − |       ACTION: creates object for handling cart phone number data
  |   | 
| − | 	This lets us stub off admin functionality here and extend it later.
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-11-29 created
  |   | 
| − |     */
  |   | 
| − |     public function SpawnPhone($iIndex, $iCtrlName) {
  |   | 
| − | 	return new clsCartField($this, $iIndex, $iCtrlName);
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       METHOD: SpawnName()
  |   | 
| − |       ACTION: creates object for handling person-name data
  |   | 
| − | 	This lets us stub off admin functionality here and extend it later.
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-11-30 created
  |   | 
| − |     */
  |   | 
| − |     public function SpawnName($iIndex, $iCtrlName) {
  |   | 
| − | 	return new clsCartField($this, $iIndex, $iCtrlName);
  |   | 
|   |      }  |   |      }  | 
|   |      /*----  |   |      /*----  | 
| − |        METHOD: clsShopCart->PersonCustObj()  | + |        RETURNS: per-package price factor for the current shipping zone  | 
| − |       USED BY: clsShopCart->GetDetailObjs(), clsOrder->RenderReceipt()
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-09-12 Extracted from clsShopCart->GetDetailObjs() so it can be used by clsOrder->RenderReceipt()
  |   | 
| − | 	2011-11-29 Now using SpawnPayment() and SpawnPerson() instead of creating directly with "new"
  |   | 
|   |      */  |   |      */  | 
| − |      public function PersonCustObj() {  | + |      protected function PerPkgFactor() {  | 
| − | 	if (empty($this->objCust)) {
  | + | 	return self::$arPkgFactors[$this->Abbr()];  | 
| − | 	    $arFields = array(
  |   | 
| − | 	      'num'		=> new clsCartField($this, KSI_CUST_CARD_NUM,	KSF_CUST_CARD_NUM),
  |   | 
| − | 	      'exp'		=> new clsCartField($this, KSI_CUST_CARD_EXP,	KSF_CUST_CARD_EXP)
  |   | 
| − | 	      );
  |   | 
| − | 	    $objPayment = $this->SpawnPayment($arFields);
  |   | 
| − | 	    $this->objCust = $this->SpawnPerson('cust','buyer');
  |   | 
| − | 	    $this->objCust->Node('payment', $objPayment);	// the buyer always has the credit card
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objCust;  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |       METHOD: clsShopCart->GetDetailObjs()
  |   | 
| − |       FUTURE: rename to GetDataTree()
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-09-12 borrowed (to be moved?) from clsPageCkout
  |   | 
| − | 	2011-11-27 the version in clsPageCkout has now been commented out
  |   | 
| − | 	2011-11-29 using SpawnPerson() instead of new clsPerson()
  |   | 
| − |     */
  |   | 
| − |     public function GetDetailObjs() {
  |   | 
| − | 	if (!$this->hasDetails) {
  |   | 
| − | 	    $objAddrShip = $this->AddrShipObj();
  |   | 
| − | 	    $objAddrCard = $this->AddrCardObj();
  |   | 
| − | 	    $objContDest = $this->ContDestObj();
  |   | 
| − | 	    $objContCust = $this->ContCustObj();
  |   | 
| − |    |   | 
| − | 	    $objDataTree = new clsContactRoot($this->Engine());
  |   | 
| − | /*
  |   | 
| − | 	    $objDataRef = new clsContactFolder_reference();
  |   | 
| − | 	    $objDataTree->Node('reference data',$objDataRef);
  |   | 
| − | 	    $objDataRef->Node('addr.ship',$objAddrShip);
  |   | 
| − | 	    $objDataRef->Node('addr.card',$objAddrCard);
  |   | 
| − | 	    $objDataRef->Node('cont.dest',$objContDest);
  |   | 
| − | 	    $objDataRef->Node('cont.cust',$objContCust);
  |   | 
| − | */
  |   | 
| − | 	    $this->objShip = $this->SpawnPerson('ship','recipient');
  |   | 
| − | 	    $this->objCust = $this->PersonCustObj();
  |   | 
| − |    |   | 
| − | 	    $objDataTree->Node('person.ship',$this->objShip);
  |   | 
| − | 	    $objDataTree->Node('person.cust',$this->objCust);
  |   | 
| − |    |   | 
| − | 	    $this->objDataTree = $objDataTree;
  |   | 
| − |    |   | 
| − | 	    $objPayment = $this->objCust->Payment();
  |   | 
| − |    |   | 
| − | 	    $this->objShip->Node('contact', $objContDest);	// shipping information is always specified
  |   | 
| − |    |   | 
| − | 	    $objContDest->Addr = $objAddrShip;	// shipping address
  |   | 
| − |    |   | 
| − | 	    $this->custShipIsCard	= $this->DataItem(KSI_SHIP_IS_CARD);
  |   | 
| − | 	    $this->custShipToSelf	= $this->DataItem(KSI_SHIP_TO_SELF);
  |   | 
| − |    |   | 
| − | 	    if ($this->custShipIsCard) {
  |   | 
| − | 		$objPayment->Node('addr', $objAddrShip);	// use shipping address for card
  |   | 
| − | 	    } else {
  |   | 
| − | 		$objPayment->Node('addr', $objAddrCard);	// use separate address for card
  |   | 
| − | 	    }
  |   | 
| − |    |   | 
| − | 	    $this->objShip->Node('name', $objAddrShip->Name());
  |   | 
| − |    |   | 
| − | 	    if ($this->custShipToSelf) {
  |   | 
| − | 		// don't use separate person data; re-use buyer contact info plus shipping address
  |   | 
| − | 		$this->objShip->Node('payment', $objPayment);	// the only buyer field the recipient doesn't have
  |   | 
| − | 		$this->objCust = $this->objShip;
  |   | 
| − | 		//$objContDest->Node('addr', $objAddrShip);			// shipping address
  |   | 
| − | 		$this->objShip->Node('contact', $objContDest);
  |   | 
| − | 		$objContDest->Node('addr', $objAddrShip);
  |   | 
| − | 	    } else {
  |   | 
| − | 		$this->objShip->Node('contact', $objContDest);
  |   | 
| − | 		$this->objCust->Node('contact', $objContCust);
  |   | 
| − | 		$this->objCust->Node('name', $objAddrCard->Name());
  |   | 
| − | 	    }
  |   | 
| − | 	    $this->hasDetails = TRUE;
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objDataTree;
  |   | 
|   |      }  |   |      }  | 
| − | 
  |   | 
|   |      /*----  |   |      /*----  | 
| − |        ASSUMES: GetDetailObjs() has been called  | + |        INPUT: base per-item shipping price  | 
|   | + |       RETURNS: calculated price for the current shipping zone  | 
|   |      */  |   |      */  | 
| − |      public function WhoCust() {  | + |      public function CalcPerItem($iBase) {  | 
| − | 	return $this->objCust;  | + | 	return $iBase * $this->PerItemFactor();  | 
|   |      }  |   |      }  | 
|   |      /*----  |   |      /*----  | 
| − |        ASSUMES: GetDetailObjs() has been called  | + |        INPUT: base per-package shipping price  | 
|   | + |       RETURNS: calculated price for the current shipping zone  | 
|   |      */  |   |      */  | 
| − |      public function WhoShip() {  | + |      public function CalcPerPkg($iBase) {  | 
| − | 	return $this->objShip;  | + | 	return $iBase * $this->PerPkgFactor();  | 
| − |     }
  |   | 
| − | }
  |   | 
| − | class clsShopCartLines extends clsTable {
  |   | 
| − |     const TableName='shop_cart_line';
  |   | 
| − |    |   | 
| − |     public function __construct($iDB) {
  |   | 
| − | 	parent::__construct($iDB);
  |   | 
| − | 	  $this->Name(self::TableName);
  |   | 
| − | 	  $this->KeyName('ID');
  |   | 
| − | 	  $this->ClassSng('clsShopCartLine');
  |   | 
| − | 	$this->seqCart = 0;
  |   | 
| − |     }
  |   | 
| − |     public function Add($iCart, $iCatNum, $iQty) {
  |   | 
| − | 	$objItems = $this->objDB->Items();
  |   | 
| − | 	$objItem = $objItems->Get_byCatNum($iCatNum);
  |   | 
| − | 	if (is_null($objItem)) {
  |   | 
| − | // TO DO: log error
  |   | 
| − | echo 'ERROR: Could not find item for catalog #'.$iCatNum.'<br>';
  |   | 
| − | 	} else {
  |   | 
| − | 	    $sqlCart = $this->objDB->SafeParam($iCart);
  |   | 
| − | 	    $sqlWhere = '(ID_Cart='.$sqlCart.') AND (ID_Item='.$objItem->ID.')';
  |   | 
| − | 	    $objLine = $this->GetData($sqlWhere,'clsShopCartLine');
  |   | 
| − | 	    $objLine->NextRow();	// load the only data row
  |   | 
| − |    |   | 
| − | 	    if (!$objLine->hasRows()) {
  |   | 
| − |  		$objLine->ID_Cart=$iCart;
  |   | 
| − | 	    }
  |   | 
| − | 	    $objLine->ID_Item = $objItem->ID;
  |   | 
| − | 	    $objLine->Qty($iQty);
  |   | 
| − | 	    $objLine->Build();
  |   | 
| − | 	}
  |   | 
|   |      }  |   |      }  | 
|   | }  |   | }  | 
| − | class clsShopCartLine extends clsDataSet {
  |   | 
| − |     public function __construct(clsDatabase $iDB=NULL, $iRes=NULL, array $iRow=NULL) {
  |   | 
| − | 	parent::__construct($iDB,$iRes,$iRow);
  |   | 
| − | // this is necessary so $this->Update() will work
  |   | 
| − | // ...but it should be done by whatever code creates the object
  |   | 
| − | 	//$this->Table = $this->objDB->CartLines();
  |   | 
| − | 	$this->ID = -1;
  |   | 
| − |     }
  |   | 
| − |     public function IsLoaded() {
  |   | 
| − | 	return ($this->hasRows());
  |   | 
| − |     }
  |   | 
| − |     public function Cart() {
  |   | 
| − | 	return $this->objDB->Carts()->GetItem($this->ID_Cart);
  |   | 
| − |     }
  |   | 
| − |     public function Item() {
  |   | 
| − | 	$doLoad = FALSE;
  |   | 
| − | 	if (empty($this->objItem)) {
  |   | 
| − | 	    $doLoad = TRUE;
  |   | 
| − | 	} elseif ($this->objItem->ID != $this->ID_Item) {
  |   | 
| − | 	    $doLoad = TRUE;
  |   | 
| − | 	}
  |   | 
| − | 	if ($doLoad) {
  |   | 
| − | 	    $this->objItem = $this->objDB->Items()->GetItem($this->ID_Item);
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objItem;
  |   | 
| − |     }
  |   | 
| − |     public function Build() {
  |   | 
| − | 	if ($this->IsLoaded()) {
  |   | 
| − | //	if ($this->HasRows()) {
  |   | 
| − | 	    $sql = 'UPDATE `'.clsShopCartLines::TableName.'` SET'
  |   | 
| − | 	      .' Qty='.$this->Qty
  |   | 
| − | 	      .' WhenEdited=NOW()'
  |   | 
| − | 		.' WHERE ID='.$this->ID;
  |   | 
| − | 	    $this->objDB->Exec($sql);
  |   | 
| − | 	} else {
  |   | 
| − | 	    $this->Seq = $this->Cart()->LineCount()+1;
  |   | 
| − | 	    $sql = 'INSERT INTO `'.clsShopCartLines::TableName
  |   | 
| − | 	      .'` (Seq,ID_Cart,ID_Item,Qty,WhenAdded)'
  |   | 
| − | 	      .' VALUES('
  |   | 
| − | 		.$this->Seq.', '
  |   | 
| − | 		.$this->ID_Cart.', '
  |   | 
| − | 		.$this->ID_Item.', '
  |   | 
| − | 		.$this->Qty.', NOW());';
  |   | 
| − | 	    $this->objDB->Exec($sql);
  |   | 
| − | 	    $this->ID = $this->objDB->NewID('cartLine.make');
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     public function Qty($iQty=NULL) {
  |   | 
| − | 	if (!is_null($iQty)) {
  |   | 
| − | 	    if ($this->Qty != $iQty) {
  |   | 
| − | 		$qtyNew = 0+$iQty;	// make sure it's an integer -- prevent injection attack
  |   | 
| − | 		$arrSet['Qty'] = SQLValue($qtyNew);
  |   | 
| − | 		$arrSet['WhenEdited'] = 'NOW()';
  |   | 
| − | 		$this->Update($arrSet);
  |   | 
| − | 		$this->Qty = $qtyNew;
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	return $this->Qty;
  |   | 
| − |     }
  |   | 
| − | /*
  |   | 
| − |     protected function ItemSpecs(array $iSpecs=NULL) {
  |   | 
| − | 	if (is_null($iSpecs)) {
  |   | 
| − | 	    $this->Item();	// make sure $this->objItem is loaded
  |   | 
| − | 	    $this->objTitle	= $this->objItem->Title();
  |   | 
| − | 	    $this->objItTyp	= $this->objItem->ItTyp();
  |   | 
| − | 	    $this->objItOpt	= $this->objItem->ItOpt();
  |   | 
|   |  |   |  | 
| − | 	    $out['tname']	= $this->objTitle->Name;
  |   | 
| − | 	    $out['ittyp']	= $this->objItTyp->Name($this->Qty);
  |   | 
| − | 	    $out['itopt']	= $this->objItOpt->Descr;
  |   | 
| − | 	    return $out;
  |   | 
| − | 	} else {
  |   | 
| − | 	    return $iSpecs;
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     public function ItemDesc(array $iSpecs=NULL) {	// plaintext
  |   | 
| − | 	$sp = $this->ItemSpecs($iSpecs);
  |   | 
| − | 
  |   | 
| − | 	$strItOpt = $sp['itopt'];
  |   | 
| − | 
  |   | 
| − | 	$out = '"'.$sp['tname'].'" ('.$sp['ittyp'];
  |   | 
| − | 	if (!is_null($strItOpt)) {
  |   | 
| − | 	    $out .= ' - '.$strItOpt;
  |   | 
| − | 	}
  |   | 
| − | 	$out .= ')';
  |   | 
| − | 
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     public function ItemDesc_ht(array $iSpecs=NULL) {	// as HTML
  |   | 
| − | 	$sp = $this->ItemSpecs($iSpecs);
  |   | 
| − | 
  |   | 
| − | 	$htTitleName = '<i>'.$this->objTitle->LinkName().'</i>';
  |   | 
| − | 	$strItOpt = $sp['itopt'];
  |   | 
| − | 
  |   | 
| − | 	$out = $htTitleName.' ('.$sp['ittyp'];
  |   | 
| − | 	if (!is_null($strItOpt)) {
  |   | 
| − | 	    $out .= ' - '.$strItOpt;
  |   | 
| − | 	}
  |   | 
| − | 	$out .= ')';
  |   | 
| − | 
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     public function ItemDesc_wt(array $iSpecs=NULL) {	// as wikitext
  |   | 
| − | 	$sp = $this->ItemSpecs($iSpecs);
  |   | 
| − | 
  |   | 
| − | 	$wtTitleName = "''".$this->objTitle->LinkName_wt()."''";
  |   | 
| − | 	$strItOpt = $sp['itopt'];
  |   | 
| − | 
  |   | 
| − | 	$out = $wtTitleName.' ('.$sp['ittyp'];
  |   | 
| − | 	if (!is_null($strItOpt)) {
  |   | 
| − | 	    $out .= ' - '.$strItOpt;
  |   | 
| − | 	}
  |   | 
| − | 	$out .= ')';
  |   | 
| − | 
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − | */
  |   | 
| − |     /*-----
  |   | 
| − |       PURPOSE: Do calculations necessary for rendering the cart line
  |   | 
| − |       USED BY:
  |   | 
| − | 	* the shopping cart form
  |   | 
| − | 	* the final order display
  |   | 
| − | 	* the conversion from cart to order
  |   | 
| − |     */
  |   | 
| − |     public function RenderCalc(clsShipZone $iZone) {
  |   | 
| − | 	$arItem = $this->Item()->DescSpecs();
  |   | 
| − | 	$txtItemDesc = $this->Item()->DescLong($arItem);
  |   | 
| − | 	$htmItemDesc = $this->Item()->DescLong_ht($arItem);
  |   | 
| − | 
  |   | 
| − | 	$objItem = $this->Item();
  |   | 
| − | 	$this->PriceItem = $objItem->PriceSell;
  |   | 
| − | 	$this->Value('CatNum',$objItem->CatNum);
  |   | 
| − | 
  |   | 
| − | // save for copying to order line object:
  |   | 
| − | 	$this->DescText = $txtItemDesc;
  |   | 
| − | 	$this->DescHtml = $htmItemDesc;
  |   | 
| − | 
  |   | 
| − | // save so cart can figure totals;
  |   | 
| − | 	$idsZone = $iZone->Abbr();
  |   | 
| − | 	$this->ShipPkgDest = $objItem->ShipPricePkg($idsZone);
  |   | 
| − | 	$this->ShipItmDest = $objItem->ShipPriceItem($idsZone);
  |   | 
| − | 
  |   | 
| − | // calculate costs:
  |   | 
| − | 	$this->CostItemQty = $this->Qty * $this->PriceItem;
  |   | 
| − | 	$this->CostShipQty = $this->Qty * $this->ShipItmDest;
  |   | 
| − |     }
  |   | 
| − |     /*
  |   | 
| − |       ACTION: Render the current cart line using static HTML (no form elements; read-only)
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-04-01 adapting this to use clsOrdLine->RenderStatic()
  |   | 
| − |     */
  |   | 
| − |     public function RenderHtml(clsShopCart $iCart) {
  |   | 
| − | 
  |   | 
| − | 	$objOLine = $this->Engine()->OrdLines()->SpawnItem();
  |   | 
| − | 	$this->RenderCalc($iCart->objShipZone);	// calculate some needed fields
  |   | 
| − | 	$objOLine->Init_fromCartLine($this);
  |   | 
| − | 	return $objOLine->RenderStatic($iCart->objShipZone);
  |   | 
| − | /*/
  |   | 
| − | // calculate display fields:
  |   | 
| − | 	if ($this->Qty) {
  |   | 
| − | 	    $this->RenderCalc($iCart->objShipZone);
  |   | 
| − | 
  |   | 
| − | 	    $strQty = $this->Qty;
  |   | 
| − | 	    $htLineCtrl = $strQty;
  |   | 
| − | 
  |   | 
| − | 	    $mnyPrice = $this->PriceItem;	// item price
  |   | 
| − | 	    $mnyPerItm = $this->ShipItmDest;	// per-item shipping
  |   | 
| − | 	    $mnyPerPkg = $this->ShipPkgDest;	// per-pkg minimum shipping
  |   | 
| − | 	    $mnyPriceQty = $this->CostItemQty;	// line total sale
  |   | 
| − | 	    $mnyPerItmQty = $this->CostShipQty;	// line total per-item shipping
  |   | 
| − | 	    $mnyLineTotal = $mnyPriceQty + $mnyPerItmQty;	// line total overall (does not include per-pkg minimum)
  |   | 
| − | 
  |   | 
| − | 	    $strCatNum = $this->CatNum;
  |   | 
| − | 	    $strPrice = FormatMoney($mnyPrice);
  |   | 
| − | 	    $strPerItm = FormatMoney($mnyPerItm);
  |   | 
| − | 	    $strPriceQty = FormatMoney($mnyPriceQty);
  |   | 
| − | 	    $strPerItmQty = FormatMoney($mnyPerItmQty);
  |   | 
| − | 	    $strLineTotal = FormatMoney($mnyLineTotal);
  |   | 
| − | 
  |   | 
| − | 	    $strShipPkg = FormatMoney($mnyPerPkg);
  |   | 
| − | 
  |   | 
| − | 	    $htDesc = $this->DescHtml;
  |   | 
| − | 
  |   | 
| − | 	    $htDelBtn = '';
  |   | 
| − | 
  |   | 
| − | 	    $out = <<<__END__
  |   | 
| − | <tr>
  |   | 
| − | <td>$htDelBtn$strCatNum</td>
  |   | 
| − | <td>$htDesc</td>
  |   | 
| − | <td class=cart-price align=right>$strPrice</td>
  |   | 
| − | <td class=shipping align=right>$strPerItm</td>
  |   | 
| − | <td class=qty align=right>$htLineCtrl</td>
  |   | 
| − | <td class=cart-price align=right>$strPriceQty</td>
  |   | 
| − | <td class=shipping align=right>$strPerItmQty</td>
  |   | 
| − | <td class=total align=right>$strLineTotal</td>
  |   | 
| − | <td class=shipping align=right>$strShipPkg</td>
  |   | 
| − | </tr>
  |   | 
| − | __END__;
  |   | 
| − | 	    return $out;
  |   | 
| − | 	}
  |   | 
| − | /**/
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       ACTION: Render the current cart line as part of an interactive HTML form
  |   | 
| − |     */
  |   | 
| − |     public function RenderForm(clsShopCart $iCart) {
  |   | 
| − | // calculate display fields:
  |   | 
| − | 	if ($this->Qty) {
  |   | 
| − | 	    $this->RenderCalc($iCart->objShipZone);
  |   | 
| − | 
  |   | 
| − | 	    //$htLineName = 'cart-line-'.$this->Seq;
  |   | 
| − | 	    $htLineName = 'qty-'.$this->CatNum;
  |   | 
| − | 	    $strQty = $this->Qty;
  |   | 
| − | 	    $htLineCtrl = '<input size=2 align=right name="'.$htLineName.'" value='.$strQty.'>';
  |   | 
| − | 
  |   | 
| − | 	    $mnyPrice = $this->PriceItem;	// item price
  |   | 
| − | 	    $mnyPerItm = $this->ShipItmDest;	// per-item shipping
  |   | 
| − | 	    $mnyPerPkg = $this->ShipPkgDest;	// per-pkg minimum shipping
  |   | 
| − | 	    $mnyPriceQty = $this->CostItemQty;	// line total sale
  |   | 
| − | 	    $mnyPerItmQty = $this->CostShipQty;	// line total per-item shipping
  |   | 
| − | 	    $mnyLineTotal = $mnyPriceQty + $mnyPerItmQty;	// line total overall (does not include per-pkg minimum)
  |   | 
| − | 
  |   | 
| − | 	    $strCatNum = $this->CatNum;
  |   | 
| − | 	    $strPrice = FormatMoney($mnyPrice);
  |   | 
| − | 	    $strPerItm = FormatMoney($mnyPerItm);
  |   | 
| − | 	    $strPriceQty = FormatMoney($mnyPriceQty);
  |   | 
| − | 	    $strPerItmQty = FormatMoney($mnyPerItmQty);
  |   | 
| − | 	    $strLineTotal = FormatMoney($mnyLineTotal);
  |   | 
| − | 
  |   | 
| − | 	    $strShipPkg = FormatMoney($mnyPerPkg);
  |   | 
| − | 
  |   | 
| − | 	    $htDesc = $this->DescHtml;
  |   | 
| − | 
  |   | 
| − | 	    $htDelBtn = '<span class=text-btn>[<a href="?item='.$this->ID_Item.'&action=del" title="remove '.$strCatNum.' from cart">remove</a>]</span> ';
  |   | 
| − | 
  |   | 
| − | 	    $out = <<<__END__
  |   | 
| − | <tr>
  |   | 
| − | <td>$htDelBtn$strCatNum</td>
  |   | 
| − | <td>$htDesc</td>
  |   | 
| − | <td class=cart-price align=right>$strPrice</td>
  |   | 
| − | <td class=shipping align=right>$strPerItm</td>
  |   | 
| − | <td class=qty align=right>$htLineCtrl</td>
  |   | 
| − | <td class=cart-price align=right>$strPriceQty</td>
  |   | 
| − | <td class=shipping align=right>$strPerItmQty</td>
  |   | 
| − | <td class=total align=right>$strLineTotal</td>
  |   | 
| − | <td class=shipping align=right>$strShipPkg</td>
  |   | 
| − | </tr>
  |   | 
| − | __END__;
  |   | 
| − | 	    return $out;
  |   | 
| − | /**/
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     public function RenderText(clsShopCart $iCart,$iFmt) {
  |   | 
| − | 	if ($this->Qty) {
  |   | 
| − | 	    $this->RenderCalc($iCart->objShipZone);
  |   | 
| − | 
  |   | 
| − | 	    $dlrPrice = $this->PriceItem;	// item price
  |   | 
| − | 	    $dlrPerItm = $this->ShipItmDest;	// per-item shipping
  |   | 
| − | 	    $dlrPerPkg = $this->ShipPkgDest;	// per-pkg minimum shipping
  |   | 
| − | 	    $dlrPriceQty = $this->CostItemQty;	// line total sale
  |   | 
| − | 	    $dlrPerItmQty = $this->CostShipQty;	// line total per-item shipping
  |   | 
| − | 	    $dlrLineTotal = $dlrPriceQty + $dlrPerItmQty;	// line total overall (does not include per-pkg minimum)
  |   | 
| − | 
  |   | 
| − | 	    $ftCatNum = $this->CatNum;
  |   | 
| − | 	    $ftPrice = FormatMoney($dlrPrice);
  |   | 
| − | 	    $ftPerItm = FormatMoney($dlrPerItm);
  |   | 
| − | 	    $ftQty = $this->Qty;
  |   | 
| − | 	    $ftPriceQty = FormatMoney($dlrPriceQty);	// price x qty
  |   | 
| − | 	    $ftPerItmQty = FormatMoney($dlrPerItmQty);	// per-item shipping x qty
  |   | 
| − | 	    $ftLineTotal = FormatMoney($dlrLineTotal);
  |   | 
| − | 
  |   | 
| − | 	    $ftShipPkg = FormatMoney($dlrPerPkg);
  |   | 
| − | 
  |   | 
| − | 	    $ftDesc = $this->DescText;
  |   | 
| − | 
  |   | 
| − | 	    $out = "\n".sprintf($iFmt,$ftCatNum,$ftPrice,$ftPerItm,$ftQty,$ftPriceQty,$ftPerItmQty,$ftLineTotal);
  |   | 
| − | 	    $out .= "\n - $ftDesc";
  |   | 
| − | 	    return $out;
  |   | 
| − |     }
  |   | 
| − | /* (2010-02-18) actually, this is probably superceded by RenderText()
  |   | 
| − |     public function RenderEmail($iCart) {
  |   | 
| − | /* TO DO: to be written; the following is from Perl:
  |   | 
| − | 	      $out .= "$intLine. $lineDescText\n";
  |   | 
| − | 	      $out .= "Catalog #: $lineCatNum\n";
  |   | 
| − | 	      $out .= "Price each        : ".AlignDollars($linePrice)."\n";
  |   | 
| − | 	      $out .= "Shipping each     : ".AlignDollars($lineShipItemDest)."\n";
  |   | 
| − | 	      $out .= "Min. base shipping: ".AlignDollars($lineShipPkgDest)."\n";
  |   | 
| − | 	      if ($lineQty != 1) {
  |   | 
| − | 		      $textOrder .= "##### QTY $strQty #####\n";
  |   | 
| − | 	      }
  |   | 
| − | 	      $out .= "\n";
  |   | 
| − | */
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
|   | // ShopCart Log  |   | // ShopCart Log  | 
|   | class clsShopCartLog extends clsTable {  |   | class clsShopCartLog extends clsTable {  | 
| Line 2,743: | 
Line 376: | 
|   |      */  |   |      */  | 
|   |      public function IsValidNow($iKey) {  |   |      public function IsValidNow($iKey) {  | 
| − | 	  $ok = ($this->Token == $iKey);
  | + | 	$ok = ($this->Token == $iKey);  | 
| − | 	  if ($ok) {
  | + | 	if ($ok) {  | 
| − | 	      $idClientWas = $this->ID_Client;
  | + | 	    $idClientWas = $this->ID_Client;  | 
| − | 	      $objClient = $this->Client();
  | + | 	    $objClient = $this->Client();  | 
| − | 	      if ($idClientWas != $this->ID_Client) {
  | + | 	    if ($idClientWas != $this->ID_Client) {  | 
| − | 		  // not an error, but could indicate a hacking attempt -- so log it, flagged as severe:
  | + | 		// not an error, but could indicate a hacking attempt -- so log it, flagged as severe:  | 
| − | 		  $this->objDB->LogEvent(
  | + | 		$this->objDB->LogEvent(  | 
| − | 		    'session.valid',
  | + | 		  'session.valid',  | 
| − | 		    'KEY='.$iKey,' OLD-CLIENT='.$idClientWas.' NEW-CLIENT='.$this->ID_Client,
  | + | 		  'KEY='.$iKey,' OLD-CLIENT='.$idClientWas.' NEW-CLIENT='.$this->ID_Client,  | 
| − | 		    'stored session client mismatch','XCRED',FALSE,TRUE);
  | + | 		  'stored session client mismatch','XCRED',FALSE,TRUE);  | 
| − | 		  $ok = FALSE;
  | + | 		$ok = FALSE;  | 
| − | 	      }
  | + | 	    }  | 
| − | 	  }
  | + | 	}  | 
| − | 	  return $ok;
  | + | 	return $ok;  | 
|   |      }  |   |      }  | 
|   |      public function SetCart($iID) {  |   |      public function SetCart($iID) {  | 
| Line 2,774: | 
Line 407: | 
|   |      public function SessKey() {  |   |      public function SessKey() {  | 
|   | 	return $this->ID.'-'.$this->Token;  |   | 	return $this->ID.'-'.$this->Token;  | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: TRUE if the cart is usable within the current context
  |   | 
| − | 	If we're displaying checkout stuff, it's okay to use a cart
  |   | 
| − | 	  which has already been turned into an order.
  |   | 
| − | 	If we're still in the cart-editing phase, then we need to
  |   | 
| − | 	  fetch a new cart if the old one has been ordered (or, later,
  |   | 
| − | 	  give the user options -- add to existing order, edit existing order,
  |   | 
| − | 	  create completely new order...).
  |   | 
| − |     */
  |   | 
| − |     private function IsCartUsable($iCart) {
  |   | 
| − | 	if ($this->Engine()->inCkout) {
  |   | 
| − | 	    // for voided, still need a new cart; ordered is ok
  |   | 
| − | 	    return !$iCart->IsVoided();
  |   | 
| − | 	} else {
  |   | 
| − | 	    // ordered or voided means we need a new cart
  |   | 
| − | 	    return !$iCart->IsLocked();
  |   | 
| − | 	}
  |   | 
|   |      }  |   |      }  | 
|   |      /*----  |   |      /*----  | 
| Line 2,803: | 
Line 418: | 
|   |      public function Cart() {	// DEPRECATED FORM  |   |      public function Cart() {	// DEPRECATED FORM  | 
|   | 	return $this->CartObj();  |   | 	return $this->CartObj();  | 
| − |     }
  |   | 
| − |     public function CartObj() {
  |   | 
| − | // if there's a cart for this session, load it; otherwise create a new one but don't save it:
  |   | 
| − | 	if (!isset($this->objCart)) {
  |   | 
| − | 	    $objCarts = $this->objDB->Carts();
  |   | 
| − | 	    if (!is_null($this->ID_Cart)) {
  |   | 
| − | 		$objCart = $objCarts->GetItem($this->ID_Cart);
  |   | 
| − | /*
  |   | 
| − | 		// KLUGE for testing - should not normally be necessary:
  |   | 
| − | 		if ($this->objCart->ID_Sess==0) {
  |   | 
| − | 		    $this->objCart->ID_Sess = $this->ID;
  |   | 
| − | 		    $this->objCart->Update(array('ID_Sess'=>$this->ID));
  |   | 
| − | 		}
  |   | 
| − | */
  |   | 
| − | 		if (!$this->IsCartUsable($objCart)) {
  |   | 
| − | 		    $this->ID_Cart = NULL;	// get a new cart if the order is locked
  |   | 
| − | 		}
  |   | 
| − | 	    }
  |   | 
| − | 	    if (is_null($this->ID_Cart)) {
  |   | 
| − | 		$objCart = $this->objDB->Carts()->SpawnItem();
  |   | 
| − | 		$objCart->InitNew($this->ID);
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	assert('is_object($objCart);');	// we should always have a cart at this point
  |   | 
| − | 	$this->objCart = $objCart;
  |   | 
| − | 	return $objCart;
  |   | 
|   |      }  |   |      }  | 
|   |      public function Client() {  |   |      public function Client() {  | 
| Line 2,900: | 
Line 489: | 
|   | }  |   | }  | 
|   |  |   |  | 
| − | /* ======================
  |   | 
| − |  ORDER MANAGEMENT CLASSES
  |   | 
| − |  Should probably be in a separate file
  |   | 
| − |  LATER: Split into 3 parts:
  |   | 
| − |   (1) core pieces, like table name
  |   | 
| − |   (2) pieces only used by cart
  |   | 
| − |   (3) (already done - class VbzAdminOrders) pieces only used by admin system
  |   | 
| − | */
  |   | 
| − | class clsOrders extends clsTable {
  |   | 
| − |     const TableName='core_orders';
  |   | 
|   |  |   |  | 
| − |     public function __construct($iDB) {
  |   | 
| − | 	parent::__construct($iDB);
  |   | 
| − | 	  $this->Name(self::TableName);
  |   | 
| − | 	  $this->KeyName('ID');
  |   | 
| − | 	  $this->ClassSng('clsOrder');
  |   | 
| − | 	  $this->ActionKey(KS_URL_PAGE_ORDER);
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |     | FUNCTION: Create()
  |   | 
| − |     | ACTION: create the order record (fill in minimal fields)
  |   | 
| − |     */
  |   | 
| − |     public function Create() {
  |   | 
| − | 	$intSeq = $this->NextOrdSeq();
  |   | 
| − | 	$strSeq = sprintf(KS_ORD_NUM_FMT,$intSeq);
  |   | 
| − | 	$strNum = KC_ORD_NUM_PFX.$strSeq;
  |   | 
| − | 	$arIns = array(
  |   | 
| − | 	    'Number'	=> SQLValue($strNum),
  |   | 
| − | 	    'SortPfx'	=> SQLValue(KC_ORD_NUM_SORT),
  |   | 
| − | 	    'WhenStarted'	=> 'NOW()'
  |   | 
| − | 	    );
  |   | 
| − | 	$this->Insert($arIns);
  |   | 
| − | 	$id = $this->objDB->NewID();
  |   | 
| − | 	assert('$id > 0');
  |   | 
| − | 	return $id;
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |     | FUNCTION: NextOrdSeq()
  |   | 
| − |     | ACTION: get the next order sequence number
  |   | 
| − |     */
  |   | 
| − |     private function NextOrdSeq() {
  |   | 
| − | 	$objVars = $this->objDB->VarsGlobal();
  |   | 
| − | 	$intOrdLast = $objVars->Val('ord_seq_prev');
  |   | 
| − | 	$intOrdThis = $intOrdLast+1;
  |   | 
| − | 	$objVars->Val('ord_seq_prev',$intOrdThis);
  |   | 
| − | 	return $intOrdThis;
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |     | FUNCTION: Populate()
  |   | 
| − |     | ACTION: fill in the order record with data from the cart
  |   | 
| − |     */
  |   | 
| − |     public function CopyCart($iOrdID, clsShopCart $iCartObj) {
  |   | 
| − | 	assert('$iOrdID > 0');
  |   | 
| − | 
  |   | 
| − | // ** ITEMS IN CART (convert from cart lines to order items)
  |   | 
| − | 	//$objTbl = new clsShopCartLines($this->objDB);
  |   | 
| − | 	//$objRows = $objTbl->GetData('ID_Cart='.$this->ID);
  |   | 
| − | 
  |   | 
| − | 	$objOrd = $this->GetItem($iOrdID);
  |   | 
| − | 	$objOrd->CopyCart($iCartObj);
  |   | 
| − | 
  |   | 
| − | // should this code be in $objOrd->CopyCart?
  |   | 
| − | 	// in session object, set Order ID and clear Cart ID
  |   | 
| − | 	// 2011-03-27 wrong. just set Order ID.
  |   | 
| − | 	$arUpd = array(
  |   | 
| − | 	  'ID_Order'	=> $iOrdID,
  |   | 
| − | 	  //'ID_Cart'	=> 'NULL'
  |   | 
| − | 	  );
  |   | 
| − | 	$iCartObj->Session()->Update($arUpd);
  |   | 
| − | 	// log the event
  |   | 
| − | 	$this->Engine()->LogEvent(
  |   | 
| − | 	  __METHOD__,
  |   | 
| − | 	  '|ord ID='.$iOrdID.'|cart ID='.$iCartObj->KeyValue(),
  |   | 
| − | 	  'Converted cart to order; SQL='.SQLValue($iCartObj->Session()->sqlExec),
  |   | 
| − | 	  'C>O',FALSE,FALSE);
  |   | 
| − | 
  |   | 
| − | 	return $objOrd;	// this is used by the checkout process
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | class clsOrder extends clsVbzRecs {
  |   | 
| − |     /*----
  |   | 
| − |       NOTE: "Code" is now an integer. It used to be a string.
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-10-08 Moved here from VbzAdminOrder
  |   | 
| − |     */
  |   | 
| − | /*
  |   | 
| − |     public function StartEvent($iWhere,$iCode,$iDescr,$iNotes=NULL) {
  |   | 
| − | 	$arEvent = array(
  |   | 
| − | 	  //'type'	=> clsEvents::kTypeOrd,
  |   | 
| − | 	  'type'	=> $this->Table->ActionKey(),
  |   | 
| − | 	  'id'		=> $this->ID,
  |   | 
| − | 	  'where'	=> $iWhere,
  |   | 
| − | 	  'code'	=> $iCode,
  |   | 
| − | 	  'descr'	=> $iDescr
  |   | 
| − | 	  );
  |   | 
| − | 	if (!is_null($iNotes)) {
  |   | 
| − | 	    $arEvent['notes'] = $iNotes;
  |   | 
| − | 	}
  |   | 
| − | 	$this->idEvent = $this->objDB->Events()->StartEvent($arEvent);
  |   | 
| − |     }
  |   | 
| − | */
  |   | 
| − |     protected function StartEvent_Simple($iCode,$iDescr,$iWhere) {
  |   | 
| − | 	//$this->objDB->OrderLog()->Add($this->ID,$iCode,$iDescr);
  |   | 
| − | 	$arEv = array(
  |   | 
| − | 	  'descr'	=> $iDescr,
  |   | 
| − | 	  'type'	=> $this->Table->ActionKey(),
  |   | 
| − | 	  'id'		=> $this->KeyValue(),
  |   | 
| − | 	  'where'	=> $iWhere,
  |   | 
| − | 	  'code'	=> $iCode);
  |   | 
| − | //	$this->objDB->Events()->StartEvent($arEv);
  |   | 
| − | 	$this->StartEvent($arEv);
  |   | 
| − |     }
  |   | 
| − |     /*====
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-12-05 boilerplate event logging added to VbzAdminSupplier
  |   | 
| − | 	2011-03-23 copied from VbzAdminSupplier to VbzAdminOrder
  |   | 
| − | 	2011-03-27 moved from VbzAdminOrder to clsOrder
  |   | 
| − |     */
  |   | 
| − |     public function Log() {
  |   | 
| − | 	if (!is_object($this->logger)) {
  |   | 
| − | 	    $this->logger = new clsLogger_DataSet($this,$this->objDB->Events());
  |   | 
| − | 	}
  |   | 
| − | 	return $this->logger;
  |   | 
| − |     }
  |   | 
| − |     public function StartEvent(array $iArgs) {
  |   | 
| − | 	return $this->Log()->StartEvent($iArgs);
  |   | 
| − |     }
  |   | 
| − |     public function FinishEvent(array $iArgs=NULL) {
  |   | 
| − | 	return $this->Log()->FinishEvent($iArgs);
  |   | 
| − |     }
  |   | 
| − |     public function EventListing() {
  |   | 
| − | 	return $this->Log()->EventListing();
  |   | 
| − |     }
  |   | 
| − |     //====
  |   | 
| − | /*
  |   | 
| − |     protected function FinishEvent(array $iArgs=NULL) {
  |   | 
| − | 	$this->objDB->Events()->FinishEvent($iArgs);
  |   | 
| − |     }
  |   | 
| − | */
  |   | 
| − |     /*----
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-10-08 Moved here from VbzAdminOrder; supercedes existing protected function
  |   | 
| − | 	  which might not have been working properly anyway (no idEvent)
  |   | 
| − |     */
  |   | 
| − | /* 2011-03-27 this is now redundant
  |   | 
| − |     public function FinishEvent(array $iArgs=NULL) {
  |   | 
| − | 	$this->objDB->Events()->FinishEvent($this->idEvent,$iArgs);
  |   | 
| − |     }
  |   | 
| − | */
  |   | 
| − |     /*-----
  |   | 
| − |       TO DO: Explain why this object sometimes doesn't know what the cart is yet
  |   | 
| − | 	(presumably happens during the cart->order transfer process)
  |   | 
| − |       USED BY:
  |   | 
| − | 	Created for VbzAdmin, where it is sometimes necessary to pass the cart ID (why?)
  |   | 
| − | 	Now used by this class for displaying order receipt.
  |   | 
| − |     */
  |   | 
| − |     protected function Cart($iID=NULL) {
  |   | 
| − | 	if (is_null($iID)) {
  |   | 
| − | 	    $idCart = $this->ID_Cart;
  |   | 
| − | 	} else {
  |   | 
| − | 	    $idCart = $iID;
  |   | 
| − | 	}
  |   | 
| − | 
  |   | 
| − | 	$doLoad = TRUE;
  |   | 
| − | 	if (isset($this->objCart)) {
  |   | 
| − | 	    if ($this->objCart->ID == $idCart) {
  |   | 
| − | 		$doLoad = FALSE;
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	if ($doLoad) {
  |   | 
| − | 	    $objCart = $this->objDB->Carts()->GetItem($idCart);
  |   | 
| − | 	    $this->objDB->Cart($objCart);
  |   | 
| − | 	    $this->objCart = $objCart;
  |   | 
| − | 	}
  |   | 
| − | 	return $objCart;
  |   | 
| − |     }
  |   | 
| − | 
  |   | 
| − |     public function Lines() {
  |   | 
| − | 	$objTbl = $this->objDB->OrdLines();
  |   | 
| − | 	$objRows = $objTbl->GetData('ID_Order='.$this->ID);
  |   | 
| − | 	return $objRows;
  |   | 
| − |     }
  |   | 
| − |     public function CopyCart(clsShopCart $iCartObj) {
  |   | 
| − | 	$this->CopyCartData($iCartObj);
  |   | 
| − | 	$this->CopyCartLines($iCartObj);
  |   | 
| − |     }
  |   | 
| − |     /* =====
  |   | 
| − |       ACTION: Copy over basic cart information (totals, etc.)
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-10-06 added cart ID to update -- otherwise final order confirmation page can't find cart data
  |   | 
| − |     */
  |   | 
| − |     private function CopyCartData(clsShopCart $iCartObj) {
  |   | 
| − | 	$curItemTotal = $iCartObj->DataItem(KSI_ITEM_TOTAL);
  |   | 
| − | 	$curShipItem = $iCartObj->DataItem(KSI_PER_ITEM_TOTAL);
  |   | 
| − | 	$curShipPkg = $iCartObj->DataItem(KSI_PER_PKG_TOTAL);
  |   | 
| − | 	$idCart = $iCartObj->ID;
  |   | 
| − | 	$this->ID_Cart;
  |   | 
| − | 
  |   | 
| − | 	$arUpd = array(
  |   | 
| − | 	  'ID_Cart'		=> $idCart,
  |   | 
| − | 	  'WebTotal_Merch'	=> SQLValue($curItemTotal),
  |   | 
| − | 	  'WebTotal_Ship' 	=> SQLValue($curShipItem+$curShipPkg),
  |   | 
| − | 	  'WebTotal_Final'	=> SQLValue($curItemTotal+$curShipItem+$curShipPkg)
  |   | 
| − | 	  );
  |   | 
| − | 	$this->StartEvent_Simple('CDC','Copying data from cart ID='.$idCart,__METHOD__);
  |   | 
| − | 	$this->Update($arUpd);	// we're assuming the order record exists at this point
  |   | 
| − | 	$this->FinishEvent();
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |      ACTION: Create order lines from cart lines
  |   | 
| − |     */
  |   | 
| − |     private function CopyCartLines(clsShopCart $iCartObj) {
  |   | 
| − | 	$objRows = $iCartObj->GetLines();	// shopping cart lines to convert
  |   | 
| − | 	$objTbl = new clsOrderLines($this->objDB);
  |   | 
| − | 	$objTbl->Update(array('QtyOrd'=>0),'ID_Order='.$this->ID);	// zero out any existing order lines in case customer edits cart
  |   | 
| − | 	if ($objRows->HasRows()) {
  |   | 
| − | 	    $this->StartEvent_Simple('CLC','copying cart lines',__METHOD__);
  |   | 
| − | 	    $intNew = 0;
  |   | 
| − | 	    $intUpd = 0;
  |   | 
| − | 	    while ($objRows->NextRow()) {
  |   | 
| − | 		$intSeq = $objRows->Seq;
  |   | 
| − | 		$idItem = $objRows->ID_Item;
  |   | 
| − | 		$intQty = $objRows->Qty;
  |   | 
| − | 		$dtWhenAdded = $objRows->WhenAdded;
  |   | 
| − | 		$dtWhenEdited = $objRows->WhenEdited;
  |   | 
| − | 
  |   | 
| − | 		$objRows->RenderCalc($iCartObj->objShipZone);
  |   | 
| − | 
  |   | 
| − | 		$arUpd = array(
  |   | 
| − | 		  'CatNum'	=> SQLValue($objRows->CatNum),
  |   | 
| − | 		  'Descr'	=> SQLValue($objRows->Item()->DescLong()),
  |   | 
| − | 		  'QtyOrd'	=> $intQty,
  |   | 
| − | 		  'Price'	=> SQLValue($objRows->PriceItem),
  |   | 
| − | 		  'ShipPkg'	=> SQLValue($objRows->ShipPkgDest),
  |   | 
| − | 		  'ShipItm'	=> SQLValue($objRows->ShipItmDest),
  |   | 
| − | 		  'isShipEst'	=> 'FALSE'
  |   | 
| − | 		  );
  |   | 
| − | 
  |   | 
| − | 		// has this item already been transcribed?
  |   | 
| − | 		$sqlFilt = '(ID_Order='.$this->ID.') AND (ID_Item='.$idItem.')';
  |   | 
| − | 		$objOrdItems = $objTbl->GetData($sqlFilt);
  |   | 
| − | 		if ($objOrdItems->RowCount() > 0)  {
  |   | 
| − | 		    // already transcribed -- update existing record
  |   | 
| − | 		    $objTbl->Update($arUpd,$sqlFilt);
  |   | 
| − | 		    $intUpd++;
  |   | 
| − | 		} else {
  |   | 
| − | 		    // not already transcribed -- insert new record
  |   | 
| − | 		    $arIns = $arUpd + array(
  |   | 
| − | 		      'ID_Order' 	=> $this->ID,
  |   | 
| − | 		      'Seq'		=> $intSeq,
  |   | 
| − | 		      'ID_Item'		=> $idItem
  |   | 
| − | 		      );
  |   | 
| − | 		    $objTbl->Insert($arIns);
  |   | 
| − | 		    $intNew++;
  |   | 
| − | 		}
  |   | 
| − | 	    }
  |   | 
| − | 	    $strLines = $intNew.' order line'.Pluralize($intNew).' created';
  |   | 
| − | 	    if ($intUpd > 0) {
  |   | 
| − | 		$strLines .= ' and '.$intUpd.' order line'.Pluralize($intUpd).' updated';
  |   | 
| − | 	    }
  |   | 
| − | 	    $strLines .= ' from cart lines';
  |   | 
| − | 	    $this->FinishEvent(array('descr'=>$strLines));
  |   | 
| − | 	} else {
  |   | 
| − | 	    $this->StartEvent_Simple('CL0','No cart lines found at order creation time',__METHOD__);
  |   | 
| − | 	    $out = '<b>There has been an error</b>: your cart contents seem to be missing.';
  |   | 
| − | 	    // TO DO: send email alerting webmaster; print more helpful message.
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       PURPOSE: nomenclatural purity. I could have just added the display code to RenderReceipt(),
  |   | 
| − | 	but it seemed like a good idea to have a function that just generates the output without
  |   | 
| − | 	displaying it. It also seemed like a good idea not to add the code to the calling function,
  |   | 
| − | 	because that's untidy, So here we are, with a function shorter than its comments.
  |   | 
| − |       NOTE: admin functions will generally want to handle the display themselves, so it is good practice
  |   | 
| − | 	to be able to retrieve the output to be displayed without displaying it.
  |   | 
| − | 	It may be redundant to have ShowReceipt() at all, however.
  |   | 
| − |     */
  |   | 
| − |     public function ShowReceipt() {
  |   | 
| − | 	$out = $this->RenderReceipt();
  |   | 
| − | 	echo $out;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       ACTION: Render the receipt in HTML
  |   | 
| − | 	Shows the order as generated from *cart* data, not what's in the order record.
  |   | 
| − | 	...except for the order number.
  |   | 
| − |     */
  |   | 
| − |     public function RenderReceipt() {
  |   | 
| − | 	$out = NULL;
  |   | 
| − | 
  |   | 
| − | // 2010-10-17 This should have been loaded earlier, but I don't have time to track it down now.
  |   | 
| − | //	The problem is that it forgets the cart ID otherwise.
  |   | 
| − | 	$this->Reload();
  |   | 
| − | 
  |   | 
| − | 	// get core objects, for code clarity/portability
  |   | 
| − | 	$objOrd = $this;
  |   | 
| − | 	$objCart = $this->Cart();
  |   | 
| − | 	$idOrder = $this->KeyValue();
  |   | 
| − | 	$idCart = $this->Value('ID_Cart');
  |   | 
| − | 
  |   | 
| − | 	if (!is_object($objCart)) {
  |   | 
| − | 	    throw new exception("Receipt could not get cart object: Cart ID=[$idCart]");
  |   | 
| − | 	}
  |   | 
| − | 	if (($objOrd->IsNew()) || ($objCart->IsNew())) {
  |   | 
| − | 	    throw new exception("Receipt has missing object: Order ID=[$idOrder] Cart ID=[$idCart]");
  |   | 
| − | 	}
  |   | 
| − | 
  |   | 
| − | 	$objCart->GetDetailObjs();
  |   | 
| − | 	$objPay = $objCart->PersonCustObj()->Payment();
  |   | 
| − | 	$objAddrCard = $objCart->AddrCardObj();
  |   | 
| − | 	// the next line is a kluge which only works as long as payment is always ccard
  |   | 
| − | 	// it's also not clear why GetDetailObjs() isn't loading it properly
  |   | 
| − | 	$objPay->Node('addr', $objAddrCard);
  |   | 
| − | 
  |   | 
| − | 	$arVars = array(
  |   | 
| − | 	  'ord.num'	=> $objOrd->Number,
  |   | 
| − | 	  'timestamp'	=> date(KF_RCPT_TIMESTAMP),
  |   | 
| − | 	  'cart.id'	=> $objCart->ID,
  |   | 
| − | 	  'cart.detail'	=> $objCart->RenderConfirm(),
  |   | 
| − | 	  'ship.name'	=> $objCart->AddrShipObj()->Name()->Value(),
  |   | 
| − | 	  'ship.addr'	=> $objCart->AddrShipObj()->AsText("\n<br>"),
  |   | 
| − | 	  'pay.name'	=> $objPay->Addr()->Name()->Value(),
  |   | 
| − | 	  'pay.spec'	=> $objPay->SafeDisplay(),
  |   | 
| − | 	  'email.short'	=> 'orders-'.date('Y').'@vbz.net'
  |   | 
| − | 	  );
  |   | 
| − | 	$objStrTplt = new clsStringTemplate_array(NULL,NULL,$arVars);
  |   | 
| − | 	$objStrTplt->MarkedValue(KHT_RCPT_TPLT);
  |   | 
| − | 	$out = "<!-- ORDER ID: [$idOrder] / CART ID from order: [$idCart] -->";
  |   | 
| − | 	$out .= $objStrTplt->Replace();
  |   | 
| − | 	return $out;
  |   | 
| − | 
  |   | 
| − | //	return $objCart->RenderReceipt();
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: Email address to use for this order (includes order number in address, as a way of tracking
  |   | 
| − | 	spam and possibly later as a way of automatically filing order-related correspondence)
  |   | 
| − |     */
  |   | 
| − |     public function EmailAddr() {
  |   | 
| − | 	return 'order-'.$this->Number.'@vbz.net';
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: Order receipt (from cart data) in text-only format, suitable for emailing
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-09-13 writing started
  |   | 
| − |     */
  |   | 
| − |     public function RenderReceipt_Text() {
  |   | 
| − | 	$out = $this->Cart()->RenderOrder_Text();
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: array of values needed to plug into email templates
  |   | 
| − |       USED BY:
  |   | 
| − | 	cart class, to generate confirmation email
  |   | 
| − | 	order admin class, to display/generate confirmation email
  |   | 
| − |     */
  |   | 
| − |     public function TemplateVars() {
  |   | 
| − | //	global $ksTextEmail;
  |   | 
| − | 	$this->Reload(); // 2010-10-28 kluge attempt - see also RenderReceipt()
  |   | 
| − | 
  |   | 
| − | 	$objCart = $this->Cart();
  |   | 
| − | 	$objCust = $objCart->PersonCustObj();
  |   | 
| − | 	$objPay = $objCust->Payment();
  |   | 
| − | 
  |   | 
| − | 	assert('is_object($objCart)');
  |   | 
| − | 	assert('is_object($objCart)');
  |   | 
| − | 	assert('is_object($objPay)');
  |   | 
| − | 
  |   | 
| − | 	$objCardAddr = $objCart->AddrCardObj();
  |   | 
| − | 	$objCustCont = $objCart->ContCustObj();
  |   | 
| − | 
  |   | 
| − | 	assert('is_object($objCardAddr)');
  |   | 
| − | 	assert('is_object($objCustCont)');
  |   | 
| − | 
  |   | 
| − | 	$arVars = array(
  |   | 
| − | // THIS IS A KLUGE -- should be something like objCart->CustObj->Name
  |   | 
| − | 	  'cust-name' => $objCardAddr->Name()->Value(),
  |   | 
| − | 	  'cust-email' => $objCustCont->Email()->Value(),
  |   | 
| − | 	  'orders-email' => $this->EmailAddr(),
  |   | 
| − | 	  'ord-num' => $this->Number
  |   | 
| − | 	  );
  |   | 
| − | 	return $arVars;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: array of values needed for sending confirmation email
  |   | 
| − |       USED BY:
  |   | 
| − | 	cart class, to generate confirmation email
  |   | 
| − | 	order admin class, to display/generate confirmation email
  |   | 
| − |     */
  |   | 
| − |     public function EmailParams(array $iarVars, $iSubj=NULL, $iMsgPre=NULL) {
  |   | 
| − | 	$arVars = $iarVars;
  |   | 
| − | 	$objStrTplt = new clsStringTemplate_array(KS_TPLT_OPEN,KS_TPLT_SHUT,$arVars);
  |   | 
| − | 
  |   | 
| − | 	$txtSubj = $iSubj;
  |   | 
| − | 	if (empty($txtSubj)) {
  |   | 
| − | 	    //$objStrTplt->MarkedValue(KS_TEXT_EMAIL_SUBJECT);
  |   | 
| − | 	    $txtSubj = $objStrTplt->Replace(KS_TPLT_EMAIL_SUBJECT);
  |   | 
| − | 	}
  |   | 
| − | 	$arOut['subj'] = $txtSubj;
  |   | 
| − | 
  |   | 
| − | 	$txtMsgPre = $iMsgPre;
  |   | 
| − | 	if (empty($txtMsgPre)) {
  |   | 
| − | 	    $txtMsgPre = $objStrTplt->Replace(KS_TPLT_EMAIL_MSG_TOP);
  |   | 
| − | 	}
  |   | 
| − | 	$arOut['msg.pre'] = $txtMsgPre;
  |   | 
| − | 
  |   | 
| − | 	$arVars['subject'] = $txtSubj;
  |   | 
| − | 	$objStrTplt->List = $arVars;
  |   | 
| − | 
  |   | 
| − | 	//$objStrTplt->MarkedValue(KS_TEXT_EMAIL_ADDR_SELF);
  |   | 
| − | 	$txtAddr_Self = $objStrTplt->Replace(KS_TPLT_EMAIL_ADDR_SELF);
  |   | 
| − | 	$arOut['addr.self'] = $txtAddr_Self;
  |   | 
| − | 
  |   | 
| − | 	//$objStrTplt->MarkedValue(KS_TEXT_EMAIL_ADDR_CUST);
  |   | 
| − | 	$txtAddr_Cust = $objStrTplt->Replace(KS_TPLT_EMAIL_ADDR_CUST);
  |   | 
| − | 	$arOut['addr.cust'] = $txtAddr_Cust;
  |   | 
| − | 
  |   | 
| − | 	// Calculate text of email to send:
  |   | 
| − | 	$txtEmailBody = $txtMsgPre."\n".$this->RenderReceipt_Text();
  |   | 
| − | 	$arOut['msg.body'] = $txtEmailBody;
  |   | 
| − | 
  |   | 
| − | 	return $arOut;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       ACTION: Email an order confirmation (or show what would be sent)
  |   | 
| − |       RETURNS: contents of the email
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2010-10-08 moved from VbzAdminOrder to clsOrder
  |   | 
| − |       INPUT:
  |   | 
| − | 	iReally: if FALSE, does not actually send the email, but only renders (and returns) it
  |   | 
| − | 	iParams: array of specifications for how to send the email
  |   | 
| − | 	  iParams['to-self']: if TRUE, send a copy to the store admin's address
  |   | 
| − | 	  iParams['to-cust']: if TRUE, send a copy to the customer's address
  |   | 
| − | 	  iParams['addr-self']: email address for store admin
  |   | 
| − | 	  iParams['addr-cust']: email address for customer
  |   | 
| − | 	  iParams['subject']: subject of the email
  |   | 
| − | 	  iParams['message']: body of the email
  |   | 
| − |     */
  |   | 
| − |     //public function EmailConfirm($iReally, $iSendToSelf, $iSendToCust, $iAddrSelf, $iAddrCust, $iSubject, $iMessage) {
  |   | 
| − |     public function EmailConfirm($iReally, array $iParams, clsLogger $iLog) {
  |   | 
| − | 	$iSendToSelf = $iParams['to-self'];
  |   | 
| − | 	$iSendToCust = $iParams['to-cust'];
  |   | 
| − | 	$iAddrSelf = $iParams['addr-self'];
  |   | 
| − | 	$iAddrCust = $iParams['addr-cust'];
  |   | 
| − | 	$iSubject = $iParams['subject'];
  |   | 
| − | 	$iMessage = $iParams['message'];
  |   | 
| − | 	$htEmailAddr_Self = htmlspecialchars($iAddrSelf);
  |   | 
| − | 	$htEmailAddr_Cust = htmlspecialchars($iAddrCust);
  |   | 
| − | 
  |   | 
| − | 	$htEmailBody = htmlspecialchars($iMessage);
  |   | 
| − | 	$txtSubj = $iSubject;
  |   | 
| − | 	$htSubj = htmlspecialchars($txtSubj);
  |   | 
| − | 	$intCustCopy = $iSendToCust?'1':'0';
  |   | 
| − | 	$txtCustCopy = $iSendToCust?'YES':'no';
  |   | 
| − | 	$txtSelfCopy = $iSendToSelf?'YES':'no';
  |   | 
| − | 	$doSend = $iReally;
  |   | 
| − | 
  |   | 
| − | 	$out = NULL;
  |   | 
| − | 	$out .= '<table>';
  |   | 
| − | 	$out .= "<tr><td align=right><b>Store's email address</b>:</td><td>$htEmailAddr_Self</td></tr>";
  |   | 
| − | 	$out .= "<tr><td align=right><b>Customer's email address</b>:</td><td>$htEmailAddr_Cust</td></tr>";
  |   | 
| − | 	$out .= "<tr><td align=right><b>Subject</b>:</td><td>$htSubj</td></tr>";
  |   | 
| − | 	$out .= "<tr><td align=right><b>Copying to customer?</td><td>$txtCustCopy</td></tr>";
  |   | 
| − | 	$out .= '</table>';
  |   | 
| − |       
  |   | 
| − | 	$out .= '<pre>'.$htEmailBody.'</pre>';
  |   | 
| − | 	if (!$doSend) {
  |   | 
| − | 	    $out .= '<form method=post>';
  |   | 
| − | 	    $out .= '<input type=hidden name=email-body value="'.$htEmailBody.'">';
  |   | 
| − | 	    $out .= '<input type=hidden name=send-to-cust value="'.$intCustCopy.'">';
  |   | 
| − | 	    $out .= '<input type=hidden name=addr-self value="'.$htEmailAddr_Self.'">';
  |   | 
| − | 	    $out .= '<input type=hidden name=addr-cust value="'.$htEmailAddr_Cust.'">';
  |   | 
| − | 	    $out .= '<input type=hidden name=subject value="'.$htSubj.'">';
  |   | 
| − | 	    $out .= '<input type=submit name=btnSend value="Send email">';
  |   | 
| − | 	    $out .= '</form>';
  |   | 
| − | 	}
  |   | 
| − | 
  |   | 
| − | 	if ($iReally) {
  |   | 
| − | 	    // if we're actually sending, then actually send the email and log it:
  |   | 
| − | 
  |   | 
| − | 	    // log attempt to send email (EM: email/manual)
  |   | 
| − | 	    $arEv = array(
  |   | 
| − | 	      'descr'	=> 'manual order email (self:'.$txtSelfCopy.' cust:'.$txtCustCopy.') Subject: '.$txtSubj,
  |   | 
| − | 	      'code'	=> 'OEM',
  |   | 
| − | 	      'where'	=> __METHOD__
  |   | 
| − | 	      );
  |   | 
| − | 	    $iLog->StartEvent($arEv);
  |   | 
| − | 
  |   | 
| − | 	    if ($iSendToCust) {
  |   | 
| − | 		// if being sent to customer. record the email in the messages table
  |   | 
| − | 	    }
  |   | 
| − | 	    $okSelf = TRUE;
  |   | 
| − | 	    if ($iSendToSelf) {
  |   | 
| − | 		// send our copy of the email
  |   | 
| − | 		$okSelf = mail($iAddrSelf,$txtSubj.' (store copy)',$iMessage,"From: $iAddrCust");
  |   | 
| − | 	    }
  |   | 
| − | 	    $okCust = TRUE;
  |   | 
| − | 	    if ($iSendToCust) {
  |   | 
| − | 		// log the message we're trying to send
  |   | 
| − | 		global $vgUserName;
  |   | 
| − | 		$this->LogEmail(NULL,$vgUserName,'customer',$txtSubj,$iMessage);
  |   | 
| − | 
  |   | 
| − | 		// send the message to the customer
  |   | 
| − | 		$okCust = mail($iAddrCust,$txtSubj,$iMessage,"From: $iAddrSelf");
  |   | 
| − | 	    }
  |   | 
| − | 
  |   | 
| − | 	    // log event completion
  |   | 
| − | 	    if ($okSelf && $okCust) {
  |   | 
| − | 		$arEv = NULL;
  |   | 
| − | 	    } else {
  |   | 
| − | 		$arEv = array(
  |   | 
| − | 		  'descrfin'	=> "self ok:$okSelf | cust ok:$okCust",
  |   | 
| − | 		  'error'	=> TRUE,
  |   | 
| − | 		  'severe'	=> TRUE);
  |   | 
| − | 	    }
  |   | 
| − | 	    $this->FinishEvent($arEv);
  |   | 
| − | 	}
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     public function LogEmail($iPackage,$iTxtFrom,$iTxtTo,$iSubject,$iMessage) {
  |   | 
| − | 	$this->LogMessage(
  |   | 
| − | 	  $iPackage,
  |   | 
| − | 	  KSI_ORD_MSG_EMAIL,
  |   | 
| − | 	  $iTxtFrom,
  |   | 
| − | 	  $iTxtTo,
  |   | 
| − | 	  $iSubject,
  |   | 
| − | 	  $iMessage);
  |   | 
| − |     }
  |   | 
| − |     public function LogMessage($iPackage,$iMethod,$iTxtFrom,$iTxtTo,$iSubject,$iMessage) {
  |   | 
| − | 	$this->objDB->OrdMsgs()->Add(
  |   | 
| − | 	  $this->ID,
  |   | 
| − | 	  $iPackage,
  |   | 
| − | 	  $iMethod,
  |   | 
| − | 	  $iTxtFrom,
  |   | 
| − | 	  $iTxtTo,
  |   | 
| − | 	  $iSubject,
  |   | 
| − | 	  $iMessage);
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | class clsOrderLines extends clsTable {
  |   | 
| − |     const TableName='ord_lines';
  |   | 
| − | 
  |   | 
| − |     public function __construct($iDB) {
  |   | 
| − | 	parent::__construct($iDB);
  |   | 
| − | 	  $this->Name(self::TableName);
  |   | 
| − | 	  $this->KeyName('ID');
  |   | 
| − | 	  $this->ClassSng('clsOrderLine');
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | class clsOrderLine extends clsDataSet {
  |   | 
| − |     public function Init_fromCartLine(clsShopCartLine $iLine) {
  |   | 
| − | 	// some fields get copied over directly
  |   | 
| − | 	$arNames = array(
  |   | 
| − | 	  'Seq'		=> 'Seq',
  |   | 
| − | 	  'ID_Item'	=> 'ID_Item',
  |   | 
| − | 	  'Qty'		=> 'QtyOrd',
  |   | 
| − | 	  'CatNum'	=> 'CatNum',
  |   | 
| − | 	  'PriceItem'	=> 'Price',
  |   | 
| − | 	  'ShipPkgDest'	=> 'ShipPkg',
  |   | 
| − | 	  'ShipItmDest'	=> 'ShipItm',
  |   | 
| − | 	  'DescText'	=> 'Descr',
  |   | 
| − | 	  //'DescHtml'	=> 'DescrHTML'	// we may eventually add this field
  |   | 
| − | 	  );
  |   | 
| − | 	foreach($arNames as $srce => $dest) {
  |   | 
| − | 	    $val = $iLine->Value($srce);
  |   | 
| − | 	    $this->Value($dest,$val);
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-03-23 created for AdminPage()
  |   | 
| − |     */
  |   | 
| − |     protected $objItem, $idItem;
  |   | 
| − |     public function ItemObj() {
  |   | 
| − | 	$doLoad = TRUE;
  |   | 
| − | 	$id = $this->Value('ID_Item');
  |   | 
| − | 	if (isset($this->idItem)) {
  |   | 
| − | 	    if ($this->idItem == $id) {
  |   | 
| − | 		$doLoad = FALSE;
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	if ($doLoad) {
  |   | 
| − | 	    $this->objItem = $this->Engine()->Items($id);
  |   | 
| − | 	    $this->idItem = $id;
  |   | 
| − | 	}
  |   | 
| − | 	return $this->objItem;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: selling price
  |   | 
| − | 	if order line has no price, falls back to catalog item
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-03-23 created for "charge for package" process
  |   | 
| − |     */
  |   | 
| − |     public function PriceSell() {
  |   | 
| − | 	$prc = $this->Value('Price');
  |   | 
| − | 	if (is_null($prc)) {
  |   | 
| − | 	    $prc = $this->ItemObj()->PriceSell();
  |   | 
| − | 	}
  |   | 
| − | 	return $prc;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: shipping per-package price
  |   | 
| − | 	if order line has no per-package price, falls back to catalog item
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-03-23 created for "charge for package" process
  |   | 
| − |     */
  |   | 
| − |     public function ShPerPkg() {
  |   | 
| − | 	$prc = $this->Value('ShipPkg');
  |   | 
| − | 	if (is_null($prc)) {
  |   | 
| − | 	    $prc = $this->ItemObj()->ShPerPkg();
  |   | 
| − | 	}
  |   | 
| − | 	return $prc;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: shipping per-item price -- defaults to catalog item's data
  |   | 
| − | 	unless specified in package line
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-03-23 created for "charge for package" process
  |   | 
| − |     */
  |   | 
| − |     public function ShPerItm() {
  |   | 
| − | 	$prc = $this->Value('ShipItm');
  |   | 
| − | 	if (is_null($prc)) {
  |   | 
| − | 	    $prc = $this->ItemObj()->ShPerItm();
  |   | 
| − | 	}
  |   | 
| − | 	return $prc;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       RETURNS: array of calculated values for this order line
  |   | 
| − | 	array[sh-pkg]: shipping charge per package
  |   | 
| − | 	array[sh-itm.qty]: shipping charge per item, adjusted for quantity ordered
  |   | 
| − | 	array[cost-sell.qty]: selling cost, adjusted for quantity ordered
  |   | 
| − |       USED BY: so far, only admin functions (shopping functions use Cart objects, not Order)
  |   | 
| − |     */
  |   | 
| − |     public function FigureStats() {
  |   | 
| − | 	$qty = $this->Value('QtyOrd');
  |   | 
| − | 	if ($qty != 0) {
  |   | 
| − | 	    $prcShPkg = $this->ShPerPkg();
  |   | 
| − | 	} else {
  |   | 
| − | 	    // none of this item in package, so don't require this minimum
  |   | 
| − | 	    $prcShPkg = 0;
  |   | 
| − | 	}
  |   | 
| − | 	$arOut['sh-pkg'] = $prcShPkg;
  |   | 
| − | 	$arOut['sh-itm.qty'] = $this->ShPerItm() * $qty;
  |   | 
| − | 	$arOut['cost-sell.qty'] = $this->PriceSell() * $qty;
  |   | 
| − | 	return $arOut;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       ACTION: Figures totals for the current rowset
  |   | 
| − |       USED BY: so far, only admin functions (shopping functions use Cart objects, not Order)
  |   | 
| − |       RETURNS: array in same format as FigureStats(), except with ".qty" removed from index names
  |   | 
| − |     */
  |   | 
| − |     public function FigureTotals() {
  |   | 
| − | 	$arSum = NULL;
  |   | 
| − | 	while ($this->NextRow()) {
  |   | 
| − | 	    $ar = $this->FigureStats();
  |   | 
| − | 
  |   | 
| − | 	    $prcShItmSum = nzArray($arSum,'sh-itm',0);
  |   | 
| − | 	    $prcShPkgMax = nzArray($arSum,'sh-pkg',0);
  |   | 
| − | 	    $prcSaleSum = nzArray($arSum,'cost-sell',0);
  |   | 
| − | 
  |   | 
| − | 	    $prcShItmThis = $ar['sh-itm.qty'];
  |   | 
| − | 	    $prcShPkgThis = $ar['sh-pkg'];
  |   | 
| − | 	    $prcSaleThis = $ar['cost-sell.qty'];
  |   | 
| − | 
  |   | 
| − | 	    $arSum['sh-itm'] = $prcShItmSum + $prcShItmThis;
  |   | 
| − | 	    $arSum['cost-sell'] = $prcSaleSum + $prcSaleThis;
  |   | 
| − | 	    if ($prcShPkgMax < $prcShPkgThis) {
  |   | 
| − | 		$prcShPkgMax = $prcShPkgThis;
  |   | 
| − | 	    }
  |   | 
| − | 	    $arSum['sh-pkg'] = $prcShPkgMax;
  |   | 
| − | 	}
  |   | 
| − | 	return $arSum;
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       ACTION: Render the current order line using static HTML (no form elements; read-only)
  |   | 
| − |       HISTORY:
  |   | 
| − | 	2011-04-01 adapted from clsShopCartLine::RenderHtml() to clsOrderLine::RenderStatic()
  |   | 
| − |     */
  |   | 
| − |     public function RenderStatic(clsShipZone $iZone) {
  |   | 
| − | // calculate display fields:
  |   | 
| − | 	$qty = $this->Value('QtyOrd');
  |   | 
| − | 	if ($qty) {
  |   | 
| − | 	    //$this->RenderCalc($iZone);
  |   | 
| − | 
  |   | 
| − | 	    $htLineCtrl = $qty;
  |   | 
| − | 
  |   | 
| − | 	    $mnyPrice = $this->Value('Price');	// item price
  |   | 
| − | 	    $mnyPerItm = $this->Value('ShipItm');	// per-item shipping
  |   | 
| − | 	    $mnyPerPkg = $this->Value('ShipPkg');	// per-pkg minimum shipping
  |   | 
| − | 	    $intQty = $this->Value('QtyOrd');
  |   | 
| − | 	    $mnyPriceQty = $mnyPrice * $intQty;		// line total sale
  |   | 
| − | 	    $mnyPerItmQty = $mnyPerItm * $intQty;	// line total per-item shipping
  |   | 
| − | 	    $mnyLineTotal = $mnyPriceQty + $mnyPerItmQty;	// line total overall (does not include per-pkg minimum)
  |   | 
| − | 
  |   | 
| − | 	    $strCatNum = $this->Value('CatNum');
  |   | 
| − | 	    $strPrice = FormatMoney($mnyPrice);
  |   | 
| − | 	    $strPerItm = FormatMoney($mnyPerItm);
  |   | 
| − | 	    $strPriceQty = FormatMoney($mnyPriceQty);
  |   | 
| − | 	    $strPerItmQty = FormatMoney($mnyPerItmQty);
  |   | 
| − | 	    $strLineTotal = FormatMoney($mnyLineTotal);
  |   | 
| − | 
  |   | 
| − | 	    $strShipPkg = FormatMoney($mnyPerPkg);
  |   | 
| − | 
  |   | 
| − | 	    $htDesc = $this->Value('Descr');	// was 'DescHtml', but that field doesn't exist here
  |   | 
| − | 
  |   | 
| − | 	    $htDelBtn = '';
  |   | 
| − | 
  |   | 
| − | 	    $out = <<<__END__
  |   | 
| − | <tr>
  |   | 
| − | <td>$htDelBtn$strCatNum</td>
  |   | 
| − | <td>$htDesc</td>
  |   | 
| − | <td class=cart-price align=right>$strPrice</td>
  |   | 
| − | <td class=shipping align=right>$strPerItm</td>
  |   | 
| − | <td class=qty align=right>$htLineCtrl</td>
  |   | 
| − | <td class=cart-price align=right>$strPriceQty</td>
  |   | 
| − | <td class=shipping align=right>$strPerItmQty</td>
  |   | 
| − | <td class=total align=right>$strLineTotal</td>
  |   | 
| − | <td class=shipping align=right>$strShipPkg</td>
  |   | 
| − | </tr>
  |   | 
| − | __END__;
  |   | 
| − | 	    return $out;
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | 
  |   | 
| − | /*----------
  |   | 
| − |   CLASS PAIR: order messages (table ord_msg)
  |   | 
| − | */
  |   | 
| − | class clsOrderMsgs extends clsTable {
  |   | 
| − |     public function __construct($iDB) {
  |   | 
| − | 	parent::__construct($iDB);
  |   | 
| − | 	  $this->Name('ord_msg');
  |   | 
| − | 	  $this->KeyName('ID');
  |   | 
| − | 	  $this->ClassSng('clsOrderMsg');
  |   | 
| − |     }
  |   | 
| − |     /*----
  |   | 
| − |       ACTION: Adds a message to the order
  |   | 
| − |     */
  |   | 
| − |     public function Add($iOrder,$iPackage,$iMethod,$iTxtFrom,$iTxtTo,$iSubject,$iMessage) {
  |   | 
| − | 	global $vgUserName;
  |   | 
| − | 
  |   | 
| − | 	$arIns = array(
  |   | 
| − | 	  'ID_Ord'	=> $iOrder,
  |   | 
| − | 	  'ID_Pkg'	=> SQLValue($iPackage),	// might be NULL
  |   | 
| − | 	  'ID_Media'	=> SQLValue($iMethod),
  |   | 
| − | 	  'TxtFrom'	=> SQLValue($iTxtFrom),
  |   | 
| − | 	  'TxtTo'	=> SQLValue($iTxtTo),
  |   | 
| − | 	  'TxtRe'	=> SQLValue($iSubject),
  |   | 
| − | 	  'doRelay'	=> 'FALSE',	// 2010-09-23 this field needs to be re-thought
  |   | 
| − | 	  'WhenCreated'	=> 'NOW()',	// later: add this as an optional argument, if needed
  |   | 
| − | 	  'WhenEntered'	=> 'NOW()',
  |   | 
| − | 	  'WhenRelayed' => 'NULL',
  |   | 
| − | 	  'Message'	=> SQLValue($iMessage)
  |   | 
| − | 	  );
  |   | 
| − | 	$this->Insert($arIns);
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
| − | class clsOrderMsg extends clsDataSet {
  |   | 
| − | }
  |   | 
| − | 
  |   | 
| − | /* =======
  |   | 
| − |  CREDIT CARD UTILITY CLASS
  |   | 
| − | */
  |   | 
| − | class clsCustCards extends clsTable {
  |   | 
| − |     public function __construct($iDB) {
  |   | 
| − | 	parent::__construct($iDB);
  |   | 
| − | 	  $this->Name('cust_cards');
  |   | 
| − | 	  $this->KeyName('ID');
  |   | 
| − |     }
  |   | 
| − | 
  |   | 
| − |     // STATIC section //
  |   | 
| − | 
  |   | 
| − |    public static function CardTypeChar($iNum) {
  |   | 
| − | 	$chDigit = substr($iNum,0,1);
  |   | 
| − | 	$arDigits = array(
  |   | 
| − | 	  '3' => 'A',
  |   | 
| − | 	  '4' => 'V',
  |   | 
| − | 	  '5' => 'M',
  |   | 
| − | 	  '6' => 'D'
  |   | 
| − | 	  );
  |   | 
| − | 	if (isset($arDigits[$chDigit])) {
  |   | 
| − | 	    $chOut = $arDigits[$chDigit];
  |   | 
| − | 	} else {
  |   | 
| − | 	    $chOut = '?'.$chDigit;
  |   | 
| − | 	}
  |   | 
| − | 	return $chOut;
  |   | 
| − |     }
  |   | 
| − |     public static function CardTypeName($iNum) {
  |   | 
| − | 	$chDigit = substr($iNum,0,1);
  |   | 
| − | 	$arDigits = array(
  |   | 
| − | 	  '3' => 'Amex',
  |   | 
| − | 	  '4' => 'Visa',
  |   | 
| − | 	  '5' => 'MasterCard',
  |   | 
| − | 	  '6' => 'Discover'
  |   | 
| − | 	  );
  |   | 
| − | 	if (isset($arDigits[$chDigit])) {
  |   | 
| − | 	    $out = $arDigits[$chDigit];
  |   | 
| − | 	} else {
  |   | 
| − | 	    $out = '?'.$chDigit;
  |   | 
| − | 	}
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     public static function SafeDescr_Short($iNum,$iExp) {
  |   | 
| − | 	//$dtExp = strtotime($this->CardExp);
  |   | 
| − | 	$dtExp = self::ExpDate($iExp);
  |   | 
| − | 	if (is_null($dtExp)) {
  |   | 
| − | 	    $strDate = '?/?';
  |   | 
| − | 	} else {
  |   | 
| − | 	    $strDate = $dtExp->format('n/y');
  |   | 
| − | 	}
  |   | 
| − | 	$out = self::CardTypeChar($iNum).'-'.substr($iNum,-4).'x'.$strDate;
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     public static function SafeDescr_Long($iNum,$iExp) {
  |   | 
| − | 	$dtExp = self::ExpDate($iExp);
  |   | 
| − | 	if (is_null($dtExp)) {
  |   | 
| − | 	    $strDate = '?/?';
  |   | 
| − | 	} else {
  |   | 
| − | 	    $strDate = $dtExp->format('F').' of '.$dtExp->format('Y').' ('.$dtExp->format('n/y').')';
  |   | 
| − | 	}
  |   | 
| − | 	$out = self::CardTypeName($iNum).' # ends with -'.substr($iNum,-4).' expires in '.$strDate;
  |   | 
| − | 	return $out;
  |   | 
| − |     }
  |   | 
| − |     public static function Searchable($iRaw) {
  |   | 
| − | 	$xts = new xtString(strtolower($iRaw),TRUE);
  |   | 
| − | 	$xts->KeepOnly('0-9');	// keep only numerics
  |   | 
| − | 	return $xts->Value;
  |   | 
| − |     }
  |   | 
| − |     /*-----
  |   | 
| − |       INPUT:
  |   | 
| − | 	iMaxFuture: if year is given as 2 digits, then this is the furthest in the future the year
  |   | 
| − | 	  is allowed to be (# of years from now). NOTE: Should be tested with current dates after 2050
  |   | 
| − | 	  (or between 1950 and 1999) to make sure it doesn't allow a year too far in the past.
  |   | 
| − |       OUTPUT: EXP as a DateTime object
  |   | 
| − |     */
  |   | 
| − |     public static function ExpDate($iRaw,$iMaxFuture=50) {
  |   | 
| − | 	$strExp = $iRaw;
  |   | 
| − | 	// -- split into month/year or month/day/year
  |   | 
| − | 	$arExp = preg_split('/[\/.\- ]/',$strExp);
  |   | 
| − | 	$intParts = count($arExp);
  |   | 
| − | 	switch ($intParts) {
  |   | 
| − | 	  case 1:	// for now, we're going to assume MMYY[YY]
  |   | 
| − | 	    // TO DO: if people start typing in M with no leading zero, will have to check for even/odd # of chars
  |   | 
| − | 	    $intMo = substr($strExp,0,2);
  |   | 
| − | 	    $intYr = substr($strExp,2);
  |   | 
| − | 	    break;
  |   | 
| − | 	  case 2:	// month/year
  |   | 
| − | 	    $intMo = $arExp[0];
  |   | 
| − | 	    $intYr = $arExp[1];
  |   | 
| − | 	    break;
  |   | 
| − | 	  case 3:	// month/day/year or year/month/day
  |   | 
| − | 	    if (strlen($arExp[0]) > 3) {
  |   | 
| − | 	      $intYr = $arExp[0];
  |   | 
| − | 	      $intMo = $arExp[1];
  |   | 
| − | 	      $intDy = $arExp[2];
  |   | 
| − | 	    } else {
  |   | 
| − | 	      $intMo = $arExp[0];
  |   | 
| − | 	      $intDy = $arExp[1];
  |   | 
| − | 	      $intYr = $arExp[2];
  |   | 
| − | 	    }
  |   | 
| − | 	    break;
  |   | 
| − | 	  default:
  |   | 
| − | 	    // unknown format, can't do anything
  |   | 
| − | 	}
  |   | 
| − | 	// check for validity:
  |   | 
| − | 	$ok = FALSE;
  |   | 
| − | 	if (isset($intYr)) {
  |   | 
| − | 	    if ($intYr > 0) {
  |   | 
| − | 		if (($intMo > 0) && ($intMo < 13)) {
  |   | 
| − | 		    $ok = TRUE;
  |   | 
| − | 		}
  |   | 
| − | 	    }
  |   | 
| − | 	}
  |   | 
| − | 	if ($ok) {
  |   | 
| − | 	    if ($intYr < 100) {	// if year has no century, give it one
  |   | 
| − | 		$intYrNowPart = strftime('%y');
  |   | 
| − | 		$intCent = (int)substr(strftime('%Y'),0,2);
  |   | 
| − | 		if ($intYr < $intYrNowPart) {
  |   | 
| − | 		    $intCent++;
  |   | 
| − | 		}
  |   | 
| − | 		$intYr += ($intCent*100);
  |   | 
| − | 		$intYrNowFull = (int)strftime('%Y');
  |   | 
| − | 		if ($intYr - $intYrNowFull > $iMaxFuture) {
  |   | 
| − | 		    $intYr -= 100;
  |   | 
| − | 		}
  |   | 
| − | 	    }
  |   | 
| − | 	    if (!isset($intDy)) {
  |   | 
| − | 		$intDy = cal_days_in_month(CAL_GREGORIAN, $intMo, $intYr);	// set to last day of month
  |   | 
| − | 	    }
  |   | 
| − | 	    $dtOut = $datetime = new DateTime();
  |   | 
| − | 	    $dtOut->setDate($intYr, $intMo, $intDy);
  |   | 
| − | 	    return $dtOut;
  |   | 
| − | 	} else {
  |   | 
| − | 	    return NULL;	// if no year, then could not parse format
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − |     public static function ExpDateSQL($iRaw) {
  |   | 
| − | 	$dt = self::ExpDate($iRaw);
  |   | 
| − | 	if (is_object($dt)) {
  |   | 
| − | 	    return '"'.$dt->format('Y-m-d').'"';
  |   | 
| − | 	} else {
  |   | 
| − | 	    return 'NULL';
  |   | 
| − | 	}
  |   | 
| − |     }
  |   | 
| − | }
  |   | 
|   |  |   |  | 
|   | /* ===============  |   | /* ===============  |