493 lines
17 KiB
PHP
493 lines
17 KiB
PHP
<?php
|
||
/**
|
||
* TNT OFFICIAL MODULE FOR PRESTASHOP.
|
||
*
|
||
* @author GFI Informatique <www.gfi.fr>
|
||
* @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');
|
||
}
|
||
}
|