PageRenderTime 141ms CodeModel.GetById 46ms RepoModel.GetById 0ms app.codeStats 1ms

/controllers/admin/AdminSupplyOrdersController.php

https://bitbucket.org/enurkov/prestashop
PHP | 2181 lines | 1598 code | 272 blank | 311 comment | 239 complexity | 772194e66535c3115ccfe786f7306a39 MD5 | raw file
  1. <?php
  2. /*
  3. * 2007-2012 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2012 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. /**
  27. * @since 1.5.0
  28. */
  29. class AdminSupplyOrdersControllerCore extends AdminController
  30. {
  31. /*
  32. * @var array List of warehouses
  33. */
  34. protected $warehouses;
  35. public function __construct()
  36. {
  37. $this->context = Context::getContext();
  38. $this->table = 'supply_order';
  39. $this->className = 'SupplyOrder';
  40. $this->identifier = 'id_supply_order';
  41. $this->lang = false;
  42. $this->is_template_list = false;
  43. $this->multishop_context = Shop::CONTEXT_ALL;
  44. $this->addRowAction('updatereceipt');
  45. $this->addRowAction('changestate');
  46. $this->addRowAction('edit');
  47. $this->addRowAction('view');
  48. $this->addRowAction('details');
  49. $this->list_no_link = true;
  50. $this->fields_list = array(
  51. 'reference' => array(
  52. 'title' => $this->l('Reference'),
  53. 'width' => 250,
  54. 'havingFilter' => true
  55. ),
  56. 'supplier' => array(
  57. 'title' => $this->l('Supplier'),
  58. 'width' => 130,
  59. 'filter_key' => 's!name'
  60. ),
  61. 'warehouse' => array(
  62. 'title' => $this->l('Warehouse'),
  63. 'width' => 130,
  64. 'filter_key' => 'w!name'
  65. ),
  66. 'state' => array(
  67. 'title' => $this->l('Status'),
  68. 'width' => 200,
  69. 'filter_key' => 'stl!name',
  70. 'color' => 'color',
  71. ),
  72. 'date_add' => array(
  73. 'title' => $this->l('Creation'),
  74. 'width' => 150,
  75. 'align' => 'left',
  76. 'type' => 'date',
  77. 'havingFilter' => true,
  78. 'filter_key' => 'a!date_add'
  79. ),
  80. 'date_upd' => array(
  81. 'title' => $this->l('Last modification'),
  82. 'width' => 150,
  83. 'align' => 'left',
  84. 'type' => 'date',
  85. 'havingFilter' => true,
  86. 'filter_key' => 'a!date_upd'
  87. ),
  88. 'date_delivery_expected' => array(
  89. 'title' => $this->l('Delivery (expected)'),
  90. 'width' => 150,
  91. 'align' => 'left',
  92. 'type' => 'date',
  93. 'havingFilter' => true,
  94. 'filter_key' => 'a!date_delivery_expected'
  95. ),
  96. 'id_export' => array(
  97. 'title' => $this->l('Export'),
  98. 'width' => 80,
  99. 'callback' => 'printExportIcons',
  100. 'orderby' => false,
  101. 'search' => false
  102. ),
  103. );
  104. // gets the list of warehouses available
  105. $this->warehouses = Warehouse::getWarehouses(true);
  106. // gets the final list of warehouses
  107. array_unshift($this->warehouses, array('id_warehouse' => -1, 'name' => $this->l('All Warehouses')));
  108. parent::__construct();
  109. }
  110. /**
  111. * AdminController::init() override
  112. * @see AdminController::init()
  113. */
  114. public function init()
  115. {
  116. parent::init();
  117. if (Tools::isSubmit('addsupply_order') ||
  118. Tools::isSubmit('submitAddsupply_order') ||
  119. (Tools::isSubmit('updatesupply_order') && Tools::isSubmit('id_supply_order')))
  120. {
  121. // override table, lang, className and identifier for the current controller
  122. $this->table = 'supply_order';
  123. $this->className = 'SupplyOrder';
  124. $this->identifier = 'id_supply_order';
  125. $this->lang = false;
  126. $this->action = 'new';
  127. $this->display = 'add';
  128. if (Tools::isSubmit('updatesupply_order'))
  129. if ($this->tabAccess['edit'] === '1')
  130. $this->display = 'edit';
  131. else
  132. $this->errors[] = Tools::displayError($this->l('You do not have permission to edit here.'));
  133. }
  134. if (Tools::isSubmit('update_receipt') && Tools::isSubmit('id_supply_order'))
  135. {
  136. // change the display type in order to add specific actions to
  137. $this->display = 'update_receipt';
  138. // display correct toolBar
  139. $this->initToolbar();
  140. }
  141. }
  142. /**
  143. * AdminController::renderForm() override
  144. * @see AdminController::renderForm()
  145. */
  146. public function renderForm()
  147. {
  148. if (Tools::isSubmit('addsupply_order') ||
  149. Tools::isSubmit('updatesupply_order') ||
  150. Tools::isSubmit('submitAddsupply_order') ||
  151. Tools::isSubmit('submitUpdatesupply_order'))
  152. {
  153. if (Tools::isSubmit('addsupply_order') || Tools::isSubmit('submitAddsupply_order'))
  154. $this->toolbar_title = $this->l('Stock: Create new supply order');
  155. if (Tools::isSubmit('updatesupply_order') || Tools::isSubmit('submitUpdatesupply_order'))
  156. $this->toolbar_title = $this->l('Stock: Manage supply order');
  157. if (Tools::isSubmit('mod') && Tools::getValue('mod') === 'template' || $this->object->is_template)
  158. $this->toolbar_title .= ' ('.$this->l('template').')';
  159. $this->addJqueryUI('ui.datepicker');
  160. //get warehouses list
  161. $warehouses = Warehouse::getWarehouses(true);
  162. // displays warning if there are no warehouses
  163. if (!$warehouses)
  164. $this->displayWarning($this->l('You must have at least one warehouse. See Stock/Warehouses'));
  165. //get currencies list
  166. $currencies = Currency::getCurrencies();
  167. $id_default_currency = Configuration::get('PS_CURRENCY_DEFAULT');
  168. $default_currency = Currency::getCurrency($id_default_currency);
  169. if ($default_currency)
  170. $currencies = array_merge(array($default_currency, '-'), $currencies);
  171. //get suppliers list
  172. $suppliers = Supplier::getSuppliers();
  173. //get languages list
  174. $languages = Language::getLanguages(true);
  175. $id_default_lang = Configuration::get('PS_LANG_DEFAULT');
  176. $default_lang = Language::getLanguage($id_default_lang);
  177. if ($default_lang)
  178. $languages = array_merge(array($default_lang, '-'), $languages);
  179. $this->fields_form = array(
  180. 'legend' => array(
  181. 'title' => $this->l('Order information'),
  182. 'image' => '../img/admin/edit.gif'
  183. ),
  184. 'input' => array(
  185. array(
  186. 'type' => 'text',
  187. 'label' => $this->l('Reference:'),
  188. 'name' => 'reference',
  189. 'size' => 50,
  190. 'required' => true,
  191. 'desc' => $this->l('This is the reference for your order.'),
  192. ),
  193. array(
  194. 'type' => 'select',
  195. 'label' => $this->l('Supplier:'),
  196. 'name' => 'id_supplier',
  197. 'required' => true,
  198. 'options' => array(
  199. 'query' => $suppliers,
  200. 'id' => 'id_supplier',
  201. 'name' => 'name'
  202. ),
  203. 'desc' => $this->l('Select the supplier from whom you are buying products.'),
  204. 'hint' => $this->l('Be careful! When changing this field, all products already added to the order will be removed.')
  205. ),
  206. array(
  207. 'type' => 'select',
  208. 'label' => $this->l('Warehouse:'),
  209. 'name' => 'id_warehouse',
  210. 'required' => true,
  211. 'options' => array(
  212. 'query' => $warehouses,
  213. 'id' => 'id_warehouse',
  214. 'name' => 'name'
  215. ),
  216. 'desc' => $this->l('Select the warehouse to which you want the order to be sent.'),
  217. ),
  218. array(
  219. 'type' => 'select',
  220. 'label' => $this->l('Currency:'),
  221. 'name' => 'id_currency',
  222. 'required' => true,
  223. 'options' => array(
  224. 'query' => $currencies,
  225. 'id' => 'id_currency',
  226. 'name' => 'name'
  227. ),
  228. 'desc' => $this->l('The currency of the order.'),
  229. 'hint' => $this->l('Be careful! When changing this field, all products already added to the order will be removed.')
  230. ),
  231. array(
  232. 'type' => 'select',
  233. 'label' => $this->l('Order Language:'),
  234. 'name' => 'id_lang',
  235. 'required' => true,
  236. 'options' => array(
  237. 'query' => $languages,
  238. 'id' => 'id_lang',
  239. 'name' => 'name'
  240. ),
  241. 'desc' => $this->l('The language of the order.')
  242. ),
  243. array(
  244. 'type' => 'text',
  245. 'label' => $this->l('Global discount rate (%):'),
  246. 'name' => 'discount_rate',
  247. 'size' => 10,
  248. 'required' => true,
  249. 'desc' => $this->l('This is the global discount rate in percent for the order.'),
  250. ),
  251. array(
  252. 'type' => 'text',
  253. 'label' => $this->l('Automatically load products:'),
  254. 'name' => 'load_products',
  255. 'size' => 10,
  256. 'required' => false,
  257. 'hint' => $this->l('This will reset the order'),
  258. 'desc' => $this->l('If specified, each product which quantity is less than or equal to this value will be loaded.'),
  259. ),
  260. ),
  261. 'submit' => array(
  262. 'title' => $this->l('Save order'),
  263. )
  264. );
  265. if (Tools::isSubmit('mod') && Tools::getValue('mod') === 'template' ||
  266. $this->object->is_template)
  267. {
  268. $this->fields_form['input'][] = array(
  269. 'type' => 'hidden',
  270. 'name' => 'is_template'
  271. );
  272. $this->fields_form['input'][] = array(
  273. 'type' => 'hidden',
  274. 'name' => 'date_delivery_expected',
  275. );
  276. }
  277. else
  278. {
  279. $this->fields_form['input'][] = array(
  280. 'type' => 'date',
  281. 'label' => $this->l('Expected delivery date:'),
  282. 'name' => 'date_delivery_expected',
  283. 'size' => 10,
  284. 'required' => true,
  285. 'desc' => $this->l('This is the expected delivery date for this order.'),
  286. );
  287. }
  288. //specific discount display
  289. if (isset($this->object->discount_rate))
  290. $this->object->discount_rate = Tools::ps_round($this->object->discount_rate, 4);
  291. //specific date display
  292. if (isset($this->object->date_delivery_expected))
  293. {
  294. $date = explode(' ', $this->object->date_delivery_expected);
  295. if ($date)
  296. $this->object->date_delivery_expected = $date[0];
  297. }
  298. $this->displayInformation(
  299. $this->l('Please note that if you wish to order products, they have to be available for the specified Supplier/Warehouse.')
  300. .' '.
  301. $this->l('See Catalog/Products/Your Product/Suppliers & Warehouses')
  302. .'<br />'.
  303. $this->l('Also, changing the currency or the supplier will reset the order.')
  304. .'<br />'
  305. .'<br />'.
  306. $this->l('Finally, please note that you can only order from one supplier at a time.')
  307. );
  308. return parent::renderForm();
  309. }
  310. }
  311. /**
  312. * AdminController::getList() override
  313. * @see AdminController::getList()
  314. */
  315. public function getList($id_lang, $order_by = null, $order_way = null, $start = 0, $limit = null, $id_lang_shop = false)
  316. {
  317. if (Tools::isSubmit('csv_orders') || Tools::isSubmit('csv_orders_details') || Tools::isSubmit('csv_order_details'))
  318. $limit = false;
  319. // defines button specific for non-template supply orders
  320. if (!$this->is_template_list)
  321. {
  322. // adds export csv buttons
  323. $this->toolbar_btn['export-csv-orders'] = array(
  324. 'short' => 'Export Orders',
  325. 'href' => $this->context->link->getAdminLink('AdminSupplyOrders').'&amp;csv_orders&id_warehouse='.$this->getCurrentWarehouse(),
  326. 'desc' => $this->l('Export Orders (CSV)'),
  327. );
  328. $this->toolbar_btn['export-csv-details'] = array(
  329. 'short' => 'Export Orders Details',
  330. 'href' => $this->context->link->getAdminLink('AdminSupplyOrders').'&amp;csv_orders_details&id_warehouse='.$this->getCurrentWarehouse(),
  331. 'desc' => $this->l('Export Orders Details (CSV)'),
  332. );
  333. unset($this->toolbar_btn['new']);
  334. if ($this->tabAccess['add'] === '1')
  335. {
  336. $this->toolbar_btn['new'] = array(
  337. 'href' => self::$currentIndex.'&amp;add'.$this->table.'&amp;token='.$this->token,
  338. 'desc' => $this->l('Add new')
  339. );
  340. }
  341. }
  342. parent::getList($id_lang, $order_by, $order_way, $start, $limit, $id_lang_shop);
  343. // adds colors depending on the receipt state
  344. if ($order_by == 'quantity_expected')
  345. {
  346. $nb_items = count($this->_list);
  347. for ($i = 0; $i < $nb_items; ++$i)
  348. {
  349. $item = &$this->_list[$i];
  350. if ($item['quantity_received'] == $item['quantity_expected'])
  351. $item['color'] = '#00bb35';
  352. else if ($item['quantity_received'] > $item['quantity_expected'])
  353. $item['color'] = '#fb0008';
  354. }
  355. }
  356. // actions filters on supply orders list
  357. if ($this->table == 'supply_order')
  358. {
  359. $nb_items = count($this->_list);
  360. for ($i = 0; $i < $nb_items; $i++)
  361. {
  362. // if the current state doesn't allow order edit, skip the edit action
  363. if ($this->_list[$i]['editable'] == 0)
  364. $this->addRowActionSkipList('edit', $this->_list[$i]['id_supply_order']);
  365. if ($this->_list[$i]['enclosed'] == 1 && $this->_list[$i]['receipt_state'] == 0)
  366. $this->addRowActionSkipList('changestate', $this->_list[$i]['id_supply_order']);
  367. if (1 != $this->_list[$i]['pending_receipt'])
  368. $this->addRowActionSkipList('updatereceipt', $this->_list[$i]['id_supply_order']);
  369. }
  370. }
  371. }
  372. /**
  373. * AdminController::renderList() override
  374. * @see AdminController::renderList()
  375. */
  376. public function renderList()
  377. {
  378. $this->displayInformation($this->l('This interface allows you to manage supply orders.').'<br />');
  379. $this->displayInformation($this->l('Also, you can create templates that you can use later to generate actual orders.').'<br />');
  380. if (count($this->warehouses) <= 1)
  381. $this->displayWarning($this->l('You must have at least one warehouse before creating supply orders. See Stock/Warehouses'));
  382. // assigns warehouses
  383. $this->tpl_list_vars['warehouses'] = $this->warehouses;
  384. $this->tpl_list_vars['current_warehouse'] = $this->getCurrentWarehouse();
  385. $this->tpl_list_vars['filter_status'] = $this->getFilterStatus();
  386. // overrides query
  387. $this->_select = '
  388. s.name AS supplier,
  389. w.name AS warehouse,
  390. stl.name AS state,
  391. st.delivery_note,
  392. st.editable,
  393. st.enclosed,
  394. st.receipt_state,
  395. st.pending_receipt,
  396. st.color AS color,
  397. a.id_supply_order as id_export';
  398. $this->_join = 'LEFT JOIN `'._DB_PREFIX_.'supply_order_state_lang` stl ON
  399. (
  400. a.id_supply_order_state = stl.id_supply_order_state
  401. AND stl.id_lang = '.(int)$this->context->language->id.'
  402. )
  403. LEFT JOIN `'._DB_PREFIX_.'supply_order_state` st ON a.id_supply_order_state = st.id_supply_order_state
  404. LEFT JOIN `'._DB_PREFIX_.'supplier` s ON a.id_supplier = s.id_supplier
  405. LEFT JOIN `'._DB_PREFIX_.'warehouse` w ON (w.id_warehouse = a.id_warehouse)';
  406. $this->_where = ' AND a.is_template = 0';
  407. if ($this->getCurrentWarehouse() != -1)
  408. {
  409. $this->_where .= ' AND a.id_warehouse = '.$this->getCurrentWarehouse();
  410. self::$currentIndex .= '&id_warehouse='.(int)$this->getCurrentWarehouse();
  411. }
  412. if ($this->getFilterStatus() != 0)
  413. {
  414. $this->_where .= ' AND st.enclosed != 1';
  415. self::$currentIndex .= '&filter_status=on';
  416. }
  417. $first_list = parent::renderList();
  418. if (Tools::isSubmit('csv_orders') || Tools::isSubmit('csv_orders_details') || Tools::isSubmit('csv_order_details'))
  419. {
  420. if (count($this->_list) > 0)
  421. {
  422. $this->renderCSV();
  423. die;
  424. }
  425. else
  426. $this->displayWarning($this->l('There is nothing to export as a CSV.'));
  427. }
  428. // second list : templates
  429. $second_list = null;
  430. $this->is_template_list = true;
  431. unset($this->tpl_list_vars['warehouses']);
  432. unset($this->tpl_list_vars['current_warehouse']);
  433. unset($this->tpl_list_vars['filter_status']);
  434. // unsets actions
  435. $this->actions = array();
  436. unset($this->toolbar_btn['export-csv-orders']);
  437. unset($this->toolbar_btn['export-csv-details']);
  438. // adds actions
  439. $this->addRowAction('view');
  440. $this->addRowAction('edit');
  441. $this->addRowAction('createsupplyorder');
  442. $this->addRowAction('delete');
  443. // unsets some fields
  444. unset($this->fields_list['state'],
  445. $this->fields_list['date_upd'],
  446. $this->fields_list['id_pdf'],
  447. $this->fields_list['date_delivery_expected'],
  448. $this->fields_list['id_export']);
  449. // $this->fields_list['date_add']['align'] = 'left';
  450. // adds filter, to gets only templates
  451. unset($this->_where);
  452. $this->_where = ' AND a.is_template = 1';
  453. if ($this->getCurrentWarehouse() != -1)
  454. $this->_where .= ' AND a.id_warehouse = '.$this->getCurrentWarehouse();
  455. // re-defines toolbar & buttons
  456. $this->toolbar_title = $this->l('Stock: Supply order templates');
  457. $this->initToolbar();
  458. unset($this->toolbar_btn['new']);
  459. $this->toolbar_btn['new'] = array(
  460. 'href' => self::$currentIndex.'&amp;add'.$this->table.'&mod=template&amp;token='.$this->token,
  461. 'desc' => $this->l('Add new template')
  462. );
  463. // inits list
  464. $second_list = parent::renderList();
  465. return $first_list.$second_list;
  466. }
  467. /**
  468. * Init the content of change state action
  469. */
  470. public function initChangeStateContent()
  471. {
  472. $id_supply_order = (int)Tools::getValue('id_supply_order', 0);
  473. if ($id_supply_order <= 0)
  474. {
  475. $this->errors[] = Tools::displayError($this->l('The specified supply order is not valid'));
  476. return parent::initContent();
  477. }
  478. $supply_order = new SupplyOrder($id_supply_order);
  479. $supply_order_state = new SupplyOrderState($supply_order->id_supply_order_state);
  480. if (!Validate::isLoadedObject($supply_order) || !Validate::isLoadedObject($supply_order_state))
  481. {
  482. $this->errors[] = Tools::displayError($this->l('The specified supply order is not valid'));
  483. return parent::initContent();
  484. }
  485. // change the display type in order to add specific actions to
  486. $this->display = 'update_order_state';
  487. // overrides parent::initContent();
  488. $this->initToolbar();
  489. // given the current state, loads available states
  490. $states = SupplyOrderState::getSupplyOrderStates($supply_order->id_supply_order_state);
  491. // gets the state that are not allowed
  492. $allowed_states = array();
  493. foreach ($states as &$state)
  494. {
  495. $allowed_states[] = $state['id_supply_order_state'];
  496. $state['allowed'] = 1;
  497. }
  498. $not_allowed_states = SupplyOrderState::getStates($allowed_states);
  499. // generates the final list of states
  500. $index = count($allowed_states);
  501. foreach ($not_allowed_states as &$not_allowed_state)
  502. {
  503. $not_allowed_state['allowed'] = 0;
  504. $states[$index] = $not_allowed_state;
  505. ++$index;
  506. }
  507. // loads languages
  508. $this->getlanguages();
  509. // defines the fields of the form to display
  510. $this->fields_form[]['form'] = array(
  511. 'legend' => array(
  512. 'title' => $this->l('Supply Order Status'),
  513. 'image' => '../img/admin/cms.gif'
  514. ),
  515. );
  516. $this->displayInformation($this->l('Be careful when changing status. Some of them cannot be changed afterwards (Canceled, for instance).'));
  517. // sets up the helper
  518. $helper = new HelperForm();
  519. $helper->submit_action = 'submitChangestate';
  520. $helper->currentIndex = self::$currentIndex;
  521. $helper->toolbar_btn = $this->toolbar_btn;
  522. $helper->toolbar_scroll = false;
  523. $helper->token = $this->token;
  524. $helper->id = null; // no display standard hidden field in the form
  525. $helper->languages = $this->_languages;
  526. $helper->default_form_language = $this->default_form_language;
  527. $helper->allow_employee_form_lang = $this->allow_employee_form_lang;
  528. $helper->title = sprintf($this->l('Stock: Change supply order status #%s'), $supply_order->reference);
  529. $helper->override_folder = 'supply_orders_change_state/';
  530. // assigns our content
  531. $helper->tpl_vars['show_change_state_form'] = true;
  532. $helper->tpl_vars['supply_order_state'] = $supply_order_state;
  533. $helper->tpl_vars['supply_order'] = $supply_order;
  534. $helper->tpl_vars['supply_order_states'] = $states;
  535. // generates the form to display
  536. $content = $helper->generateForm($this->fields_form);
  537. $this->context->smarty->assign(array(
  538. 'content' => $content,
  539. 'url_post' => self::$currentIndex.'&token='.$this->token,
  540. ));
  541. }
  542. /**
  543. * Init the content of change state action
  544. */
  545. public function initUpdateSupplyOrderContent()
  546. {
  547. $this->addJqueryPlugin('autocomplete');
  548. // load supply order
  549. $id_supply_order = (int)Tools::getValue('id_supply_order', null);
  550. if ($id_supply_order != null)
  551. {
  552. $supply_order = new SupplyOrder($id_supply_order);
  553. $currency = new Currency($supply_order->id_currency);
  554. if (Validate::isLoadedObject($supply_order))
  555. {
  556. // load products of this order
  557. $products = $supply_order->getEntries();
  558. $product_ids = array();
  559. if (isset($this->order_products_errors) && is_array($this->order_products_errors))
  560. {
  561. //for each product in error array, check if it is in products array, and remove it to conserve last user values
  562. foreach ($this->order_products_errors as $pe)
  563. foreach ($products as $index_p => $p)
  564. if (($p['id_product'] == $pe['id_product']) && ($p['id_product_attribute'] == $pe['id_product_attribute']))
  565. unset($products[$index_p]);
  566. // then merge arrays
  567. $products = array_merge($this->order_products_errors, $products);
  568. }
  569. foreach ($products as &$item)
  570. {
  571. // calculate md5 checksum on each product for use in tpl
  572. $item['checksum'] = md5(_COOKIE_KEY_.$item['id_product'].'_'.$item['id_product_attribute']);
  573. $item['unit_price_te'] = Tools::ps_round($item['unit_price_te'], 2);
  574. // add id to ids list
  575. $product_ids[] = $item['id_product'].'_'.$item['id_product_attribute'];
  576. }
  577. $this->tpl_form_vars['products_list'] = $products;
  578. $this->tpl_form_vars['product_ids'] = implode($product_ids, '|');
  579. $this->tpl_form_vars['product_ids_to_delete'] = '';
  580. $this->tpl_form_vars['supplier_id'] = $supply_order->id_supplier;
  581. $this->tpl_form_vars['currency'] = $currency;
  582. }
  583. }
  584. $this->tpl_form_vars['content'] = $this->content;
  585. $this->tpl_form_vars['token'] = $this->token;
  586. $this->tpl_form_vars['show_product_management_form'] = true;
  587. // call parent initcontent to render standard form content
  588. parent::initContent();
  589. }
  590. /**
  591. * Inits the content of 'update_receipt' action
  592. * Called in initContent()
  593. * @see AdminSuppliersOrders::initContent()
  594. */
  595. public function initUpdateReceiptContent()
  596. {
  597. $id_supply_order = (int)Tools::getValue('id_supply_order', null);
  598. // if there is no order to fetch
  599. if (null == $id_supply_order)
  600. return parent::initContent();
  601. $supply_order = new SupplyOrder($id_supply_order);
  602. // if it's not a valid order
  603. if (!Validate::isLoadedObject($supply_order))
  604. return parent::initContent();
  605. // re-defines fields_list
  606. $this->fields_list = array(
  607. 'supplier_reference' => array(
  608. 'title' => $this->l('Supplier Reference'),
  609. 'align' => 'left',
  610. 'width' => 50,
  611. 'orderby' => false,
  612. 'filter' => false,
  613. 'search' => false,
  614. ),
  615. 'reference' => array(
  616. 'title' => $this->l('Reference'),
  617. 'align' => 'left',
  618. 'width' => 30,
  619. 'orderby' => false,
  620. 'filter' => false,
  621. 'search' => false,
  622. ),
  623. 'ean13' => array(
  624. 'title' => $this->l('EAN13'),
  625. 'align' => 'left',
  626. 'width' => 30,
  627. 'orderby' => false,
  628. 'filter' => false,
  629. 'search' => false,
  630. ),
  631. 'upc' => array(
  632. 'title' => $this->l('UPC'),
  633. 'align' => 'left',
  634. 'width' => 30,
  635. 'orderby' => false,
  636. 'filter' => false,
  637. 'search' => false,
  638. ),
  639. 'name' => array(
  640. 'title' => $this->l('Name'),
  641. 'align' => 'left',
  642. 'width' => 300,
  643. 'orderby' => false,
  644. 'filter' => false,
  645. 'search' => false,
  646. ),
  647. 'quantity_received_today' => array(
  648. 'title' => $this->l('Quantity received today?'),
  649. 'align' => 'left',
  650. 'width' => 20,
  651. 'type' => 'editable',
  652. 'orderby' => false,
  653. 'filter' => false,
  654. 'search' => false,
  655. 'hint' => $this->l('Enter here the quantity you received today'),
  656. ),
  657. 'quantity_received' => array(
  658. 'title' => $this->l('Quantity received'),
  659. 'align' => 'left',
  660. 'width' => 20,
  661. 'orderby' => false,
  662. 'filter' => false,
  663. 'search' => false,
  664. 'hint' => 'Note that you can see details on the receptions - per products',
  665. ),
  666. 'quantity_expected' => array(
  667. 'title' => $this->l('Quantity expected'),
  668. 'align' => 'left',
  669. 'width' => 40,
  670. 'orderby' => false,
  671. 'filter' => false,
  672. 'search' => false,
  673. ),
  674. 'quantity_left' => array(
  675. 'title' => $this->l('Quantity left'),
  676. 'align' => 'left',
  677. 'width' => 20,
  678. 'orderby' => false,
  679. 'filter' => false,
  680. 'search' => false,
  681. 'hint' => $this->l('This is the quantity left to receive'),
  682. )
  683. );
  684. // attributes override
  685. unset($this->_select, $this->_join, $this->_where, $this->_orderBy, $this->_orderWay, $this->_group, $this->_filterHaving, $this->_filter);
  686. $this->table = 'supply_order_detail';
  687. $this->identifier = 'id_supply_order_detail';
  688. $this->className = 'SupplyOrderDetail';
  689. $this->list_simple_header = false;
  690. $this->list_no_link = true;
  691. $this->colorOnBackground = true;
  692. $this->row_hover = false;
  693. $this->bulk_actions = array('Update' => array('text' => $this->l('Update selected'), 'confirm' => $this->l('Update selected items?')));
  694. $this->addRowAction('details');
  695. // sets toolbar title with order reference
  696. $this->toolbar_title = sprintf($this->l('Receipt of products for supply order #%s'), $supply_order->reference);
  697. $this->lang = false;
  698. $lang_id = (int)$this->context->language->id; //employee lang
  699. // gets values corresponding to fields_list
  700. $this->_select = '
  701. a.id_supply_order_detail as id,
  702. a.quantity_received as quantity_received,
  703. a.quantity_expected as quantity_expected,
  704. IF (a.quantity_expected < a.quantity_received, 0, a.quantity_expected - a.quantity_received) as quantity_left,
  705. IF (a.quantity_expected < a.quantity_received, 0, a.quantity_expected - a.quantity_received) as quantity_received_today';
  706. $this->_where = 'AND a.`id_supply_order` = '.(int)$id_supply_order;
  707. $this->_group = 'GROUP BY a.id_supply_order_detail';
  708. // gets the list ordered by price desc, without limit
  709. $this->getList($lang_id, 'quantity_expected', 'DESC', 0, false, false);
  710. // defines action for POST
  711. $action = '&id_supply_order='.$id_supply_order;
  712. // unsets some buttons
  713. unset($this->toolbar_btn['export-csv-orders']);
  714. unset($this->toolbar_btn['export-csv-details']);
  715. unset($this->toolbar_btn['new']);
  716. // renders list
  717. $helper = new HelperList();
  718. $this->setHelperDisplay($helper);
  719. $helper->actions = array('details');
  720. $helper->override_folder = 'supply_orders_receipt_history/';
  721. $helper->toolbar_btn = $this->toolbar_btn;
  722. $helper->ajax_params = array(
  723. 'display_product_history' => 1,
  724. );
  725. $helper->currentIndex = self::$currentIndex.$action;
  726. // display these global order informations
  727. $this->displayInformation($this->l('This interface allows you to update the quantities of this ongoing order.').'<br />');
  728. $this->displayInformation($this->l('Be careful : once you update, you cannot go back unless you add new negative stock movements.').'<br />');
  729. $this->displayInformation($this->l('Please not that a green line means that you received what you expected, and a red line means that you received more than expected.').'<br />');
  730. // generates content
  731. $content = $helper->generateList($this->_list, $this->fields_list);
  732. // assigns var
  733. $this->context->smarty->assign(array(
  734. 'content' => $content,
  735. ));
  736. }
  737. /**
  738. * AdminController::initContent() override
  739. * @see AdminController::initContent()
  740. */
  741. public function initContent()
  742. {
  743. if (!Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT'))
  744. {
  745. $this->warnings[md5('PS_ADVANCED_STOCK_MANAGEMENT')] = $this->l('You need to activate advanced stock management prior to use this feature.');
  746. return false;
  747. }
  748. // Manage the add stock form
  749. if (Tools::isSubmit('changestate'))
  750. $this->initChangeStateContent();
  751. elseif (Tools::isSubmit('update_receipt') && Tools::isSubmit('id_supply_order'))
  752. $this->initUpdateReceiptContent();
  753. elseif (Tools::isSubmit('viewsupply_order') && Tools::isSubmit('id_supply_order'))
  754. {
  755. $this->action = 'view';
  756. $this->display = 'view';
  757. parent::initContent();
  758. }
  759. elseif (Tools::isSubmit('updatesupply_order'))
  760. $this->initUpdateSupplyOrderContent();
  761. else
  762. parent::initContent();
  763. }
  764. /**
  765. * Ths method manage associated products to the order when updating it
  766. */
  767. public function manageOrderProducts()
  768. {
  769. // load supply order
  770. $id_supply_order = (int)Tools::getValue('id_supply_order', null);
  771. $products_already_in_order = array();
  772. if ($id_supply_order != null)
  773. {
  774. $supply_order = new SupplyOrder($id_supply_order);
  775. if (Validate::isLoadedObject($supply_order))
  776. {
  777. // tests if the supplier or currency have changed in the supply order
  778. $new_supplier_id = (int)Tools::getValue('id_supplier');
  779. $new_currency_id = (int)Tools::getValue('id_currency');
  780. if (($new_supplier_id != $supply_order->id_supplier) ||
  781. ($new_currency_id != $supply_order->id_currency))
  782. {
  783. // resets all products in this order
  784. $supply_order->resetProducts();
  785. }
  786. else
  787. {
  788. $products_already_in_order = $supply_order->getEntries();
  789. $currency = new Currency($supply_order->id_ref_currency);
  790. // gets all product ids to manage
  791. $product_ids_str = Tools::getValue('product_ids', null);
  792. $product_ids = explode('|', $product_ids_str);
  793. $product_ids_to_delete_str = Tools::getValue('product_ids_to_delete', null);
  794. $product_ids_to_delete = array_unique(explode('|', $product_ids_to_delete_str));
  795. //delete products that are not managed anymore
  796. foreach ($products_already_in_order as $paio)
  797. {
  798. $product_ok = false;
  799. foreach ($product_ids_to_delete as $id)
  800. {
  801. $id_check = $paio['id_product'].'_'.$paio['id_product_attribute'];
  802. if ($id_check == $id)
  803. $product_ok = true;
  804. }
  805. if ($product_ok === true)
  806. {
  807. $entry = new SupplyOrderDetail($paio['id_supply_order_detail']);
  808. $entry->delete();
  809. }
  810. }
  811. // manage each product
  812. foreach ($product_ids as $id)
  813. {
  814. $errors = array();
  815. // check if a checksum is available for this product and test it
  816. $check = Tools::getValue('input_check_'.$id, '');
  817. $check_valid = md5(_COOKIE_KEY_.$id);
  818. if ($check_valid != $check)
  819. continue;
  820. $pos = strpos($id, '_');
  821. if ($pos === false)
  822. continue;
  823. // Load / Create supply order detail
  824. $entry = new SupplyOrderDetail();
  825. $id_supply_order_detail = (int)Tools::getValue('input_id_'.$id, 0);
  826. if ($id_supply_order_detail > 0)
  827. {
  828. $existing_entry = new SupplyOrderDetail($id_supply_order_detail);
  829. if (Validate::isLoadedObject($supply_order))
  830. $entry = &$existing_entry;
  831. }
  832. // get product informations
  833. $entry->id_product = substr($id, 0, $pos);
  834. $entry->id_product_attribute = substr($id, $pos + 1);
  835. $entry->unit_price_te = (float)str_replace(array(' ', ','), array('', '.'), Tools::getValue('input_unit_price_te_'.$id, 0));
  836. $entry->quantity_expected = (int)str_replace(array(' ', ','), array('', '.'), Tools::getValue('input_quantity_expected_'.$id, 0));
  837. $entry->discount_rate = (float)str_replace(array(' ', ','), array('', '.'), Tools::getValue('input_discount_rate_'.$id, 0));
  838. $entry->tax_rate = (float)str_replace(array(' ', ','), array('', '.'), Tools::getValue('input_tax_rate_'.$id, 0));
  839. $entry->reference = Tools::getValue('input_reference_'.$id, '');
  840. $entry->supplier_reference = Tools::getValue('input_supplier_reference_'.$id, '');
  841. $entry->ean13 = Tools::getValue('input_ean13_'.$id, '');
  842. $entry->upc = Tools::getValue('input_upc_'.$id, '');
  843. //get the product name in the order language
  844. $entry->name = Product::getProductName($entry->id_product, $entry->id_product_attribute, $supply_order->id_lang);
  845. if (empty($entry->name))
  846. $entry->name = '';
  847. if ($entry->supplier_reference == null)
  848. $entry->supplier_reference = '';
  849. $entry->exchange_rate = $currency->conversion_rate;
  850. $entry->id_currency = $currency->id;
  851. $entry->id_supply_order = $supply_order->id;
  852. $errors = $entry->validateController();
  853. //get the product name displayed in the backoffice according to the employee language
  854. $entry->name_displayed = Tools::getValue('input_name_displayed_'.$id, '');
  855. // if there is a problem, handle error for the current product
  856. if (count($errors) > 0)
  857. {
  858. // add the product to error array => display again product line
  859. $this->order_products_errors[] = array(
  860. 'id_product' => $entry->id_product,
  861. 'id_product_attribute' => $entry->id_product_attribute,
  862. 'unit_price_te' => $entry->unit_price_te,
  863. 'quantity_expected' => $entry->quantity_expected,
  864. 'discount_rate' => $entry->discount_rate,
  865. 'tax_rate' => $entry->tax_rate,
  866. 'name' => $entry->name,
  867. 'name_displayed' => $entry->name_displayed,
  868. 'reference' => $entry->reference,
  869. 'supplier_reference' => $entry->supplier_reference,
  870. 'ean13' => $entry->ean13,
  871. 'upc' => $entry->upc,
  872. );
  873. $error_str = '<ul>';
  874. foreach ($errors as $e)
  875. $error_str .= '<li>'.$this->l('field').$e.'</li>';
  876. $error_str .= '</ul>';
  877. $this->errors[] = Tools::displayError($this->l('Please verify the product information:').$entry->name.' '.$error_str);
  878. }
  879. else
  880. $entry->save();
  881. }
  882. }
  883. }
  884. }
  885. }
  886. /**
  887. * AdminController::postProcess() override
  888. * @see AdminController::postProcess()
  889. */
  890. public function postProcess()
  891. {
  892. $this->is_editing_order = false;
  893. // Checks access
  894. if (Tools::isSubmit('submitAddsupply_order') && !($this->tabAccess['add'] === '1'))
  895. $this->errors[] = Tools::displayError($this->l('You do not have the required permission to add a supply order.'));
  896. if (Tools::isSubmit('submitBulkUpdatesupply_order_detail') && !($this->tabAccess['edit'] === '1'))
  897. $this->errors[] = Tools::displayError($this->l('You do not have the required permission to edit an order.'));
  898. // Trick to use both Supply Order as template and actual orders
  899. if (Tools::isSubmit('is_template'))
  900. $_GET['mod'] = 'template';
  901. // checks if supply order reference is unique
  902. if (Tools::isSubmit('reference'))
  903. {
  904. // gets the reference
  905. $ref = pSQL(Tools::getValue('reference'));
  906. if (Tools::getValue('id_supply_order') != 0 && SupplyOrder::getReferenceById((int)Tools::getValue('id_supply_order')) != $ref)
  907. {
  908. if ((int)SupplyOrder::exists($ref) != 0)
  909. $this->errors[] = Tools::displayError($this->l('The reference has to be unique.'));
  910. }
  911. else if (Tools::getValue('id_supply_order') == 0 && (int)SupplyOrder::exists($ref) != 0)
  912. $this->errors[] = Tools::displayError($this->l('The reference has to be unique.'));
  913. }
  914. if ($this->errors)
  915. return;
  916. // Global checks when add / update a supply order
  917. if (Tools::isSubmit('submitAddsupply_order') || Tools::isSubmit('submitAddsupply_orderAndStay'))
  918. {
  919. $this->action = 'save';
  920. $this->is_editing_order = true;
  921. // get supplier ID
  922. $id_supplier = (int)Tools::getValue('id_supplier', 0);
  923. if ($id_supplier <= 0 || !Supplier::supplierExists($id_supplier))
  924. $this->errors[] = Tools::displayError($this->l('The selected supplier is not valid.'));
  925. // get warehouse id
  926. $id_warehouse = (int)Tools::getValue('id_warehouse', 0);
  927. if ($id_warehouse <= 0 || !Warehouse::exists($id_warehouse))
  928. $this->errors[] = Tools::displayError($this->l('The selected warehouse is not valid.'));
  929. // get currency id
  930. $id_currency = (int)Tools::getValue('id_currency', 0);
  931. if ($id_currency <= 0 || ( !($result = Currency::getCurrency($id_currency)) || empty($result) ))
  932. $this->errors[] = Tools::displayError($this->l('The selected currency is not valid.'));
  933. // get delivery date
  934. $delivery_expected = new DateTime(pSQL(Tools::getValue('date_delivery_expected')));
  935. // converts date to timestamp
  936. if ($delivery_expected <= (new DateTime('yesterday')))
  937. $this->errors[] = Tools::displayError($this->l('The date you specified cannot be in the past.'));
  938. // gets threshold
  939. $quantity_threshold = null;
  940. if (Tools::getValue('load_products') && Validate::isInt(Tools::getValue('load_products')))
  941. $quantity_threshold = (int)Tools::getValue('load_products');
  942. if (!count($this->errors))
  943. {
  944. // forces date for templates
  945. if (Tools::isSubmit('is_template') && !Tools::getValue('date_delivery_expected'))
  946. $_POST['date_delivery_expected'] = date('Y-m-d h:i:s');
  947. // specify initial state
  948. $_POST['id_supply_order_state'] = 1; //defaut creation state
  949. // specify global reference currency
  950. $_POST['id_ref_currency'] = Currency::getDefaultCurrency()->id;
  951. // specify supplier name
  952. $_POST['supplier_name'] = Supplier::getNameById($id_supplier);
  953. //specific discount check
  954. $_POST['discount_rate'] = (float)str_replace(array(' ', ','), array('', '.'), Tools::getValue('discount_rate', 0));
  955. }
  956. // manage each associated product
  957. $this->manageOrderProducts();
  958. // if the threshold is defined and we are saving the order
  959. if (Tools::isSubmit('submitAddsupply_order') && $quantity_threshold != null)
  960. $this->loadProducts($quantity_threshold);
  961. }
  962. // Manage state change
  963. if (Tools::isSubmit('submitChangestate')
  964. && Tools::isSubmit('id_supply_order')
  965. && Tools::isSubmit('id_supply_order_state'))
  966. {
  967. if ($this->tabAccess['edit'] != '1')
  968. $this->errors[] = Tools::displayError($this->l('You do not have permission to change the order status.'));
  969. // get state ID
  970. $id_state = (int)Tools::getValue('id_supply_order_state', 0);
  971. if ($id_state <= 0)
  972. $this->errors[] = Tools::displayError($this->l('The selected supply order status is not valid.'));
  973. // get supply order ID
  974. $id_supply_order = (int)Tools::getValue('id_supply_order', 0);
  975. if ($id_supply_order <= 0)
  976. $this->errors[] = Tools::displayError($this->l('The supply order id is not valid.'));
  977. if (!count($this->errors))
  978. {
  979. // try to load supply order
  980. $supply_order = new SupplyOrder($id_supply_order);
  981. if (Validate::isLoadedObject($supply_order))
  982. {
  983. // get valid available possible states for this order
  984. $states = SupplyOrderState::getSupplyOrderStates($supply_order->id_supply_order_state);
  985. foreach ($states as $state)
  986. {
  987. // if state is valid, change it in the order
  988. if ($id_state == $state['id_supply_order_state'])
  989. {
  990. $new_state = new SupplyOrderState($id_state);
  991. $old_state = new SupplyOrderState($supply_order->id_supply_order_state);
  992. // special case of validate state - check if there are products in the order and the required state is not an enclosed state
  993. if ($supply_order->isEditable() && !$supply_order->hasEntries() && !$new_state->enclosed)
  994. $this->errors[] = Tools::displayError(
  995. $this->l('It is not possible to change the status of this order because you did not order any products')
  996. );
  997. if (!count($this->errors))
  998. {
  999. $supply_order->id_supply_order_state = $state['id_supply_order_state'];
  1000. if ($supply_order->save())
  1001. {
  1002. // if pending_receipt,
  1003. // or if the order is being canceled,
  1004. // synchronizes StockAvailable
  1005. if (($new_state->pending_receipt && !$new_state->receipt_state) ||
  1006. ($old_state->receipt_state && $new_state->enclosed && !$new_state->receipt_state))
  1007. {
  1008. $supply_order_details = $supply_order->getEntries();
  1009. $products_done = array();
  1010. foreach ($supply_order_details as $supply_order_detail)
  1011. {
  1012. if (!in_array($supply_order_detail['id_product'], $products_done))
  1013. {
  1014. StockAvailable::synchronize($supply_order_detail['id_product']);
  1015. $products_done[] = $supply_order_detail['id_product'];
  1016. }
  1017. }
  1018. }
  1019. $token = Tools::getValue('token') ? Tools::getValue('token') : $this->token;
  1020. $redirect = self::$currentIndex.'&token='.$token;
  1021. $this->redirect_after = $redirect.'&conf=5';
  1022. }
  1023. }
  1024. }
  1025. }
  1026. }
  1027. else
  1028. $this->errors[] = Tools::displayError($this->l('The selected supplier is not valid.'));
  1029. }
  1030. }
  1031. // updates receipt
  1032. if (Tools::isSubmit('submitFiltersupply_order_detail') && Tools::isSubmit('submitBulkUpdatesupply_order_detail') && Tools::isSubmit('id_supply_order'))
  1033. $this->postProcessUpdateReceipt();
  1034. // use template to create a supply order
  1035. if (Tools::isSubmit('create_supply_order') && Tools::isSubmit('id_supply_order'))
  1036. $this->postProcessCopyFromTemplate();
  1037. if ((!count($this->errors) && $this->is_editing_order) || !$this->is_editing_order)
  1038. parent::postProcess();
  1039. }
  1040. /**
  1041. * Exports CSV
  1042. */
  1043. protected function renderCSV()
  1044. {
  1045. // exports orders
  1046. if (Tools::isSubmit('csv_orders'))
  1047. {
  1048. $ids = array();
  1049. foreach ($this->_list as $entry)
  1050. $ids[] = $entry['id_supply_order'];
  1051. if (count($ids) <= 0)
  1052. return;
  1053. $id_lang = Context::getContext()->language->id;
  1054. $orders = new Collection('SupplyOrder', $id_lang);
  1055. $orders->where('is_template', '=', false);
  1056. $orders->where('id_supply_order', 'in', $ids);
  1057. $id_warehouse = $this->getCurrentWarehouse();
  1058. if ($id_warehouse != -1)
  1059. $orders->where('id_warehouse', '=', $id_warehouse);
  1060. $orders->getAll();
  1061. $csv = new CSV($orders, $this->l('supply_orders'));
  1062. $csv->export();
  1063. }
  1064. // exports details for all orders
  1065. else if (Tools::isSubmit('csv_orders_details'))
  1066. {
  1067. // header
  1068. header('Content-type: text/csv');
  1069. header('Content-Type: application/force-download; charset=UTF-8');
  1070. header('Cache-Control: no-store, no-cache');
  1071. header('Content-disposition: attachment; filename="'.$this->l('supply_orders_details').'.csv"');
  1072. // echoes details
  1073. $ids = array();
  1074. foreach ($this->_list as $entry)
  1075. $ids[] = $entry['id_supply_order'];
  1076. if (count($ids) <= 0)
  1077. return;
  1078. // for each supply order
  1079. $keys = array('id_product', 'id_product_attribute', 'reference', 'supplier_reference', 'ean13', 'upc', 'name',
  1080. 'unit_price_te', 'quantity_expected', 'quantity_received', 'price_te', 'discount_rate', 'discount_value_te',
  1081. 'price_with_discount_te', 'tax_rate', 'tax_value', 'price_ti', 'tax_value_with_order_discount',
  1082. 'price_with_order_discount_te', 'id_supply_order');
  1083. echo sprintf("%s\n", implode(';', array_map(array('CSVCore', 'wrap'), $keys)));
  1084. // overrides keys (in order to add FORMAT calls)
  1085. $keys = array('sod.id_product', 'sod.id_product_attribute', 'sod.reference', 'sod.supplier_reference', 'sod.ean13',
  1086. 'sod.upc', 'sod.name',
  1087. 'FORMAT(sod.unit_price_te, 2)', 'sod.quantity_expected', 'sod.quantity_received', 'FORMAT(sod.price_te, 2)',
  1088. 'FORMAT(sod.discount_rate, 2)', 'FORMAT(sod.discount_value_te, 2)',
  1089. 'FORMAT(sod.price_with_discount_te, 2)', 'FORMAT(sod.tax_rate, 2)', 'FORMAT(sod.tax_value, 2)',
  1090. 'FORMAT(sod.price_ti, 2)', 'FORMAT(sod.tax_value_with_order_discount, 2)',
  1091. 'FORMAT(sod.price_with_order_discount_te, 2)', 'sod.id_supply_order');
  1092. foreach ($ids as $id)
  1093. {
  1094. $query = new DbQuery();
  1095. $query->select(implode(', ', $keys));
  1096. $query->from('supply_order_detail', 'sod');
  1097. $query->leftJoin('supply_order', 'so', 'so.id_supply_order = sod.id_supply_order');
  1098. $id_warehouse = $this->getCurrentWarehouse();
  1099. if ($id_warehouse != -1)
  1100. $query->where('so.id_warehouse = '.(int)$id_warehouse);
  1101. $query->where('sod.id_supply_order = '.(int)$id);
  1102. $query->orderBy('sod.id_supply_order_detail DESC');
  1103. $resource = Db::getInstance()->query($query);
  1104. // gets details
  1105. while ($row = Db::getInstance()->nextRow($resource))
  1106. echo sprintf("%s\n", implode(';', array_map(array('CSVCore', 'wrap'), $row)));
  1107. }
  1108. }
  1109. // exports details for the given order
  1110. else if (Tools::isSubmit('csv_order_details') && Tools::getValue('id_supply_order'))
  1111. {
  1112. $supply_order = new SupplyOrder((int)Tools::getValue('id_supply_order'));
  1113. if (Validate::isLoadedObject($supply_order))
  1114. {
  1115. $details = $supply_order->getEntriesCollection();
  1116. $details->getAll();
  1117. $csv = new CSV($details, $this->l('supply_order').'_'.$supply_order->reference.'_details');
  1118. $csv->export();
  1119. }
  1120. }
  1121. }
  1122. /**
  1123. * Helper function for AdminSupplyOrdersController::postProcess()
  1124. *
  1125. * @see AdminSupplyOrdersController::postProcess()
  1126. */
  1127. protected function postProcessUpdateReceipt()
  1128. {
  1129. // gets all box selected
  1130. $rows = Tools::getValue('supply_order_detailBox');
  1131. if (!$rows)
  1132. {
  1133. $this->errors[] = Tools::displayError($this->l('You did not select any product to update'));
  1134. return;
  1135. }
  1136. // final array with id_supply_order_detail and value to update
  1137. $to_update = array();
  1138. // gets quantity for each id_order_detail
  1139. foreach ($rows as $row)
  1140. {
  1141. if (Tools::getValue('quantity_received_today_'.$row))
  1142. $to_update[$row] = (int)Tools::getValue('quantity_received_today_'.$row);
  1143. }
  1144. // checks if there is something to update
  1145. if (!count($to_update))
  1146. {
  1147. $this->errors[] = Tools::displayError($this->l('You did not select any product to update'));
  1148. return;
  1149. }
  1150. foreach ($to_update as $id_supply_order_detail => $quantity)
  1151. {
  1152. $supply_order_detail = new SupplyOrderDetail($id_supply_order_detail);
  1153. $supply_order = new SupplyOrder((int)Tools::getValue('id_supply_order'));
  1154. if (Validate::isLoadedObject($supply_order_detail) && Validate::isLoadedObject($supply_order))
  1155. {
  1156. // checks if quantity is valid
  1157. // It's possible to receive more quantity than expected in case of a shipping error from the supplier
  1158. if (!Validate::isInt($quantity) || $quantity <= 0)
  1159. $this->errors[] = sprintf(Tools::displayError($this->l('Quantity (%d) for product #%d is not valid')), (int)$quantity, (int)$id_supply_order_detail);
  1160. else // everything is valid : updates
  1161. {
  1162. // creates the history
  1163. $supplier_receipt_history = new SupplyOrderReceiptHistory();
  1164. $supplier_receipt_history->id_supply_order_detail = (int)$id_supply_order_detail;
  1165. $supplier_receipt_history->id_employee = (int)$this->context->employee->id;
  1166. $supplier_receipt_history->employee_firstname = pSQL($this->context->employee->firstname);
  1167. $supplier_receipt_history->employee_lastname = pSQL($this->context->employee->lastname);
  1168. $supplier_receipt_history->id_supply_order_state = (int)$supply_order->id_supply_order_state;
  1169. $supplier_receipt_history->quantity = (int)$quantity;
  1170. // updates quantity received
  1171. $supply_order_detail->quantity_received += (int)$quantity;
  1172. // if current state is "Pending receipt", then we sets it to "Order received in part"
  1173. if (3 == $supply_order->id_supply_order_state)
  1174. $supply_order->id_supply_order_state = 4;
  1175. // Adds to stock
  1176. $warehouse = new Warehouse($supply_order->id_warehouse);
  1177. if (!Validate::isLoadedObject($warehouse))
  1178. {
  1179. $this->errors[] = Tools::displayError($this->l('Warehouse could not be loaded'));
  1180. return;
  1181. }
  1182. $price = $supply_order_detail->unit_price_te;
  1183. // converts the unit price to the warehouse currency if needed
  1184. if ($supply_order->id_currency != $warehouse->id_currency)
  1185. {
  1186. // first, converts the price to the default currency
  1187. $price_converted_to_default_currency = Tools::convertPrice($supply_order_detail->unit_price_te, $supply_order->id_currency, false);
  1188. // then, converts the newly calculated price from the default currency to the needed currency
  1189. $price = Tools::ps_round(Tools::convertPrice($price_converted_to_default_currency,
  1190. $warehouse->id_currency,
  1191. true),
  1192. 6);
  1193. }
  1194. $manager = StockManagerFactory::getManager();
  1195. $res = $manager->addProduct($supply_order_detail->id_product,
  1196. $supply_order_detail->id_product_attribute,
  1197. $warehouse,
  1198. (int)$quantity,
  1199. Configuration::get('PS_STOCK_MVT_SUPPLY_ORDER'),
  1200. $price,
  1201. true,
  1202. $supply_order->id);
  1203. if ($res) // if product has been added
  1204. {
  1205. $supplier_receipt_history->add();
  1206. $supply_order_detail->save();
  1207. $supply_order->save();
  1208. }
  1209. else
  1210. $this->errors[] = Tools::displayError($this->l('Something went wrong when adding products to the warehouse'));
  1211. }
  1212. }
  1213. }
  1214. if (!count($this->errors))
  1215. {
  1216. // display confirm message
  1217. $token = Tools::getValue('token') ? Tools::getValue('token') : $this->token;
  1218. $redirect = self::$currentIndex.'&token='.$token;
  1219. $this->redirect_after = $redirect.'&conf=4';
  1220. }
  1221. }
  1222. /**
  1223. * Display state action link
  1224. * @param string $token the token to add to the link
  1225. * @param int $id the identifier to add to the link
  1226. * @return string
  1227. */
  1228. public function displayUpdateReceiptLink($token = null, $id)
  1229. {
  1230. if (!array_key_exists('Receipt', self::$cache_lang))
  1231. self::$cache_lang['Receipt'] = $this->l('Update ongoing receipt of products');
  1232. $this->context->smarty->assign(array(
  1233. 'href' => self::$currentIndex.
  1234. '&'.$this->identifier.'='.$id.
  1235. '&update_receipt&token='.($token != null ? $token : $this->token),
  1236. 'action' => self::$cache_lang['Receipt'],
  1237. ));
  1238. return $this->context->smarty->fetch('helpers/list/list_action_supply_order_receipt.tpl');
  1239. }
  1240. /**
  1241. * Display receipt action link
  1242. * @param string $token the token to add to the link
  1243. * @param int $id the identifier to add to the link
  1244. * @return string
  1245. */
  1246. public function displayChangestateLink($token = null, $id)
  1247. {
  1248. if (!array_key_exists('State', self::$cache_lang))
  1249. self::$cache_lang['State'] = $this->l('Change state');
  1250. $this->context->smarty->assign(array(
  1251. 'href' => self::$currentIndex.
  1252. '&'.$this->identifier.'='.$id.
  1253. '&changestate&token='.($token != null ? $token : $this->token),
  1254. 'action' => self::$cache_lang['State'],
  1255. ));
  1256. return $this->context->smarty->fetch('helpers/list/list_action_supply_order_change_state.tpl');
  1257. }
  1258. /**
  1259. * Display state action link
  1260. * @param string $token the token to add to the link
  1261. * @param int $id the identifier to add to the link
  1262. * @return string
  1263. */
  1264. public function displayCreateSupplyOrderLink($token = null, $id)
  1265. {
  1266. if (!array_key_exists('CreateSupplyOrder', self::$cache_lang))
  1267. self::$cache_lang['CreateSupplyOrder'] = $this->l('Use this template to create a supply order');
  1268. if (!array_key_exists('CreateSupplyOrderConfirm', self::$cache_lang))
  1269. self::$cache_lang['CreateSupplyOrderConfirm'] = $this->l('Are you sure you want to use this template?');
  1270. $this->context->smarty->assign(array(
  1271. 'href' => self::$currentIndex.
  1272. '&'.$this->identifier.'='.$id.
  1273. '&create_supply_order&token='.($token != null ? $token : $this->token),
  1274. 'confirm' => self::$cache_lang['CreateSupplyOrderConfirm'],
  1275. 'action' => self::$cache_lang['CreateSupplyOrder'],
  1276. ));
  1277. return $this->context->smarty->fetch('helpers/list/list_action_supply_order_create_from_template.tpl');
  1278. }
  1279. /**
  1280. * method call when ajax request is made with the details row action
  1281. * @see AdminController::postProcess()
  1282. */
  1283. public function ajaxProcess()
  1284. {
  1285. // tests if an id is submit
  1286. if (Tools::isSubmit('id') && !Tools::isSubmit('display_product_history'))
  1287. {
  1288. // overrides attributes
  1289. $this->identifier = 'id_supply_order_history';
  1290. $this->table = 'supply_order_history';
  1291. $this->display = 'list';
  1292. $this->lang = false;
  1293. // gets current lang id
  1294. $lang_id = (int)$this->context->language->id;
  1295. // gets supply order id
  1296. $id_supply_order = (int)Tools::getValue('id');
  1297. // creates new fields_list
  1298. unset($this->fields_list);
  1299. $this->fields_list = array(
  1300. 'history_date' => array(
  1301. 'title' => $this->l('Last update'),
  1302. 'width' => 50,
  1303. 'align' => 'left',
  1304. 'type' => 'datetime',
  1305. 'havingFilter' => true
  1306. ),
  1307. 'history_employee' => array(
  1308. 'title' => $this->l('Employee'),
  1309. 'width' => 100,
  1310. 'align' => 'left',
  1311. 'havingFilter' => true
  1312. ),
  1313. 'history_state_name' => array(
  1314. 'title' => $this->l('Status'),
  1315. 'width' => 100,
  1316. 'align' => 'left',
  1317. 'color' => 'color',
  1318. 'havingFilter' => true
  1319. ),
  1320. );
  1321. // loads history of the given order
  1322. unset($this->_select, $this->_join, $this->_where, $this->_orderBy, $this->_orderWay, $this->_group, $this->_filterHaving, $this->_filter);
  1323. $this->_select = '
  1324. a.`date_add` as history_date,
  1325. CONCAT(a.`employee_lastname`, \' \', a.`employee_firstname`) as history_employee,
  1326. sosl.`name` as history_state_name,
  1327. sos.`color` as color';
  1328. $this->_join = '
  1329. LEFT JOIN `'._DB_PREFIX_.'supply_order_state` sos ON (a.`id_state` = sos.`id_supply_order_state`)
  1330. LEFT JOIN `'._DB_PREFIX_.'supply_order_state_lang` sosl ON
  1331. (
  1332. a.`id_state` = sosl.`id_supply_order_state`
  1333. AND sosl.`id_lang` = '.(int)$lang_id.'
  1334. )';
  1335. $this->_where = 'AND a.`id_supply_order` = '.(int)$id_supply_order;
  1336. $this->_orderBy = 'a.`date_add`';
  1337. $this->_orderWay = 'DESC';
  1338. // gets list and forces no limit clause in the request
  1339. $this->getList($lang_id, 'date_add', 'DESC', 0, false, false);
  1340. // renders list
  1341. $helper = new HelperList();
  1342. $helper->no_link = true;
  1343. $helper->show_toolbar = false;
  1344. $helper->toolbar_scroll = false;
  1345. $helper->shopLinkType = '';
  1346. $helper->identifier = $this->identifier;
  1347. //$helper->colorOnBackground = true;
  1348. $helper->simple_header = true;
  1349. $content = $helper->generateList($this->_list, $this->fields_list);
  1350. echo Tools::jsonEncode(array('use_parent_structure' => false, 'data' => $content));
  1351. }
  1352. else if (Tools::isSubmit('id') && Tools::isSubmit('display_product_history'))
  1353. {
  1354. $this->identifier = 'id_supply_order_receipt_history';
  1355. $this->table = 'supply_order_receipt_history';
  1356. $this->display = 'list';
  1357. $this->lang = false;
  1358. $lang_id = (int)$this->context->language->id;
  1359. $id_supply_order_detail = (int)Tools::getValue('id');
  1360. unset($this->fields_list);
  1361. $this->fields_list = array(
  1362. 'date_add' => array(
  1363. 'title' => $this->l('Last update'),
  1364. 'width' => 50,
  1365. 'align' => 'left',
  1366. 'type' => 'datetime',
  1367. 'havingFilter' => true
  1368. ),
  1369. 'employee' => array(
  1370. 'title' => $this->l('Employee'),
  1371. 'width' => 100,
  1372. 'align' => 'left',
  1373. 'havingFilter' => true
  1374. ),
  1375. 'quantity' => array(
  1376. 'title' => $this->l('Quantity received'),
  1377. 'width' => 100,
  1378. 'align' => 'left',
  1379. 'havingFilter' => true
  1380. ),
  1381. );
  1382. // loads history of the given order
  1383. unset($this->_select, $this->_join, $this->_where, $this->_orderBy, $this->_orderWay, $this->_group, $this->_filterHaving, $this->_filter);
  1384. $this->_select = 'CONCAT(a.`employee_lastname`, \' \', a.`employee_firstname`) as employee';
  1385. $this->_where = 'AND a.`id_supply_order_detail` = '.(int)$id_supply_order_detail;
  1386. // gets list and forces no limit clause in the request
  1387. $this->getList($lang_id, 'date_add', 'DESC', 0, false, false);
  1388. // renders list
  1389. $helper = new HelperList();
  1390. $helper->no_link = true;
  1391. $helper->show_toolbar = false;
  1392. $helper->toolbar_scroll = false;
  1393. $helper->shopLinkType = '';
  1394. $helper->identifier = $this->identifier;
  1395. $helper->colorOnBackground = true;
  1396. $helper->simple_header = true;
  1397. $content = $helper->generateList($this->_list, $this->fields_list);
  1398. echo Tools::jsonEncode(array('use_parent_structure' => false, 'data' => $content));
  1399. }
  1400. die;
  1401. }
  1402. /**
  1403. * method call when ajax request is made for search product to add to the order
  1404. * @TODO - Update this method to retreive the reference, ean13, upc corresponding to a product attribute
  1405. */
  1406. public function ajaxProcessSearchProduct()
  1407. {
  1408. // Get the search pattern
  1409. $pattern = pSQL(Tools::getValue('q', false));
  1410. if (!$pattern || $pattern == '' || strlen($pattern) < 1)
  1411. die();
  1412. // get supplier id
  1413. $id_supplier = (int)Tools::getValue('id_supplier', false);
  1414. // gets the currency
  1415. $id_currency = (int)Tools::getValue('id_currency', false);
  1416. // get lang from context
  1417. $id_lang = (int)Context::getContext()->language->id;
  1418. $query = new DbQuery();
  1419. $query->select('
  1420. CONCAT(p.id_product, \'_\', IFNULL(pa.id_product_attribute, \'0\')) as id,
  1421. ps.product_supplier_reference as supplier_reference,
  1422. IFNULL(pa.reference, IFNULL(p.reference, \'\')) as reference,
  1423. IFNULL(pa.ean13, IFNULL(p.ean13, \'\')) as ean13,
  1424. IFNULL(pa.upc, IFNULL(p.upc, \'\')) as upc,
  1425. md5(CONCAT(\''._COOKIE_KEY_.'\', p.id_product, \'_\', IFNULL(pa.id_product_attribute, \'0\'))) as checksum,
  1426. IFNULL(CONCAT(pl.name, \' : \', GROUP_CONCAT(DISTINCT agl.name, \' - \', al.name SEPARATOR \', \')), pl.name) as name
  1427. ');
  1428. $query->from('product', 'p');
  1429. $query->innerJoin('product_lang', 'pl', 'pl.id_product = p.id_product AND pl.id_lang = '.$id_lang);
  1430. $query->leftJoin('product_attribute', 'pa', 'pa.id_product = p.id_product');
  1431. $query->leftJoin('product_attribute_combination', 'pac', 'pac.id_product_attribute = pa.id_product_attribute');
  1432. $query->leftJoin('attribute', 'atr', 'atr.id_attribute = pac.id_attribute');
  1433. $query->leftJoin('attribute_lang', 'al', 'al.id_attribute = atr.id_attribute AND al.id_lang = '.$id_lang);
  1434. $query->leftJoin('attribute_group_lang', 'agl', 'agl.id_attribute_group = atr.id_attribute_group AND agl.id_lang = '.$id_lang);
  1435. $query->leftJoin('product_supplier', 'ps', 'ps.id_product = p.id_product AND ps.id_product_attribute = IFNULL(pa.id_product_attribute, 0)');
  1436. $query->where('(pl.name LIKE \'%'.$pattern.'%\' OR p.reference LIKE \'%'.$pattern.'%\' OR ps.product_supplier_reference LIKE \'%'.$pattern.'%\')');
  1437. $query->where('p.id_product NOT IN (SELECT pd.id_product FROM `'._DB_PREFIX_.'product_download` pd WHERE (pd.id_product = p.id_product))');
  1438. $query->where('p.is_virtual = 0 AND p.cache_is_pack = 0');
  1439. if ($id_supplier)
  1440. $query->where('ps.id_supplier = '.$id_supplier.' OR p.id_supplier = '.$id_supplier);
  1441. $query->groupBy('p.id_product, pa.id_product_attribute');
  1442. $items = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query);
  1443. foreach ($items as &$item)
  1444. {
  1445. $ids = explode('_', $item['id']);
  1446. $prices = ProductSupplier::getProductSupplierPrice($ids[0], $ids[1], $id_supplier, true);
  1447. if (count($prices))
  1448. $item['unit_price_te'] = Tools::convertPriceFull($prices['product_supplier_price_te'],
  1449. new Currency((int)$prices['id_currency']),
  1450. new Currency($id_currency));
  1451. }
  1452. if ($items)
  1453. die(Tools::jsonEncode($items));
  1454. die(1);
  1455. }
  1456. /**
  1457. * @see AdminController::renderView()
  1458. */
  1459. public function renderView()
  1460. {
  1461. $this->show_toolbar = true;
  1462. $this->toolbar_scroll = false;
  1463. $this->table = 'supply_order_detail';
  1464. $this->identifier = 'id_supply_order_detail';
  1465. $this->className = 'SupplyOrderDetail';
  1466. $this->colorOnBackground = false;
  1467. $this->lang = false;
  1468. $this->list_simple_header = true;
  1469. $this->list_no_link = true;
  1470. // gets the id supplier to view
  1471. $id_supply_order = (int)Tools::getValue('id_supply_order');
  1472. // gets global order information
  1473. $supply_order = new SupplyOrder((int)$id_supply_order);
  1474. if (Validate::isLoadedObject($supply_order))
  1475. {
  1476. if (!$supply_order->is_template)
  1477. $this->displayInformation($this->l('This interface allows you to display detailed information on your order.').'<br />');
  1478. else
  1479. $this->displayInformation($this->l('This interface allows you to display detailed information on your order template.').'<br />');
  1480. $lang_id = (int)$supply_order->id_lang;
  1481. // just in case..
  1482. unset($this->_select, $this->_join, $this->_where, $this->_orderBy, $this->_orderWay, $this->_group, $this->_filterHaving, $this->_filter);
  1483. // gets all information on the products ordered
  1484. $this->_where = 'AND a.`id_supply_order` = '.(int)$id_supply_order;
  1485. // gets the list ordered by price desc, without limit
  1486. $this->getList($lang_id, 'price_te', 'DESC', 0, false, false);
  1487. // gets the currency used in this order
  1488. $currency = new Currency($supply_order->id_currency);
  1489. // gets the warehouse where products will be received
  1490. $warehouse = new Warehouse($supply_order->id_warehouse);
  1491. // sets toolbar title with order reference
  1492. if (!$supply_order->is_template)
  1493. $this->toolbar_title = sprintf($this->l('Details on supply order #%s'), $supply_order->reference);
  1494. else
  1495. $this->toolbar_title = sprintf($this->l('Details on supply order template #%s'), $supply_order->reference);
  1496. // re-defines fields_list
  1497. $this->fields_list = array(
  1498. 'supplier_reference' => array(
  1499. 'title' => $this->l('Supplier Reference'),
  1500. 'align' => 'center',
  1501. 'width' => 120,
  1502. 'orderby' => false,
  1503. 'filter' => false,
  1504. 'search' => false,
  1505. ),
  1506. 'reference' => array(
  1507. 'title' => $this->l('Reference'),
  1508. 'align' => 'center',
  1509. 'width' => 120,
  1510. 'orderby' => false,
  1511. 'filter' => false,
  1512. 'search' => false,
  1513. ),
  1514. 'ean13' => array(
  1515. 'title' => $this->l('EAN13'),
  1516. 'align' => 'center',
  1517. 'width' => 100,
  1518. 'orderby' => false,
  1519. 'filter' => false,
  1520. 'search' => false,
  1521. ),
  1522. 'upc' => array(
  1523. 'title' => $this->l('UPC'),
  1524. 'align' => 'center',
  1525. 'width' => 100,
  1526. 'orderby' => false,
  1527. 'filter' => false,
  1528. 'search' => false,
  1529. ),
  1530. 'name' => array(
  1531. 'title' => $this->l('Name'),
  1532. 'orderby' => false,
  1533. 'filter' => false,
  1534. 'search' => false,
  1535. ),
  1536. 'unit_price_te' => array(
  1537. 'title' => $this->l('Unit price (tax excl.)'),
  1538. 'align' => 'right',
  1539. 'width' => 80,
  1540. 'orderby' => false,
  1541. 'filter' => false,
  1542. 'search' => false,
  1543. 'type' => 'price',
  1544. 'currency' => true,
  1545. ),
  1546. 'quantity_expected' => array(
  1547. 'title' => $this->l('Quantity'),
  1548. 'align' => 'right',
  1549. 'width' => 80,
  1550. 'orderby' => false,
  1551. 'filter' => false,
  1552. 'search' => false,
  1553. ),
  1554. 'price_te' => array(
  1555. 'title' => $this->l('Price (tax excl.)'),
  1556. 'align' => 'right',
  1557. 'width' => 80,
  1558. 'orderby' => false,
  1559. 'filter' => false,
  1560. 'search' => false,
  1561. 'type' => 'price',
  1562. 'currency' => true,
  1563. ),
  1564. 'discount_rate' => array(
  1565. 'title' => $this->l('Discount rate'),
  1566. 'align' => 'right',
  1567. 'width' => 80,
  1568. 'orderby' => false,
  1569. 'filter' => false,
  1570. 'search' => false,
  1571. 'suffix' => '%',
  1572. ),
  1573. 'discount_value_te' => array(
  1574. 'title' => $this->l('Discount value (tax excl.)'),
  1575. 'align' => 'right',
  1576. 'width' => 80,
  1577. 'orderby' => false,
  1578. 'filter' => false,
  1579. 'search' => false,
  1580. 'type' => 'price',
  1581. 'currency' => true,
  1582. ),
  1583. 'price_with_discount_te' => array(
  1584. 'title' => $this->l('Price with product discount (tax excl.)'),
  1585. 'align' => 'right',
  1586. 'width' => 80,
  1587. 'orderby' => false,
  1588. 'filter' => false,
  1589. 'search' => false,
  1590. 'type' => 'price',
  1591. 'currency' => true,
  1592. ),
  1593. 'tax_rate' => array(
  1594. 'title' => $this->l('Tax rate'),
  1595. 'align' => 'right',
  1596. 'width' => 80,
  1597. 'orderby' => false,
  1598. 'filter' => false,
  1599. 'search' => false,
  1600. 'suffix' => '%',
  1601. ),
  1602. 'tax_value' => array(
  1603. 'title' => $this->l('Tax value'),
  1604. 'align' => 'right',
  1605. 'width' => 80,
  1606. 'orderby' => false,
  1607. 'filter' => false,
  1608. 'search' => false,
  1609. 'type' => 'price',
  1610. 'currency' => true,
  1611. ),
  1612. 'price_ti' => array(
  1613. 'title' => $this->l('Price (tax incl.)'),
  1614. 'align' => 'right',
  1615. 'width' => 80,
  1616. 'orderby' => false,
  1617. 'filter' => false,
  1618. 'search' => false,
  1619. 'type' => 'price',
  1620. 'currency' => true,
  1621. ),
  1622. );
  1623. //some staff before render list
  1624. foreach ($this->_list as &$item)
  1625. {
  1626. $item['discount_rate'] = Tools::ps_round($item['discount_rate'], 4);
  1627. $item['tax_rate'] = Tools::ps_round($item['tax_rate'], 4);
  1628. $item['id_currency'] = $currency->id;
  1629. }
  1630. // unsets some buttons
  1631. unset($this->toolbar_btn['export-csv-orders']);
  1632. unset($this->toolbar_btn['export-csv-details']);
  1633. unset($this->toolbar_btn['new']);
  1634. // renders list
  1635. $helper = new HelperList();
  1636. $this->setHelperDisplay($helper);
  1637. $helper->actions = array();
  1638. $helper->show_toolbar = false;
  1639. $helper->toolbar_btn = $this->toolbar_btn;
  1640. $content = $helper->generateList($this->_list, $this->fields_list);
  1641. // display these global order informations
  1642. $this->tpl_view_vars = array(
  1643. 'supply_order_detail_content' => $content,
  1644. 'supply_order_warehouse' => (Validate::isLoadedObject($warehouse) ? $warehouse->name : ''),
  1645. 'supply_order_reference' => $supply_order->reference,
  1646. 'supply_order_supplier_name' => $supply_order->supplier_name,
  1647. 'supply_order_creation_date' => Tools::displayDate($supply_order->date_add, $lang_id, false),
  1648. 'supply_order_last_update' => Tools::displayDate($supply_order->date_upd, $lang_id, false),
  1649. 'supply_order_expected' => Tools::displayDate($supply_order->date_delivery_expected, $lang_id, false),
  1650. 'supply_order_discount_rate' => Tools::ps_round($supply_order->discount_rate, 2),
  1651. 'supply_order_total_te' => Tools::displayPrice($supply_order->total_te, $currency),
  1652. 'supply_order_discount_value_te' => Tools::displayPrice($supply_order->discount_value_te, $currency),
  1653. 'supply_order_total_with_discount_te' => Tools::displayPrice($supply_order->total_with_discount_te, $currency),
  1654. 'supply_order_total_tax' => Tools::displayPrice($supply_order->total_tax, $currency),
  1655. 'supply_order_total_ti' => Tools::displayPrice($supply_order->total_ti, $currency),
  1656. 'supply_order_currency' => $currency,
  1657. 'is_template' => $supply_order->is_template,
  1658. );
  1659. }
  1660. return parent::renderView();
  1661. }
  1662. /**
  1663. * Callback used to display custom content for a given field
  1664. * @param int $id_supply_order
  1665. * @param string $tr
  1666. * @return string $content
  1667. */
  1668. public function printExportIcons($id_supply_order, $tr)
  1669. {
  1670. $supply_order = new SupplyOrder((int)$id_supply_order);
  1671. if (!Validate::isLoadedObject($supply_order))
  1672. return;
  1673. $supply_order_state = new SupplyOrderState($supply_order->id_supply_order_state);
  1674. if (!Validate::isLoadedObject($supply_order_state))
  1675. return;
  1676. $content = '<span style="width:20px; margin-right:5px;">';
  1677. if ($supply_order_state->editable == false)
  1678. $content .= '<a href="'.$this->context->link->getAdminLink('AdminPdf').'&submitAction=generateSupplyOrderFormPDF&id_supply_order='.(int)$supply_order->id.'" title="'.$this->l('Export as PDF').'"><img src="../img/admin/pdf.gif" alt=""/></a>';
  1679. else
  1680. $content .= '-';
  1681. $content .= '</span>';
  1682. $content .= '<span style="width:20px">';
  1683. if ($supply_order_state->enclosed == true && $supply_order_state->receipt_state == true)
  1684. $content .= '<a href="'.$this->context->link->getAdminLink('AdminSupplyOrders').'&amp;id_supply_order='.(int)$supply_order->id.'
  1685. &csv_order_details" title='.$this->l('Export as CSV').'">
  1686. <img src="../img/admin/excel_file.png" alt=""/></a>';
  1687. else
  1688. $content .= '-';
  1689. $content .= '</span>';
  1690. return $content;
  1691. }
  1692. /**
  1693. * Assigns default actions in toolbar_btn smarty var, if they are not set.
  1694. * uses override to specifically add, modify or remove items
  1695. * @see AdminSupplier::initToolbar()
  1696. */
  1697. public function initToolbar()
  1698. {
  1699. switch ($this->display)
  1700. {
  1701. case 'update_order_state':
  1702. $this->toolbar_btn['save'] = array(
  1703. 'href' => '#',
  1704. 'desc' => $this->l('Save')
  1705. );
  1706. case 'update_receipt':
  1707. // Default cancel button - like old back link
  1708. if (!isset($this->no_back) || $this->no_back == false)
  1709. {
  1710. $back = Tools::safeOutput(Tools::getValue('back', ''));
  1711. if (empty($back))
  1712. $back = self::$currentIndex.'&token='.$this->token;
  1713. $this->toolbar_btn['cancel'] = array(
  1714. 'href' => $back,
  1715. 'desc' => $this->l('Cancel')
  1716. );
  1717. }
  1718. break;
  1719. case 'add':
  1720. case 'edit':
  1721. $this->toolbar_btn['save-and-stay'] = array(
  1722. 'href' => '#',
  1723. 'desc' => $this->l('Save and stay')
  1724. );
  1725. default:
  1726. parent::initToolbar();
  1727. }
  1728. }
  1729. /**
  1730. * Overrides AdminController::afterAdd()
  1731. * @see AdminController::afterAdd()
  1732. * @param ObjectModel $object
  1733. * @return bool
  1734. */
  1735. protected function afterAdd($object)
  1736. {
  1737. if (Tools::getValue('load_products') && Validate::isInt(Tools::getValue('load_products')))
  1738. $this->loadProducts((int)Tools::getValue('load_products'));
  1739. $this->object = $object;
  1740. return true;
  1741. }
  1742. /**
  1743. * Loads products which quantity (hysical quantity) is equal or less than $threshold
  1744. * @param int $threshold
  1745. */
  1746. protected function loadProducts($threshold)
  1747. {
  1748. // if there is already an order
  1749. if (Tools::getValue('id_supply_order'))
  1750. $supply_order = new SupplyOrder((int)Tools::getValue('id_supply_order'));
  1751. else // else, we just created a new order
  1752. $supply_order = $this->object;
  1753. // if order is not valid, return;
  1754. if (!Validate::isLoadedObject($supply_order))
  1755. return;
  1756. // resets products if needed
  1757. if (Tools::getValue('id_supply_order'))
  1758. $supply_order->resetProducts();
  1759. // gets products
  1760. $query = new DbQuery();
  1761. $query->select('ps.id_product,
  1762. ps.id_product_attribute,
  1763. ps.product_supplier_reference as supplier_reference,
  1764. ps.product_supplier_price_te as unit_price_te,
  1765. ps.id_currency,
  1766. IFNULL(pa.reference, IFNULL(p.reference, \'\')) as reference,
  1767. IFNULL(pa.ean13, IFNULL(p.ean13, \'\')) as ean13,
  1768. IFNULL(pa.upc, IFNULL(p.upc, \'\')) as upc');
  1769. $query->from('product_supplier', 'ps');
  1770. $query->leftJoin('stock', 's', '
  1771. s.id_product = ps.id_product
  1772. AND s.id_product_attribute = ps.id_product_attribute
  1773. AND s.id_warehouse = '.(int)$supply_order->id_warehouse);
  1774. $query->innerJoin('warehouse_product_location', 'wpl', '
  1775. wpl.id_product = ps.id_product
  1776. AND wpl.id_product_attribute = ps.id_product_attribute
  1777. AND wpl.id_warehouse = '.(int)$supply_order->id_warehouse.'
  1778. ');
  1779. $query->leftJoin('product', 'p', 'p.id_product = ps.id_product');
  1780. $query->leftJoin('product_attribute', 'pa', '
  1781. pa.id_product_attribute = ps.id_product_attribute
  1782. AND p.id_product = ps.id_product
  1783. ');
  1784. $query->where('ps.id_supplier = '.(int)$supply_order->id_supplier);
  1785. // gets items
  1786. $items = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query);
  1787. // loads order currency
  1788. $order_currency = new Currency($supply_order->id_currency);
  1789. if (!Validate::isLoadedObject($order_currency))
  1790. return;
  1791. $manager = StockManagerFactory::getManager();
  1792. foreach ($items as $item)
  1793. {
  1794. $diff = (int)$threshold;
  1795. if ($supply_order->is_template != 1)
  1796. {
  1797. $real_quantity = (int)$manager->getProductRealQuantities($item['id_product'], $item['id_product_attribute'], $supply_order->id_warehouse, true);
  1798. $diff = (int)$threshold - (int)$real_quantity;
  1799. }
  1800. if ($diff > 0)
  1801. {
  1802. // sets supply_order_detail
  1803. $supply_order_detail = new SupplyOrderDetail();
  1804. $supply_order_detail->id_supply_order = $supply_order->id;
  1805. $supply_order_detail->id_currency = $order_currency->id;
  1806. $supply_order_detail->id_product = $item['id_product'];
  1807. $supply_order_detail->id_product_attribute = $item['id_product_attribute'];
  1808. $supply_order_detail->reference = $item['reference'];
  1809. $supply_order_detail->supplier_reference = $item['supplier_reference'];
  1810. $supply_order_detail->name = Product::getProductName($item['id_product'], $item['id_product_attribute'], $supply_order->id_lang);
  1811. $supply_order_detail->ean13 = $item['ean13'];
  1812. $supply_order_detail->upc = $item['upc'];
  1813. $supply_order_detail->quantity_expected = (int)$diff;
  1814. $supply_order_detail->exchange_rate = $order_currency->conversion_rate;
  1815. $product_currency = new Currency($item['id_currency']);
  1816. if (Validate::isLoadedObject($product_currency))
  1817. $supply_order_detail->unit_price_te = Tools::convertPriceFull($item['unit_price_te'], $product_currency, $order_currency);
  1818. else
  1819. $supply_order_detail->unit_price_te = 0;
  1820. $supply_order_detail->save();
  1821. unset($product_currency);
  1822. }
  1823. }
  1824. // updates supply order
  1825. $supply_order->update();
  1826. }
  1827. /**
  1828. * Overrides AdminController::beforeAdd()
  1829. * @see AdminController::beforeAdd()
  1830. * @param ObjectModel $object
  1831. */
  1832. public function beforeAdd($object)
  1833. {
  1834. if (Tools::isSubmit('is_template'))
  1835. $object->is_template = 1;
  1836. return true;
  1837. }
  1838. /**
  1839. * Helper function for AdminSupplyOrdersController::postProcess()
  1840. * @see AdminSupplyOrdersController::postProcess()
  1841. */
  1842. protected function postProcessCopyFromTemplate()
  1843. {
  1844. // gets SupplyOrder and checks if it is valid
  1845. $id_supply_order = (int)Tools::getValue('id_supply_order');
  1846. $supply_order = new SupplyOrder($id_supply_order);
  1847. if (!Validate::isLoadedObject($supply_order))
  1848. $this->errors[] = Tools::displayError($this->l('This template could not be copied.'));
  1849. // gets SupplyOrderDetail
  1850. $entries = $supply_order->getEntriesCollection($supply_order->id_lang);
  1851. // updates SupplyOrder so that it is not a template anymore
  1852. $language = new Language($supply_order->id_lang);
  1853. $ref = $supply_order->reference;
  1854. $ref .= ' ('.date($language->date_format_full).')';
  1855. $supply_order->reference = $ref;
  1856. $supply_order->is_template = 0;
  1857. $supply_order->id = (int)0;
  1858. $supply_order->save();
  1859. // copies SupplyOrderDetail
  1860. foreach ($entries as $entry)
  1861. {
  1862. $entry->id_supply_order = $supply_order->id;
  1863. $entry->id = (int)0;
  1864. $entry->save();
  1865. }
  1866. // redirect when done
  1867. $token = Tools::getValue('token') ? Tools::getValue('token') : $this->token;
  1868. $redirect = self::$currentIndex.'&token='.$token;
  1869. $this->redirect_after = $redirect.'&conf=19';
  1870. }
  1871. /**
  1872. * Gets the current warehouse used
  1873. *
  1874. * @return int id_warehouse
  1875. */
  1876. protected function getCurrentWarehouse()
  1877. {
  1878. static $warehouse = 0;
  1879. if ($warehouse == 0)
  1880. {
  1881. $warehouse = -1; // all warehouses
  1882. if ((int)Tools::getValue('id_warehouse'))
  1883. $warehouse = (int)Tools::getValue('id_warehouse');
  1884. }
  1885. return $warehouse;
  1886. }
  1887. /**
  1888. * Gets the current filter used
  1889. *
  1890. * @return int status
  1891. */
  1892. protected function getFilterStatus()
  1893. {
  1894. static $status = 0;
  1895. $status = 0;
  1896. if (Tools::getValue('filter_status') === 'on')
  1897. $status = 1;
  1898. return $status;
  1899. }
  1900. public function initProcess()
  1901. {
  1902. if (!Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT'))
  1903. {
  1904. $this->warnings[md5('PS_ADVANCED_STOCK_MANAGEMENT')] = $this->l('You need to activate advanced stock management prior to use this feature.');
  1905. return false;
  1906. }
  1907. parent::initProcess();
  1908. }
  1909. }