* @copyright 2017 My Addons * @license myaddons.io * @version 1.1.8 * @since File available since Release 1.0 */ if (!defined('_PS_VERSION_')) { exit; } class BtPartialShipment extends Module { public function __construct() { $this->author = 'MyAddons'; $this->tab = 'shipping_logistics'; $this->need_instance = 0; $this->module_key = "8d245847e707d6117465cfd66851e1e3"; $this->author_address = '0xE4a7B4d225E2D9ACDf6d9e579EA04925912d32A0'; $this->name = 'btpartialshipment'; $this->version = '1.1.8'; $this->bootstrap = true; parent::__construct(); $this->displayName = $this->l('Manage partial shipment by order'); $this->description = $this->l('This module allows you to manage your partial shipments by orders and products.'); $this->confirmUninstall = $this->l('Are you sure you want to delete this module ?'); /* Retrocompatibility */ $this->initContext(); $this->css_path = $this->_path.'views/css/'; $this->js_path = $this->_path.'views/js/'; require_once _PS_MODULE_DIR_.'btpartialshipment/classes/PartialShipment.php'; require_once _PS_MODULE_DIR_.'btpartialshipment/classes/PartialShipmentDetail.php'; require_once _PS_MODULE_DIR_.'btpartialshipment/classes/HTMLTemplatePartialShipment.php'; } /* Retrocompatibility 1.4/1.5 */ private function initContext() { $this->context = Context::getContext(); if (!$this->context->shop->id) { $this->context->shop->id = 1; } } public function install() { if (Shop::isFeatureActive()) { Shop::setContext(Shop::CONTEXT_ALL); } $sql = ''; include(dirname(__FILE__).'/sql/install.php'); foreach ($sql as $s) { if (!Db::getInstance()->execute($s)) { return false; } } if (!parent::install() || !$this->registerHook('displayAdminOrder') || !$this->registerHook('displayOrderDetail') || !$this->registerHook('displayBackOfficeHeader') || !$this->installOrderPartialStatus() || !$this->installOrderCompleteStatus() || !$this->installOrderRemainingStatus() ) { return false; } return true; } private function installOrderPartialStatus() { if (!Configuration::get('BT_PARTIAL_SHIPMENT_STATE')) { $order_state = new OrderState(); $order_state->name = array(); foreach (Language::getLanguages() as $language) { switch (Tools::strtolower($language['iso_code'])) { case 'fr': $order_state->name[$language['id_lang']] = 'Expédition partielle'; break; case 'it': $order_state->name[$language['id_lang']] = 'Parzialmente Spedito'; break; case 'es': $order_state->name[$language['id_lang']] = 'Parcialmente Enviado'; break; case 'pt': $order_state->name[$language['id_lang']] = 'Enviado parcialmente'; break; case 'de': $order_state->name[$language['id_lang']] = 'Teilweise geliefert'; break; case 'en': $order_state->name[$language['id_lang']] = 'Partial shipment'; break; case 'br': $order_state->name[$language['id_lang']] = 'Enviado parcialmente'; break; default: $order_state->name[$language['id_lang']] = 'Partial shipment'; break; } } $order_state->send_email = false; $order_state->color = '#00d3c3'; $order_state->hidden = true; $order_state->delivery = false; $order_state->logable = true; $order_state->invoice = false; $order_state->paid = false; if ($order_state->save()) { if (version_compare(_PS_VERSION_, '1.5.4.1', '>')) { Tools::copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } else { $this->copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } } else { return false; } Configuration::updateValue('BT_PARTIAL_SHIPMENT_STATE', (int) $order_state->id); return true; } else { $order_state = new OrderState((int) Configuration::get('BT_PARTIAL_SHIPMENT_STATE')); if (!Validate::isLoadedObject($order_state)) { $order_state = new OrderState(); $order_state->name = array(); foreach (Language::getLanguages() as $language) { switch (Tools::strtolower($language['iso_code'])) { case 'fr': $order_state->name[$language['id_lang']] = 'Expédition partielle'; break; case 'it': $order_state->name[$language['id_lang']] = 'Parzialmente Spedito'; break; case 'es': $order_state->name[$language['id_lang']] = 'Parcialmente Enviado'; break; case 'pt': $order_state->name[$language['id_lang']] = 'Enviado parcialmente'; break; case 'br': $order_state->name[$language['id_lang']] = 'Enviado parcialmente'; break; case 'de': $order_state->name[$language['id_lang']] = 'Teilweise geliefert'; break; case 'en': $order_state->name[$language['id_lang']] = 'Partial shipment'; break; default: $order_state->name[$language['id_lang']] = 'Partial shipment'; break; } } $order_state->send_email = false; $order_state->color = '#00d3c3'; $order_state->hidden = true; $order_state->delivery = false; $order_state->logable = true; $order_state->invoice = true; if ($order_state->save()) { if (version_compare(_PS_VERSION_, '1.5.4.1', '>')) { Tools::copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } else { $this->copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } } else { return false; } Configuration::updateValue('BT_PARTIAL_SHIPMENT_STATE', (int) $order_state->id); return true; } return true; } } private function installOrderCompleteStatus() { if (!Configuration::get('BT_PARTIAL_SHIPMENT_COMPLETE')) { $order_state = new OrderState(); $order_state->name = array(); foreach (Language::getLanguages() as $language) { switch (Tools::strtolower($language['iso_code'])) { case 'fr': $order_state->name[$language['id_lang']] = 'Expédition complète'; break; case 'it': $order_state->name[$language['id_lang']] = 'Completa spedizione'; break; case 'es': $order_state->name[$language['id_lang']] = 'Completa de envío'; break; case 'pt': $order_state->name[$language['id_lang']] = 'Envio completa'; break; case 'br': $order_state->name[$language['id_lang']] = 'Envio completa'; break; case 'de': $order_state->name[$language['id_lang']] = 'Kompletter Versand'; break; case 'en': $order_state->name[$language['id_lang']] = 'Complete shipment'; break; default: $order_state->name[$language['id_lang']] = 'Complete shipment'; break; } } $order_state->send_email = false; $order_state->color = '#c7ff65'; $order_state->hidden = true; $order_state->delivery = false; $order_state->logable = true; $order_state->invoice = false; $order_state->paid = false; if ($order_state->save()) { if (version_compare(_PS_VERSION_, '1.5.4.1', '>')) { Tools::copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } else { $this->copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } } else { return false; } Configuration::updateValue('BT_PARTIAL_SHIPMENT_COMPLETE', (int) $order_state->id); return true; } else { $order_state = new OrderState((int) Configuration::get('BT_PARTIAL_SHIPMENT_COMPLETE')); if (!Validate::isLoadedObject($order_state)) { $order_state = new OrderState(); $order_state->name = array(); foreach (Language::getLanguages() as $language) { switch (Tools::strtolower($language['iso_code'])) { case 'fr': $order_state->name[$language['id_lang']] = 'Expédition complète'; break; case 'it': $order_state->name[$language['id_lang']] = 'Completa spedizione'; break; case 'es': $order_state->name[$language['id_lang']] = 'Completa de envío'; break; case 'pt': $order_state->name[$language['id_lang']] = 'Envio completa'; break; case 'br': $order_state->name[$language['id_lang']] = 'Envio completa'; break; case 'de': $order_state->name[$language['id_lang']] = 'Kompletter Versand'; break; case 'en': $order_state->name[$language['id_lang']] = 'Complete shipment'; break; default: $order_state->name[$language['id_lang']] = 'Complete shipment'; break; } } $order_state->send_email = false; $order_state->color = '#c7ff65'; $order_state->hidden = true; $order_state->delivery = false; $order_state->logable = true; $order_state->invoice = true; if ($order_state->save()) { if (version_compare(_PS_VERSION_, '1.5.4.1', '>')) { Tools::copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } else { $this->copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } } else { return false; } Configuration::updateValue('BT_PARTIAL_SHIPMENT_COMPLETE', (int) $order_state->id); return true; } return true; } } private function installOrderRemainingStatus() { if (!Configuration::get('BT_PARTIAL_REMAINING_STATE')) { $order_state = new OrderState(); $order_state->name = array(); foreach (Language::getLanguages() as $language) { switch (Tools::strtolower($language['iso_code'])) { case 'fr': $order_state->name[$language['id_lang']] = 'Reliquat de commande'; break; case 'it': $order_state->name[$language['id_lang']] = 'Resto dell\'ordine'; break; case 'es': $order_state->name[$language['id_lang']] = 'Pedido pendiente'; break; case 'pt': $order_state->name[$language['id_lang']] = 'Resto do pedido'; break; case 'br': $order_state->name[$language['id_lang']] = 'Resto do pedido'; break; case 'de': $order_state->name[$language['id_lang']] = 'Rest der Bestellung'; break; case 'en': $order_state->name[$language['id_lang']] = 'Remaining order (backorder)'; break; default: $order_state->name[$language['id_lang']] = 'Remaining order (backorder)'; break; } } $order_state->send_email = false; $order_state->color = '#F7DCF2'; $order_state->hidden = true; $order_state->delivery = false; $order_state->logable = true; $order_state->invoice = false; $order_state->paid = false; if ($order_state->save()) { if (version_compare(_PS_VERSION_, '1.5.4.1', '>')) { Tools::copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } else { $this->copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } } else { return false; } Configuration::updateValue('BT_PARTIAL_REMAINING_STATE', (int) $order_state->id); return true; } else { $order_state = new OrderState((int) Configuration::get('BT_PARTIAL_REMAINING_STATE')); if (!Validate::isLoadedObject($order_state)) { $order_state = new OrderState(); $order_state->name = array(); foreach (Language::getLanguages() as $language) { switch (Tools::strtolower($language['iso_code'])) { case 'fr': $order_state->name[$language['id_lang']] = 'Reliquat de commande'; break; case 'it': $order_state->name[$language['id_lang']] = 'Resto dell\'ordine'; break; case 'es': $order_state->name[$language['id_lang']] = 'Pedido pendiente'; break; case 'pt': $order_state->name[$language['id_lang']] = 'Resto do pedido'; break; case 'br': $order_state->name[$language['id_lang']] = 'Resto do pedido'; break; case 'de': $order_state->name[$language['id_lang']] = 'Rest der Bestellung'; break; case 'en': $order_state->name[$language['id_lang']] = 'Remaining order (backorder)'; break; default: $order_state->name[$language['id_lang']] = 'Remaining order (backorder)'; break; } } $order_state->send_email = false; $order_state->color = '#F7DCF2'; $order_state->hidden = true; $order_state->delivery = false; $order_state->logable = true; $order_state->invoice = true; if ($order_state->save()) { if (version_compare(_PS_VERSION_, '1.5.4.1', '>')) { Tools::copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } else { $this->copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', dirname(__FILE__).'/../../img/os/'.(int) $order_state->id.'.gif'); } } else { return false; } Configuration::updateValue('BT_PARTIAL_REMAINING_STATE', (int) $order_state->id); return true; } return true; } } public function uninstall() { if (!parent::uninstall()) { return false; } return true; } /************************************************************************************************* DISPLAY BACK OFFICE MANAGEMENT **************************************************************************************************/ /** * Loads asset resources. */ public function loadAsset() { $css_compatibility = $js_compatibility = array(); /* Load CSS */ $css = array( $this->css_path.'bootstrap-select.min.css', $this->css_path.'DT_bootstrap.css', $this->css_path.'fix.css', ); if (version_compare(_PS_VERSION_, '1.6', '<')) { $css_compatibility = array( $this->css_path.'bootstrap.css', $this->css_path.'bootstrap.extend.css', $this->css_path.'bootstrap-responsive.min.css', $this->css_path.'font-awesome.min.css', $this->css_path.'back.1.5.css', ); $css = array_merge($css_compatibility, $css); } $this->context->controller->addCSS($css, 'all'); unset($css, $css_compatibility); $this->context->controller->addJquery(); /* Load JS */ $js = array( $this->js_path.'bootstrap-select.min.js', $this->js_path.'bootstrap-dialog.js', $this->js_path.'jquery.autosize.min.js', $this->js_path.'jquery.dataTables.js', $this->js_path.'DT_bootstrap.js', $this->js_path.'dynamic_table_init.js', $this->js_path.'jscolor.js', $this->js_path.'module.js', ); if (version_compare(_PS_VERSION_, '1.6', '<')) { $js_compatibility = array( $this->js_path.'bootstrap.min.js', ); $js = array_merge($js_compatibility, $js); } $this->context->controller->addJS($js); /* Clean memory */ unset($js, $js_compatibility); } public function getContent() { $this->loadAsset(); /** * If values have been submitted in the form, process. */ $smarty = $this->context->smarty; $smarty->assign('module_dir', $this->_path); $smarty->assign('update_msg', $this->postProcess()); $smarty->assign('bt_config_form', $this->renderForm()); $smarty->assign('version', $this->version); if (Tools::isSubmit('btnSubmit')) { $is_submit = 1; } else { $is_submit = 0; } $smarty->assign('is_submit', $is_submit); return $this->display(__FILE__, 'views/templates/admin/configure.tpl'); } private function postProcess() { if (Tools::isSubmit('submitBtModule')) { $form_values = $this->getConfigFormValues(); $valid = true; foreach (array_keys($form_values) as $key) { if (!Configuration::updateValue($key, Tools::getValue($key))) { $valid = false; } } return $valid ? $this->displayConfirmation('Settings updated') : $this->displayError('Error on setttings update'); } } /** * Create the form that will be displayed in the configuration of your module. */ protected function renderForm() { $helper = new HelperForm(); $helper->show_toolbar = false; $helper->table = $this->table; $helper->module = $this; $helper->default_form_language = $this->context->language->id; $helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG', 0); $helper->identifier = $this->identifier; $helper->submit_action = 'submitBtModule'; $helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false) .'&configure='.$this->name.'&tab_module='.$this->tab.'&module_name='.$this->name; $helper->token = Tools::getAdminTokenLite('AdminModules'); $helper->tpl_vars = array( 'fields_value' => $this->getConfigFormValues(), /* Add values for your inputs */ 'languages' => $this->context->controller->getLanguages(), 'id_language' => $this->context->language->id, ); return $helper->generateForm(array($this->getConfigForm())); } /** * Create the structure of your form. */ protected function getConfigForm() { $order_states = OrderState::getOrderStates($this->context->language->id); array_unshift($order_states, array('id_order_state' => null, 'name' => $this->l('-- Select new status --'))); // 1.5 version doesn't include switch type $bool_type = version_compare(_PS_VERSION_, '1.6', '<') ? 'radio' : 'switch'; return array( 'form' => array( 'legend' => array( 'title' => $this->l('Settings'), 'icon' => 'icon-cogs', ), 'input' => array( array( 'type' => $bool_type, 'label' => $this->l('Send an email'), 'name' => 'BT_PARTIAL_SHIPMENT_SEND_EMAIL', 'is_bool' => true, 'class' => 't', 'desc' => $this->l('Send an email to your customer when you create partial shipment. This email contains delivery slip and tracking link.'), 'values' => array( array( 'id' => 'active_on', 'value' => true, 'label' => $this->l('Enabled'), ), array( 'id' => 'active_off', 'value' => false, 'label' => $this->l('Disabled'), ), ), ), array( 'type' => 'select', 'label' => $this->l('New status (partial shipment):'), 'name' => 'BT_PARTIAL_SHIPMENT_STATE', 'required' => true, 'options' => array( 'query' => $order_states, 'id' => 'id_order_state', 'name' => 'name', ), 'desc' => $this->l('New status of your order when you create partial shipment without remaining order.'), ), array( 'type' => 'select', 'label' => $this->l('New status (Complete shipment):'), 'name' => 'BT_PARTIAL_SHIPMENT_COMPLETE', 'required' => true, 'options' => array( 'query' => $order_states, 'id' => 'id_order_state', 'name' => 'name', ), 'desc' => $this->l('Update your order in this status if order is full shipment.'), ), array( 'type' => $bool_type, 'label' => $this->l('Generate remaining order ?'), 'name' => 'BT_PARTIAL_GENERATE_REMAINING', 'is_bool' => true, 'class' => 't', 'desc' => $this->l('When you create partial shpiment do you want to generate remaining order by default. You can modifiy this information for each partial shipment.'), 'values' => array( array( 'id' => 'active_on', 'value' => true, 'label' => $this->l('Enabled'), ), array( 'id' => 'active_off', 'value' => false, 'label' => $this->l('Disabled'), ), ), ), array( 'type' => 'select', 'label' => $this->l('Remaining order state:'), 'name' => 'BT_PARTIAL_REMAINING_STATE', 'required' => true, 'options' => array( 'query' => $order_states, 'id' => 'id_order_state', 'name' => 'name', ), 'desc' => $this->l('New status of your order when you create new order with state remaining order.'), ), array( 'type' => $bool_type, 'label' => $this->l('Generate invoive ? (in general for B2B)'), 'name' => 'BT_PARTIAL_GENERATE_INVOICE', 'is_bool' => true, 'class' => 't', 'desc' => $this->l('If you chose to create remaining order do you want to create a new invoice. In general, this function is used for B2B (one order, one invoice). In B2C mode, there is one payment one invoice even if you create remaining order.'), 'values' => array( array( 'id' => 'active_on', 'value' => true, 'label' => $this->l('Enabled'), ), array( 'id' => 'active_off', 'value' => false, 'label' => $this->l('Disabled'), ), ), ), ), 'submit' => array( 'title' => $this->l('Save settings'), ), ), ); } /** * Set values for the inputs. */ protected function getConfigFormValues() { return array( 'BT_PARTIAL_SHIPMENT_SEND_EMAIL' => Configuration::get('BT_PARTIAL_SHIPMENT_SEND_EMAIL'), 'BT_PARTIAL_SHIPMENT_STATE' => Configuration::get('BT_PARTIAL_SHIPMENT_STATE'), 'BT_PARTIAL_SHIPMENT_COMPLETE' => Configuration::get('BT_PARTIAL_SHIPMENT_COMPLETE'), 'BT_PARTIAL_REMAINING_STATE' => Configuration::get('BT_PARTIAL_REMAINING_STATE'), 'BT_PARTIAL_GENERATE_INVOICE' => Configuration::get('BT_PARTIAL_GENERATE_INVOICE'), 'BT_PARTIAL_GENERATE_REMAINING' => Configuration::get('BT_PARTIAL_GENERATE_REMAINING'), ); } public function hookDisplayAdminOrder($params) { if (!$this->active) { return; } if (Tools::isSubmit('submitPartialShipment')) { $this->processPartialShipment(); } if (isset($params['id_order'])) { $order = new Order((int)$params['id_order']); if (Validate::isLoadedObject($order)) { //We use this var to verify if there more than product to ship in this order $shippableQuantity = 0; $orderDetails = $order->getProducts(); if ($orderDetails) { foreach ($orderDetails as &$orderDetail) { if ($orderDetail['id_warehouse'] != 0) { $warehouse = new Warehouse((int) $orderDetail['id_warehouse']); if (Validate::isLoadedObject($warehouse)) { $orderDetail['warehouse'] = $warehouse->name; } else { $orderDetail['warehouse'] = false; } } else { $orderDetail['warehouse'] = false; } //We verify if this product order detail partial shipment $shippedQuantity = PartialShipmentDetail::getQuantityShippiedByIdOrderDetail((int) $orderDetail['id_order_detail']); $orderDetail['shippedQuantity'] = (int) $shippedQuantity; $shippableQuantity += (int) $orderDetail['product_quantity'] - (int) $orderDetail['product_quantity_refunded'] - (int) $shippedQuantity; $orderDetail['shippableQuantity'] = (int) $orderDetail['product_quantity'] - (int) $orderDetail['product_quantity_refunded'] - (int) $shippedQuantity; if ($orderDetail['image'] != null) { $name = 'product_mini_'.(int)$orderDetail['product_id'].(isset($orderDetail['product_attribute_id']) ? '_'.(int)$orderDetail['product_attribute_id'] : '').'.jpg'; // generate image cache, only for back office $orderDetail['image_tag'] = ImageManager::thumbnail(_PS_IMG_DIR_.'p/'.$orderDetail['image']->getExistingImgPath().'.jpg', $name, 45, 'jpg'); if (file_exists(_PS_TMP_IMG_DIR_.$name)) { $orderDetail['image_size'] = getimagesize(_PS_TMP_IMG_DIR_.$name); } else { $orderDetail['image_size'] = false; } } } } //We retrieve product partial shipment already exists for this order $shipments = PartialShipment::getPartialShipmentByOrderId((int) $order->id); if ($shipments) { $partialShipment = array(); $i = 0; foreach ($shipments as $shipment) { $partialShipmentObj = new PartialShipment((int) $shipment['id_partial_shipment']); if (Validate::isLoadedObject($partialShipmentObj)) { $partialShipment[$i]['idPartial'] = $partialShipmentObj->id; $partialShipment[$i]['trackingNumber'] = $partialShipmentObj->tracking_number; $partialShipment[$i]['date'] = Tools::displayDate($partialShipmentObj->date_add); $partialShipment[$i]['products'] = array(); $partialShipment[$i]['url'] = $this->getFrontUrl('order', (int) $partialShipmentObj->id); $details = $partialShipmentObj->getPartialShipmentDetail(); if (is_array($details) && count($details) > 0) { foreach ($details as &$detail) { $orderDetailObj = new OrderDetail((int)$detail['id_order_detail']); if (Validate::isLoadedObject($orderDetailObj)) { $detail['product_name'] = $orderDetailObj->product_name; } } } $partialShipment[$i]['products'] = $details; unset($details); $i++; } } } else { if ($order->hasBeenShipped()) { //We inform e-merchant with data $partialShipment = array(); $partialShipment[0]['idPartial'] = $order->delivery_number; //We retrieve order carrier data $idOrderCarrier = (int)Db::getInstance()->getValue(' SELECT `id_order_carrier` FROM `'._DB_PREFIX_.'order_carrier` WHERE `id_order` = '.(int)$order->id); if ($idOrderCarrier) { $orderCarrier = new OrderCarrier((int) $idOrderCarrier); if (Validate::isloadedObject($orderCarrier)) { $partialShipment[0]['trackingNumber'] = $orderCarrier->tracking_number; $partialShipment[0]['date'] = Tools::displayDate($orderCarrier->date_add); $partialShipment[0]['products'] = array(); foreach ($orderDetails as $orderDetail) { $partialShipment[0]['products'][] = array( 'product_name' => $orderDetail['product_name'], 'quantity' => $orderDetail['product_quantity'], ); } } } } } $this->context->smarty->assign( array( 'orderDetails' => $orderDetails, 'order' => $order, 'id_order' => $order->id, 'shippableQuantity' => $shippableQuantity, 'partialShipment' => (isset($partialShipment) ? $partialShipment : false), 'generateRemaining' => Configuration::get('BT_PARTIAL_GENERATE_REMAINING'), 'generateInvoice' => Configuration::get('BT_PARTIAL_GENERATE_INVOICE'), ) ); return $this->display(__FILE__, Tools::substr(_PS_VERSION_, 0, 3).'/admin_order.tpl'); } else { return; } } } public function hookDisplayOrderDetail($params) { if (!$this->active) { return; } $order = $params['order']; if (Validate::isLoadedObject($order)) { //We retrieve product partial shipment already exists for this order $shipments = PartialShipment::getPartialShipmentByOrderId((int) $order->id); //We retrieve carrier for this order $carrier = new Carrier((int) $order->id_carrier); if ($shipments) { $partialShipment = array(); $i = 0; foreach ($shipments as $shipment) { $partialShipmentObj = new PartialShipment((int) $shipment['id_partial_shipment']); if (Validate::isLoadedObject($partialShipmentObj)) { $partialShipment[$i]['idPartial'] = $partialShipmentObj->id; $partialShipment[$i]['trackingNumber'] = $partialShipmentObj->tracking_number; $partialShipment[$i]['date'] = Tools::displayDate($partialShipmentObj->date_add); $partialShipment[$i]['products'] = array(); $partialShipment[$i]['urlDelivery'] = $this->getFrontUrl('order', (int) $partialShipmentObj->id); if (Validate::isLoadedObject($carrier)) { $partialShipment[$i]['carrierUrl'] = $carrier->url; } $details = $partialShipmentObj->getPartialShipmentDetail(); if (is_array($details) && count($details) > 0) { foreach ($details as &$detail) { $orderDetailObj = new OrderDetail((int)$detail['id_order_detail']); if (Validate::isLoadedObject($orderDetailObj)) { $detail['product_name'] = $orderDetailObj->product_name; } } } $partialShipment[$i]['products'] = $details; unset($details); $i++; } } } if (isset($partialShipment)) { $this->context->smarty->assign( array( 'partialShipment' => (isset($partialShipment) ? $partialShipment : false), ) ); return $this->display(__FILE__, Tools::substr(_PS_VERSION_, 0, 3).'/front_order_detail.tpl'); } } } /** * Add the CSS & JavaScript files you want to be loaded in the BO. */ public function hookDisplayBackOfficeHeader() { if (version_compare(_PS_VERSION_, '1.5.5.0', '>')) { if ((isset(Context::getContext()->controller->controller_name) && preg_match('/AdminOrders/i', Context::getContext()->controller->controller_name))) { $this->context->controller->addJquery(); $this->context->controller->addJS($this->js_path.'back.js'); $this->context->controller->addCSS($this->css_path.'back.css'); } if (version_compare(_PS_VERSION_, '1.6', '<') && (isset(Context::getContext()->controller->controller_name) && preg_match('/AdminOrders/i', Context::getContext()->controller->controller_name))) { $this->context->controller->addCSS($this->css_path.'back.1.5.css'); $this->context->controller->addJs($this->js_path.'bootstrap.min.js'); } } } public function processPartialShipment() { if (!$this->active) { return; } if (Tools::isSubmit('submitPartialShipment')) { //We retrieve all field with shipped quantity $qtyShipped = Tools::getValue('quantity_shipped'); $shippedProcess = false; $partialShipment = false; //Manage Partial Shipment with object if (is_array($qtyShipped)) { foreach ($qtyShipped as $key => $value) { if ($value && $value > 0) { $orderDetail = new OrderDetail((int) $key); if (Validate::isLoadedObject($orderDetail)) { //We verify if quantity Shipped <= as shippable quantity $shippedQuantity = PartialShipmentDetail::getQuantityShippiedByIdOrderDetail((int) $orderDetail->id); $shippableQty = (int) $orderDetail->product_quantity - (int) $orderDetail->product_quantity_refunded - (int) $shippedQuantity; if ($shippableQty >= (int) $value) { if (!$partialShipment || !Validate::isLoadedObject($partialShipment)) { //We create Partial Shipment object $partialShipment = new PartialShipment(); $partialShipment->tracking_number = pSQL(Tools::getValue('trackingNumber')); $partialShipment->id_order = (int) $orderDetail->id_order; $partialShipment->save(); } if (Validate::isLoadedObject($partialShipment)) { //We create partial Shipment Detail $partialShipmentDetail = new PartialShipmentDetail(); $partialShipmentDetail->id_order_detail = (int) $orderDetail->id; $partialShipmentDetail->id_partial_shipment = (int) $partialShipment->id; $partialShipmentDetail->quantity = (int) $value; $partialShipmentDetail->save(); $shippedProcess = true; } } } } } //We verify if order is full shipped $order = new Order((int) Tools::getValue('id_order')); $order->shipping_number = Tools::getValue('trackingNumber'); if (Validate::isLoadedObject($order) && $shippedProcess) { //We use this var to verify if there more than product to ship in this order $totalShippableQuantity = 0; $totalQtyShipped = 0; $orderDetails = $order->getProducts(); $carrier = new Carrier((int) $order->id_carrier); if ($orderDetails) { foreach ($orderDetails as &$orderDetail) { //We verify if this product order detail partial shipment $shippedQuantity = PartialShipmentDetail::getQuantityShippiedByIdOrderDetail((int) $orderDetail['id_order_detail']); $totalQtyShipped += (int) $shippedQuantity; $totalShippableQuantity += (int) $orderDetail['product_quantity'] - (int) $orderDetail['product_quantity_refunded'] - (int) $shippedQuantity; } } if (Configuration::get('BT_PARTIAL_SHIPMENT_STATE') && $totalShippableQuantity > 0 && $order->current_state != Configuration::get('BT_PARTIAL_SHIPMENT_STATE') && !Tools::getValue('remainingOrder')) { if ($order->current_state != Configuration::get('BT_PARTIAL_SHIPMENT_STATE')) { //We Update status $new_history = new OrderHistory(); $new_history->id_order = (int) $order->id; $new_history->changeIdOrderState((int) Configuration::get('BT_PARTIAL_SHIPMENT_STATE'), (int) $order->id); $new_history->add(); } } if ((Configuration::get('BT_PARTIAL_SHIPMENT_COMPLETE') && $totalShippableQuantity <= 0 && $order->current_state != Configuration::get('BT_PARTIAL_SHIPMENT_COMPLETE')) || Tools::getValue('remainingOrder')) { if ($order->current_state != Configuration::get('BT_PARTIAL_SHIPMENT_COMPLETE')) { //We Update status $new_history = new OrderHistory(); $new_history->id_order = (int) $order->id; $new_history->changeIdOrderState((int) Configuration::get('BT_PARTIAL_SHIPMENT_COMPLETE'), (int) $order->id); $new_history->add(); } } } //Manage inform customer //We retrieve data of this order $customer = new Customer((int) $order->id_customer); if ($shippedProcess && Configuration::get('BT_PARTIAL_SHIPMENT_SEND_EMAIL') && Validate::isLoadedObject($order) && $order->current_state != Configuration::get('BT_PARTIAL_SHIPMENT_COMPLETE')) { //We retrieve all order detail shipped in this process if (isset($partialShipment) && Validate::isLoadedObject($partialShipment)) { if (Validate::isLoadedObject($customer)) { $iso = Language::getIsoById((int) $customer->id_lang); switch ($iso) { case 'fr': $subject = $this->l('Votre commande est expédiée partiellement'); break; case 'es': $subject = $this->l('Su pedido se envía parcialmente'); break; case 'it': $subject = $this->l('Il vostro ordine viene spedito parzialmente'); break; case 'pt': $subject = $this->l('Sua ordem é enviada parcialmente'); break; case 'de': $subject = $this->l('Ihre Bestellung wird ausgeliefert teilweise'); break; case 'en': $subject = $this->l('You order was partialy shipped out'); break; default: $subject = $this->l('You order was partialy shipped out'); $iso = 'en'; break; } $template_vars = array( '{followup}' => str_replace('@', $order->shipping_number, $carrier->url), '{firstname}' => $customer->firstname, '{lastname}' => $customer->lastname, '{id_order}' => $order->id, '{shipping_number}' => $order->shipping_number, '{order_name}' => $order->getUniqReference(), ); $template = 'partialshipment'; $txt_file = dirname(__FILE__).'/mails/'.$iso.'/'.$template.'.txt'; $html_file = dirname(__FILE__).'/mails/'.$iso.'/'.$template.'.html'; $file_attachement = array(); $pdf = new PDF((object)$partialShipment, 'PartialShipment', Context::getContext()->smarty); $file_attachement[] = array( 'content' => $pdf->render(false), 'name' => 'BL_'.sprintf('%06d', (int) $partialShipment->id).'.pdf', 'mime' => 'application/pdf', ); if (file_exists($txt_file) && file_exists($html_file)) { Mail::Send( (int) $order->id_lang, $template, $subject, $template_vars, $customer->email, null, Configuration::get('PS_SHOP_EMAIL'), Configuration::get('PS_SHOP_NAME'), $file_attachement, null, dirname(__FILE__).'/mails/' ); } } } } else { $this->displayError('Error during partial shipment process.'); } //We verify if you create remaining order if ($partialShipment && Validate::isLoadedObject($partialShipment) && $shippedProcess) { if (Tools::getValue('remainingOrder') && $totalShippableQuantity > 0) { $newOrder = $partialShipment->cloneOrderObject(); if (Validate::isLoadedObject($newOrder)) { if (Tools::getValue('generateNewInvoice')) { $newOrder->invoice_number = 0; $newOrder->save(); } if (is_array($qtyShipped)) { $validQtyShipped = false; foreach ($qtyShipped as $key => $value) { $orderDetail = new OrderDetail((int) $key); if (Validate::isLoadedObject($orderDetail)) { if ($orderDetail->product_quantity - $orderDetail->product_quantity_refunded > (int) $value) { if ($partialShipment->modifyOrderDetail($newOrder, $orderDetail, (int) $value, Tools::getValue('generateNewInvoice'))) { $validQtyShipped = true; } } } } //We refresh order Object after modify origibal object $order = new Order((int) $partialShipment->id_order); $newOrder = new Order((int) $newOrder->id); $total_discounts = $order->total_discounts; $total_discounts_tax_incl = $order->total_discounts_tax_incl; $total_discounts_tax_excl = $order->total_discounts_tax_excl; if (isset($total_discounts) && $total_discounts) { $order->total_discounts = round($total_discounts * $order->total_products_wt / ($order->total_products_wt + $newOrder->total_products_wt), 2); $order->total_discounts_tax_incl = round($total_discounts_tax_incl * $order->total_products_wt / ($order->total_products_wt + $newOrder->total_products_wt), 2); $order->total_discounts_tax_excl = round($total_discounts_tax_excl * $order->total_products / ($order->total_products + $newOrder->total_products), 2); $newOrder->total_discounts = round($total_discounts - $order->total_discounts, 2); $newOrder->total_discounts_tax_incl = round($total_discounts_tax_incl - $order->total_discounts_tax_incl, 2); $newOrder->total_discounts_tax_excl = round($total_discounts_tax_excl - $order->total_discounts_tax_excl, 2); $order->total_paid_tax_incl += $newOrder->total_discounts_tax_incl; $order->total_paid_tax_excl += $newOrder->total_discounts_tax_excl; $order->total_paid += $newOrder->total_discounts_tax_incl; if ($order->total_paid_real > 0) { $order->total_paid_real += $newOrder->total_discounts_tax_incl; } $order->save(); $newOrder->total_paid_tax_incl -= $newOrder->total_discounts_tax_incl; $newOrder->total_paid_tax_excl -= $newOrder->total_discounts_tax_excl; $newOrder->total_paid -= $newOrder->total_discounts_tax_incl; if ($newOrder->total_paid_real > 0) { $newOrder->total_paid_real -= $newOrder->total_discounts_tax_incl; } $newOrder->save(); } //We fix Order Parent Order Invoice if you generate new invoice if (Tools::getValue('generateNewInvoice')) { $sql = 'SELECT `id_order_invoice` FROM `'._DB_PREFIX_.'order_invoice` WHERE number = '.(int) $order->invoice_number.' ORDER BY `id_order` ASC'; $id_order_invoice = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql); if ($id_order_invoice) { $orderInvoice = new OrderInvoice((int) $id_order_invoice); if (Validate::isLoadedObject($orderInvoice)) { $orderInvoice->total_discount_tax_incl -= $newOrder->total_discounts_tax_incl; $orderInvoice->total_discount_tax_excl -= $newOrder->total_discounts_tax_excl; $orderInvoice->total_paid_tax_incl -= $newOrder->total_paid_tax_incl; $orderInvoice->total_paid_tax_excl -= $newOrder->total_paid_tax_excl; $orderInvoice->total_products -= $newOrder->total_products; $orderInvoice->total_products_wt -= $newOrder->total_products_wt; $orderInvoice->save(); } } } if ($validQtyShipped) { // Create new OrderHistory $history = new OrderHistory(); $history->id_order = $newOrder->id; $history->id_employee = (int) $this->context->employee->id; if ($order->hasInvoice() && Tools::getValue('generateNewInvoice')) { $newOrder->setInvoice(true); $useExistingsPayment = true; } else { $useExistingsPayment = false; } $currentState = $newOrder->getCurrentOrderState(); if (Validate::isLoadedObject($currentState)) { if ($currentState->id != (int) Configuration::get('BT_PARTIAL_REMAINING_STATE')) { $history->changeIdOrderState(Configuration::get('BT_PARTIAL_REMAINING_STATE'), $newOrder, $useExistingsPayment); $history->add(); } } } } } else { $this->displayError('Error during during remaining order creation.'); } } } //We update order_carrier if (Tools::getValue('trackingNumber')) { $id_order_carrier = (int)Db::getInstance()->getValue(' SELECT `id_order_carrier` FROM `'._DB_PREFIX_.'order_carrier` WHERE `id_order` = '.(int) $order->id); if ($id_order_carrier) { $order_carrier = new OrderCarrier((int) $id_order_carrier); if (Validate::isloadedObject($order_carrier)) { $order->shipping_number = Tools::getValue('trackingNumber'); $order->update(); // Update order_carrier $order_carrier->tracking_number = pSQL(Tools::getValue('trackingNumber')); if ($order_carrier->update()) { Hook::exec('actionAdminOrdersTrackingNumberUpdate', array('order' => $order, 'customer' => $customer, 'carrier' => $carrier), null, false, true, false, (int) $order->id_shop); } } } } } else { $this->displayError('Error during partial shipment process.'); } Tools::redirectAdmin('index.php?controller=AdminOrders&id_order='. (int) Tools::getValue('id_order').'&vieworder&conf=4&token='.Tools::getValue('token')); } } public function generatePartialPDF() { if (Tools::getValue('idPartialShipment')) { $partialShipment = new PartialShipment((int) Tools::getValue('idPartialShipment')); if (Validate::isLoadedObject($partialShipment)) { $pdf = new PDF((object)$partialShipment, 'PartialShipment', Context::getContext()->smarty); $pdf->render(); } else { $this->displayError('Error with PartialShipment Object.'); } } else { $this->displayError('No delivery slip found.'); } } private function getFrontUrl($action, $idPartialShipment) { $token = Tools::getAdminToken('btpartialshipment'); $use_ssl = (bool)Configuration::get('PS_SSL_ENABLED'); $query = array( 'ajax' => true, 'action' => $action, 'idPartialShipment' => $idPartialShipment, 'token' => $token ); return $this->context->link->getModuleLink($this->name, 'generateDeliverySlip', $query, $use_ssl); } }