'Service Delivery Method', 'items' => 'Products Weight', 'shipperCountry' => 'Shipper Country Code', 'shipperState' => 'Shipper State/Province Code', 'shipperPostalCode' => 'Shipper Postal Code', 'shipToCountry' => 'Destination Country Code', 'shipToState' => 'Destination State/Province Code', 'shipToPostalCode' => 'Destination Postal Code', ]; /** @var App_Service_Shipping_Config */ protected $_config = []; /** @var App_Service_Shipping_ResponseAbstract */ protected $_response = null; /** * Service name (Ups, FedEx, Usps, ...) * * @var string */ protected $_serviceName = null; /** * Allowed service codes * * @var array */ protected $_serviceCodes = []; protected $_packagesInformation = []; protected $_errorMap = [ self::ERROR_VALIDATE_REQUIRED_PARAMS => 'Please set %s', self::ERROR_SERVICE_CONNECTION => '"%s" Service is not responding', self::ERROR_PRODUCT_GREATER_WEIGHT => '"%s" Product\'s weight can\'t be greater than %s pound(s) with selected shipping service.', ]; /** * Method calls shipping service and retrieves rate information * * @abstract * @return mixed */ abstract public function getRate(); function __construct($options = []) { $this->setOptions($options); $this->initResponseObject(); } public function setOptions($options) { $options = Qs_Options::setOptions($this, $options); $this->_options = array_merge($this->_options, $options); return $this; } public function initResponseObject() { if (empty($this->_response) || !($this->_response instanceof App_Service_Shipping_ResponseAbstract)) { $responseClassName = 'App_Service_Shipping_' . $this->getServiceName() . '_Response'; if (class_exists($responseClassName)) { $this->_response = new $responseClassName(); $this->_response->setServiceDetails([ 'name' => $this->getServiceName(), 'serviceCode' => $this->getOption('serviceCode'), ]); } else { throw new Qs_Db_Exception('Response Object "' . $responseClassName . '" is not exists'); } } return $this; } public function getConfig() { if (empty($this->_config)) { $this->_config = App_Service_Shipping_Config::get( 'Service_Shipping_' . $this->getServiceName(), Qs_Config::APP_TYPE ); } return $this->_config; } /** * Method validates required fields that need rate service * * @abstract * @return mixed */ protected function _validateRequiredRateFields() { $errors = []; foreach ($this->_requiredOptionsMap as $option => $description) { $optionValue = $this->getOption($option); $value = is_string($optionValue) ? trim($optionValue) : $optionValue; if (empty($value)) { $errors[] = sprintf( $this->_errorMap[self::ERROR_VALIDATE_REQUIRED_PARAMS], $description ); } } $packagesImformation = $this->_getPackagesInformation(); if (isset($packagesImformation['error'])) { $errors[] = $packagesImformation['error']; } return empty($errors) ? true : $errors; } /** * @return string */ public function getServiceName() { if (empty($this->_serviceName)) { $reflectionClass = new ReflectionClass($this); $this->_serviceName = basename($reflectionClass->getFileName(), '.php'); } return $this->_serviceName; } public function getOptions() { return $this->_options; } public function getOption($option) { if (array_key_exists($option, $this->_options)) { return $this->_options[$option]; } elseif (null != ($defaultOption = $this->getConfig()->{'default' . ucfirst($option)})) { return $defaultOption; } return false; } /** * @return array */ public function getServiceCodes() { if (empty($this->_serviceCodes)) { $codes = $this->getConfig()->serviceCodes; if ($codes) { $this->_serviceCodes = $codes->toArray(); } } return $this->_serviceCodes; } /** * Method prepares weight. Examples: 234.24 => 234.3, 234.26 => 234.3, 234.2 => 234.2 * * @param $weight * @return string */ protected function _prepareWeight($weight) { return Zend_Locale_Math::Div(ceil(Zend_Locale_Math::Mul(floatval($weight), 10, 4)), 10, 1); } protected function _getPackagesInformation() { if (empty($this->_packagesInformation)) { $items = $this->getOption('items'); foreach ($items as $item) { if ((float) $item['weight'] > $this->getConfig()->packageWeightLimit) { return [ 'error' => sprintf( $this->_errorMap[self::ERROR_PRODUCT_GREATER_WEIGHT], htmlspecialchars($item['title']), $this->getConfig()->packageWeightLimit ), ]; } for ($i = 1; $i <= $item['quantity']; $i++) { $this->_preparePackages($item, $this->_packagesInformation); } } } return $this->_packagesInformation; } protected function _preparePackages($item, &$packages) { $packagesSize = sizeof($packages); $needNewPackage = true; for ($i = 0; $i <= $packagesSize; $i++) { $packageWeight = (!empty($packages[$i]['weight']) ? $packages[$i]['weight'] : 0); if ((float) $packageWeight == $this->getConfig()->packageWeightLimit || Zend_Locale_Math::Add($packageWeight, $item['weight'], 4) > $this->getConfig()->packageWeightLimit ) { continue; } $packages[$i] = [ 'weight' => Zend_Locale_Math::Add($packageWeight, $item['weight'], 4), ]; $needNewPackage = false; break; } if (true === $needNewPackage) { $newPackageKey = $packagesSize + 1; $packages[$newPackageKey] = [ 'weight' => $item['weight'], ]; } return $this; } }