* @copyright 2016-2017 GFI Informatique, 2016-2017 TNT * @license https://opensource.org/licenses/MIT MIT License */ require_once _PS_MODULE_DIR_.'tntofficiel/tntofficiel.php'; require_once _PS_MODULE_DIR_.'tntofficiel/classes/TNTOfficielCart.php'; require_once _PS_MODULE_DIR_.'tntofficiel/libraries/TNTOfficiel_Debug.php'; require_once _PS_MODULE_DIR_.'tntofficiel/libraries/TNTOfficiel_Parcel.php'; require_once _PS_MODULE_DIR_.'tntofficiel/libraries/TNTOfficiel_SoapClient.php'; require_once _PS_MODULE_DIR_.'tntofficiel/libraries/helper/TNTOfficiel_OrderHelper.php'; require_once _PS_MODULE_DIR_.'tntofficiel/libraries/exceptions/TNTOfficiel_MaxPackageWeightException.php'; class TNTOfficiel_ParcelsHelper { /** @var TNTOfficiel_ParcelsHelper */ private static $_instance = null; /** * Constructor. */ public function __construct() { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); } /** * Creates a singleton. * * @param void * * @return TNTOfficiel_ParcelsHelper */ public static function getInstance() { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); if (is_null(TNTOfficiel_ParcelsHelper::$_instance)) { TNTOfficiel_ParcelsHelper::$_instance = new TNTOfficiel_ParcelsHelper(); } return TNTOfficiel_ParcelsHelper::$_instance; } /** * Creates the parcels for an order. * * @param $objCart * @param $intArgOrderID * * @return bool */ public function createParcels($objCart, $intArgOrderID) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $orderedProducts = $this->_getProductsOrderedByWeight($objCart->getProducts()); $fltMaxParcelWeight = $this->_getMaxPackageWeight($intArgOrderID); $parcels = array(); $parcel = new TNTOfficiel_Parcel(); foreach ($orderedProducts as $product) { $productQuantity = $product['quantity']; for ($i = 0; $i < $productQuantity; ++$i) { //check if parcel's weight exceeds the maximum parcel weight //and remove the last added product in the parcel if it's not the only product in it if (($parcel->getWeight() + $product['weight']) <= $fltMaxParcelWeight) { $parcel->addProduct($product); } else { $parcels[] = $parcel; $parcel = new TNTOfficiel_Parcel(); $parcel->addProduct($product); } } } //if the parcels contains at least one product we add it to the list if (count($parcel->getProductList())) { $parcels[] = $parcel; } //save the parcels $this->_saveParcels($parcels, $intArgOrderID); return true; } /** * Order a list of products by weight. * * @param $products * * @return array */ private function _getProductsOrderedByWeight($products) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); usort($products, array(__CLASS__, 'compareProductByWeight')); return $products; } /** * Compare two products by their weight. * * @param $productA * @param $productB * * @return int */ public static function compareProductByWeight($productA, $productB) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); if ($productA['weight'] == $productB['weight']) { return 0; } return ($productA['weight'] < $productB['weight']) ? -1 : 1; } /** * Save the parcels in the database. * * @param $parcels array * @param $orderId int */ private function _saveParcels($parcels, $orderId) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); foreach ($parcels as $parcel) { //insert into the parcel table $this->addParcel($orderId, $parcel->getWeight(), true); } } /** * Get the parcels of an order. * * @param $orderId int * * @return array */ public function getParcels($orderId) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $sql = 'SELECT * FROM '._DB_PREFIX_.'tntofficiel_order_parcels WHERE id_order ='.(int)$orderId; $parcels = Db::getInstance()->executeS($sql); return $parcels; } /** * Remove a parcel. * * @param $parcelId int * * @return bool */ public function removeParcel($parcelId) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); return Db::getInstance()->delete('tntofficiel_order_parcels', 'id_parcel = '.(int)$parcelId); } /** * Add a parcel. * * @param $intArgOrderID int * @param $fltArgWeight float * @param $isOrderCreation * * @return array * * @throws TNTOfficiel_MaxPackageWeightException */ public function addParcel($intArgOrderID, $fltArgWeight, $isOrderCreation = true) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $fltMaxPackageWeight = $this->_getMaxPackageWeight($intArgOrderID); //Does not throw an exception when this is an order creation //the parcel can contains one product which weight exceeds the maximum weight if ((float)$fltArgWeight > $fltMaxPackageWeight && !$isOrderCreation) { throw new TNTOfficiel_MaxPackageWeightException('Le poids d\'un colis ne peut dépasser '.$fltMaxPackageWeight.'Kg'); } Db::getInstance()->insert('tntofficiel_order_parcels', array( 'id_order' => (int)$intArgOrderID, 'weight' => max(round((float)$fltArgWeight, 1, PHP_ROUND_HALF_UP), 0.1), )); $sql = 'SELECT * FROM '._DB_PREFIX_.'tntofficiel_order_parcels WHERE id_parcel ='.Db::getInstance()->Insert_ID(); return Db::getInstance()->executeS($sql); } /** * Update a parcel. * * @param $parcelId int * @param $weight float * @param $intArgOrderID int * * @return bool * * @throws TNTOfficiel_MaxPackageWeightException */ public function updateParcel($parcelId, $weight, $intArgOrderID) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $fltMaxPackageWeight = $this->_getMaxPackageWeight($intArgOrderID); if ((float)$weight > $fltMaxPackageWeight) { throw new TNTOfficiel_MaxPackageWeightException('Le poids d\'un colis ne peut dépasser '.$fltMaxPackageWeight.'Kg'); } $weight = max(round($weight, 1, PHP_ROUND_HALF_UP), 0.1); $result = array(); $result['result'] = Db::getInstance()->update( 'tntofficiel_order_parcels', array('weight' => (float)$weight), 'id_parcel = '.(int)$parcelId ); $result['weight'] = $weight; return $result; } /** * @param $parcel array * * @return array */ public function getTrackingData($parcel) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $response = $this->_callTntWs($parcel['parcel_number']); $response = (array)$response; $returnData = ''; if (count($response) && isset($response['Parcel'])) { $returnData = $response['Parcel']; } if ($returnData) { $this->_savePdl($returnData, $parcel['id_parcel']); $returnData = array( 'history' => $this->_getHistory($returnData), 'status' => $this->_getStatus($returnData), 'allStatus' => $this->_getAllStatus($this->_getStatus($returnData)), ); } return $returnData; } /** * Get the maximum package weight for the order. * * @param $intArgOrderID * * @return float * * @throws Exception */ private function _getMaxPackageWeight($intArgOrderID) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); try { $arrTNTOrder = TNTOfficiel_OrderHelper::getInstance()->getOrderData($intArgOrderID); $strCarrierCode = $arrTNTOrder['carrier_code']; } catch (Exception $objException) { // TODO : Should not happen ! Why using carrier code from cart ?? $objContext = Context::getContext(); $objCart = $objContext->cart; $intCartID = (int)$objCart->id; // Load TNT cart info or create a new one for it's ID. $objTNTCartModel = TNTOfficielCart::loadCartID($intCartID); if(Validate::isLoadedObject($objTNTCartModel)) { $strCarrierCode = $objTNTCartModel->carrier_code; } // throw $objException; } // If carrier code is B2B. if (strpos($strCarrierCode, 'ENTERPRISE')) { $fltMaxPackageWeight = (float)Configuration::get('TNT_CARRIER_MAX_PACKAGE_B2B'); } else { $fltMaxPackageWeight = (float)Configuration::get('TNT_CARRIER_MAX_PACKAGE_B2C'); } return $fltMaxPackageWeight; } /** * Call the tnt. * * @param $parcelNumber * * @return stdClass */ private function _callTntWs($parcelNumber) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $client = new TNTOfficiel_SoapClient(); $result = $client->trackingByConsignment($parcelNumber); return $result; } /** * Return All Status de display : depends on status -> maximum 5 status. * * @param $status * * @return array */ protected function _getAllStatus($status) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $statusArray = array( 1 => TNTOfficiel_ParcelsHelper::translate('Colis chez l’expéditeur'), 2 => TNTOfficiel_ParcelsHelper::translate('Ramassage du Colis'), 3 => TNTOfficiel_ParcelsHelper::translate('Acheminement'), 4 => TNTOfficiel_ParcelsHelper::translate('Livraison en cours'), 5 => TNTOfficiel_ParcelsHelper::translate('Livré'), ); switch ($status) { case 6: $statusArray[5] = TNTOfficiel_ParcelsHelper::translate('Incident'); break; case 7: $statusArray[5] = TNTOfficiel_ParcelsHelper::translate("Retourné à l'expéditeur"); break; } return $statusArray; } /** * @param $parcel * * @return bool|int */ protected function _getStatus($parcel) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $parcel = (array)$parcel; $statusLabel = isset($parcel['shortStatus']) ? $parcel['shortStatus'] : false; if (!$statusLabel) { return false; } $mapping = array( TNTOfficiel_ParcelsHelper::translate('En attente') => 1, '--' => 2, TNTOfficiel_ParcelsHelper::translate('En cours d\'acheminement') => 3, TNTOfficiel_ParcelsHelper::translate('En cours de livraison') => 4, TNTOfficiel_ParcelsHelper::translate('En agence TNT') => 4, TNTOfficiel_ParcelsHelper::translate('Récupéré à l\'agence TNT') => 5, TNTOfficiel_ParcelsHelper::translate('Livré') => 5, TNTOfficiel_ParcelsHelper::translate('En attente de vos instructions') => 6, TNTOfficiel_ParcelsHelper::translate('En attente d\'instructions') => 6, TNTOfficiel_ParcelsHelper::translate('En attente d\'instructions') => 6, TNTOfficiel_ParcelsHelper::translate('Incident de livraison') => 6, TNTOfficiel_ParcelsHelper::translate('Incident intervention') => 6, TNTOfficiel_ParcelsHelper::translate('Colis refusé par le destinataire') => 6, TNTOfficiel_ParcelsHelper::translate('Livraison reprogrammée') => 6, TNTOfficiel_ParcelsHelper::translate('Prise de rendez-vous en cours') => 6, TNTOfficiel_ParcelsHelper::translate('Prise de rendez-vous en cours') => 6, TNTOfficiel_ParcelsHelper::translate('Problème douane') => 6, TNTOfficiel_ParcelsHelper::translate('Enlevé au dépôt') => 3, TNTOfficiel_ParcelsHelper::translate('En dépôt restant') => 3, TNTOfficiel_ParcelsHelper::translate('Retourné à l\'expéditeur') => 7, ); return isset($mapping[$statusLabel]) ? $mapping[$statusLabel] : 1; } /** * @param $parcel * * @return bool */ protected function _getHistory($parcel) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); if (!$parcel->events) { return false; } $history = array(); $states = array(1 => 'request', 2 => 'process', 3 => 'arrival', 4 => 'deliveryDeparture', 5 => 'delivery'); $events = (array)$parcel->events; foreach ($states as $idx => $state) { if ((isset($events[$state.'Center']) || isset($events[$state.'Date'])) && Tools::strlen($events[$state.'Date']) ) { $history[$idx] = array( 'label' => $this->_getLabel($state), 'date' => isset($events[$state.'Date']) ? $events[$state.'Date'] : '', 'center' => isset($events[$state.'Center']) ? $events[$state.'Center'] : '', ); } } return $history; } protected function _getLabel($state) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $labels = array( 'request' => TNTOfficiel_ParcelsHelper::translate('Colis chez l’expéditeur'), 'process' => TNTOfficiel_ParcelsHelper::translate('Ramassage du colis'), 'arrival' => TNTOfficiel_ParcelsHelper::translate('Acheminement du colis'), 'deliveryDeparture' => TNTOfficiel_ParcelsHelper::translate('Livraison du colis en cours'), 'delivery' => TNTOfficiel_ParcelsHelper::translate('Livraison du colis'), ); return isset($labels[$state]) ? $labels[$state] : ''; } /** * Save the PDL. * * @param $data * @param $parcelId * * @return bool */ protected function _savePdl($data, $parcelId) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $result = false; $pdl = $this->_getPdl($data); if ($pdl) { $result = Db::getInstance()->update('tntofficiel_order_parcels', array('pdl' => pSQL($pdl)), 'id_parcel = '.(int)$parcelId); } return $result; } /** * Get POD Url from parcel data. * * @param $data * * @return bool|string */ protected function _getPdl($data) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); $data = (array)$data; return isset($data['primaryPODUrl']) && $data['primaryPODUrl'] ? $data['primaryPODUrl'] : (isset($data['secondaryPODUrl']) && $data['secondaryPODUrl'] ? $data['secondaryPODUrl'] : false); } /** * get translation. * * @param $string * * @return string */ public static function translate($string) { TNTOfficiel_Debug::log(array('msg' => '>>', 'file' => __FILE__, 'line' => __LINE__)); // TODO return Translate::getModuleTranslation(TNTOfficiel::MODULE_NAME, $string, 'parcelshelper'); } }