studiou-wc-ord-print-statuses.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <?php
  2. /**
  3. * Plugin Name: QDR - Studiou WC Order Print Statuses
  4. * Plugin URI: https://www.quadarax.com/plugins/studiou-wc-ord-print-statuses
  5. * Description: Adds custom order statuses (to-print,in-print), bulk actions, and status change timestamps to WooCommerce for print orders to third party provider
  6. * Version: 1.0
  7. * Author: Dalibor Votruba
  8. * Author URI: https://www.quadarax.com
  9. * License: GPL2
  10. */
  11. defined('ABSPATH') or die('Direct access not allowed!');
  12. /**
  13. * Main plugin class
  14. */
  15. class Studiou_WC_Ord_Print_Statuses {
  16. /**
  17. * Constructor
  18. */
  19. public function __construct() {
  20. add_action('plugins_loaded', array($this, 'init'));
  21. }
  22. /**
  23. * Initialize the plugin
  24. */
  25. public function init() {
  26. // Check if WooCommerce is active
  27. if (!class_exists('WooCommerce')) {
  28. add_action('admin_notices', array($this, 'woocommerce_missing_notice'));
  29. return;
  30. }
  31. // Initialize plugin features
  32. $this->add_custom_order_statuses();
  33. $this->add_custom_order_fields();
  34. $this->modify_order_search();
  35. $this->add_bulk_actions();
  36. $this->add_custom_columns();
  37. $this->add_import_buttons();
  38. }
  39. /**
  40. * Display a notice if WooCommerce is not active
  41. */
  42. public function woocommerce_missing_notice() {
  43. ?>
  44. <div class="error">
  45. <p><?php _e('Studiou WC Order Print Statuses requires WooCommerce to be installed and active.', 'studiou-wc-ord-print-statuses'); ?></p>
  46. </div>
  47. <?php
  48. }
  49. /**
  50. * Add custom order statuses
  51. */
  52. private function add_custom_order_statuses() {
  53. add_action('init', array($this, 'register_custom_order_statuses'));
  54. add_filter('wc_order_statuses', array($this, 'add_custom_order_statuses_to_list'));
  55. }
  56. /**
  57. * Register custom order statuses
  58. */
  59. public function register_custom_order_statuses() {
  60. register_post_status('wc-to-print', array(
  61. 'label' => _x('Prepare to Printing', 'Order status', 'studiou-wc-ord-print-statuses'),
  62. 'public' => true,
  63. 'exclude_from_search' => false,
  64. 'show_in_admin_all_list' => true,
  65. 'show_in_admin_status_list' => true,
  66. 'label_count' => _n_noop('Prepare to Printing <span class="count">(%s)</span>', 'Prepare to Printing <span class="count">(%s)</span>')
  67. ));
  68. register_post_status('wc-in-print', array(
  69. 'label' => _x('In Printing', 'Order status', 'studiou-wc-ord-print-statuses'),
  70. 'public' => true,
  71. 'exclude_from_search' => false,
  72. 'show_in_admin_all_list' => true,
  73. 'show_in_admin_status_list' => true,
  74. 'label_count' => _n_noop('In Printing <span class="count">(%s)</span>', 'In Printing <span class="count">(%s)</span>')
  75. ));
  76. }
  77. /**
  78. * Add custom order statuses to the list of WooCommerce order statuses
  79. */
  80. public function add_custom_order_statuses_to_list($order_statuses) {
  81. $new_order_statuses = array();
  82. foreach ($order_statuses as $key => $status) {
  83. $new_order_statuses[$key] = $status;
  84. if ($key === 'wc-processing') {
  85. $new_order_statuses['wc-to-print'] = _x('Prepare to Printing', 'Order status', 'studiou-wc-ord-print-statuses');
  86. $new_order_statuses['wc-in-print'] = _x('In Printing', 'Order status', 'studiou-wc-ord-print-statuses');
  87. }
  88. }
  89. return $new_order_statuses;
  90. }
  91. /**
  92. * Add custom order fields
  93. */
  94. private function add_custom_order_fields() {
  95. add_action('woocommerce_admin_order_data_after_order_details', array($this, 'display_custom_order_fields'));
  96. add_action('woocommerce_process_shop_order_meta', array($this, 'save_custom_order_fields'));
  97. }
  98. /**
  99. * Display custom order fields in the order edit page
  100. */
  101. public function display_custom_order_fields($order) {
  102. ?>
  103. <div class="order_data_column">
  104. <h4><?php _e('Print Information', 'studiou-wc-ord-print-statuses'); ?></h4>
  105. <p>
  106. <label for="to_print_date"><?php _e('Prepare To Print Date:', 'studiou-wc-ord-print-statuses'); ?></label>
  107. <input type="text" class="date-picker" name="to_print_date" id="to_print_date" value="<?php echo esc_attr($order->get_meta('to_print_date')); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])">
  108. </p>
  109. <p>
  110. <label for="in_print_date"><?php _e('In Print Date:', 'studiou-wc-ord-print-statuses'); ?></label>
  111. <input type="text" class="date-picker" name="in_print_date" id="in_print_date" value="<?php echo esc_attr($order->get_meta('in_print_date')); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])">
  112. </p>
  113. <p>
  114. <label for="external_ref_ord_no"><?php _e('External Order Number:', 'studiou-wc-ord-print-statuses'); ?></label>
  115. <input type="text" name="external_ref_ord_no" id="external_ref_ord_no" value="<?php echo esc_attr($order->get_meta('external_ref_ord_no')); ?>">
  116. </p>
  117. <p>
  118. <label for="external_ref_ord_delivered"><?php _e('External Order Delivered:', 'studiou-wc-ord-print-statuses'); ?></label>
  119. <input type="text" class="date-picker" name="external_ref_ord_delivered" id="external_ref_ord_delivered" value="<?php echo esc_attr($order->get_meta('external_ref_ord_delivered')); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])">
  120. </p>
  121. </div>
  122. <?php
  123. }
  124. /**
  125. * Save custom order fields
  126. */
  127. public function save_custom_order_fields($order_id) {
  128. $order = wc_get_order($order_id);
  129. if (isset($_POST['to_print_date'])) {
  130. $order->update_meta_data('to_print_date', sanitize_text_field($_POST['to_print_date']));
  131. }
  132. if (isset($_POST['in_print_date'])) {
  133. $order->update_meta_data('in_print_date', sanitize_text_field($_POST['in_print_date']));
  134. }
  135. if (isset($_POST['external_ref_ord_no'])) {
  136. $order->update_meta_data('external_ref_ord_no', sanitize_text_field($_POST['external_ref_ord_no']));
  137. }
  138. if (isset($_POST['external_ref_ord_delivered'])) {
  139. $order->update_meta_data('external_ref_ord_delivered', sanitize_text_field($_POST['external_ref_ord_delivered']));
  140. }
  141. $order->save();
  142. }
  143. /**
  144. * Modify order search to include external reference order number
  145. */
  146. private function modify_order_search() {
  147. add_filter('woocommerce_shop_order_search_fields', array($this, 'add_external_ref_ord_no_to_search'));
  148. }
  149. /**
  150. * Add external reference order number to search fields
  151. */
  152. public function add_external_ref_ord_no_to_search($search_fields) {
  153. $search_fields[] = 'external_ref_ord_no';
  154. return $search_fields;
  155. }
  156. /**
  157. * Add bulk actions to the orders list
  158. */
  159. private function add_bulk_actions() {
  160. add_filter('bulk_actions-edit-shop_order', array($this, 'register_bulk_actions'));
  161. add_filter('handle_bulk_actions-edit-shop_order', array($this, 'handle_bulk_actions'), 10, 3);
  162. }
  163. /**
  164. * Register custom bulk actions
  165. */
  166. public function register_bulk_actions($bulk_actions) {
  167. $bulk_actions['prepare_to_printing_export'] = __('Prepare to Printing Export', 'studiou-wc-ord-print-statuses');
  168. $bulk_actions['set_status_to_prepare_to_printing'] = __('Set Status to Prepare to Printing', 'studiou-wc-ord-print-statuses');
  169. $bulk_actions['set_status_to_in_printing'] = __('Set Status to In Printing', 'studiou-wc-ord-print-statuses');
  170. return $bulk_actions;
  171. }
  172. /**
  173. * Handle custom bulk actions
  174. */
  175. public function handle_bulk_actions($redirect_to, $action, $post_ids) {
  176. if ($action === 'prepare_to_printing_export') {
  177. $this->prepare_to_printing_export($post_ids);
  178. } elseif ($action === 'set_status_to_prepare_to_printing') {
  179. $this->set_status_to_prepare_to_printing($post_ids);
  180. } elseif ($action === 'set_status_to_in_printing') {
  181. $this->set_status_to_in_printing($post_ids);
  182. }
  183. return $redirect_to;
  184. }
  185. /**
  186. * Handle the "Prepare to Printing Export" bulk action
  187. */
  188. private function prepare_to_printing_export($post_ids) {
  189. global $wpdb;
  190. $results = $wpdb->get_results("SELECT * FROM `vsu_orders_car_prod_qty_img` WHERE order_id IN (" . implode(',', array_map('intval', $post_ids)) . ")");
  191. // Generate CSV file
  192. $filename = 'prepare_to_printing_export_' . date('Y-m-d_His') . '.csv';
  193. header('Content-Type: text/csv');
  194. header('Content-Disposition: attachment; filename="' . $filename . '"');
  195. $output = fopen('php://output', 'w');
  196. fputcsv($output, array_keys((array)$results[0])); // CSV header
  197. foreach ($results as $row) {
  198. fputcsv($output, (array)$row);
  199. }
  200. fclose($output);
  201. // Update order statuses and to-print-date
  202. foreach ($post_ids as $post_id) {
  203. $order = wc_get_order($post_id);
  204. $order->update_status('wc-to-print');
  205. $order->update_meta_data('to_print_date', current_time('mysql'));
  206. $order->save();
  207. }
  208. exit();
  209. }
  210. /**
  211. * Handle the "Set Status to Prepare to Printing" bulk action
  212. */
  213. private function set_status_to_prepare_to_printing($post_ids) {
  214. foreach ($post_ids as $post_id) {
  215. $order = wc_get_order($post_id);
  216. $order->update_status('wc-to-print');
  217. $order->update_meta_data('to_print_date', current_time('mysql'));
  218. $order->save();
  219. }
  220. }
  221. /**
  222. * Handle the "Set Status to In Printing" bulk action
  223. */
  224. private function set_status_to_in_printing($post_ids) {
  225. foreach ($post_ids as $post_id) {
  226. $order = wc_get_order($post_id);
  227. $order->update_status('wc-in-print');
  228. $order->update_meta_data('in_print_date', current_time('mysql'));
  229. $order->save();
  230. }
  231. }
  232. /**
  233. * Add custom columns to the orders list
  234. */
  235. private function add_custom_columns() {
  236. add_filter('manage_edit-shop_order_columns', array($this, 'add_custom_shop_order_column'), 20);
  237. add_action('manage_shop_order_posts_custom_column', array($this, 'add_custom_shop_order_column_content'), 20, 2);
  238. }
  239. /**
  240. * Add custom columns to the orders list table
  241. */
  242. public function add_custom_shop_order_column($columns) {
  243. $new_columns = array();
  244. foreach ($columns as $column_name => $column_info) {
  245. $new_columns[$column_name] = $column_info;
  246. if ('order_total' === $column_name) {
  247. $new_columns['to_print_date'] = __('To Print Date', 'studiou-wc-ord-print-statuses');
  248. $new_columns['in_print_date'] = __('In Print Date', 'studiou-wc-ord-print-statuses');
  249. $new_columns['external_ref_ord_no'] = __('External Order Number', 'studiou-wc-ord-print-statuses');
  250. }
  251. }
  252. return $new_columns;
  253. }
  254. /**
  255. * Add content to custom columns in the orders list table
  256. */
  257. public function add_custom_shop_order_column_content($column, $post_id) {
  258. $order = wc_get_order($post_id);
  259. switch ($column) {
  260. case 'to_print_date':
  261. echo esc_html($order->get_meta('to_print_date'));
  262. break;
  263. case 'in_print_date':
  264. echo esc_html($order->get_meta('in_print_date'));
  265. break;
  266. case 'external_ref_ord_no':
  267. echo esc_html($order->get_meta('external_ref_ord_no'));
  268. break;
  269. }
  270. }
  271. /**
  272. * Add import buttons to the orders page
  273. */
  274. private function add_import_buttons() {
  275. add_action('woocommerce_order_list_table_restrict_manage_orders', array($this, 'add_import_buttons_to_orders_page'));
  276. add_action('admin_post_import_inprint_protocol', array($this, 'handle_import_inprint_protocol'));
  277. add_action('admin_post_import_delivered_protocol', array($this, 'handle_import_delivered_protocol'));
  278. }
  279. /**
  280. * Add import buttons to the orders page
  281. */
  282. public function add_import_buttons_to_orders_page() {
  283. ?>
  284. <div class="alignleft actions">
  285. <button type="button" class="button import-inprint-protocol"><?php _e('Import InPrint Protocol', 'studiou-wc-ord-print-statuses'); ?></button>
  286. <button type="button" class="button import-delivered-protocol"><?php _e('Import Delivered Protocol', 'studiou-wc-ord-print-statuses'); ?></button>
  287. </div>
  288. <script>
  289. jQuery(function($) {
  290. // Move our buttons after the "Add Order" button
  291. var $addOrderButton = $('.page-title-action').first();
  292. $('.import-inprint-protocol, .import-delivered-protocol').insertAfter($addOrderButton);
  293. $('.import-inprint-protocol').click(function() {
  294. var fileInput = $('<input type="file" accept=".csv" style="display:none;">');
  295. fileInput.appendTo('body').trigger('click');
  296. fileInput.on('change', function() {
  297. var formData = new FormData();
  298. formData.append('action', 'import_inprint_protocol');
  299. formData.append('file', this.files[0]);
  300. $.ajax({
  301. url: '<?php echo admin_url('admin-post.php'); ?>',
  302. type: 'POST',
  303. data: formData,
  304. processData: false,
  305. contentType: false,
  306. success: function(response) {
  307. alert('InPrint Protocol import completed successfully.');
  308. location.reload();
  309. },
  310. error: function() {
  311. alert('InPrint Protocol import failed. Please try again.');
  312. }
  313. });
  314. });
  315. });
  316. $('.import-delivered-protocol').click(function() {
  317. var fileInput = $('<input type="file" accept=".csv" style="display:none;">');
  318. fileInput.appendTo('body').trigger('click');
  319. fileInput.on('change', function() {
  320. var formData = new FormData();
  321. formData.append('action', 'import_delivered_protocol');
  322. formData.append('file', this.files[0]);
  323. $.ajax({
  324. url: '<?php echo admin_url('admin-post.php'); ?>',
  325. type: 'POST',
  326. data: formData,
  327. processData: false,
  328. contentType: false,
  329. success: function(response) {
  330. alert('Delivered Protocol import completed successfully.');
  331. location.reload();
  332. },
  333. error: function() {
  334. alert('Delivered Protocol import failed. Please try again.');
  335. }
  336. });
  337. });
  338. });
  339. });
  340. </script>
  341. <?php
  342. }
  343. /**
  344. * Handle the import of InPrint protocol
  345. */
  346. public function handle_import_inprint_protocol() {
  347. if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
  348. wp_die('File upload failed.');
  349. }
  350. $file = fopen($_FILES['file']['tmp_name'], 'r');
  351. if ($file === false) {
  352. wp_die('Failed to open the uploaded file.');
  353. }
  354. // Skip the header row
  355. fgetcsv($file);
  356. while (($data = fgetcsv($file)) !== false) {
  357. $order_number = $data[0];
  358. $external_order = $data[1];
  359. $external_order_date = $data[2];
  360. $order = wc_get_order($order_number);
  361. if ($order) {
  362. $order->update_status('wc-in-print');
  363. $order->update_meta_data('in_print_date', current_time('mysql'));
  364. $order->update_meta_data('external_ref_ord_no', $external_order);
  365. $order->save();
  366. }
  367. }
  368. fclose($file);
  369. wp_redirect(admin_url('edit.php?post_type=shop_order'));
  370. exit;
  371. }
  372. /**
  373. * Handle the import of Delivered protocol
  374. */
  375. public function handle_import_delivered_protocol() {
  376. if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
  377. wp_die('File upload failed.');
  378. }
  379. $file = fopen($_FILES['file']['tmp_name'], 'r');
  380. if ($file === false) {
  381. wp_die('Failed to open the uploaded file.');
  382. }
  383. // Skip the header row
  384. fgetcsv($file);
  385. while (($data = fgetcsv($file)) !== false) {
  386. $order_number = $data[0];
  387. $order = wc_get_order($order_number);
  388. if ($order) {
  389. $order->update_status('wc-completed');
  390. $order->update_meta_data('external_ref_ord_delivered', current_time('mysql'));
  391. $order->save();
  392. }
  393. }
  394. fclose($file);
  395. wp_redirect(admin_url('edit.php?post_type=shop_order'));
  396. exit;
  397. }
  398. }
  399. // Initialize the plugin
  400. new Studiou_WC_Ord_Print_Statuses();