PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / 2.3.14
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). v2.3.14
3.0.3 3.0.2 3.0.1 trunk 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.3.0 2.3.1 2.3.10 2.3.11 2.3.12 2.3.13 2.3.14 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.3.9 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.7.91 2.7.92 2.7.93 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0
commercebird / includes / classes / purchase-orders / class-cmbird-purchase-order.php
commercebird / includes / classes / purchase-orders Last commit date
class-cmbird-purchase-admin.php 1 year ago class-cmbird-purchase-automation.php 1 year ago class-cmbird-purchase-order-email.php 1 year ago class-cmbird-purchase-order.php 1 year ago class-cmbird-rest-shop-purchase-controller.php 1 year ago index.php 1 year ago
class-cmbird-purchase-order.php
460 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 // Ensure WooCommerce is active
8 if ( ! class_exists( 'WC_Order' ) ) {
9 return;
10 }
11
12 /**
13 * Purchase Order Class
14 *
15 * This class extends the WC_Order class to add custom functionality for purchase orders.
16 */
17
18 class CMBIRD_Purchase_Order extends WC_Order {
19
20 public $order_type = 'shop_purchase';
21
22 public function __construct( $order_id = 0 ) {
23 parent::__construct( $order_id );
24 $this->order_type = 'shop_purchase';
25 // Custom initialization code for purchase orders can go here
26 }
27
28 public function get_type() {
29 return 'shop_purchase';
30 }
31
32 // You can override or add methods for handling purchase orders
33 public function is_purchase() {
34 return true;
35 }
36
37 public function remove_item( $item_id ) {
38 parent::remove_item( $item_id );
39 parent::calculate_totals();
40 }
41
42 // update meta data
43 public function update_meta_data( $key, $value, $meta_id = 0 ) {
44 parent::update_meta_data( $key, $value, $meta_id );
45 // Custom logic for updating meta data for purchase orders
46 }
47 }
48
49 // Register the custom order type
50 function cmbird_register_shop_purchase_order_type() {
51 wc_register_order_type(
52 'shop_purchase',
53 array(
54 'labels' => array(
55 'name' => __( 'Purchase Orders', 'commercebird' ),
56 'singular_name' => __( 'Purchase Order', 'commercebird' ),
57 'add_new' => _x( 'Add Purchase', 'custom post type setting', 'commercebird' ),
58 'add_new_item' => _x( 'Add New Purchase', 'custom post type setting', 'commercebird' ),
59 'edit' => _x( 'Edit', 'custom post type setting', 'commercebird' ),
60 'edit_item' => _x( 'Edit Purchase', 'custom post type setting', 'commercebird' ),
61 'new_item' => _x( 'New Purchase', 'custom post type setting', 'commercebird' ),
62 'view' => _x( 'View Purchase', 'custom post type setting', 'commercebird' ),
63 'view_item' => _x( 'View Purchase', 'custom post type setting', 'commercebird' ),
64 'search_items' => __( 'Search Purchases', 'commercebird' ),
65 'not_found_in_trash' => _x( 'No Purchases found in trash', 'custom post type setting', 'commercebird' ),
66 'parent' => _x( 'Parent Purchases', 'custom post type setting', 'commercebird' ),
67 'menu_name' => __( 'Purchases', 'commercebird' ),
68 ),
69 'description' => __( 'This is where purchase orders are stored.', 'commercebird' ),
70 'public' => false,
71 'show_ui' => true,
72 'capability_type' => 'shop_order',
73 'map_meta_cap' => true,
74 'publicly_queryable' => false,
75 'exclude_from_search' => true,
76 'show_in_menu' => current_user_can( 'manage_woocommerce' ) ? 'woocommerce' : true,
77 'hierarchical' => false,
78 'show_in_nav_menus' => false,
79 'rewrite' => false,
80 'query_var' => false,
81 'supports' => array( 'title', 'comments', 'custom-fields' ),
82 'has_archive' => false,
83 // wc_register_order_type() params
84 'exclude_from_orders_screen' => true,
85 'exclude_from_order_webhooks' => true,
86 'exclude_from_order_count' => true,
87 'exclude_from_order_views' => true,
88 'exclude_from_order_reports' => true,
89 'exclude_from_order_sales_reports' => true,
90 'add_order_meta_boxes' => true,
91 'class_name' => 'CMBIRD_Purchase_Order',
92 )
93 );
94
95 // register custom statuses
96 global $wc_order_statuses;
97 $statuses = cmbird_get_purchase_order_statuses();
98 foreach ( $statuses as $status => $label ) {
99 register_post_status(
100 $status,
101 array(
102 'label' => $label,
103 'public' => true,
104 'exclude_from_search' => false,
105 'show_in_admin_all_list' => true,
106 'show_in_admin_status_list' => true,
107 'label_count' => _n_noop( $label . ' <span class="count">(%s)</span>', $label . ' <span class="count">(%s)</span>', 'commercebird' ),
108 'post_type' => array( 'shop_order', 'shop_purchase' ),
109 )
110 );
111 }
112 // Create the "vendor" role
113 add_role(
114 'vendor',
115 __( 'Vendor', 'commercebird' ),
116 array(
117 'read' => true,
118 )
119 );
120 }
121 // Register our custom purchase order type after WC_Post_types::register_post_types()
122 add_action( 'init', 'cmbird_register_shop_purchase_order_type', 5 );
123
124 // Create array of Purchase Order Statuses
125 function cmbird_get_purchase_order_statuses() {
126 return array(
127 'wc-awaiting-approval' => _x( 'Awaiting Approval', 'Order status', 'commercebird' ),
128 'wc-approved' => _x( 'Approved', 'Order status', 'commercebird' ),
129 'wc-part-received' => _x( 'Partially Received', 'Order status', 'commercebird' ),
130 'wc-received' => _x( 'Received', 'Order status', 'commercebird' ),
131 );
132 }
133
134 // Load custom class for Purchase Orders
135 function cmbird_load_purchase_order_class( $order_classname, $order_type, $order_id ) {
136 if ( 'shop_purchase' === $order_type ) {
137 $order_classname = 'CMBIRD_Purchase_Order';
138 }
139 return $order_classname;
140 }
141 add_filter( 'woocommerce_order_class', 'cmbird_load_purchase_order_class', 10, 3 );
142
143 /**
144 * Custom order statuses for Purchase Orders
145 * @param mixed $order_statuses
146 * @return array
147 * @since 1.0.0
148 */
149 // Add custom order statuses for 'shop_purchase'
150 function cmbird_custom_order_statuses( $order_statuses ) {
151 // Add custom statuses once, outside the loop
152 $custom_statuses = array(
153 'wc-awaiting-approval' => 'Awaiting Approval',
154 'wc-approved' => 'Approved',
155 'wc-part-received' => 'Partially Received',
156 'wc-received' => 'Received',
157 );
158
159 return array_merge( $order_statuses, $custom_statuses );
160 }
161 add_filter( 'wc_order_statuses', 'cmbird_custom_order_statuses' );
162
163 function cmbird_add_valid_custom_statuses_for_shop_purchase( $statuses ) {
164 $statuses[] = 'wc-awaiting-approval';
165 $statuses[] = 'wc-approved';
166 $statuses[] = 'wc-part-received';
167 $statuses[] = 'wc-received';
168 return $statuses;
169 }
170 add_filter( 'woocommerce_register_shop_order_post_statuses', 'cmbird_add_valid_custom_statuses_for_shop_purchase' );
171
172 function cmbird_custom_shop_purchase_bulk_actions( $bulk_actions ) {
173 $bulk_actions['mark_awaiting-approval'] = __( 'Change status to awaiting approval', 'commercebird' );
174 $bulk_actions['mark_approved'] = __( 'Change status to approved', 'commercebird' );
175 $bulk_actions['mark_part-received'] = __( 'Change status to partially received', 'commercebird' );
176 $bulk_actions['mark_received'] = __( 'Change status to received', 'commercebird' );
177
178 // unset the statuses that are not needed - e.g. 'mark_processing'
179 unset( $bulk_actions['mark_processing'] );
180 unset( $bulk_actions['mark_on-hold'] );
181 unset( $bulk_actions['mark_completed'] );
182
183 return $bulk_actions;
184 }
185 add_filter( 'bulk_actions-edit-shop_purchase', 'cmbird_custom_shop_purchase_bulk_actions' );
186 // HPOS Screens
187 add_filter( 'bulk_actions-woocommerce_page_wc-orders--shop_purchase', 'cmbird_custom_shop_purchase_bulk_actions' );
188
189
190 function cmbird_handle_custom_status_transitions( $order_id, $old_status, $new_status ) {
191
192 if ( 'awaiting-approval' === $new_status ) {
193 // Logic for when the order is awaiting approval
194 }
195
196 if ( 'approved' === $new_status ) {
197 // Logic for when the order is approved
198 }
199
200 if ( 'part-received' === $new_status ) {
201 // logic for when the order is partially received
202 }
203
204 if ( 'received' === $new_status ) {
205 $order = wc_get_order( $order_id );
206
207 foreach ( $order->get_items() as $item_id => $item ) {
208 $product = $item->get_product();
209
210 if ( $product && $product->managing_stock() ) {
211 $quantity_ordered = $item->get_quantity();
212 $already_received = (int) wc_get_order_item_meta( $item_id, 'received', true );
213 $quantity_to_add = $quantity_ordered;
214
215 if ( 'part-received' === $old_status ) {
216 // Only add the remaining quantity not yet received
217 $quantity_to_add = max( 0, $quantity_ordered - $already_received );
218 }
219
220 if ( $quantity_to_add > 0 ) {
221 $current_stock = $product->get_stock_quantity();
222 $new_stock = $current_stock + $quantity_to_add;
223
224 // Update stock
225 $product->set_stock_quantity( $new_stock );
226 $product->save();
227 }
228
229 // Optionally, update the "received" meta to reflect full receipt
230 wc_update_order_item_meta( $item_id, 'received', $quantity_ordered );
231 }
232 }
233 }
234 }
235 add_action( 'woocommerce_order_status_changed', 'cmbird_handle_custom_status_transitions', 10, 3 );
236
237 // Set cost price in purchase orders when created or edited via Admin
238 function cmbird_set_cost_price_for_admin_purchase_order( $post_id, $post, $update ) {
239 if ( 'shop_purchase' === wc_get_order_type( $post_id ) ) {
240
241 $order = wc_get_order( $post_id );
242
243 if ( $order && 'shop_purchase' === $order->get_type() ) {
244
245 foreach ( $order->get_items() as $item_id => $item ) {
246 $product = $item->get_product();
247
248 if ( $product ) {
249 // Get the cost price from product meta
250 $cost_price = get_post_meta( $product->get_id(), '_cost_price', true );
251
252 // If cost price is set, update the item price in the order
253 if ( $cost_price && is_numeric( $cost_price ) ) {
254 $item->set_subtotal( $cost_price * $item->get_quantity() );
255 $item->set_total( $cost_price * $item->get_quantity() );
256 $item->save();
257 }
258 }
259 }
260
261 // Recalculate totals for the order after setting cost price
262 $order->calculate_totals();
263
264 // Set the store's address as the shipping address for purchase orders
265 $location_address = get_option( 'woocommerce_wh_address', '' );
266 $location_address_2 = get_option( 'woocommerce_wh_address_2', '' );
267 $location_city = get_option( 'woocommerce_wh_city', '' );
268 $location_state = get_option( 'woocommerce_wh_state', '' );
269 $location_country = get_option( 'woocommerce_wh_country', '' );
270 $location_zip = get_option( 'woocommerce_wh_postcode', '' );
271
272 // Update the shipping address with store details
273 $shipping_address = array(
274 'first_name' => 'Store',
275 'last_name' => 'Address',
276 'company' => get_option( 'blogname' ),
277 'address_1' => $location_address,
278 'address_2' => $location_address_2,
279 'city' => $location_city,
280 'state' => $location_state,
281 'postcode' => $location_zip,
282 'country' => $location_country,
283 );
284
285 // Set the new shipping address for the order
286 $order->set_address( $shipping_address, 'shipping' );
287
288 // save shipping address as customer shipping address.
289 $customer = $order->get_customer();
290 $customer->set_shipping_address( $shipping_address );
291 $customer->save();
292 }
293 }
294 }
295 add_action( 'save_post_shop_purchase', 'cmbird_set_cost_price_for_admin_purchase_order', 10, 3 );
296
297 // Add location address in woocommerce settings
298 add_filter( 'woocommerce_general_settings', 'cmbird_additional_store_addresses_admin', 9999 );
299
300 function cmbird_additional_store_addresses_admin( $settings ) {
301
302 $new_settings = array(
303
304 array(
305 'title' => 'location Address',
306 'type' => 'title',
307 'id' => 'wh_address',
308 ),
309
310 array(
311 'title' => __( 'Address line 1', 'commercebird' ),
312 'id' => 'woocommerce_wh_address',
313 'type' => 'text',
314 ),
315
316 array(
317 'title' => __( 'Address line 2', 'commercebird' ),
318 'id' => 'woocommerce_wh_address_2',
319 'type' => 'text',
320 ),
321
322 array(
323 'title' => __( 'City', 'commercebird' ),
324 'id' => 'woocommerce_wh_city',
325 'type' => 'text',
326 ),
327
328 array(
329 'title' => __( 'State', 'commercebird' ),
330 'id' => 'woocommerce_wh_state',
331 'type' => 'text',
332 ),
333
334 array(
335 'title' => __( 'Country', 'commercebird' ),
336 'id' => 'woocommerce_wh_country',
337 'type' => 'single_select_country',
338 ),
339
340 array(
341 'title' => __( 'Postcode / ZIP', 'commercebird' ),
342 'id' => 'woocommerce_wh_postcode',
343 'type' => 'text',
344 ),
345
346 array(
347 'type' => 'sectionend',
348 'id' => 'wh_address',
349 ),
350
351 );
352
353 return array_merge( array_slice( $settings, 0, 7 ), $new_settings, array_slice( $settings, 7 ) );
354 }
355
356 /**
357 * Use 'woocommerce_rest_insert_customer' action hook to change the user role to vendor when creating customer.
358 *
359 * @param WP_User $user_data Data used to create the customer.
360 * @param WP_REST_Request $request Request object.
361 * @param boolean $creating True when creating customer, false when updating customer.
362 */
363 function cmbird_change_user_role_to_vendor( $user_data, $request, $creating ) {
364 // Get the property 'role' from the request object
365 $role = $request->get_param( 'role' );
366
367 // Change the user role to 'vendor'
368 if ( ! empty( $role ) && 'vendor' === $role ) {
369 $user_data->set_role( 'vendor' );
370 }
371
372 // Return the user data
373 return $user_data;
374 }
375 add_action( 'woocommerce_rest_insert_customer', 'cmbird_change_user_role_to_vendor', 10, 3 );
376
377 /**
378 * Register the custom email class for Purchase Orders
379 * @param array $email_classes
380 * @return array
381 */
382 function cmbird_register_purchase_order_email( $email_classes ) {
383 require_once 'class-cmbird-purchase-order-email.php';
384 $email_classes['CMBIRD_Email_Purchase_Order'] = new CMBIRD_Email_Purchase_Order();
385 return $email_classes;
386 }
387 add_action( 'woocommerce_email_classes', 'cmbird_register_purchase_order_email' );
388
389 function cmbird_format_purchase_order_number( $order_number, $order ) {
390 if ( 'shop_purchase' === $order->get_type() ) {
391 return 'PO-' . $order_number;
392 }
393 return $order_number;
394 }
395 add_filter( 'woocommerce_order_number', 'cmbird_format_purchase_order_number', 10, 2 );
396
397 function cmbird_set_vendor_email_recipient( $recipient, $order ) {
398 if ( ! $order || ! is_a( $order, 'WC_Order' ) ) {
399 return $recipient;
400 }
401
402 if ( 'shop_purchase' === $order->get_type() ) {
403 $vendor_id = $order->get_customer_id();
404 $vendor = get_userdata( $vendor_id );
405
406 if ( $vendor && in_array( 'vendor', (array) $vendor->roles ) ) {
407 $recipient = $vendor->user_email;
408 }
409 }
410 return $recipient;
411 }
412 add_filter( 'woocommerce_email_recipient_new_order', 'cmbird_set_vendor_email_recipient', 10, 2 );
413
414 function cmbird_customize_purchase_order_email_subject( $subject, $order ) {
415 if ( ! $order || ! is_a( $order, 'WC_Order' ) ) {
416 return $subject;
417 }
418
419 if ( 'shop_purchase' === $order->get_type() ) {
420 $subject = sprintf( __( 'New Purchase Order: PO-%s', 'commercebird' ), $order->get_id() );
421 }
422 return $subject;
423 }
424 add_filter( 'woocommerce_email_subject_new_order', 'cmbird_customize_purchase_order_email_subject', 10, 2 );
425
426 function cmbird_customize_purchase_order_email_heading( $heading, $email ) {
427 if ( isset( $email->object ) && is_a( $email->object, 'WC_Order' ) && 'shop_purchase' === $email->object->get_type() ) {
428 $heading = __( 'You Have a New Purchase Order', 'commercebird' );
429 }
430 return $heading;
431 }
432 add_filter( 'woocommerce_email_heading_new_order', 'cmbird_customize_purchase_order_email_heading', 10, 2 );
433
434 function cmbird_remove_order_totals_for_shop_purchase( $totals, $order ) {
435 if ( $order->get_type() === 'shop_purchase' ) {
436 // Remove pricing-related rows
437 unset( $totals['cart_subtotal'] );
438 unset( $totals['discount'] );
439 unset( $totals['shipping'] );
440 unset( $totals['payment_method'] );
441 unset( $totals['order_total'] );
442 }
443 return $totals;
444 }
445 add_filter( 'woocommerce_get_order_item_totals', 'cmbird_remove_order_totals_for_shop_purchase', 10, 2 );
446
447 // function cmbird_remove_item_prices_for_shop_purchase( $items, $order ) {
448 // if ( $order instanceof WC_Order && $order->get_type() === 'shop_purchase' ) {
449 // foreach ( $items as $item_id => $item ) {
450 // // Ensure that only product items are modified
451 // if ( $item instanceof WC_Order_Item_Product ) {
452 // $item->set_subtotal( value: 0 );
453 // $item->set_total( 0 );
454 // }
455 // }
456 // }
457 // return $items;
458 // }
459 // add_filter( 'woocommerce_order_get_items', 'cmbird_remove_item_prices_for_shop_purchase', 10, 2 );
460