PageRenderTime 62ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/controllers/admin/AdminSupplyOrdersController.php

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