<?php

/**
 * QuickBooks action queue - A queue of actions to be performed via the QBWC
 *
 * Copyright (c) {2010-04-16} {Keith Palmer / ConsoliBYTE, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * The QuickBooks action queue can be used to queue up actions that need to be
 * pushed out to QuickBooks, or queue up items which need to be fetched from
 * QuickBooks.
 *
 * For instance, everytime someone creates an account on your website, you
 * would "push" their name, e-mail, etc. into a customer account within
 * QuickBooks. You could use the QuickBooks_Queue class to enqueue a request
 * for each new client like this:
 * <code>
 * require_once 'QuickBooks/Queue.php';
 * $queue = new QuickBooks_Queue('mysql://user:pass@localhost/database');
 * $queue->enqueue(QUICKBOOKS_ADD_CUSTOMER, $the_customer_ID_number_here);
 * </code>
 *
 * The next time the QuickBooks Web Connector calls your QuickBooks SOAP server,
 * the SOAP server calls your SOAP server handler function associated with
 * adding customer records, your handler function will generate the qbXML
 * request, and the request will be sent off to QuickBooks and (if everything
 * goes well) the customer will appear in QuickBooks.
 *
 * If you need to perform a particular action on a set schedule, say, for
 * example, pull customers from QuickBooks to your web application once per
 * day, then you can register a recurring event instead of queueing up a
 * request every single day.
 *
 * @author Keith Palmer <keith@consolibyte.com>
 * @license LICENSE.txt
 *
 * @package QuickBooks
 */

/**
 * Various QuickBooks-related utilities
 */
QuickBooks_Loader::load('/QuickBooks/Utilities.php');

/**
 * Helper singleton class
 */
QuickBooks_Loader::load('/QuickBooks/WebConnector/Queue/Singleton.php');

/**
 * QuickBooks queueing class - Queue up actions to be performed in QuickBooks
 */
class QuickBooks_WebConnector_Queue
{
	/**
	 * The default username to use when queueing items
	 * @var string
	 */
	protected $_user;

	/**
	 * Create a new QuickBooks queue instance
	 *
	 * @param mixed $dsn_or_conn	A DSN-style connection string (i.e.: mysq://root:pass@locahost/database) or a database connection (if you wish to re-use an existing database connection)
	 * @param array $config			Configuration array for the driver
	 */
	public function __construct($dsn_or_conn, $user = null, $config = array())
	{
		//$this->_driver = QuickBooks_Utilities::driverFactory($dsn_or_conn, $config);
		$this->_driver = QuickBooks_Driver_Factory::create($dsn_or_conn, $config);

		// No default username was provided, fetch the default from the driver
		if (!$user)
		{
			$user = $this->_driver->authDefault();
		}

		// Set the default username
		$this->_user = $user;
	}

	/**
	 * Get or set the username the queue is operating on/for
	 *
	 * @param string $user
	 * @return string
	 */
	public function user($user = null)
	{
		if ($user)
		{
			$this->_user = $user;
		}

		return $this->_user;
	}

	/**
	 * Request to enter "Interactive Mode" with the Web Connector
	 *
	 * * This function *does not* work. Please don't use it.
	 *
	 * @param integer $priority
	 * @param string $user
	 * @return boolean
	 */
	public function interactive($priority = 0, $user = null)
	{
		if ($this->_driver)
		{
			$tmp = array_merge(range('a', 'z'), range(0, 9));
			shuffle($tmp);
			$random = substr(implode('', $tmp), 0, 8);

			/*
			if (!$user)
			{
				$user = $this->_driver->authDefault();
			}
			*/

			if (!$user)
			{
				$user = $this->_user;
			}

			return $this->_driver->queueEnqueue(QUICKBOOKS_INTERACTIVE_MODE, $random, true, $priority, $user);
		}

		return false;
	}

	/**
	 * Register a recurring event
	 *
	 * Recurring events are actions that get queued up to run once every so
	 * often. So, for instance, if you want to make sure you issue a
	 * CustomerQuery every day, you can register a recurring event that occurs
	 * every day, and then the SOAP server/Web Connector will try to make sure
	 * that it gets run every day.
	 *
	 * Note that the SOAP server can't guarantee that an action will occur
	 * every interval *unless* the Web Connector is set to connect at that same
	 * interval or (preferably) more often.
	 *
	 * @param mixed $run_every		This can be either an integer number of seconds, or a string like: "15 minutes", "2 days", "hour", etc.
	 * @param string $action		The QuickBooks action you want to execute
	 * @param mixed $ident			An optional ident string (say you wanted to do a CustomerQuery for customer #14 every 15 minutes, you'd pass CustomerQuery for the action, and 14 for the ident)
	 * @param integer $priority		The priority of the action (higher priorities run first)
	 * @param mixed $extra			Any extra data to include for the request handler
	 * @param string $user			The username of the QuickBooks Web Connector user this event should be registered for
	 * @param boolean $replace		Whether or not this should replace any other recurring events with this action/ident
	 * @return boolean
	 */
	public function recurring($run_every, $action, $ident = null, $priority = 0, $extra = null, $user = null, $qbxml = null, $replace = true)
	{
		$run_every = QuickBooks_Utilities::intervalToSeconds($run_every);

		if (!strlen($ident))
		{
			$tmp = array_merge(array('a', 'z'), range(0, 9));
			shuffle($tmp);
			$ident = substr(implode('', $tmp), 0, 8);
		}

		if ($this->_driver)
		{
			/*
			if (!$user)
			{
				$user = $this->_driver->authDefault();
			}
			*/

			// Use the default user (provided in __construct) if none is given
			if (!$user)
			{
				$user = $this->_user;
			}

			return $this->_driver->recurEnqueue($user, $run_every, $action, substr($ident, 0, 40), $replace, $priority, $extra, $qbxml);
		}

		return false;
	}

	/**
	 * Add a new item/action to the QuickBooks queue
	 *
	 * Queue up a request for an action/ident pair to be sent over to
	 * QuickBooks. The next time the Web Connector connects, your request
	 * handler for the given action will be called, generating a qbXML request
	 * which will then be passed to QuickBooks. QuickBooks will pass the
	 * response back to the SOAP server and your response handler will be
	 * called.
	 *
	 * @param string $action		An action to be performed within QuickBooks (see the qbXML and QuickBooks SDK documentation, i.e.: "CustomerAdd", "InvoiceAdd", "CustomerMod", etc.)
	 * @param mixed $ident			A unique identifier (if required) for a record being operated on (i.e. if you're doing a "CustomerAdd", you'd probaly put a unique customer ID number here, so you're SOAP handler function knows which customer it is supposed to add)
	 * @param integer $priority		The priority of the update (higher priority actions will be pushed to QuickBooks before lower priority actions)
	 * @param array $extra			If you need to make additional bits of data available to your request/response functions, you can pass an array of extra data here
	 * @param string $user			The username of the QuickBooks Web Connector user this item should be queued for
	 * @param boolean $replace		Whether or not to replace any other currently queued entries with the same action/ident
	 * @return boolean
	 */
	public function enqueue($action, $ident = null, $priority = 0, $extra = null, $user = null, $qbxml = null, $replace = true)
	{
		if (!strlen($ident))
		{
			// If they didn't provide an $ident, generate a random, unique one

			$tmp = array_merge(range('a', 'z'), range(0, 9));
			shuffle($tmp);
			$ident = substr(implode('', $tmp), 0, 8);
		}

		if ($this->_driver)
		{
			/*
			if (!$user)
			{
				$user = $this->_driver->authDefault();
			}
			*/

			// Use the default user (provided in __construct) if none is given
			if (!$user)
			{
				$user = $this->_user;
			}

			return $this->_driver->queueEnqueue($user, $action, substr($ident, 0, 40), $replace, $priority, $extra, $qbxml);
		}

		return false;
	}

	/**
	 * Tell whether or not an action/ident already exists in the queue
	 *
	 * @param string $action	An action to be performed within QuickBooks
	 * @param mixed $ident		A unique identifier (if required) for the record being operated on
	 * @param string $user		The username of the user to check if the queued item exists for
	 * @return boolean			Whether or not that action/ident tuple is already in the queue
	 */
	public function exists($action, $ident, $user = null)
	{
		if ($this->_driver)
		{
			// Use the default user (provided in __construct) if none is given
			if (!$user)
			{
				$user = $this->_user;
			}

			return $this->_driver->queueExists($user, $action, $ident);
		}

		return null;
	}

	/**
	 * Tell the number of items currently in the queue
	 *
	 * @param string $user		The username of the user to check the queue size for
	 * @return integer			The number of items in the queue
	 */
	public function size($user = null)
	{
		if ($this->_driver)
		{
			// Use the default user (provided in __construct) if none is given
			if (!$user)
			{
				$user = $this->_user;
			}

			$queued = true;
			return $this->_driver->queueLeft($user, $queued);
		}

		return null;
	}

	/**
	 * Forcibly remove an item from the queue
	 *
	 * @param string $action
	 * @param string $ident
	 * @param string $user
	 * @return boolean
	 */
	public function remove($action, $ident, $user = null)
	{
		if ($this->_driver)
		{
			// Use the default user (provided in __construct) if none is given
			if (!$user)
			{
				$user = $this->_user;
			}

			$ticket = null;
			$new_status = QUICKBOOKS_STATUS_CANCELLED;
			return $this->_driver->queueRemove($user, $action, $ident);
		}

		return null;
	}

	/*public function identifier($type, $ident, $user = null)
	{
		$types = QuickBooks_Utilities::listObjects();
		//if (

		if ($this->_driver)
		{
			//if (!$user)
			//{
			//	$user = $this->_driver->authDefault();
			//}

			// Use the default user (provided in __construct) if none is given
			if (!$user)
			{
				$user = $this->_user;
			}

			$editseq = '';
			return $this->_driver->identFetch($user, $type, $ident, $editseq);
		}

		return null;
	}

	public function sequence($type, $ident, $user = null)
	{

	}
	*/

	/**
	 * Get debugging information from the queue
	 *
	 * @return array
	 */
	public function debug()
	{
		return array(
			'driver' => var_export($this->_driver),
			);
	}
}


<?php

/**
 * QuickBooks CreditCardRefund object
 *
 * @author Jayson Lindsley <jay.lindsley@gmail.com>
 * @author Keith Palmer <keith@consolibyte.com>
 *
 * @license LICENSE.txt
 *
 * @package QuickBooks
 * @subpackage Object
 */

/**
 * Base object class
 */
QuickBooks_Loader::load('/QuickBooks/QBXML/Object.php');

/**
* QuickBooks object class
 */
 class Quickbooks_QBXML_Object_CreditCardRefund extends QuickBooks_QBXML_Object
 {
 	/**
	 * Create a new QuickBooks_Object_Customer object
	 *
	 * @param array $arr
	 */

	public function __construct($arr = array())
	{
		parent::__construct($arr);
	}

	public function setCustomerListID($ListID)
	{
		return $this->set('CustomerRef ListID', $ListID);
	}

	public function getCustomerListID()
	{
		return $this->get('CustomerRef ListID');
	}

	public function setCustomerFullName($name)
	{
		return $this->set('CustomerRef FullName', $name);
	}

	public function getCustomerFullName()
	{
		return $this->get('CustomerRef FullName');
	}

	public function setAccountRefFullName($account)
	{
		return $this->set('RefundFromAccountRef FullName', $account);
	}

	public function getAccountRefFullName()
	{
		return $this->get('RefundFromAccountRef FullName');
	}

	public function setARAccountListID($ListID)
	{
		return $this->set('ARAccountRef ListID', $ListID);
	}

	public function setARAccountName($name)
	{
		return $this->set('ARAccountRef FullName', $name);
	}

	public function getARAccountListID()
	{
		return $this->get('ARAccountRef ListID');
	}

	public function getARAccountName()
	{
		return $this->get('ARAccountRef FullName');
	}

	public function setPaymentMethodName($name)
	{
		return $this->set('PaymentMethodRef FullName', $name);
	}

	public function getPaymentMethodName()
	{
		return $this->get('PaymentMethodRef FullName');
	}

	public function setPaymentMethodListID($ListID)
	{
		return $this->set('PaymentMethodRef ListID', $ListID);
	}

	public function getPaymentMethodListID()
	{
		return $this->get('PaymentMethodRef ListID');
	}

	public function setExchangeRate($rate)
	{
		return $this->set('ExchangeRate', (float)$rate);
	}

	public function getExchangeRate()
	{
		return $this->get('ExchangeRate');
	}

	public function setExternalGUID($guid)
	{
		return $this->set('ExternalGUID', $guid);
	}

	public function getExternalGUID()
	{
		return $this->get('ExternalGUID');
	}

	public function setMemo($memo)
	{
		return $this->set('Memo', $memo);
	}

	public function getMemo()
	{
		return $this->get('Memo');
	}

	public function setRefundAppliedToTxnID($ID)
	{
		return $this->set('RefundAppliedToTxnAdd TxnID', $ID);
	}

	public function getRefundAppliedToTxnID()
	{
		return $this->get('RefundAppliedToTxnAdd TxnID');
	}

	public function setRefundAmount($amount)
	{
		return $this->set('RefundAppliedToTxnAdd RefundAmount', $amount);
	}

	public function setRefNumber($ref)
	{
		return $this->set('RefNumber', $ref);
	}

	public function setTxnID($TxnID)
	{
		return $this->set('TxnID', $TxnID);
	}

	public function getTransactionID()
	{
		return $this->get('TxnID');
	}

	/* The properties below are used when querying only */
	/* They are processed by QB as or statements */

	public function setFromModifiedDate($date)
	{
		return $this->set('ModifiedDateRangeFilter FromModifiedDate', $date);
	}

	public function getFromModifiedDate()
	{
		return $this->get('ModifiedDateRangeFilter FromModifiedDate');
	}

	public function setToModifiedDate($date)
	{
		return $this->set('ModifiedDateRangeFilter ToModifiedDate', $date);
	}

	public function getToModifiedDate()
	{
		return $this->get('ModifiedDateRangeFilter ToModifiedDate');
	}

	public function setFromTxnDate($date)
	{
		return $this->set('TxnDateRangeFilter FromTxnDate', $date);
	}

	public function getFromTxnDate()
	{
		return $this->get('TxnDateRangeFilter FromTxnDate');
	}

	public function setToTxnDate($date)
	{
		return $this->set('TxnDateRangeFilter ToTxnDate', $date);
	}

	public function getToTxnDate()
	{
		return $this->get('TxnDateRangeFilter ToTxnDate');
	}

	public function setDateMacro($date){
		return $this->set('TxnDateRangeFilter DateMacro', $date);
	}

	public function getDateMacro()
	{
		return $this->get('TxnDateRangeFilter DateMacro');
	}

	public function setEntityFilterListID($ID)
	{
		return $this->set('EntityFilter ListID', $ID);
	}

	public function getEntityFilterListID($ID)
	{
		return $this->set('EntityFilter ListID');
	}

	public function setAccountFilterListID($ID)
	{
		return $this->set('AccountFilter ListID', $ID);
	}

	public function getAccountFilterListID()
	{
		return $this->get('AccountFilter ListID');
	}

	public function setAccountFilterFullName($fullname)
	{
		return $this->set('AccountFilter FullName', $fullname);
	}

	public function getAccountFilterFullName()
	{
		return $this->get('AccountFilter FullName');
	}

	/**
	 * Set the credit card information for this refund
	 *
	 * @param string $cardno		The credit card number
	 * @param integer $expmonth		The expiration month (1 is January, 2 is February, etc.)
	 * @param integer $expyear		The expiration year
	 * @param string $name			The name on the credit card
	 * @param string $address		The billing address for the credit card
	 * @param string $postalcode	The postal code for the credit card
	 * @return boolean
	 */
	public function setCreditCardInfo($cardno, $expmonth, $expyear, $name, $address, $postalcode)
	{
		// should probably do better checking here for failed sets.
		$b = FALSE;
		$b = $this->set('CreditCardInfo CreditCardNumber', $cardno);
		$b = $this->set('CreditCardInfo ExpirationMonth', $expmonth);
		$b = $this->set('CreditCardInfo ExpirationYear', $expyear);
		$b = $this->set('CreditCardInfo NameOnCard', $name);
		$b = $this->set('CreditCardInfo CreditCardAddress', $address);
		$b = $this->set('CreditCardInfo CreditCardPostalCode', $postalcode);

		return $b;
	}

	/**
	 * Get credit card information from the refund
	 *
	 * @param string $part		If you just want a specific part of the card info, specify it here
	 * @param array $defaults	Defaults for the card data if you want the entire array
	 * @return mixed			If you specify a part, a string part is returned, otherwise an array of card data
	 */
	public function getCreditCardInfo($part = null, $defaults = array())
	{
		if (!is_null($part))
		{
			return $this->get('CreditCardInfo ' . $part);
		}

		return $this->getArray('CreditCardInfo *', $defaults);
	}

	/**
	 * Set the address for the refund (optional)
	 *
	 * @param string $addr1			Address line 1
	 * @param string $addr2			Address line 2
	 * @param string $addr3			Address line 3
	 * @param string $addr4			Address line 4
	 * @param string $addr5			Address line 5
	 * @param string $city			City
	 * @param string $state			State
	 * @param string $province		Province (Canadian editions of QuickBooks only!)
	 * @param string $postalcode	Postal code
	 * @param string $country		Country
	 * @param string $note			Notes
	 * @return void
	 */
	public function setAddress($addr1, $addr2 = '', $addr3 = '', $addr4 = '', $addr5 = '', $city = '', $state = '', $province = '', $postalcode = '', $country = '', $note = '')
	{
		for ($i = 1; $i <= 5; $i++)
		{
			$this->set('Address Addr' . $i, ${'addr' . $i});
		}

		$this->set('Address City', $city);
		$this->set('Address State', $state);
		$this->set('Address Province', $province);
		$this->set('Address PostalCode', $postalcode);
		$this->set('Address Country', $country);
		$this->set('Address Note', $note);
	}

	/**
	 * Get the address
	 *
	 * @param string $part			A specific portion of the address to get (i.e. "Addr1" or "State")
	 * @param array $defaults		Default values if a value isn't filled in
	 * @return array				The address
	 */
	public function getAddress($part = null, $defaults = array())
	{
		if (!is_null($part))
		{
			return $this->get('Address ' . $part);
		}

		return $this->getArray('Address *', $defaults);
	}

	/**
	 * Set the transaction date
	 *
	 * @param string $date
	 * @return boolean
	 */
	public function setTxnDate($date)
	{
		return $this->setDateType('TxnDate', $date);
	}

	/**
	 * Get the transaction date
	 *
	 * @param string $format	The format you want the date in (as for {@link http://www.php.net/date})
	 * @return string
	 */
	public function getTxnDate($format = 'Y-m-d')
	{
		//return date($format, strtotime($this->get('TxnDate')));
		return $this->getDateType('TxnDate', $format);
	}

	protected function _cleanup()
	{
		return true;
	}

	public function asArray($request, $nest = true)
	{
		$this->_cleanup();
		return parent::asArray($request, $nest);
	}

	/**
	 * Convert this object to a valid qbXML request
	 *
	 * @param string $request						The type of request to convert this to (ARRefundCreditCardAddRq, or ARRefundCreditCardQuery)
	 * @param boolean $todo_for_empty_elements		A constant, one of: QUICKBOOKS_XML_XML_COMPRESS, QUICKBOOKS_XML_XML_DROP, QUICKBOOKS_XML_XML_PRESERVE
	 * @param string $indent
	 * @param string $root
	 * @return string
	 */
	public function asQBXML($request, $todo_for_empty_elements = NULL, $indent = "\t", $root = null)
	{
		$this->_cleanup();

		return parent::asQBXML($request, $todo_for_empty_elements, $indent, $root);
	}

	public function object()
	{
		return QUICKBOOKS_OBJECT_CREDITCARDREFUND;
	}
}


<?php

/**
 * QuickBooks IPP class for communicating with the Intuit Partner Platform
 *
 * Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * @license LICENSE.txt
 * @author Keith Palmer <Keith@ConsoliBYTE.com>
 *
 * @package QuickBooks
 * @subpackage IPP
 */

// Load the HTTP request class
QuickBooks_Loader::load('/QuickBooks/HTTP.php');

// XML parser
QuickBooks_Loader::load('/QuickBooks/XML.php');

// Context element (holds application information)
QuickBooks_Loader::load('/QuickBooks/IPP/Context.php');

// IPP XML parser
QuickBooks_Loader::load('/QuickBooks/IPP/Parser.php');

// SAML federation of applications
QuickBooks_Loader::load('/QuickBooks/IPP/Federator.php');

// OAuth
QuickBooks_Loader::load('/QuickBooks/IPP/OAuth.php');

// IntuitAnywhere widgets
QuickBooks_Loader::load('/QuickBooks/IPP/IntuitAnywhere.php');

// IDS (Intuit Data Services) base class
QuickBooks_Loader::load('/QuickBooks/IPP/IDS.php');

// Import all IDS service classes
QuickBooks_Loader::import('/QuickBooks/IPP/Service');

/**
 *
 *
 *
 */
class QuickBooks_IPP
{
	const API_ADDRECORD = 'API_AddRecord';

	const API_GETBILLINGSTATUS = 'API_GetBillingStatus';

	/**
	 * This is not a real API call!
	 */
	const API_GETBASEURL = '_getBaseURL_';

	const API_GETDBINFO = 'API_GetDBInfo';

	const API_GETDBVAR = 'API_GetDBVar';

	const API_GETUSERINFO = 'API_GetUserInfo';

	const API_GETUSERROLE = 'API_GetUserRole';

	const API_GETSCHEMA = 'API_GetSchema';

	const API_SETDBVAR = 'API_SetDBVar';

	const API_GETISREALMQBO = 'API_GetIsRealmQBO';

	const API_GETIDSREALM = 'API_GetIDSRealm';

	const API_ATTACHIDSREALM = 'API_AttachIDSRealm';

	const API_DETACHIDSREALM = 'API_DetachIDSRealm';

	const API_RENAMEAPP = 'API_RenameApp';

	const API_ASSERTFEDERATEDIDENTITY = 'API_AssertFederatedIdentity';

	const API_GETENTITLEMENTVALUES = 'API_GetEntitlementValues';

	const API_GETENTITLEMENTVALUESANDUSERROLE = 'API_GetEntitlementValuesAndUserRole';

	const AUTHMODE_FEDERATED = 'federated';
	const AUTHMODE_OAUTH = 'oauth';

	/**
	 *
	 * @var unknown_type
	 */
	const COOKIE = 'ippfedcookie';

	/**
	 *
	 * @var string
	 */
	const REQUEST_IPP = 'ipp';

	/**
	 *
	 * @var string
	 */
	const REQUEST_IDS = 'ids';

	/**
	 * An IDS request to add an object
	 * @deprecated
	 */
	//const IDS_ADD = 'ids-add';

	/**
	 * An IDS request to modify an object
	 * @deprecated
	 */
	//const IDS_MOD = 'ids-mod';

	/**
	 * An IDS request to search/query for an object
	 * @deprecated
	 */
	//const IDS_QUERY = 'ids-query';

	/**
	 * An IDS request to get a report
	 * @deprecated
	 * @var unknown_type
	 */
	//const IDS_REPORT = 'ids-report';

	/**
	 * No error occurred
	 * @var integer
	 */
	const OK = QUICKBOOKS_ERROR_OK;

	/**
	 * No error occurred
	 * @var integer
	 */
	const ERROR_OK = QUICKBOOKS_ERROR_OK;

	/**
	 * Indicates a generic internal error
	 * @param integer
	 */
	const ERROR_INTERNAL = -1091;

	/**
	 * Indicates an error when parsing an XML stream
	 * @param integer
	 */
	const ERROR_XML = -1092;

	/**
	 * Indicates an error establishing a socket connection to QBMS
	 * @param integer
	 */
	const ERROR_SOCKET = -1093;

	/**
	 * Indicates an error with a parameter passed to QBMS
	 * @param integer
	 */
	const ERROR_PARAM = -1094;

	/**
	 * Indicates an internal SSL-related error
	 * @param integer
	 */
	const ERROR_SSL = -1095;

	/**
	 *
	 *
	 */
	const ERROR_HTTP = -1096;

	protected $_test;

	protected $_key;

	protected $_username;
	protected $_password;
	protected $_ticket;
	protected $_token;
	protected $_dbid;

	protected $_flavor;
	protected $_baseurl;
	protected $_sandbox;

	protected $_authmode;
	protected $_authuser;
	protected $_authcred;

	/**
	 * Auth signing method (if applicable)
	 * @var string
	 */
	protected $_authsign;

	/**
	 * Auth key (if applicable)
	 * @var string
	 */
	protected $_authkey;

	protected $_debug;

	protected $_last_request;
	protected $_last_response;
	protected $_last_debug;

	protected $_masking;

	protected $_driver;

	protected $_certificate;

	protected $_errcode;
	protected $_errtext;
	protected $_errdetail;

	/**
	 * An array of cookies returned by the deprecated ->authenticate() method
	 * @var array
	 */
	protected $_cookies;

	/**
	 * Whether or not to use the IDS parser and parse XML responses into objects
	 * @var boolean
	 */
	protected $_ids_parser;

	/**
	 * The version of IDS to use
	 * @var string
	 */
	protected $_ids_version;

	public function __construct($dsn = null, $encryption_key = null, $config = array(), $log_level = QUICKBOOKS_LOG_NORMAL)
	{
		// Are we in sandbox mode?
		$this->_sandbox = false;

		// Use a test gateway?
		$this->_test = false;

		// Use debug mode?
		$this->_debug = false;

		// Mask sensitive data in the logs (tickets, credit card numbers, etc.)
		$this->_masking = true;

		// Parse returned IDS responses into objects?
		$this->_ids_parser = true;

		// What version of IDS to use
		$this->_ids_version = QuickBooks_IPP_IDS::VERSION_3;

		// Driver class for logging
		$this->_driver = null;

		if ($dsn)
		{
			$this->_driver = QuickBooks_Driver_Factory::create($dsn, $config, $log_level);
			$this->_driver->setLogLevel($log_level);
		}

		$this->_cookies = array();

		$this->_certificate = null;

		$this->_errcode = QuickBooks_IPP::OK;
		$this->_errtext = '';
		$this->_errdetail = '';

		$this->_last_request = null;
		$this->_last_response = null;
		$this->_last_debug = array();

		$this->_authmode = QuickBooks_IPP::AUTHMODE_FEDERATED;
		$this->_authuser = null;
		$this->_authcred = null;

		$this->_authsign = null;
		$this->_authkey = null;

		// Encryption key (used for database storage)
		$this->_key = $encryption_key;

		// Default to QuickBooks desktop
		//$this->flavor(QuickBooks_IPP_IDS::FLAVOR_DESKTOP);
	}

	/**
	 * Authenticate to the IPP web service
	 *
	 * IMPORTANT NOTE:
	 * Intuit disallows this method within live applications! You can use it to
	 * test your application, but when you go live you'll need to instead use
	 * a SAML gateway for single-sign-on authentication. Take a look at the
	 * QuickBooks_IPP_Federator class for a working SAML gateway.
	 *
	 * @param string $username
	 * @param string $password
	 * @param string $token
	 * @return boolean
	 */
	public function authenticate($username, $password, $token)
	{
		$this->_username = $username;
		$this->_password = $password;
		$this->_token = $token;

		$url = 'https://workplace.intuit.com/db/main?act=API_Authenticate';
		$action = 'API_Authenticate';

		$xml = '<?xml version="1.0" encoding="UTF-8" ?>
			<qdbapi>
				<username>' . $username . '</username>
				<password>' . $password . '</password>
				<apptoken>' . $token . '</apptoken>
			</qdbapi>';

		$Context = null;
		$response = $this->_request($Context, QuickBooks_IPP::REQUEST_IPP, $url, $action, $xml);

		if (!$this->_hasErrors($response) and
			$ticket = QuickBooks_XML::extractTagContents('ticket', $response))
		{
			$this->_ticket = $ticket;

			$cookies = array(
				'scache',
				'ptest',
				'stest',
				'luid',
				'TICKET',
				'qbn.ticket',
				'qbn.tkt',
				'qbn.authid',
				'qbn.gauthid',
				'qbn.agentid',
				'iamValidationTime'
				);

			foreach ($cookies as $cookie)
			{
				if ($value = $this->_extractCookie($cookie, $response))
				{
					$this->_cookies[$cookie] = $value;
				}
			}

			return new QuickBooks_IPP_Context($this, $ticket, $token);
		}

		return false;
	}

	/**
	 * Create a Context object (used for session management) for a given ticket and token
	 *
	 *
	 */
	public function context($ticket = null, $token = null, $check_if_valid = true)
	{
		$Context = null;

		if ($this->_authmode == QuickBooks_IPP::AUTHMODE_OAUTH)
		{
			$Context = new QuickBooks_IPP_Context($this, null, $token);

			// @todo Support for checking if it's valid or not
		}
		else
		{
			if (is_null($ticket))
			{
				$ticket = QuickBooks_IPP_Federator::getCookie();
			}

			$Context = new QuickBooks_IPP_Context($this, $ticket, $token);

			//print('check if valid [' . $check_if_valid . ']');

			if ($check_if_valid)
			{
				// Now, let's check to make sure the context is valid
				$User = $this->getUserInfo($Context);

				if (!$User or
					!is_object($User) or
					$User->isAnonymous())
				{
					return null;
				}
			}
		}

		//print_r($Context);

		return $Context;
	}

	/**
	 *
	 *
	 * @deprecated
	 *
	 */
	public function cookies($glob_them_together = false)
	{
		if ($glob_them_together)
		{
			$tmp = array();
			foreach ($this->_cookies as $cookie => $value)
			{
				$tmp[] = $cookie . '=' . $value;
			}

			return implode('; ', $tmp);
		}

		return $this->_cookies;
	}

	public function username()
	{
		return $this->_username;
	}

	public function password()
	{
		return $this->_password;
	}

	/*
	public function ticket($ticket = null)
	{
		if ($ticket)
		{
			$this->_ticket = $ticket;
		}

		return $this->_ticket;
	}

	public function token($token = null)
	{
		if ($token)
		{
			$this->_token = $token;
		}

		return $this->_token;
	}
	*/

	/**
	 *
	 *
	 */
	public function flavor($flavor = null)
	{
		if ($flavor)
		{
			$this->_flavor = $flavor;

			if ($flavor == QuickBooks_IPP_IDS::FLAVOR_DESKTOP)
			{
				$this->baseURL(QuickBooks_IPP_IDS::BASEURL_DESKTOP);
			}
		}

		return $this->_flavor;
	}

	public function sandbox($sandbox = null)
	{
		if (!is_null($sandbox))
		{
			$this->_sandbox = (bool) $sandbox;
		}

		return $this->_sandbox;
	}

	public function baseURL($baseURL = null)
	{
		if ($baseURL)
		{
			$this->_baseurl = $baseURL;
		}

		return $this->_baseurl;
	}

	public function authcreds()
	{
		return $this->_authcred;
	}

	/**
	 * Set the authorization mode for HTTP requests (Federated, or OAuth)
	 *
	 * @param string $authmode		The new auth mode
	 * @return string				The currently set auth mode
	 */
	public function authMode($authmode = null, $authuser = null, $authcred = null, $authsign = null, $authkey = null)
	{
		if ($authmode)
		{
			$this->_authmode = $authmode;
			$this->_authuser = $authuser;
			$this->_authcred = $authcred;

			$this->_authsign = $authsign;
			$this->_authkey = $authkey;
		}

		return $this->_authmode;
	}

	/**
	 * Get or set the DBID of the attached federated app
	 *
	 * @param string $dbid
	 * @return string
	 */
	public function dbid($dbid = null)
	{
		if ($dbid)
		{
			$this->_dbid = $dbid;
		}

		return $this->_dbid;
	}

	/**
	 *
	 *
	 *
	 */
	protected function _IPP($Context, $url, $action, $xml, $post = true)
	{
		// Ick, special case here...
		$type = QuickBooks_IPP::REQUEST_IPP;
		if ($action == QuickBooks_IPP::API_GETBASEURL)
		{
			$type = QuickBooks_IPP::REQUEST_IDS;
		}

		// Make the HTTP request
		$response = $this->_request($Context, $type, $url, $action, $xml, $post);

		if ($this->_hasErrors($response))
		{
			return false;
		}

		// These methods don't need a parsed response. If we've gotten this far,
		//	then we know there wasn't an API error, and we can just return TRUE
		//	because the request succeeded and there's no real meaningful data
		//	that we need to parse out and return in the response.
		switch ($action)
		{
			case QuickBooks_IPP::API_SETDBVAR:
			case QuickBooks_IPP::API_ATTACHIDSREALM:
			case QuickBooks_IPP::API_DETACHIDSREALM:
			case QuickBooks_IPP::API_RENAMEAPP:
				return true;
		}

		// Remove HTTP headers from response
		$data = $this->_stripHTTPHeaders($response);

		$xml_errnum = null;
		$xml_errmsg = null;
		$err_code = null;
		$err_desc = null;
		$err_db = null;

		$Parser = $this->_parserInstance();

		// Try to parse the response from IPP
		$parsed = $Parser->parseIPP($data, $action, $xml_errnum, $xml_errmsg, $err_code, $err_desc, $err_db);

		/*
		print('parsed out: [');
		print_r($parsed);
		print(']');
		*/

		//$this->_setLastDebug(__CLASS__, array( 'ipp_parser_duration' => microtime(true) - $start ));

		if ($xml_errnum != QuickBooks_XML::ERROR_OK)
		{
			// Error parsing the returned XML?
			$this->_setError(QuickBooks_IPP::ERROR_XML, 'XML parser said: ' . $xml_errnum . ': ' . $xml_errmsg);

			return false;
		}
		else if ($err_code != QuickBooks_IPP::ERROR_OK)
		{
			// Some other IPP error
			$this->_setError($err_code, $err_desc, 'Database error code: ' . $err_db);

			return false;
		}

		return $parsed;
	}

	public function getBaseURL($Context, $realmID)
	{
		/*
		$url = 'https://qbo.intuit.com/qbo1/rest/user/v2/' . $realmID;
		$action = QuickBooks_IPP::API_GETBASEURL;
		$xml = null;

		$post = false;
		return $this->_IPP($Context, $url, $action, $xml, $post);
		*/

		return QuickBooks_IPP_IDS::URL_V3;
	}

	public function getIsRealmQBO($Context)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_GETISREALMQBO;

		$xml = '<qdbapi>
				<ticket>' . $Context->ticket() . '</ticket>
   				<apptoken>' . $Context->token() . '</apptoken>
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function assertFederatedIdentity($Context, $provider, $target_url, $udata = null)
	{
		$url = 'https://workplace.intuit.com/db/main';
		$action = QuickBooks_IPP::API_ASSERTFEDERATEDIDENTITY;

		$xml = '<qdbapi>
				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>
				<serviceProviderID>' . htmlspecialchars($provider) . '</serviceProviderID>
				<targetURL>' . htmlspecialchars($target_url, ENT_QUOTES) . '</targetURL>
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function renameApp($Context, $name)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_RENAMEAPP;

		$xml = '<qdbapi>
				<ticket>' . $Context->ticket() . '</ticket>
   				<apptoken>' . $Context->token() . '</apptoken>
   				<newappname>' . htmlspecialchars($name) . '</newappname>
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function getIDSRealm($Context)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_GETIDSREALM;

		$xml = '<qdbapi>
   				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function getAvailableCompanies($Context)
	{
		$url = 'https://services.intuit.com/sb/company/' . $this->_ids_version . '/available';
		$action = null;
		$xml = null;

		$response = $this->_request($Context, QuickBooks_IPP::REQUEST_IDS, $url, $action, $xml);

		if ($this->_hasErrors($response))
		{
			return false;
		}

		// @todo Parse and return an object?
		return $response;
	}

	/*
	public function getEntitlementValues($Context)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_GETENTITLEMENTVALUES;

		$xml = '<qdbapi>
			<ticket>' . $Context->ticket() . '</ticket>
			<apptoken>' . $Context->token() . '</apptoken>
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function getEntitlementValuesAndUserRole($Context)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_GETENTITLEMENTVALUESANDUSERROLE;

		$xml = '<qdbapi>
			<ticket>' . $Context->ticket() . '</ticket>
			<apptoken>' . $Context->token() . '</apptoken>
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}
	*/

	public function provisionUser($Context, $email, $fname, $lname, $roleid = null, $udata = null)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = 'API_ProvisionUser';

		$xml = '<qdbapi>
					<ticket>' . $Context->ticket() . '</ticket>
					<apptoken>' . $Context->token() . '</apptoken>';

		if ($roleid)
		{
			$xml .= '<roleid>' . $roleid . '</roleid>';
		}

		$xml .= '
				<email>' . $email . '</email>
				<fname>' . $fname . '</fname>
				<lname>' . $lname . '</lname>';

		if ($udata)
		{
			$xml .= '<udata>' . $udata . '</udata>';
		}

		$xml .= '
			</qdbapi>';

		$response = $this->_request($Context, QuickBooks_IPP::REQUEST_IPP, $url, $action, $xml);

		if ($this->_hasErrors($response))
		{
			return false;
		}

		return true;
	}

	public function getUserRoles($Context, $userid, $udata = null)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_GETUSERROLE;
		$xml = '<qdbapi>
				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>
				<userid>' . htmlspecialchars($userid) . '</userid>';

		if ($udata)
		{
			$xml .= '<udata>' . $udata . '</udata>';
		}

		$xml .= '
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function getUserInfo($Context, $email = null, $udata = null)
	{
		$url = 'https://workplace.intuit.com/db/main';
		$action = QuickBooks_IPP::API_GETUSERINFO;
		$xml = '<qdbapi>
   				<ticket>' . $Context->ticket() . '</ticket>
   				<apptoken>' . $Context->token() . '</apptoken>';

		if ($email)
		{
			$xml .= '<email>' . htmlspecialchars($email) . '</email>';
		}

		if ($udata)
		{
			$xml .= '<udata>' . htmlspecialchars($udata) . '</udata>';
		}

		$xml .= '
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function sendInvitation($Context, $userid, $usertext, $udata = null)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = 'API_SendInvitation';
		$xml = '<qdbapi>
				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>
				<userid>' . htmlspecialchars($userid) . '</userid>
				<usertext>' . htmlspecialchars($usertext) . '</usertext>';

		if ($udata)
		{
			$xml .= '<udata>' . $udata . '</udata>';
		}

		$xml .= '
			</qdbapi>';

		$response = $this->_request($Context, QuickBooks_IPP::REQUEST_IPP, $url, $action, $xml);

		if ($this->_hasErrors($response))
		{
			return false;
		}

		return true;
	}

	public function getDBInfo($Context, $udata = null)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_GETDBINFO;
		$xml = '<qdbapi>
				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>';

		if ($udata)
		{
			$xml .= '<udata>' . $udata . '</udata>';
		}

		$xml .= '
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function setDBVar($Context, $varname, $value, $udata = null)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_SETDBVAR;
		$xml = '<qdbapi>
				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>
				<varname>' . QuickBooks_XML::encode($varname) . '</varname>
				<value>' . QuickBooks_XML::encode($value) . '</value>';

		if ($udata)
		{
			$xml .= '<udata>' . $udata . '</udata>';
		}

		$xml .= '
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function getDBVar($Context, $varname, $udata = null)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_GETDBVAR;
		$xml = '<qdbapi>
				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>
				<varname>' . QuickBooks_XML::encode($varname) . '</varname>';

		if ($udata)
		{
			$xml .= '<udata>' . $udata . '</udata>';
		}

		$xml .= '
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function createTable($Context, $tname, $pnoun, $udata = null)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = 'API_CreateTable';
		$xml = '<qdbapi>
				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>
				<tname>' . $tname . '</tname>
				<pnoun>' . $pnoun . '</pnoun>';

		if ($udata)
		{
			$xml .= '<udata>' . $udata . '</udata>';
		}

		$xml .= '
			</qdbapi>';

		$response = $this->_request($Context, QuickBooks_IPP::REQUEST_IPP, $url, $action, $xml);

		if ($this->_hasErrors($response))
		{
			return false;
		}

		return true;
	}

	public function attachIDSRealm($Context, $realm)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_ATTACHIDSREALM;
		$xml = '<qdbapi>
				<realm>' . $realm . '</realm>
				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	public function detachIDSRealm($Context, $realm)
	{
		$url = 'https://workplace.intuit.com/db/' . $this->_dbid;
		$action = QuickBooks_IPP::API_DETACHIDSREALM;
		$xml = '<qdbapi>
				<realm>' . $realm . '</realm>
				<ticket>' . $Context->ticket() . '</ticket>
				<apptoken>' . $Context->token() . '</apptoken>
			</qdbapi>';

		return $this->_IPP($Context, $url, $action, $xml);
	}

	/**
	 *
	 *
	 *
	 * @param boolean $true_or_false
	 * @return boolean
	 */
	public function useIDSParser($true_or_false)
	{
		$this->_ids_parser = (boolean) $true_or_false;
		return $this->_ids_parser;
	}

	/**
	 * Get or set the IDS version to use
	 *
	 * @param string $version		One of QuickBooks_IPP_IDS::VERSION_1, QuickBooks_IPP_IDS::VERSION_2, QuickBooks_IPP_IDS::VERSION_LATEST
	 * @return string				The IDS version currently being used
	 */
	public function version($version = null)
	{
		if ($version)
		{
			$this->_ids_version = $version;
		}

		return $this->_ids_version;
	}

	/**
	 * Make an IDS request (Intuit Data Services) to the remote server
	 *
	 * @param QuickBooks_IPP_Context $Context		The context (token and ticket) to use
	 * @param integer $realmID						The realm to query against
	 * @param string $resource						A QuickBooks_IDS::RESOURCE_* constant
	 * @param string $optype
	 * @param string $xml
	 * @return QuickBooks_IPP_Object
	 */
	public function IDS($Context, $realm, $resource, $optype, $xml = '', $ID = null)
	{
		$IPP = $Context->IPP();

		switch ($IPP->version())
		{
			case QuickBooks_IPP_IDS::VERSION_3:
			default:
				return $this->_IDS_v3($Context, $realm, $resource, $optype, $xml, $ID);
		}
	}

	protected function _IDS_v3($Context, $realm, $resource, $optype, $xml_or_query, $ID)
	{
		// All v3 URLs have the same baseURL
		$this->baseURL(QuickBooks_IPP_IDS::URL_V3);

		// If we're in sandbox mode, use the sandbox URL instead
		if ($this->sandbox())
		{
			$this->baseURL(QuickBooks_IPP_IDS::URL_V3_SANDBOX);
		}

		$post = false;
		$xml = null;
		$query = null;

		$guid = QuickBooks_Utilities::GUID();

		if ($optype == QuickBooks_IPP_IDS::OPTYPE_ADD or $optype == QuickBooks_IPP_IDS::OPTYPE_MOD)
		{
			$post = true;
			$url = $this->baseURL() . '/company/' . $realm . '/' . strtolower($resource) . '?requestid=' . $guid . '&minorversion=6';
			$xml = $xml_or_query;
		}
		else if ($optype == QuickBooks_IPP_IDS::OPTYPE_QUERY)
		{
			$post = false;
			$url = $this->baseURL() . '/company/' . $realm . '/query?query=' . $xml_or_query . '&requestid=' . $guid . '&minorversion=6';
		}
		else if ($optype == QuickBooks_IPP_IDS::OPTYPE_CDC)
		{
			$post = false;
			$url = $this->baseURL() . '/company/' . $realm . '/cdc?entities=' . implode(',', $xml_or_query[0]) . '&changedSince=' . $xml_or_query[1] . '&minorversion=6';
		}
		else if ($optype == QuickBooks_IPP_IDS::OPTYPE_ENTITLEMENTS)
		{
			$post = false;
			$url = 'https://qbo.sbfinance.intuit.com/manage/entitlements/v3/' . $realm;
		}
		else if ($optype == QuickBooks_IPP_IDS::OPTYPE_DELETE)
		{
			$post = true;
			$url = $this->baseURL() . '/company/' . $realm . '/' . strtolower($resource) . '?operation=delete&requestid=' . $guid . '&minorversion=6';
			$xml = $xml_or_query;
		}
		else if ($optype == QuickBooks_IPP_IDS::OPTYPE_VOID)
		{
			$qs = '?operation=void&requestid=' . $guid . '&minorversion=6';        // Used for invoices...

			if ($resource == QuickBooks_IPP_IDS::RESOURCE_PAYMENT)    // ... and something different used for payments *sigh*
			{
				$qs = '?operation=update&include=void&requestid=' . $guid . '&minorversion=6';
			}

			$post = true;
			$url = $this->baseURL() . '/company/' . $realm . '/' . strtolower($resource) . $qs;
			$xml = $xml_or_query;
		}
		else if ($optype == QuickBooks_IPP_IDS::OPTYPE_PDF)
		{
			$post = false;
			$url = $this->baseURL() . '/company/' . $realm . '/' . strtolower($resource) . '/' . $ID . '/pdf?requestid=' . $guid . '&minorversion=6';
		}
		else if ($optype == QuickBooks_IPP_IDS::OPTYPE_DOWNLOAD)
		{
			$post = false;
			$url = $this->baseURL() . '/company/' . $realm . '/' . strtolower($resource) . '/' . $ID;
		}
		else if ($optype == QuickBooks_IPP_IDS::OPTYPE_SEND)
		{
			$post = true;
			$url = $this->baseURL() . '/company/' . $realm . '/' . strtolower($resource) . '/' . $ID . '/send?requestid=' . $guid . '&minorversion=6';
		}

		$response = $this->_request($Context, QuickBooks_IPP::REQUEST_IDS, $url, $optype, $xml, $post);

		// print('URL is [' . $url . ']');
		//die('RESPONSE IS [' . $response . ']');

		// Check for generic IPP errors and HTTP errors
		if ($this->_hasErrors($response))
		{
			return false;
		}

		$data = $this->_stripHTTPHeaders($response);

		if (!$this->_ids_parser)
		{
			// If they don't want the responses parsed into objects, then just return the raw XML data
			return $data;
		}

		$start = microtime(true);

		$Parser = $this->_parserInstance();

		$xml_errnum = null;
		$xml_errmsg = null;
		$err_code = null;
		$err_desc = null;
		$err_db = null;

		// Try to parse the responses into QuickBooks_IPP_Object_* classes
		$parsed = $Parser->parseIDS($data, $optype, $this->flavor(), QuickBooks_IPP_IDS::VERSION_3, $xml_errnum, $xml_errmsg, $err_code, $err_desc, $err_db);

		$this->_setLastDebug(__CLASS__, array( 'ids_parser_duration' => microtime(true) - $start ));

		if ($xml_errnum != QuickBooks_XML::ERROR_OK)
		{
			// Error parsing the returned XML?
			$this->_setError(QuickBooks_IPP::ERROR_XML, 'XML parser said: ' . $xml_errnum . ': ' . $xml_errmsg);

			return false;
		}
		else if ($err_code != QuickBooks_IPP::ERROR_OK)
		{
			// Some other IPP error
			$this->_setError($err_code, $err_desc, 'Database error code: ' . $err_db);

			return false;
		}

		// Return the parsed response
		return $parsed;
	}

	protected function _IDS_v2($Context, $realmID, $resource, $optype, $xml, $ID)
	{
		if (substr($resource, 0, 6) == 'Report')
		{
			$resource = substr($resource, 6);
		}

		// This is because IDS v2 with QuickBooks Online is retarded
		if ($this->flavor() == QuickBooks_IPP_IDS::FLAVOR_ONLINE and
			$resource == QuickBooks_IPP_IDS::RESOURCE_PAYMENTMETHOD)
		{
			$resource = 'payment-method';
		}
		else if ($this->flavor() == QuickBooks_IPP_IDS::FLAVOR_ONLINE and
			$resource == QuickBooks_IPP_IDS::RESOURCE_SALESRECEIPT)
		{
			$resource = 'sales-receipt';
		}
		else if ($this->flavor() == QuickBooks_IPP_IDS::FLAVOR_ONLINE and
			$resource == QuickBooks_IPP_IDS::RESOURCE_TIMEACTIVITY)
		{
			$resource = 'time-activity';
		}
		else if ($this->flavor() == QuickBooks_IPP_IDS::FLAVOR_ONLINE and
			$resource == QuickBooks_IPP_IDS::RESOURCE_JOURNALENTRY)
		{
			$resource = 'journal-entries';
		}
		else if ($this->flavor() == QuickBooks_IPP_IDS::FLAVOR_ONLINE and
			$resource == QuickBooks_IPP_IDS::RESOURCE_BILLPAYMENT)
		{
			$resource = 'bill-payment';
		}

		if ($this->flavor() == QuickBooks_IPP_IDS::FLAVOR_ONLINE and
			$optype == QuickBooks_IPP_IDS::OPTYPE_QUERY)
		{
			// Make the resource plural... (unless it's the changedatadeleted) *sigh*
			if ($resource == QuickBooks_IPP_IDS::RESOURCE_TIMEACTIVITY)
			{
			    $resource = 'time-activities';
			}
			else if ($resource == QuickBooks_IPP_IDS::RESOURCE_CLASS)
			{
				$resource .= 'es';
			}
			else if ($resource != QuickBooks_IPP_IDS::RESOURCE_CHANGEDATADELETED)
			{
			    $resource .= 's';
            }
		}

		$post = true;
		if ($resource == QuickBooks_IPP_IDS::RESOURCE_COMPANY or 		// QuickBooks desktop
			$resource == QuickBooks_IPP_IDS::RESOURCE_COMPANYMETADATA)	// QuickBooks online
		{
			$post = false;
			$xml = '';
		}

		//$url = 'https://services.intuit.com/sb/' . strtolower($resource) . '/' . $this->_ids_version . '/' . $realmID;

		if ($this->flavor() == QuickBooks_IPP_IDS::FLAVOR_ONLINE)
		{
			if ($optype == QuickBooks_IPP_IDS::OPTYPE_FINDBYID)
			{
				$parse = QuickBooks_IPP_IDS::parseIDType($xml);

				$url = $this->_baseurl . '/' . strtolower($resource) . '/' . $this->_ids_version . '/' . $realmID . '/' . $parse[1];

				$post = false;
				$xml = null;
			}
			else if ($optype == QuickBooks_IPP_IDS::OPTYPE_MOD)
			{
				$parse = QuickBooks_IPP_IDS::parseIDType($ID);

				$url = $this->_baseurl . '/' . strtolower($resource) . '/' . $this->_ids_version . '/' . $realmID . '/' . $parse[1];
			}
			else
			{
				$url = $this->_baseurl . '/' . strtolower($resource) . '/' . $this->_ids_version . '/' . $realmID;
			}
		}

		if ($optype == QuickBooks_IPP_IDS::OPTYPE_SYNCSTATUS)
		{
			$url = $this->_baseurl . '/status/v2/' . $realmID;
		}
		else
		{
		    // Case matters on "syncActivity" #fun (everything else is lower cased)
		    if (strtolower($resource) == 'syncactivity')
		    {
		    	$resource = 'syncActivity';
		    }
		    else
		    {
		    	$resource = strtolower($resource); // everything else should be lowercase
		    }

			$url = $this->_baseurl . '/' . $resource . '/' . $this->_ids_version . '/' . $realmID;
		}

		//print('hitting URL [' . $url . ']');
		//print($xml);
		$response = $this->_request($Context, QuickBooks_IPP::REQUEST_IDS, $url, $optype, $xml, $post);
		//print($response);

		// Check for generic IPP errors and HTTP errors
		if ($this->_hasErrors($response))
		{
			return false;
		}

		$data = $this->_stripHTTPHeaders($response);

		if (!$this->_ids_parser)
		{
			// If they don't want the responses parsed into objects, then just return the raw XML data
			return $data;
		}

		$start = microtime(true);

		//$Parser = new QuickBooks_IPP_Parser();
		$Parser = $this->_parserInstance();

		$xml_errnum = null;
		$xml_errmsg = null;
		$err_code = null;
		$err_desc = null;
		$err_db = null;

		// Try to parse the responses into QuickBooks_IPP_Object_* classes
		$parsed = $Parser->parseIDS($data, $optype, $this->flavor(), QuickBooks_IPP_IDS::VERSION_2, $xml_errnum, $xml_errmsg, $err_code, $err_desc, $err_db);

		$this->_setLastDebug(__CLASS__, array( 'ids_parser_duration' => microtime(true) - $start ));

		if ($xml_errnum != QuickBooks_XML::ERROR_OK)
		{
			// Error parsing the returned XML?
			$this->_setError(QuickBooks_IPP::ERROR_XML, 'XML parser said: ' . $xml_errnum . ': ' . $xml_errmsg);

			return false;
		}
		else if ($err_code != QuickBooks_IPP::ERROR_OK)
		{
			// Some other IPP error
			$this->_setError($err_code, $err_desc, 'Database error code: ' . $err_db);

			return false;
		}

		// Return the parsed response
		return $parsed;
	}

	/**
	 *
	 *
	 * @param string $response
	 * @return string
	 */
	protected function _stripHTTPHeaders($response)
	{
		$pos = strpos($response, "\r\n\r\n");

		// @todo Error checking, what if \r\n\r\n isn't present?
		$stripped = substr($response, $pos + 4);

		// To handle "HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n .... " responses
		if (substr($stripped, 0, 8) == 'HTTP/1.1')
		{
			return $this->_stripHTTPHeaders($stripped);
		}

		return $stripped;
	}

	protected function _extractCookie($name, $response)
	{
		$lines = explode("\r\n", $response);

		foreach ($lines as $line)
		{
			// Set-Cookie: qbn.ticket=V1-47-U2v1RYBuM02GHgOYfulVmQ; expires=Tue, 19-Jan-2038 00:00:00 GMT; path=/; domain=.intuit.com; secure; HttpOnly
			$line = substr($line, 12);

			if (substr($line, 0, strlen($name)) == $name and
				false !== ($pos = strpos($line, ';')))
			{
				return substr($line, strlen($name) + 1, $pos - strlen($name) - 1);
			}
		}

		return null;
	}

	protected function _parserInstance()
	{
		static $Parser = null;
		if (is_null($Parser))
		{
			$Parser = new QuickBooks_IPP_Parser();
		}

		return $Parser;
	}

	/**
	 *
	 */
	protected function _hasErrors($response)
	{
		// @todo This should first check for HTTP errors
		// ...

		// v3 errors
		if (false !== strpos($response, '<Error'))
		{
			$errcode = QuickBooks_XML::extractTagAttribute('code', $response);
			$errtext = QuickBooks_XML::extractTagContents('Message', $response);
			$errdetail = QuickBooks_XML::extractTagContents('Detail', $response);

			$this->_setError($errcode, $errtext, $errdetail);

			return true;		// Yes, there's an error!
		}
		else if (false !== strpos($response, '<title>504 Gateway Time-out'))
		{
			// QBO v3 sometimes blows up with a 504 gateway error, catch these!

			$errcode = QUICKBOOKS_ERROR_INTERNAL;
			$errtext = '504 Gateway Time-out';
			$errdetail = 'A service call to the QuickBooks Online gateway has timed out and returned a 504 HTTP error.';

			$this->_setError($errcode, $errtext, $errdetail);

			return true;
		}
		else
		{
			// Check for generic IPP XML node errors
			$errcode = QuickBooks_XML::extractTagContents('errcode', $response);
			$errtext = QuickBooks_XML::extractTagContents('errtext', $response);
			$errdetail = QuickBooks_XML::extractTagContents('errdetail', $response);

			if ($errcode != QuickBooks_IPP::OK)
			{
				// Has errors!
				$this->_setError($errcode, $errtext, $errdetail);
				return true;
			}

			// Check for IDS XML error codes
			$errorcode = QuickBooks_XML::extractTagContents('ErrorCode', $response);
			$errordesc = QuickBooks_XML::extractTagContents('ErrorDesc', $response);

			if ($errorcode)
			{
				$this->_setError($errorcode, $errordesc);
				return true;
			}

			// Does not have any errors
			return false;
		}
	}

	/**
	 * If masking is enabled (default) then credit card numbers, connection tickets, and session tickets will be masked when output or logged
	 *
	 * @param boolean $yes_or_no
	 * @return void
	 */
	public function useMasking($yes_or_no)
	{
		$this->_masking = (boolean) $yes_or_no;
	}

	/**
	 * Turn debugging mode on or off
	 *
	 * Turning debugging mode on will result in a large amount of output being
	 * printed directly to stdout (the web browser or the console)
	 *
	 * @param boolean $yes_or_no
	 * @return void
	 */
	public function useDebugMode($yes_or_no)
	{
		$this->_debug = (boolean) $yes_or_no;
	}

	/**
	 *
	 *
	 *
	 * @param string $message
	 * @param integer $level
	 * @return boolean
	 */
	protected function _log($message, $level = QUICKBOOKS_LOG_NORMAL)
	{
		if ($this->_masking)
		{
			$message = QuickBooks_Utilities::mask($message);
		}

		if ($this->_debug)
		{
			print($message . QUICKBOOKS_CRLF);
		}

		if ($this->_driver)
		{
			//die('logging to driver: [' . $level . ']');
			// Send it to the driver to be logged
			$this->_driver->log($message, null, $level);
		}

		return true;
	}

	/**
	 * Log a message
	 *
	 *
	 */
	public function log($message, $level = QUICKBOOKS_LOG_NORMAL)
	{
		return $this->_log($message, $level);
	}

	protected function _request($Context, $type, $url, $action, $data, $post = true)
	{
		$headers = array(
			);

		//print('[' . $this->_flavor . '], ACTION [' . $action . ']');

		if ($Context->IPP()->version() == QuickBooks_IPP_IDS::VERSION_3)
		{
			if ($action == QuickBooks_IPP_IDS::OPTYPE_ADD or
				$action == QuickBooks_IPP_IDS::OPTYPE_MOD or
				$action == QuickBooks_IPP_IDS::OPTYPE_VOID or
				$action == QuickBooks_IPP_IDS::OPTYPE_DELETE)
			{
				$headers['Content-Type'] = 'application/xml';
			}
			else
			{
				$headers['Content-Type'] = 'text/plain';
			}
		}
		else
		{
			// Old v2 stuff
			if ($type == QuickBooks_IPP::REQUEST_IPP)
			{
				$headers['Content-Type'] = 'application/xml';
				$headers['QUICKBASE-ACTION'] = $action;
			}
			else if ($type == QuickBooks_IPP::REQUEST_IDS)
			{
				if ($this->_flavor == QuickBooks_IPP_IDS::FLAVOR_DESKTOP)
				{
					$headers['Content-Type'] = 'text/xml';
				}
				else if ($this->_flavor == QuickBooks_IPP_IDS::FLAVOR_ONLINE)
				{
					if ($action == QuickBooks_IPP_IDS::OPTYPE_ADD or $action == QuickBooks_IPP_IDS::OPTYPE_MOD or $action == QuickBooks_IPP_IDS::OPTYPE_DELETE)
					{
						$headers['Content-Type'] = 'application/xml';
					}
					else
					{
						$headers['Content-Type'] = 'application/x-www-form-urlencoded';
					}
				}
			}
		}

		// Authorization stuff
		if ($this->_authmode == QuickBooks_IPP::AUTHMODE_OAUTH)
		{
			// If we have credentials, sign the request
			if ($this->_authcred['oauth_access_token'] and
				$this->_authcred['oauth_access_token_secret'])
			{
				/*
				//// **** TEST STUFF **** ////
				$url = 'https://api.twitter.com/1/statuses/update.json?include_entities=true';

				$this->_authcred['oauth_consumer_key'] = 'xvz1evFS4wEEPTGEFPHBog';
				$this->_authcred['oauth_consumer_secret'] = 'kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw';

				$this->_authcred['oauth_access_token'] = '370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb';
				$this->_authcred['oauth_access_token_secret'] = 'LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE';

				$data = http_build_query(array('status' => 'Hello Ladies + Gentlemen, a signed OAuth request!'));
				$post = true;
				*/

				//print('URL [' . $url . ']' . "\n");
				//print('what is POST [' . $post . ']' . "\n");

				// Sign the request
				$OAuth = new QuickBooks_IPP_OAuth($this->_authcred['oauth_consumer_key'], $this->_authcred['oauth_consumer_secret']);

				// Different than default signature method?
				if ($this->_authsign)
				{
					$OAuth->signature($this->_authsign, $this->_authkey);
				}

				//print('signing with method and key ' . $this->_authsign . ', ' . $this->_authkey);

				if ($post)
				{
					$action = QuickBooks_IPP_OAuth::METHOD_POST;
				}
				else
				{
					$action = QuickBooks_IPP_OAuth::METHOD_GET;
				}

				$signdata = null;
				if ($data and
					$data[0] == '<')
				{
					// It's an XML body, we don't sign that
					$signdata = null;
				}
				else
				{
					// It's form-encoded data, parse it so we can sign it
					$signdata = array();
					parse_str($data, $signdata);
				}

				/*
				print('signing [');
				print($action . "\n");
				print($url . "\n");
				print_r($this->_authcred);
				print('[[' . $signdata . ']]');
				print(' all done ]');
				*/

				$signed = $OAuth->sign($action, $url, $this->_authcred['oauth_access_token'], $this->_authcred['oauth_access_token_secret'], $signdata);

				//print_r($signed);

				// Always use the header, regardless of POST or GET
				$headers['Authorization'] = $signed[3];

				if ($post)
				{
					// Add the OAuth headers
					//$headers['Authorization'] = $signed[3];

					// Remove any whitespace padding before checking
					$data = trim($data);

					if ($data and $data[0] == '<')
					{
						// Do nothing
					}
					else
					{
						$data = http_build_query($signdata);
					}
				}
				else
				{
					// Replace the URL with the signed URL
					//$url = $signed[2];
				}
			}
		}
		else if (is_object($Context))
		{
			// FEDERATED authentication

			$headers['Authorization'] = 'INTUITAUTH intuit-app-token="' . $Context->token() . '", intuit-token="' . $Context->ticket() . '"';
			$headers['Cookie'] = $this->cookies(true);
		}

		//print_r($headers);
		//exit;

		//$url = str_replace("SELECT * FROM customer", "SELECT+*+FROM+customer", $url);
		//print('NEW URL [' . $url . ']' . "\n\n");

		// Our HTTP requestor
		$HTTP = new QuickBooks_HTTP($url);

		// Set the headers
		$HTTP->setHeaders($headers);

		// Turn on debugging for the HTTP object if it's been enabled in the payment processor
		$HTTP->useDebugMode($this->_debug);

		//
		$HTTP->setRawBody($data);

		if ($this->_certificate)
		{
			$HTTP->setCertificate($this->_certificate);
		}

		// We need the headers back
		$HTTP->returnHeaders(true);

		// Send the request
		if ($post)
		{
			$return = $HTTP->POST();
		}
		else
		{
			$return = $HTTP->GET();
		}

		$this->_setLastRequestResponse($HTTP->lastRequest(), $HTTP->lastResponse());
		$this->_setLastDebug(__CLASS__, array( 'http_request_response_duration' => $HTTP->lastDuration() ));
		//$this->_last_request = $HTTP->lastRequest();
		//$this->_last_response = $HTTP->lastResponse();

		//print($HTTP->getLog());

		/*
		print("\n\n\n\n");
		print($this->_last_request);
		print("\n\n\n\n");
		print($this->_last_response);
		print("\n\n\n\n");
		exit;
		*/

		//
		$this->_log($HTTP->getLog(), QUICKBOOKS_LOG_DEBUG);

		$errnum = $HTTP->errorNumber();
		$errmsg = $HTTP->errorMessage();

		if ($errnum)
		{
			// An error occurred!
			$this->_setError(QuickBooks_IPP::ERROR_HTTP, $errnum . ': ' . $errmsg);
			return false;
		}

		// Everything is good, return the data!
		$this->_setError(QuickBooks_IPP::ERROR_OK, '');
		return $return;
	}

	/**
	 * Get the last raw XML response that was received
	 *
	 * @return string
	 */
	public function lastResponse()
	{
		return $this->_last_response;
	}

	/**
	 * Get the last raw XML request that was sent
	 *
	 * @return string
	 */
	public function lastRequest()
	{
		return $this->_last_request;
	}

	public function lastDebug()
	{
		return $this->_last_debug;
	}

	/**
	 * Get the error number of the last error that occured
	 *
	 * @return mixed		The error number (or error code, some QuickBooks error codes are hex strings)
	 */
	public function errorCode()
	{
		return $this->_errcode;
	}

	/**
	 * Alias if ->errorCode()   (here for consistency with rest of framework)
	 */
	public function errorNumber()
	{
		return $this->errorCode();
	}

	/**
	 * Get the last error message that was reported
	 *
	 * Remember that issuing new commands may cause previous unchecked errors
	 * to be *cleared*, so make sure you check for errors if you expect an
	 * error might occur!
	 *
	 * @return string
	 */
	public function errorText()
	{
		return $this->_errtext;
	}

	/**
	 * Alias of ->errorText()   (here for consistency with rest of framework)
	 */
	public function errorMessage()
	{
		return $this->errorText();
	}

	/**
	 *
	 */
	public function errorDetail()
	{
		return $this->_errdetail;
	}

	public function hasErrors()
	{
		return $this->_errcode != QuickBooks_IPP::ERROR_OK;
	}

	public function lastError()
	{
		return $this->_errcode . ': [' . $this->_errtext . ', ' . $this->_errdetail . ']';
	}

	/**
	 * Set an error message
	 *
	 * @param integer $errnum	The error number/code
	 * @param string $errmsg	The text error message
	 * @return void
	 */
	protected function _setError($errcode, $errtext = '', $errdetail = '')
	{
		$this->_errcode = $errcode;
		$this->_errtext = $errtext;
		$this->_errdetail = $errdetail;
	}

	protected function _setLastRequestResponse($request, $response)
	{
		$this->_last_request = $request;
		$this->_last_response = $response;
	}

	protected function _setLastDebug($class, $arr)
	{
		$existing = array();
		if (isset($this->_last_debug[$class]))
		{
			$existing = $this->_last_debug[$class];
		}

		$this->_last_debug[$class] = array_merge($existing, $arr);
	}
}


<?php

/**
 *
 *
 * Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * @license LICENSE.txt
 * @author Keith Palmer <Keith@ConsoliBYTE.com>
 *
 * @package QuickBooks
 * @subpackage IPP
 */

QuickBooks_Loader::load('/QuickBooks/IPP/Service.php');

class QuickBooks_IPP_Service_Company extends QuickBooks_IPP_Service
{
	/**
	 * Get a company by realmID
	 *
	 * @param QuickBooks_IPP_Context $Context
	 * @param string $realmID
	 * @return QuickBooks_IPP_Object_Customer	The customer object
	 */
	public function findById($Context, $realmID)
	{
		$xml = null;

		// WATCH OUT!   We pass in the realmID as ID value
		return parent::_findById($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_COMPANY, $realmID, $xml);
	}
}


<?php

/**
 * Bill class for QuickBooks
 *
 * @author Keith Palmer Jr. <keith@ConsoliBYTE.com>
 * @license LICENSE.txt
 *
 * @package QuickBooks
 * @subpackage Object
 */

/**
 * QuickBooks object base class
 */
QuickBooks_Loader::load('/QuickBooks/QBXML/Object.php');

/**
 *
 */
QuickBooks_Loader::load('/QuickBooks/QBXML/Object/Bill/ItemLine.php');

/**
 *
 */
QuickBooks_Loader::load('/QuickBooks/QBXML/Object/Bill/ExpenseLine.php');

/**
 *
 */
class QuickBooks_QBXML_Object_Bill extends QuickBooks_QBXML_Object
{
	/**
	 * Create a new QuickBooks_Object_JournalEntry object
	 *
	 * @param array $arr
	 */
	public function __construct($arr = array())
	{
		parent::__construct($arr);
	}

	/**
	 * Set the customer ListID
	 *
	 * @param string $ListID
	 * @return boolean
	 */
	public function setVendorListID($ListID)
	{
		return $this->set('VendorRef ListID' , $ListID);
	}

	/**
	 * Set the customer ApplicationID (auto-replaced by the API with a ListID)
	 *
	 * @param mixed $value
	 * @return boolean
	 */
	public function setVendorApplicationID($value)
	{
		return $this->set('VendorRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_VENDOR, QUICKBOOKS_LISTID, $value));
	}

	/**
	 * Set the customer name
	 *
	 * @param string $name
	 * @return boolean
	 */
	public function setVendorFullname($name)
	{
		return $this->set('VendorRef FullName', $name);
	}

	/**
	 * Get the customer ListID
	 *
	 * @return string
	 */
	public function getVendorListID()
	{
		return $this->get('VendorRef ListID');
	}

	/**
	 * Get the customer application ID
	 *
	 * @return mixed
	 */
	public function getVendorApplicationID()
	{
		return $this->extractApplicationID($this->get('VendorRef ' . QUICKBOOKS_API_APPLICATIONID));
	}

	// Path: TxnDate, datatype: DATETYPE

	/**
	 * Set the TxnDate for the JournalEntry
	 *
	 * @param string $date
	 * @return boolean
	 */
	public function setTxnDate($date)
	{
		return $this->setDateType('TxnDate', $date);
	}

	/**
	 * Get the TxnDate for the JournalEntry
	 *
	 * @param ? $format = null
	 * @return string
	 */
	public function getTxnDate($format = null)
	{
		return $this->getDateType('TxnDate', $format);
	}

	/**
	 * @see QuickBooks_Object_JournalEntry::setTxnDate()
	 */
	public function setTransactionDate($date)
	{
		return $this->setTxnDate($date);
	}

	/**
	 * @see QuickBooks_Object_JournalEntry::getTxnDate()
	 */
	public function getTransactionDate($format = null)
	{
		$this->getTxnDate($format = null);
	}
	// Path: RefNumber, datatype: STRTYPE

	public function setDueDate($date)
	{
		return $this->setDateType('DueDate', $date);
	}

	public function getDueDate($format = 'Y-m-d')
	{
		return $this->getDateType('DueDate', $format);
	}

	/**
	 * Set the RefNumber for the JournalEntry
	 *
	 * @param string $value
	 * @return boolean
	 */
	public function setRefNumber($value)
	{
		return $this->set('RefNumber', $value);
	}

	/**
	 * Get the RefNumber for the JournalEntry
	 *
	 * @return string
	 */
	public function getRefNumber()
	{
		return $this->get('RefNumber');
	}

	// Path: Memo, datatype: STRTYPE

	/**
	 * Set the Memo for the JournalEntry
	 *
	 * @param string $value
	 * @return boolean
	 */
	public function setMemo($value)
	{
		return $this->set('Memo', $value);
	}

	/**
	 * Get the Memo for the JournalEntry
	 *
	 * @return string
	 */
	public function getMemo()
	{
		return $this->get('Memo');
	}

	public function addExpenseLine($obj)
	{
		return $this->addListItem('ExpenseLine', $obj);
	}

	public function addItemLine($obj)
	{
		return $this->addListItem('ItemLine', $obj);
	}

	public function asList($request)
	{
		switch ($request)
		{
			case 'BillAddRq':

				if (isset($this->_object['ItemLine']))
				{
					$this->_object['ItemLineAdd'] = $this->_object['ItemLine'];
				}

				if (isset($this->_object['ExpenseLine']))
				{
					$this->_object['ExpenseLineAdd'] = $this->_object['ExpenseLine'];
				}

				break;
			case 'BillModRq':

				break;
		}

		return parent::asList($request);
	}

	public function asXML($root = null, $parent = null, $object = null)
	{
		if (is_null($object))
		{
			$object = $this->_object;
		}

		switch ($root)
		{
			case QUICKBOOKS_ADD_BILL:

				if (!empty($object['ItemLineAdd']))
				{
					foreach ($object['ItemLineAdd'] as $key => $obj)
					{
						$obj->setOverride('ItemLineAdd');
					}
				}

				if (!empty($object['ExpenseLineAdd']))
				{
					foreach ($object['ExpenseLineAdd'] as $key => $obj)
					{
						$obj->setOverride('ExpenseLineAdd');
					}
				}

				break;
			case QUICKBOOKS_MOD_BILL:

				if (!empty($object['ItemLineMod']))
				{
					foreach ($object['ItemLineMod'] as $key => $obj)
					{
						$obj->setOverride('ItemLineMod');
					}
				}

				if (!empty($object['ExpenseLineMod']))
				{
					foreach ($object['ExpenseLineMod'] as $key => $obj)
					{
						$obj->setOverride('ExpenseLineMod');
					}
				}

				break;
		}

		return parent::asXML($root, $parent, $object);
	}

	/**
	 * Tell the type of object this is
	 *
	 * @return string
	 */
	public function object()
	{
		return QUICKBOOKS_OBJECT_BILL;
	}
}


<?php

/**
 *
 *
 * Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * @license LICENSE.txt
 * @author Keith Palmer <Keith@ConsoliBYTE.com>
 *
 * @package QuickBooks
 * @subpackage IPP
 */

QuickBooks_Loader::load('/QuickBooks/IPP/Service.php');

class QuickBooks_IPP_Service_VendorCredit extends QuickBooks_IPP_Service
{
	/*
	public function findAll($Context, $realmID)
	{
		$xml = null;
		return parent::_findAll($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_VENDORCREDIT, $xml);
	}
	*/

	public function add($Context, $realmID, $Object)
	{
		return parent::_add($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_VENDORCREDIT, $Object);
	}

	public function update($Context, $realm, $IDType, $Object)
	{
		return parent::_update($Context, $realm, QuickBooks_IPP_IDS::RESOURCE_VENDORCREDIT, $Object, $IDType);
	}

	public function query($Context, $realm, $query)
	{
		return parent::_query($Context, $realm, $query);
	}

	/**
	 * Deletes vendor credit.
	 *
	 * @param object $Context Context.
	 * @param string $realmID Company Id.
	 * @param string $IDType  Resource.
	 *
	 * @return boolean
	 */
	public function delete($Context, $realmID, $IDType)
	{
		return parent::_delete($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_VENDORCREDIT, $IDType);
	}

}


<?php defined('SYSPATH') or die('No direct script access.');

/**
 * QuickBooks Kohana Integration Example
 *
 * This sample will let you link up to the qb_data database
 *
 * See model folder for example of how to move methods out of this
 *
 * @author Jayson Lindsley <jay.lindsley@gmail.com>
 *
 */
class Model_Qbdata extends ORM {

	public function __construct()
	{
		//link up this model to our qb_data database
		$this->_db = Database::instance('qb_data');
	}

	/*
	 * Gets all Quickbooks errors from the database and returns the array to the caller
	 *
	 */
	public function GetErrors() {
		//Build the query and condition
		$query = DB::select('quickbooks_queue_id', 'ident','qb_action','qbxml','msg','enqueue_datetime')->from('quickbooks_queue')->where('msg','!=',"");
		//Query against the models database
		$errorset = $this->_db->query(Database::SELECT, $query, FALSE)->as_array();
		//Go through the errors if you want to find some relevant data
		$errorset = Model_Qbdata::FetchDetails($errorset);
		//echo Debug::vars($errorset);
		return $errorset;
	}

	/*
	 * Gets all Log entries from the quickbooks API
	 */
	public function GetLogs(){
		$query = DB::select()->from('quickbooks_logs');
		$logentries = $this->_db->query(Database::SELECT, $query, FALSE)->as_array();
		return $logentries;
	}

	/*
	 * Check how many items are in queue right now
	 */
	public function GetQueueBreakdown() {
		$metrics = array();
		//Initialize metrics measurements
		$metrics['custadd'] = 0;
		$metrics['custmod'] = 0;
		//prepare and execute query
		$query = DB::select()->from('quickbooks_queue');
		$qentry = $this->_db->query(Database::SELECT, $query, FALSE)->as_array();

		foreach ($qentry as $entry) {
			if($entry['qb_action'] == 'CustomerAdd')
				$metrics['custadd']++;
			elseif($entry['qb_action'] == 'CustomerMod')
				$metrics['custmod']++;
		}
		return $metrics;
	}

	/*
	 * Uses the details of the action and ident to find additional information
	 */
	private function FetchDetails($errorset)
	{
		//if you'd like to check the queue to find more details you can do so here
		//for instance, of the action is CustomerAdd and it failed, you can pull the customer
		//using the details from the queue
	}
}


<?php

/**
 *
 *
 * Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * Used by:
 * 	- QuickBooks Merchant Service
 * 	- QuickBooks Online Edition
 * 	- QuickBooks WHMCS Integrator (API fetching)
 * 	- QuickBooks HTTP Hooks
 *	- QuickBooks HTTP Bridges
 *  - QuickBooks Foxycart integrator (relaying)
 *
 *
 * @todo Documentation?
 *
 * @license LICENSE.txt
 * @author Keith Palmer <keith@ConsoliBYTE.com>
 *
 * @package QuickBooks
 * @subpackage HTTP
 */

/**
 * QuickBooks utilities class
 */
QuickBooks_Loader::load('/QuickBooks/Utilities.php');

define('QUICKBOOKS_HTTP_ERROR_OK', QUICKBOOKS_ERROR_OK);

define('QUICKBOOKS_HTTP_ERROR_CERTIFICATE', 1);

define('QUICKBOOKS_HTTP_ERROR_UNSUPPORTED', 2);

define('QUICKBOOKS_HTTP_METHOD_GET', 'GET');

define('QUICKBOOKS_HTTP_METHOD_POST', 'POST');

define('QUICKBOOKS_HTTP_METHOD_DELETE', 'DELETE');

define('QUICKBOOKS_HTTP_METHOD_HEAD', 'HEAD');

class QuickBooks_HTTP
{
	const HTTP_400 = 400;
	const HTTP_401 = 401;
	const HTTP_404 = 404;
	const HTTP_500 = 500;

	protected $_url;

	protected $_request_headers;

	protected $_body;

	protected $_post;

	protected $_get;

	protected $_last_response;

	protected $_last_request;

	protected $_last_duration;

	protected $_last_info;

	protected $_errnum;

	protected $_errmsg;

	protected $_verify_peer;

	protected $_verify_host;

	protected $_certificate;

	protected $_masking;

	protected $_log;

	protected $_debug;

	protected $_test;

	protected $_return_headers;

	/**
	 * A variable indicating whether or not to make a synchronous request
	 * @var boolean
	 */
	protected $_sync;

	/**
	 * Create a new QuickBooks_HTTP object instance to make HTTP requests to remote URLs
	 *
	 * @param string $url		The URL to make an HTTP request to
	 */
	public function __construct($url = null)
	{
		$this->_url = $url;

		$this->_verify_peer = true;
		$this->_verify_host = true;

		$this->_masking = true;

		$this->_log = '';

		$this->_debug = false;
		$this->_test = false;

		$this->_sync = true;

		$this->_request_headers = array();
		$this->_return_headers = false;

		$this->_last_request = null;
		$this->_last_response = null;
		$this->_last_duration = 0.0;
	}

	/**
	 * Set the URL
	 *
	 * @param string $url
	 * @return void
	 */
	public function setURL($url)
	{
		$this->_url = $url;
	}

	/**
	 * Get the URL
	 *
	 * @return string
	 */
	public function getURL()
	{
		// @TODO Support for query string args from ->setGETValues()
		return $this->_url;
	}

	public function verifyPeer($yes_or_no)
	{
		$this->_verify_peer = (boolean) $yes_or_no;
	}

	public function verifyHost($yes_or_no)
	{
		$this->_verify_host = (boolean) $yes_or_no;
	}

	public function setHeaders($arr)
	{
		foreach ($arr as $key => $value)
		{
			if (is_numeric($key) and
				false !== ($pos = strpos($value, ':')))
			{
				// 0 => "Header: value" format

				$key = substr($value, 0, $pos);
				$value = ltrim(substr($value, $pos + 1));
			}

			// "Header" => "value" format

			$this->setHeader($key, $value);
		}
	}

	/**
	 * Tell whether or not to return the HTTP response headers
	 *
	 * @param boolean $return
	 * @return void
	 */
	public function returnHeaders($return)
	{
		$this->_return_headers = (boolean) $return;
	}

	public function setHeader($key, $value)
	{
		$this->_request_headers[$key] = $value;
	}

	public function getHeaders($as_combined_array = false)
	{
		if ($as_combined_array)
		{
			$list = array();
			foreach ($this->_request_headers as $key => $value)
			{
				$list[] = $key . ': ' . $value;
			}

			return $list;
		}

		return $this->_request_headers;
	}

	public function getHeader($key)
	{
		if (isset($this->_request_headers[$key]))
		{
			return $this->_request_headers[$key];
		}

		return null;
	}

	public function setRawBody($str)
	{
		$this->_body = $str;
	}

	public function setPOSTValues($arr)
	{
		$this->_post = $arr;
	}

	public function setGETValues($arr)
	{
		$this->_get = $arr;
	}

	/**
	 *
	 *
	 * @return string
	 */
	public function getRawBody()
	{
		if ($this->_body)
		{
			return $this->_body;
		}
		else if (count($this->_post))
		{
			return http_build_query($this->_post);
		}

		return '';
	}

	public function setCertificate($cert)
	{
		$this->_certificate = $cert;
	}

	public function GET()
	{
		return $this->_request(QUICKBOOKS_HTTP_METHOD_GET);
	}

	public function POST()
	{
		return $this->_request(QUICKBOOKS_HTTP_METHOD_POST);
	}

	public function DELETE()
	{
		return $this->_request(QUICKBOOKS_HTTP_METHOD_DELETE);
	}

	public function HEAD()
	{
		return $this->_request(QUICKBOOKS_HTTP_METHOD_HEAD);
	}

	public function useDebugMode($yes_or_no)
	{
		$prev = $this->_debug;
		$this->_debug = (boolean) $yes_or_no;

		return $prev;
	}

	/**
	 * If masking is enabled (default) then credit card numbers, connection tickets, and session tickets will be masked when output or logged
	 *
	 * @param boolean $yes_or_no
	 * @return void
	 */
	public function useMasking($yes_or_no)
	{
		$this->_masking = (boolean) $yes_or_no;
	}

	public function useTestEnvironment($yes_or_no)
	{
		$prev = $this->_test;
		$this->_test = (boolean) $yes_or_no;

		return $prev;
	}

	public function useLiveEnvironment($yes_or_no)
	{
		$prev = $this->_test;
		$this->_test = ! (boolean) $yes_or_no;

		return $prev;
	}

	/**
	 * Get the error number of the last error that occured
	 *
	 * @return integer
	 */
	public function errorNumber()
	{
		return $this->_errnum;
	}

	/**
	 * Get the error message of the last error that occured
	 *
	 * @return string
	 */
	public function errorMessage()
	{
		return $this->_errmsg;
	}

	/**
	 * Get the last raw XML response that was received
	 *
	 * @return string
	 */
	public function lastResponse()
	{
		return $this->_last_response;
	}

	/**
	 * Get the last raw XML request that was sent
	 *
	 * @return string
	 */
	public function lastRequest()
	{
		return $this->_last_request;
	}

	/**
	 *
	 *
	 */
	public function lastDuration()
	{
		return $this->_last_duration;
	}

	public function lastInfo()
	{
		return $this->_last_info;
	}

	/**
	 * Set an error message
	 *
	 * @param integer $errnum	The error number/code
	 * @param string $errmsg	The text error message
	 * @return void
	 */
	protected function _setError($errnum, $errmsg = '')
	{
		$this->_errnum = $errnum;
		$this->_errmsg = $errmsg;
	}

	/**
	 *
	 *
	 *
	 * @param string $message
	 * @param integer $level
	 * @return boolean
	 */
	protected function _log($message)
	{
		if ($this->_masking)
		{
			$message = QuickBooks_Utilities::mask($message);
		}

		if ($this->_debug)
		{
			print($message . QUICKBOOKS_CRLF);
		}

		//
		$this->_log .= $message . QUICKBOOKS_CRLF;

		return true;
	}

	public function resetLog()
	{
		$this->_log = '';
	}

	public function getLog()
	{
		return $this->_log;
	}

	/**
	 *
	 * @todo Implement support for asynchronous requests
	 *
	 */
	public function setSynchronous($yes_or_no)
	{
		$this->_sync = (boolean) $yes_or_no;
	}

	public function setAsynchronous($yes_or_no)
	{
		$this->_sync = !( (boolean) $yes_or_no);
	}

	/**
	 * Make an HTTP request
	 *
	 * @param string $method
	 * @return string
	 */
	protected function _request($method)
	{
		$start = microtime(true);

		if (!function_exists('curl_init'))
		{
			die('You must have the PHP cURL extension (php.net/curl) enabled to use this (' . QUICKBOOKS_PACKAGE_NAME . ' v' . QUICKBOOKS_PACKAGE_VERSION . ').');
		}

		$this->_log('Using CURL to send request!', QUICKBOOKS_LOG_DEVELOP);
		$return = $this->_requestCurl($method, $errnum, $errmsg);

		if ($errnum)
		{
			$this->_setError($errnum, $errmsg);
		}

		// Calculate and set how long the last HTTP request/response took to make
		$this->_last_duration = microtime(true) - $start;

		return $return;
	}

	protected function _requestCurl($method, &$errnum, &$errmsg)
	{
		$url = $this->getURL();
		$raw_body = $this->getRawBody();

		$headers = $this->getHeaders(true);

		$this->_log('Opening connection to: ' . $url, QUICKBOOKS_LOG_VERBOSE);

		$params = array();

		if ($method == QUICKBOOKS_HTTP_METHOD_POST)
		{
			$headers[] = 'Content-Length: ' . strlen($raw_body);
			$params[CURLOPT_POST] = true;
			$params[CURLOPT_POSTFIELDS] = $raw_body;
		}

		$query = '';
		if (count($this->_get))
		{
			$query = '?' . http_build_query($this->_get);
		}

		if ($qs = parse_url($url, PHP_URL_QUERY) and
			false !== strpos($qs, ' '))
		{
			$url = str_replace($qs, str_replace(' ', '+', $qs), $url);
		}

		//print(' [[[ ' . parse_url($url, PHP_URL_QUERY) . '  /  ' . $url . ' ]]] ');

		$params[CURLOPT_RETURNTRANSFER] = true;
		$params[CURLOPT_URL] = $url . $query;
		//$params[CURLOPT_TIMEOUT] = 15;
		$params[CURLOPT_HTTPHEADER] = $headers;
		$params[CURLOPT_ENCODING] = '';			// This makes it say it supports gzip *and* deflate

		$params[CURLOPT_VERBOSE] = $this->_debug;

		if ($this->_return_headers)
		{
			$params[CURLOPT_HEADER] = true;
		}

		// Some Windows servers will fail with SSL errors unless we turn this off
		if (!$this->_verify_peer)
		{
			$params[CURLOPT_SSL_VERIFYPEER] = false;
		}

		if (!$this->_verify_host)
		{
			$params[CURLOPT_SSL_VERIFYHOST] = 0;
		}

		// Check for an SSL certificate (HOSTED model of communication)
		if ($this->_certificate)
		{
			if (file_exists($this->_certificate))
			{
				$this->_log('Using SSL certificate at: ' . $this->_certificate, QUICKBOOKS_LOG_DEBUG);
				$params[CURLOPT_SSLCERT] = $this->_certificate;
			}
			else
			{
				$msg = 'Specified SSL certificate could not be located: ' . $this->_certificate;

				$this->_log($msg, QUICKBOOKS_LOG_NORMAL);
				$errnum = QUICKBOOKS_HTTP_ERROR_CERTIFICATE;
				$errmsg = $msg;
				return false;
			}
		}

		// Fudge the outgoing request because CURL won't give us it
		$request = '';

		if ($method == QUICKBOOKS_HTTP_METHOD_POST)
		{
			$request .= 'POST ';
		}
		elseif ($method == QUICKBOOKS_HTTP_METHOD_DELETE)
		{
			$request .= 'DELETE ';
			$params[CURLOPT_CUSTOMREQUEST] = 'DELETE';
		}
		elseif ($method == QUICKBOOKS_HTTP_METHOD_HEAD)
		{
			$request .= 'HEAD ';
		}
		else
		{
			$request .= 'GET ';
		}
		$request .= $params[CURLOPT_URL] . ' HTTP/1.1' . "\r\n";

		foreach ($headers as $header)
		{
			$request .= $header . "\r\n";
		}
		$request .= "\r\n";
		$request .= $this->getRawBody();

		$this->_log('CURL options: ' . print_r($params, true), QUICKBOOKS_LOG_DEBUG);

		$this->_last_request = $request;
		$this->_log('HTTP request: ' . $request, QUICKBOOKS_LOG_DEBUG);	// Set as DEBUG so that no one accidentally logs all the credit card numbers...

		$ch = curl_init();
		curl_setopt_array($ch, $params);
		$response = curl_exec($ch);

		/*
		print("\n\n\n" . '---------------------' . "\n");
		print('[[request ' . $request . ']]' . "\n\n\n");
		print('[[resonse ' . $response . ']]' . "\n\n\n\n\n");

		print_r($params);
		print_r(curl_getinfo($ch));
		print_r($headers);
		print("\n" . '---------------------' . "\n\n\n\n");
		*/

		$this->_last_response = $response;
		$this->_log('HTTP response: ' . substr($response, 0, 500) . '...', QUICKBOOKS_LOG_VERBOSE);

		$this->_last_info = curl_getinfo($ch);

		if (curl_errno($ch))
		{
			$errnum = curl_errno($ch);
			$errmsg = curl_error($ch);

			$this->_log('CURL error: ' . $errnum . ': ' . $errmsg, QUICKBOOKS_LOG_NORMAL);

			return false;
		}

		// Close the connection
		@curl_close($ch);

		return $response;
	}
}