<?php
/**
* @version	$Id: orders_item.php 16516 2017-01-20 14:12:22Z alex $
* @package	In-Commerce
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license	Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/

	defined('FULL_PATH') or die('restricted access!');

	class OrdersItem extends kDBItem
	{

		/**
		 * Sets item' fields corresponding to elements in passed $hash values.
		 * The function sets current item fields to values passed in $hash, by matching $hash keys with field names
		 * of current item. If current item' fields are unknown {@link kDBItem::PrepareFields()} is called before actually setting the fields
		 *
		 * @param Array $hash       Fields hash.
		 * @param Array $set_fields Optional param, field names in target object to set, other fields will be skipped
		 *
		 * @return void
		 */
		public function SetFieldsFromHash($hash, $set_fields = Array ())
		{
			parent::SetFieldsFromHash($hash, $set_fields);

			$options = $this->GetFieldOptions('PaymentCCExpDate');

			if ( $this->GetDirtyField($options['month_field']) || $this->GetDirtyField($options['year_field']) ) {
				$this->SetDirtyField('PaymentCCExpDate', 0);
				$this->SetField('PaymentCCExpDate', 0);
			}
		}

		/**
		 * Returns gateway data based on payment type used in order
		 *
		 * @param int $pt_id
		 * @return Array
		 * @access public
		 */
		public function getGatewayData($pt_id = null)
		{
			// get Gateway fields
			if ( !isset($pt_id) || !$pt_id ) {
				$pt_id = $this->GetDBField('PaymentType');

				if ( !$pt_id ) {
					// no Payment Type Id found for this order - escape SQL fatal below
					return false;
				}
			}

			$pt_table = $this->Application->getUnitOption('pt', 'TableName');

			$sql = 'SELECT GatewayId
					FROM %s
					WHERE PaymentTypeId = %s';
			$gw_id = $this->Conn->GetOne(sprintf($sql, $pt_table, $pt_id));

			$sql = 'SELECT *
					FROM %s
					WHERE GatewayId = %s';
			$ret = $this->Conn->GetRow(sprintf($sql, TABLE_PREFIX . 'Gateways', $gw_id));

			// get Gateway parameters based on payment type
			$gwf_table = $this->Application->getUnitOption('gwf', 'TableName');
			$gwfv_table = $this->Application->getUnitOption('gwfv', 'TableName');

			$sql = 'SELECT gwfv.Value, gwf.SystemFieldName
					FROM %s gwf
					LEFT JOIN %s gwfv ON gwf.GWConfigFieldId = gwfv.GWConfigFieldId
					WHERE gwfv.PaymentTypeId = %s AND gwf.GatewayId = %s';
			$ret['gw_params'] = $this->Conn->GetCol(sprintf($sql, $gwf_table, $gwfv_table, $pt_id, $gw_id), 'SystemFieldName');

			$ret['gw_params']['gateway_id'] = $gw_id;

			if ( $this->GetDBField('IsRecurringBilling') && $this->Application->ConfigValue('Comm_AutoProcessRecurringOrders') ) {
				if ( isset($ret['gw_params']['shipping_control']) ) {
					$ret['gw_params']['shipping_control'] = SHIPPING_CONTROL_DIRECT;
				}
			}

			return $ret;
		}

		/**
		 * Checks if tangible items are present in order
		 *
		 * @return boolean
		 */
		public function HasTangibleItems()
		{
			$oi_table = TABLE_PREFIX . 'OrderItems';

			if ( $this->IsTempTable() ) {
				$oi_table = $this->Application->GetTempName($oi_table, 'prefix:' . $this->Prefix);
			}

			$sql = 'SELECT COUNT(*)
					FROM ' . $oi_table . ' orditems
					LEFT JOIN ' . TABLE_PREFIX . 'Products p ON p.ProductId = orditems.ProductId
					WHERE (orditems.OrderId = ' . $this->GetID() . ') AND (p.Type = ' . PRODUCT_TYPE_TANGIBLE . ')';

			return $this->Conn->GetOne($sql) > 0;
		}

		/**
		 * Calculates tax value of order items based on billing & shipping country specified
		 *
		 * @return double
		 */
		function getTaxPercent()
		{
			/** @var kCountryStatesHelper $cs_helper */
			$cs_helper = $this->Application->recallObject('CountryStatesHelper');

			$shipping_country_id = $cs_helper->getCountryStateId($this->GetDBField('ShippingCountry'), DESTINATION_TYPE_COUNTRY);
			$shipping_state_id = $cs_helper->getCountryStateId($this->GetDBField('ShippingState'), DESTINATION_TYPE_STATE);
			$shipping_zip = (string) $this->GetDBField('ShippingZip');

			$billing_country_id = $cs_helper->getCountryStateId($this->GetDBField('BillingCountry'), DESTINATION_TYPE_COUNTRY);
			$billing_state_id =  $cs_helper->getCountryStateId($this->GetDBField('BillingState'), DESTINATION_TYPE_STATE);
			$billing_zip = (string) $this->GetDBField('BillingZip');

			/*
			$dest_ids = array_diff( array_unique( Array( $shipping_country_id, $shipping_state_id, $billing_country_id, $billing_state_id ) ), Array(0) );
			$dest_values = array_diff( array_unique( Array( $this->Conn->qstr($shipping_zip), $this->Conn->qstr($billing_zip) ) ), Array('\'\'') );
			*/

			$tax = false;
			$sql = 'SELECT tx.*
				FROM '.$this->Application->getUnitOption('tax', 'TableName').' tx
				LEFT JOIN '.$this->Application->getUnitOption('taxdst', 'TableName').' txd ON tx.TaxZoneId = txd.TaxZoneId
				WHERE
					(	txd.StdDestId IN ('.$shipping_country_id.','.$shipping_state_id.')
						AND
						( (txd.DestValue = "" OR txd.DestValue IS NULL)
							OR
							txd.DestValue = '.$this->Conn->qstr($shipping_zip).'
						)
					)
					OR
					(	txd.StdDestId IN ('.$billing_country_id.','.$billing_state_id.')
						AND
						( (txd.DestValue = "" OR txd.DestValue IS NULL)
							OR
							txd.DestValue = '.$this->Conn->qstr($billing_zip).'
						)
					)

				ORDER BY tx.TaxValue DESC';

			$tax = $this->Conn->GetRow($sql);
			if ($tax == false) {
				$tax['TaxValue'] = 0;
				$tax['ApplyToShipping'] = 0;
				$tax['ApplyToProcessing'] = 0;
			}

			return $tax;
		}

		function RecalculateTax()
		{
			$tax = $this->getTaxPercent();
			$this->SetDBField('VATPercent', $tax['TaxValue']);
			$this->SetDBField('ShippingTaxable', $tax['ApplyToShipping']);
			$this->SetDBField('ProcessingTaxable', $tax['ApplyToProcessing']);
			$this->UpdateTotals();

			if ( !$this->GetDBField('VATIncluded') ) {
				$subtotal = $this->GetDBField('AmountWithoutVAT');

				$tax_exempt = $this->getTaxExempt();

				if ( $tax_exempt ) {
					$subtotal -= $tax_exempt;
				}

				$this->SetDBField('VAT', round($subtotal * $tax['TaxValue'] / 100, 2));
				$this->UpdateTotals();
			}
		}

		/**
		 * Returns order amount, that is excluded from tax calculations
		 *
		 * @return float
		 * @access protected
		 */
		protected function getTaxExempt()
		{
			$sql = 'SELECT SUM(oi.Quantity * oi.Price)
					FROM ' . TABLE_PREFIX . 'OrderItems AS oi
					LEFT JOIN ' . TABLE_PREFIX . 'Products AS p ON p.ProductId = oi.ProductId
					WHERE p.Type = 6 AND oi.OrderId = ' . $this->GetDBField('OrderId');

			return $this->Conn->GetOne($sql);
		}

		function UpdateTotals()
		{
			$total = 0;
			$total += $this->GetDBField('SubTotal');

			if ( $this->GetDBField('ShippingTaxable') ) {
				$total += $this->GetDBField('ShippingCost');
			}

			if ( $this->GetDBField('ProcessingTaxable') ) {
				$total += $this->GetDBField('ProcessingFee');
			}

			if ( $this->GetDBField('VATIncluded') ) {
				$tax_exempt = $this->getTaxExempt();

				$vat_percent = $this->GetDBField('VATPercent');
				$this->SetDBField('VAT', round(($total - $tax_exempt) * $vat_percent / (100 + $vat_percent), 2));
				$this->SetDBField('AmountWithoutVAT', $total - $this->GetDBField('VAT'));
			}
			else {
				$this->SetDBField('AmountWithoutVAT', $total);
				$total += $this->GetDBField('VAT');
			}

			if ( !$this->GetDBField('ShippingTaxable') ) {
				$total += $this->GetDBField('ShippingCost');
			}

			if ( !$this->GetDBField('ProcessingTaxable') ) {
				$total += $this->GetDBField('ProcessingFee');
			}

			$total += $this->GetDBField('InsuranceFee');

			$this->SetDBField('TotalAmount', $total);
		}

		function getTotalAmount()
		{
			return 	$this->GetDBField('SubTotal') +
					$this->GetDBField('ShippingCost') +
					($this->GetDBField('VATIncluded') ? 0 : $this->GetDBField('VAT')) +
					$this->GetDBField('ProcessingFee') +
					$this->GetDBField('InsuranceFee') -
					$this->GetDBField('GiftCertificateDiscount');
		}

		function requireCreditCard()
		{
			$sql = 'SELECT RequireCCFields
					FROM ' . $this->Application->getUnitOption('pt', 'TableName') . ' pt
					LEFT JOIN '.TABLE_PREFIX.'Gateways gw ON gw.GatewayId = pt.GatewayId
					WHERE pt.PaymentTypeId = ' . $this->GetDBField('PaymentType');

			return $this->Conn->GetOne($sql);
		}

		function getNextSubNumber()
		{
			$table = $this->Application->GetLiveName($this->TableName);
			$sql = 'SELECT MAX(SubNumber) FROM '.$table.' WHERE Number = '.$this->GetDBField('Number');
			return $this->Conn->GetOne($sql) + 1;
		}

		function ResetAddress($prefix)
		{
			$fields = Array('To','Company','Phone','Fax','Email','Address1','Address2','City','State','Zip','Country');
			foreach($fields as $field)
			{
				$this->SetDBField($prefix.$field, $this->Fields[$prefix.$field]['default']);
			}
		}

		function IsProfileAddress($address_type)
		{
			return $this->Application->GetVar($this->Prefix.'_IsProfileAddress');
		}

		// ===== Gift Certificates Related =====
		function RecalculateGift($event)
		{
			$gc_id = $this->GetDBField('GiftCertificateId');
			if ($gc_id < 1) {
				return;
			}

			/** @var kDBItem $gc */
			$gc = $this->Application->recallObject('gc', null, Array('skip_autoload' => true));

			$gc->Load($gc_id);

			if ($gc->GetDBField('Status') == gcDISABLED) {
				// disabled GC
				$this->SetDBField('GiftCertificateId', 0);
				$this->SetDBField('GiftCertificateDiscount', 0);
				// disabled
				return;
			}

			$debit = $gc->GetDBField('Debit') + $this->GetDBField('GiftCertificateDiscount');

			$this->UpdateTotals();

			$total = $this->GetDBField('TotalAmount');
			$gift_certificate_discount = $debit >= $total ? $total : $debit;

			$this->SetDBField('TotalAmount', $total - $gift_certificate_discount);
			$this->GetDBField('GiftCertificateDiscount', $gift_certificate_discount);

			$debit -= $gift_certificate_discount;
			$gc->SetDBField('Debit', $debit);

			$gc->SetDBField('Status', $debit > 0 ? gcENABLED : gcUSED);
			$gc->Update();

			if ($gift_certificate_discount == 0) {
				$this->RemoveGiftCertificate($object);
				$this->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_REMOVED_AUTOMATICALLY);
			}

			$this->SetDBField('GiftCertificateDiscount', $gift_certificate_discount);
		}

		function RemoveGiftCertificate()
		{
			$gc_id = $this->GetDBField('GiftCertificateId');

			/** @var kDBItem $gc */
			$gc = $this->Application->recallObject('gc', null, Array('skip_autoload' => true));

			$gc->Load($gc_id);

			$debit = $gc->GetDBField('Debit') + $this->GetDBField('GiftCertificateDiscount');

			if ($gc->isLoaded() && ($debit > 0)) {
				$gc->SetDBField('Debit', $debit);
				$gc->SetDBField('Status', gcENABLED);
				$gc->Update();
			}

			$this->SetDBField('GiftCertificateId', 0);
			$this->SetDBField('GiftCertificateDiscount', 0);
		}

		/**
		 * Sets checkout error
		 *
		 * @param int $error_type = {product,coupon,gc}
		 * @param int $error_code
		 * @param int $product_id - {ProductId}:{OptionsSalt}:{BackOrderFlag}:{FieldName}
		 */
		function setCheckoutError($error_type, $error_code, $product_id = null)
		{
			$errors = $this->Application->RecallVar('checkout_errors');
			$errors = $errors ? unserialize($errors) : Array ();

			if ( isset($product_id) ) {
				$error_type .= ':' . $product_id;

				// any error takes priority over FIELD_UPDATE_SUCCESS error
				if ( isset($errors[$error_type]) && $error_code == OrderCheckoutError::FIELD_UPDATE_SUCCESS ) {
					return ;
				}
			}

			if ( is_numeric($error_code) ) {
				$errors[$error_type] = $error_code;
			}
			else {
				unset($errors[$error_type]);
			}

			if ( $this->Application->isDebugMode() ) {
				$this->Application->Debugger->appendHTML('CO_ERROR: ' . $error_type . ' - ' . $error_code);
			}

			$this->Application->StoreVar('checkout_errors', serialize($errors));
		}
	}
