diff --git a/assets/css/admin.scss b/assets/css/admin.scss index 8852b3d6..404f745b 100644 --- a/assets/css/admin.scss +++ b/assets/css/admin.scss @@ -283,6 +283,66 @@ a.woocommerce-gzd-input-toggle-trigger { } } +.wc-gzd-modal-preview-shipment { + .wc-backbone-modal-header { + display: flex; + justify-content: space-between; + + .shipment-status { + margin-right: 54px; + } + } + + h2 { + margin-top: 0; + } + + .wc-gzd-preview-shipment-item-list { + margin-left: -1.5em; + width: calc(100% + 3em); + margin-top: 1.5em; + } + + .wc-gzd-preview-shipment-items { + width: 100%; + margin: 0; + border-collapse: collapse; + + th, td { + padding: 1em 1.5em; + border: 0; + border-bottom: 1px solid #eee; + margin: 0; + background: 0 0; + box-shadow: none; + text-align: right; + vertical-align: top; + + &:first-child { + text-align: left; + } + } + + tr { + th { + border-color: #ccc; + } + + &:last-child { + td { + border-bottom: none; + } + } + + &.shipment-item-is-child { + td:first-child { + padding-left: 2.5em; + } + } + } + } +} + .wc-gzd-admin-shipment-modal { .wc-backbone-modal-content { min-width: 600px; @@ -1033,6 +1093,41 @@ a.button.wc-gzd-shipment-action-button { td.column-title { font-weight: bold; + .shipment-preview { + float: right; + width: 16px; + padding: 20px 4px 4px 4px; + height: 0; + overflow: hidden; + position: relative; + border: 2px solid transparent; + border-radius: 4px; + + &:hover { + border: 2px solid var(--wp-admin-theme-color, #00a0d2); + } + + &::before { + font-family: WooCommerce; + speak: never; + font-weight: 400; + font-variant: normal; + text-transform: none; + margin: 0; + text-indent: 0; + position: absolute; + left: 0; + width: 100%; + height: 100%; + text-align: center; + content: "\e010"; + line-height: 16px; + font-size: 14px; + vertical-align: middle; + top: 4px; + } + } + .shipment-title-meta { margin-top: 0; font-weight: normal; @@ -1108,6 +1203,12 @@ a.button.wc-gzd-shipment-action-button { border-bottom: none !important; } } + + &.shipment-item-is-child { + td.wc-gzd-shipment-item-column-name { + padding-left: .5em; + } + } } th, td { @@ -1473,6 +1574,12 @@ a.button.wc-gzd-shipment-action-button { padding: .5em; border-bottom: 1px solid #dfdfdf; + &.shipment-item-is-child { + .shipment-item-name { + padding-left: 2.5em; + } + } + &:last-child { border-bottom: none; } diff --git a/assets/js/static/admin-shipment.js b/assets/js/static/admin-shipment.js index 3c22e7d9..5389c6b7 100644 --- a/assets/js/static/admin-shipment.js +++ b/assets/js/static/admin-shipment.js @@ -467,7 +467,15 @@ window.germanized.admin = window.germanized.admin || {}; if ( $item.length > 0 ) { $item.slideUp( 150, function() { - $( this ).remove(); + if ( $item.hasClass( 'shipment-item-is-parent' ) ) { + $children = $item.parents( '.shipment-item-list' ).find( '.shipment-item-parent-' + data['item_id'] ); + + $children.each( function( $child ) { + $( this ) .remove(); + } ); + } + + $item.remove(); }); } diff --git a/includes/admin/views/html-order-shipment-item.php b/includes/admin/views/html-order-shipment-item.php index b614f35c..828a6baf 100644 --- a/includes/admin/views/html-order-shipment-item.php +++ b/includes/admin/views/html-order-shipment-item.php @@ -12,36 +12,38 @@ defined( 'ABSPATH' ) || exit; ?> -
+
$column ) : ?>
get_name() ); ?> get_sku() ? '(' . esc_html( $item->get_sku() ) . ')' : '' ); ?> - is_readonly() ? 'disabled' : '' ); ?>> get_order_item() ) as $reason ) : ?> - + is_readonly() ? 'readonly' : '' ); ?> data-original-value="get_quantity() ); ?>" value="get_quantity() ); ?>" /> - array( - 'classes' => 'remove-shipment-item', - 'action' => 'delete', - 'name' => _x( 'Delete item', 'shipments', 'woocommerce-germanized-shipments' ), - 'custom_attributes' => array( - 'data-delete' => $item->get_id(), + is_readonly() ) : ?> + array( + 'classes' => 'remove-shipment-item', + 'action' => 'delete', + 'name' => _x( 'Delete item', 'shipments', 'woocommerce-germanized-shipments' ), + 'custom_attributes' => array( + 'data-delete' => $item->get_id(), + ), ), - ), - ) - ); - ?> + ) + ); + ?> + get_order_shipment(); +$total_shipments = $order_shipment ? $order_shipment->get_shipment_count( $shipment->get_type() ) : 1; +$position_number = $order_shipment ? $order_shipment->get_shipment_position_number( $shipment ) : 1; +?> +
+

+ get_type() ) ), esc_html( $position_number ), esc_html( $total_shipments ) ); ?> + get_order() ) && is_callable( array( $s_order, 'get_edit_order_url' ) ) ) : ?> + get_order_number() ) ); ?> + + get_order_number() ) ); ?> + +

+ +
+
+ get_type() ) : ?> +

+
get_formatted_sender_address() ); ?>
+ +

+
get_formatted_address() ); ?>
+ + get_shipping_provider(); + $tracking_id = $shipment->get_tracking_id(); + + if ( ! empty( $provider ) && ! empty( $tracking_id ) ) : + ?> +

+ + + has_tracking() && ( $tracking_url = $shipment->get_tracking_url() ) ) : ?> + + + + +

+ + + +
+
+

+

+ get_packaging() ) : ?> + get_description() ); ?>
+ + + get_formatted_dimensions() ); ?>, get_total_weight(), $shipment->get_weight_unit() ) ); ?> +

+ + +
+ + +
+ + + +
+ + _x( 'Position', 'shipments', 'woocommerce-germanized-shipments' ), + 'quantity' => _x( 'Quantity', 'shipments', 'woocommerce-germanized-shipments' ), + 'dimensions' => _x( 'Dimensions', 'shipments', 'woocommerce-germanized-shipments' ), + 'weight' => _x( 'Weight', 'shipments', 'woocommerce-germanized-shipments' ), + ), + $shipment->get_type() + ); + ?> + + + $column_title ) : ?> + + + + + + get_items() as $item ) : ?> + + $column_title ) : ?> + + + + + +
+ + get_product() ) : ?> + get_name() ); ?> + + get_name() ); ?> + + + get_sku() ? '
' . esc_html_x( 'SKU:', 'shipments', 'woocommerce-germanized-shipments' ) . ' ' . esc_html( $item->get_sku() ) . '' : '' ); ?> + + get_quantity() ); ?>x + + get_dimensions(), $shipment->get_dimension_unit() ) ); ?> + + get_weight(), $shipment->get_weight_unit() ) ); ?> + + + +
+
+
diff --git a/includes/emails/class-wc-gzd-email-customer-guest-return-shipment-request.php b/includes/emails/class-wc-gzd-email-customer-guest-return-shipment-request.php index 25114cb0..c735503e 100644 --- a/includes/emails/class-wc-gzd-email-customer-guest-return-shipment-request.php +++ b/includes/emails/class-wc-gzd-email-customer-guest-return-shipment-request.php @@ -151,7 +151,7 @@ public function trigger( $order_id ) { * @return string */ public function get_additional_content() { - if ( is_callable( 'parent::get_additional_content' ) ) { + if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) { return parent::get_additional_content(); } diff --git a/includes/emails/class-wc-gzd-email-customer-return-shipment-delivered.php b/includes/emails/class-wc-gzd-email-customer-return-shipment-delivered.php index ae2b7b29..35324340 100644 --- a/includes/emails/class-wc-gzd-email-customer-return-shipment-delivered.php +++ b/includes/emails/class-wc-gzd-email-customer-return-shipment-delivered.php @@ -171,7 +171,7 @@ public function trigger( $shipment_id ) { * @return string */ public function get_additional_content() { - if ( is_callable( 'parent::get_additional_content' ) ) { + if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) { return parent::get_additional_content(); } diff --git a/includes/emails/class-wc-gzd-email-customer-return-shipment.php b/includes/emails/class-wc-gzd-email-customer-return-shipment.php index 72c13471..503ad910 100644 --- a/includes/emails/class-wc-gzd-email-customer-return-shipment.php +++ b/includes/emails/class-wc-gzd-email-customer-return-shipment.php @@ -184,7 +184,7 @@ public function trigger( $shipment_id, $is_confirmation = false ) { * @return string */ public function get_additional_content() { - if ( is_callable( 'parent::get_additional_content' ) ) { + if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) { return parent::get_additional_content(); } diff --git a/includes/emails/class-wc-gzd-email-customer-shipment.php b/includes/emails/class-wc-gzd-email-customer-shipment.php index d9612820..5d7246e0 100644 --- a/includes/emails/class-wc-gzd-email-customer-shipment.php +++ b/includes/emails/class-wc-gzd-email-customer-shipment.php @@ -279,7 +279,7 @@ public function trigger( $shipment_id ) { * @return string */ public function get_additional_content() { - if ( is_callable( 'parent::get_additional_content' ) ) { + if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) { return parent::get_additional_content(); } diff --git a/includes/emails/class-wc-gzd-email-new-return-shipment-request.php b/includes/emails/class-wc-gzd-email-new-return-shipment-request.php index 5c73ca24..8a2d42f3 100644 --- a/includes/emails/class-wc-gzd-email-new-return-shipment-request.php +++ b/includes/emails/class-wc-gzd-email-new-return-shipment-request.php @@ -122,7 +122,7 @@ public function trigger( $shipment_id ) { * @return string */ public function get_additional_content() { - if ( is_callable( 'parent::get_additional_content' ) ) { + if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) { return parent::get_additional_content(); } diff --git a/includes/wc-gzd-shipment-functions.php b/includes/wc-gzd-shipment-functions.php index 1e75b8c4..837d7cde 100644 --- a/includes/wc-gzd-shipment-functions.php +++ b/includes/wc-gzd-shipment-functions.php @@ -581,9 +581,7 @@ function wc_gzd_shipment_wp_error_has_errors( $error ) { * @return ShipmentReturnItem|WP_Error */ function wc_gzd_create_return_shipment_item( $shipment, $shipment_item, $args = array() ) { - try { - if ( ! $shipment_item || ! is_a( $shipment_item, '\Vendidero\Germanized\Shipments\ShipmentItem' ) ) { throw new Exception( _x( 'Invalid shipment item', 'shipments', 'woocommerce-germanized-shipments' ) ); } @@ -593,7 +591,6 @@ function wc_gzd_create_return_shipment_item( $shipment, $shipment_item, $args = $item->set_shipment( $shipment ); $item->sync( $args ); $item->save(); - } catch ( Exception $e ) { return new WP_Error( 'error', $e->getMessage() ); } diff --git a/includes/wc-gzd-shipments-template-functions.php b/includes/wc-gzd-shipments-template-functions.php index 11710eba..4445d955 100644 --- a/includes/wc-gzd-shipments-template-functions.php +++ b/includes/wc-gzd-shipments-template-functions.php @@ -5,7 +5,7 @@ * Functions for the templating system. * * @package WooCommerce\Functions - * @version 2.5.0 + * @version 2.6.0 */ use Vendidero\Germanized\Shipments\Shipment; @@ -50,7 +50,7 @@ function wc_gzd_get_email_shipment_items( $shipment, $args = array() ) { 'woocommerce_gzd_email_shipment_items_args', array( 'shipment' => $shipment, - 'items' => $shipment->get_items(), + 'items' => $shipment->get_items( 'customer' ), 'show_sku' => $args['show_sku'], 'show_image' => $args['show_image'], 'image_size' => $args['image_size'], diff --git a/src/Admin/Admin.php b/src/Admin/Admin.php index f07bca90..cacd6964 100644 --- a/src/Admin/Admin.php +++ b/src/Admin/Admin.php @@ -1700,6 +1700,7 @@ public static function admin_scripts() { 'bulk_actions' => $bulk_actions, 'create_shipment_label_load_nonce' => wp_create_nonce( 'create-shipment-label-load' ), 'create_shipment_label_submit_nonce' => wp_create_nonce( 'create-shipment-label-submit' ), + 'preview_shipment_load_nonce' => wp_create_nonce( 'preview-shipment-load' ), ) ); } diff --git a/src/Admin/Table.php b/src/Admin/Table.php index d22b15ff..01989e46 100644 --- a/src/Admin/Table.php +++ b/src/Admin/Table.php @@ -35,8 +35,8 @@ class Table extends WP_List_Table { * * @see WP_List_Table::__construct() for more information on default arguments. * - * @global WP_Post_Type $post_type_object - * @global wpdb $wpdb + * @global \WP_Post_Type $post_type_object + * @global \wpdb $wpdb * * @param array $args An associative array of arguments. */ @@ -74,6 +74,7 @@ protected function get_default_hidden_columns() { 'weight', 'dimensions', 'packaging', + 'items', ); } @@ -830,6 +831,37 @@ public function column_title( $shipment ) { echo wp_kses_post( $title ) . ' '; } + echo '' . esc_html_x( 'Preview', 'shipments', 'woocommerce-germanized-shipments' ) . ''; + + ?> + + '; if ( $packaging = $shipment->get_packaging() ) { @@ -987,7 +1019,7 @@ public function column_items( $shipment ) { get_items() as $item ) : ?> - + -
get_product() ) : ?> get_name() ); ?> @@ -1139,13 +1171,13 @@ public function column_order( $shipment ) { if ( ( $order = $shipment->get_order() ) && is_callable( array( $order, 'get_edit_order_url' ) ) ) { echo '' . esc_html( $order->get_order_number() ) . ''; } else { - echo esc_html( $shipment->get_order_id() ); + echo esc_html( $shipment->get_order_number() ); } } /** * - * @param int|WC_GZD_Shipment $shipment + * @param int|Shipment $shipment */ public function single_row( $shipment ) { $GLOBALS['shipment'] = $shipment; diff --git a/src/Ajax.php b/src/Ajax.php index 855fbe77..23b3c806 100644 --- a/src/Ajax.php +++ b/src/Ajax.php @@ -45,6 +45,7 @@ public static function add_ajax_events() { 'edit_shipping_provider_status', 'create_shipment_label_load', 'create_shipment_label_submit', + 'preview_shipment_load', 'remove_shipment_label', 'send_return_shipment_notification_email', 'confirm_return_request', @@ -170,6 +171,40 @@ public static function confirm_return_request() { } } + public static function preview_shipment_load() { + check_ajax_referer( 'preview-shipment-load', 'security' ); + + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['reference_id'] ) ) { + wp_die( -1 ); + } + + $shipment_id = absint( $_POST['reference_id'] ); + $html = ''; + $response = array(); + $response_error = array( + 'success' => false, + 'message' => '', + ); + + if ( ! $shipment = wc_gzd_get_shipment( $shipment_id ) ) { + wp_send_json( $response_error ); + } + + ob_start(); + include Package::get_path() . '/includes/admin/views/html-preview-shipment.php'; + $html = ob_get_clean(); + + $response = array( + 'fragments' => array( + '.wc-gzd-preview-shipment' => $html, + ), + 'shipment_id' => $shipment_id, + 'success' => true, + ); + + wp_send_json( $response ); + } + public static function create_shipment_label_load() { check_ajax_referer( 'create-shipment-label-load', 'security' ); diff --git a/src/Compatibility/Bundles.php b/src/Compatibility/Bundles.php index 88be3ce0..3ead8dc3 100644 --- a/src/Compatibility/Bundles.php +++ b/src/Compatibility/Bundles.php @@ -61,7 +61,7 @@ public static function apply_bundle_hierarchy( $shipment ) { $parent_id = $map[ $order_item_id ]; foreach ( $shipment_items as $shipment_item ) { - $shipment_item->set_parent_id( $parent_id ); + $shipment_item->set_item_parent_id( $parent_id ); } } } diff --git a/src/DataStores/ShipmentItem.php b/src/DataStores/ShipmentItem.php index a5d1e716..41ea24b2 100644 --- a/src/DataStores/ShipmentItem.php +++ b/src/DataStores/ShipmentItem.php @@ -44,6 +44,7 @@ class ShipmentItem extends WC_Data_Store_WP implements WC_Object_Data_Store_Inte 'name', 'product_id', 'parent_id', + 'item_parent_id', ); protected $must_exist_meta_keys = array(); @@ -69,12 +70,13 @@ public function create( &$item ) { $wpdb->insert( $wpdb->gzd_shipment_items, array( - 'shipment_id' => $item->get_shipment_id(), - 'shipment_item_quantity' => $item->get_quantity(), - 'shipment_item_order_item_id' => $item->get_order_item_id(), - 'shipment_item_product_id' => $item->get_product_id(), - 'shipment_item_parent_id' => $item->get_parent_id(), - 'shipment_item_name' => $item->get_name(), + 'shipment_id' => $item->get_shipment_id(), + 'shipment_item_quantity' => $item->get_quantity(), + 'shipment_item_order_item_id' => $item->get_order_item_id(), + 'shipment_item_product_id' => $item->get_product_id(), + 'shipment_item_parent_id' => $item->get_parent_id(), + 'shipment_item_item_parent_id' => $item->get_item_parent_id(), + 'shipment_item_name' => $item->get_name(), ) ); @@ -112,12 +114,13 @@ public function update( &$item ) { $wpdb->update( $wpdb->gzd_shipment_items, array( - 'shipment_id' => $item->get_shipment_id(), - 'shipment_item_order_item_id' => $item->get_order_item_id(), - 'shipment_item_quantity' => $item->get_quantity(), - 'shipment_item_product_id' => $item->get_product_id(), - 'shipment_item_parent_id' => $item->get_parent_id(), - 'shipment_item_name' => $item->get_name(), + 'shipment_id' => $item->get_shipment_id(), + 'shipment_item_order_item_id' => $item->get_order_item_id(), + 'shipment_item_quantity' => $item->get_quantity(), + 'shipment_item_product_id' => $item->get_product_id(), + 'shipment_item_parent_id' => $item->get_parent_id(), + 'shipment_item_item_parent_id' => $item->get_item_parent_id(), + 'shipment_item_name' => $item->get_name(), ), array( 'shipment_item_id' => $item->get_id() ) ); @@ -207,12 +210,13 @@ public function read( &$item ) { $item->set_props( array( - 'shipment_id' => $data->shipment_id, - 'order_item_id' => $data->shipment_item_order_item_id, - 'quantity' => $data->shipment_item_quantity, - 'product_id' => $data->shipment_item_product_id, - 'parent_id' => $data->shipment_item_parent_id, - 'name' => $data->shipment_item_name, + 'shipment_id' => $data->shipment_id, + 'order_item_id' => $data->shipment_item_order_item_id, + 'quantity' => $data->shipment_item_quantity, + 'product_id' => $data->shipment_item_product_id, + 'parent_id' => $data->shipment_item_parent_id, + 'item_parent_id' => $data->shipment_item_item_parent_id, + 'name' => $data->shipment_item_name, ) ); @@ -325,10 +329,49 @@ public function save_item_data( &$item ) { */ public function clear_cache( &$item ) { wp_cache_delete( 'item-' . $item->get_id(), 'shipment-items' ); + wp_cache_delete( 'item-children-' . $item->get_id(), 'shipment-items' ); wp_cache_delete( 'shipment-items-' . $item->get_shipment_id(), 'shipments' ); wp_cache_delete( $item->get_id(), $this->meta_type . '_meta' ); } + /** + * Read item's children from database. + * + * @param \Vendidero\Germanized\Shipments\ShipmentItem $item The item. + * + * @return array|\Vendidero\Germanized\Shipments\ShipmentItem[] + */ + public function read_children( &$item ) { + // Get from cache if available. + $items = wp_cache_get( 'item-children-' . $item->get_id(), 'shipment-items' ); + + if ( false === $items ) { + global $wpdb; + + $get_items_sql = $wpdb->prepare( "SELECT * FROM {$wpdb->gzd_shipment_items} WHERE shipment_item_item_parent_id = %d ORDER BY shipment_item_id;", $item->get_id() ); + $items = $wpdb->get_results( $get_items_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + + foreach ( $items as $child ) { + wp_cache_set( 'item-' . $item->get_id(), $child, 'shipment-items' ); + } + + wp_cache_set( 'item-children-' . $item->get_id(), $items, 'shipment-items' ); + } + + if ( ! empty( $items ) ) { + $items = array_map( + function( $item ) { + return wc_gzd_get_shipment_item( $item->shipment_item_id, $item->shipment_item_type ); + }, + $items + ); + } else { + $items = array(); + } + + return $items; + } + /** * Table structure is slightly different between meta types, this function will return what we need to know. * diff --git a/src/Install.php b/src/Install.php index 77891c4f..fa23324f 100644 --- a/src/Install.php +++ b/src/Install.php @@ -438,12 +438,14 @@ private static function get_schema() { shipment_item_order_item_id bigint(20) unsigned NOT NULL, shipment_item_product_id bigint(20) unsigned NOT NULL, shipment_item_parent_id bigint(20) unsigned NOT NULL, + shipment_item_item_parent_id bigint(20) unsigned NOT NULL, shipment_item_quantity smallint(4) unsigned NOT NULL DEFAULT '1', PRIMARY KEY (shipment_item_id), KEY shipment_id (shipment_id), KEY shipment_item_order_item_id (shipment_item_order_item_id), KEY shipment_item_product_id (shipment_item_product_id), - KEY shipment_item_parent_id (shipment_item_parent_id) + KEY shipment_item_parent_id (shipment_item_parent_id), + KEY shipment_item_item_parent_id (shipment_item_item_parent_id) ) $collate; CREATE TABLE {$wpdb->prefix}woocommerce_gzd_shipment_itemmeta ( meta_id bigint(20) unsigned NOT NULL auto_increment, diff --git a/src/Order.php b/src/Order.php index 6136b51b..c2283b5b 100644 --- a/src/Order.php +++ b/src/Order.php @@ -508,9 +508,19 @@ public function validate_shipment_item_quantities( $shipment_id = false ) { } /** - * @return Shipment[] Shipments + * @param array $args + * + * @return Shipment[]|SimpleShipment[]|ReturnShipment[] Shipments */ - public function get_shipments() { + public function get_shipments( $args = array() ) { + $args = wp_parse_args( + $args, + array( + 'shipped_only' => false, + 'type' => '', + ) + ); + if ( is_null( $this->shipments ) ) { $this->shipments = wc_gzd_get_shipments( array( @@ -534,41 +544,66 @@ public function get_shipments() { $shipments = (array) $this->shipments; + if ( ! empty( $args['type'] ) || $args['shipped_only'] ) { + foreach ( $shipments as $k => $shipment ) { + if ( $args['type'] !== $shipment->get_type() ) { + unset( $shipments[ $k ] ); + } + if ( $args['shipped_only'] && ! $shipment->is_shipped() ) { + unset( $shipments[ $k ] ); + } + } + + $shipments = array_values( $shipments ); + } + return $shipments; } - /** - * @return SimpleShipment[] - */ - public function get_simple_shipments( $shipped_only = false ) { - $simple = array(); + public function get_shipment_count( $type = 'simple' ) { + return count( $this->get_shipments( array( 'type' => $type ) ) ); + } - foreach ( $this->get_shipments() as $shipment ) { - if ( 'simple' === $shipment->get_type() ) { - if ( $shipped_only && ! $shipment->is_shipped() ) { - continue; - } + public function get_shipment_position_number( $shipment ) { + $number = 1; + $shipment = is_numeric( $shipment ) ? $this->get_shipment( $shipment ) : $shipment; - $simple[] = $shipment; + if ( $shipment ) { + $shipments = $this->get_shipments( array( 'type' => $shipment->get_type() ) ); + + foreach ( $shipments as $k => $loop_shipment ) { + if ( $shipment->get_id() === $loop_shipment->get_id() ) { + break; + } + $number++; } } - return $simple; + return apply_filters( 'woocommerce_gzd_shipment_order_shipment_position_number', $number, $shipment, $this ); } /** - * @return ReturnShipment[] + * @return SimpleShipment[] */ - public function get_return_shipments() { - $returns = array(); - - foreach ( $this->get_shipments() as $shipment ) { - if ( 'return' === $shipment->get_type() ) { - $returns[] = $shipment; - } - } + public function get_simple_shipments( $shipped_only = false ) { + return $this->get_shipments( + array( + 'type' => 'simple', + 'shipped_only' => $shipped_only, + ) + ); + } - return $returns; + /** + * @return ReturnShipment[] + */ + public function get_return_shipments( $shipped_only = false ) { + return $this->get_shipments( + array( + 'type' => 'return', + 'shipped_only' => $shipped_only, + ) + ); } public function add_shipment( &$shipment ) { @@ -882,13 +917,14 @@ public function get_available_items_for_return( $args = array() ) { 'shipment_id' => 0, 'delivered_only' => false, 'exclude_current_shipment' => false, + 'exclude_children' => true, ) ); $items = array(); $shipment = $args['shipment_id'] ? $this->get_shipment( $args['shipment_id'] ) : false; - foreach ( $this->get_returnable_items() as $item ) { + foreach ( $this->get_returnable_items( $args['exclude_children'] ) as $item ) { $quantity_left = $this->get_item_quantity_left_for_returning( $item->get_order_item_id(), $args ); if ( $shipment ) { @@ -1032,7 +1068,7 @@ public function get_shippable_items() { * * @return ShipmentItem[] Shippable items. */ - public function get_returnable_items() { + public function get_returnable_items( $exclude_children = true ) { $items = array(); foreach ( $this->get_simple_shipments() as $shipment ) { @@ -1041,7 +1077,7 @@ public function get_returnable_items() { } foreach ( $shipment->get_items() as $item ) { - if ( $this->order_item_is_non_returnable( $item->get_order_item_id() ) ) { + if ( $this->order_item_is_non_returnable( $item->get_order_item_id() ) || ( $exclude_children && $item->get_item_parent_id() > 0 ) ) { continue; } @@ -1123,7 +1159,7 @@ public function get_shippable_item_count() { public function get_returnable_item_count() { $count = 0; - foreach ( $this->get_returnable_items() as $item ) { + foreach ( $this->get_returnable_items( false ) as $item ) { $count += absint( $item->get_quantity() ); } diff --git a/src/Rest/ShipmentsController.php b/src/Rest/ShipmentsController.php index 4c4b4797..5ceeab7e 100644 --- a/src/Rest/ShipmentsController.php +++ b/src/Rest/ShipmentsController.php @@ -665,6 +665,8 @@ protected function set_item( $shipment, $posted ) { 'customs_description', 'manufacture_country', 'return_reason_code', + 'parent_id', + 'item_parent_id', ); foreach ( $props_to_set as $prop ) { @@ -1178,6 +1180,8 @@ public static function prepare_shipment( $shipment, $context = 'view', $dp = fal 'hs_code' => $item->get_hs_code( $context ), 'customs_description' => $item->get_customs_description( $context ), 'manufacture_country' => $item->get_manufacture_country( $context ), + 'parent_id' => $item->get_parent_id( $context ), + 'item_parent_id' => $item->get_item_parent_id( $context ), 'attributes' => $item->get_attributes( $context ), 'meta_data' => $item->get_meta_data(), ); @@ -1810,6 +1814,16 @@ public static function get_single_item_schema() { 'type' => 'string', 'context' => array( 'view', 'edit' ), ), + 'parent_id' => array( + 'description' => _x( 'Item parent id, e.g. the return item\'s parent.', 'shipments', 'woocommerce-germanized-shipments' ), + 'type' => 'string', + 'context' => array( 'view', 'edit' ), + ), + 'item_parent_id' => array( + 'description' => _x( 'The parent item id inside the current shipment.', 'shipments', 'woocommerce-germanized-shipments' ), + 'type' => 'string', + 'context' => array( 'view', 'edit' ), + ), 'manufacture_country' => array( 'description' => _x( 'Item country of manufacture in ISO 3166-1 alpha-2 format.', 'shipments', 'woocommerce-germanized-shipments' ), 'type' => 'string', diff --git a/src/ReturnShipment.php b/src/ReturnShipment.php index 6f23fcd3..53d52356 100644 --- a/src/ReturnShipment.php +++ b/src/ReturnShipment.php @@ -340,7 +340,6 @@ public function sync( $args = array() ) { */ public function sync_items( $args = array() ) { try { - if ( ! $order_shipment = $this->get_order_shipment() ) { throw new Exception( _x( 'Invalid shipment order', 'shipments', 'woocommerce-germanized-shipments' ) ); } @@ -362,7 +361,6 @@ public function sync_items( $args = array() ) { ); foreach ( $available_items as $order_item_id => $item_data ) { - if ( $item = $order_shipment->get_simple_shipment_item( $order_item_id ) ) { $quantity = $item_data['max_quantity']; $return_reason_code = ''; @@ -415,11 +413,30 @@ public function sync_items( $args = array() ) { } else { $shipment_item->sync( $sync_data ); } + + if ( $item->has_children() ) { + $children = $item->get_children(); + + foreach ( $children as $child_item ) { + $child_order_item_id = $child_item->get_order_item_id(); + $sync_child_data = array( + 'quantity' => $child_item->get_quantity(), + 'item_parent_id' => $shipment_item->get_id(), + ); + + if ( ! $shipment_child_item = $this->get_item_by_order_item_id( $child_order_item_id ) ) { + $shipment_child_item = wc_gzd_create_return_shipment_item( $this, $child_item, $sync_child_data ); + + $this->add_item( $shipment_child_item ); + } else { + $shipment_child_item->sync( $sync_child_data ); + } + } + } } } foreach ( $this->get_items() as $item ) { - // Remove non-existent items if ( ! $shipment_item = $order_shipment->get_simple_shipment_item( $item->get_order_item_id() ) ) { $this->remove_item( $item->get_id() ); diff --git a/src/Shipment.php b/src/Shipment.php index fa01f778..1ea046c3 100644 --- a/src/Shipment.php +++ b/src/Shipment.php @@ -2128,17 +2128,34 @@ public function update_packaging() { * * @return ShipmentItem[] */ - public function get_items() { + public function get_items( $context = 'admin' ) { $items = array(); if ( is_null( $this->items ) ) { $this->items = array_filter( $this->data_store->read_items( $this ) ); + foreach ( $this->items as $k => $item ) { + if ( $item->get_item_parent_id() > 0 && isset( $this->items[ $item->get_item_parent_id() ] ) ) { + $this->items[ $item->get_item_parent_id() ]->add_child( $item ); + } + } + $items = (array) $this->items; } else { $items = (array) $this->items; } + /** + * By default, remove children in customer context. + */ + if ( 'customer' === $context ) { + foreach ( $items as $k => $item ) { + if ( $item->get_item_parent_id() > 0 ) { + unset( $items[ $k ] ); + } + } + } + /** * Filter to adjust items belonging to a Shipment. * @@ -2153,7 +2170,7 @@ public function get_items() { * @since 3.0.0 * @package Vendidero/Germanized/Shipments */ - return apply_filters( "{$this->get_hook_prefix()}items", $items, $this ); + return apply_filters( "{$this->get_hook_prefix()}items", $items, $this, $context ); } /** @@ -2213,6 +2230,19 @@ public function remove_item( $item_id ) { unset( $this->items[ $item->get_id() ] ); + if ( $item->has_children() ) { + foreach ( $item->get_children() as $child ) { + // Unset and remove later. + $this->items_to_delete[] = $child; + + if ( isset( $this->items[ $child->get_id() ] ) ) { + unset( $this->items[ $child->get_id() ] ); + } + + $item->remove_child( $child->get_id() ); + } + } + $this->reset_content_data(); $this->calculate_totals(); $this->sync_packaging(); diff --git a/src/ShipmentItem.php b/src/ShipmentItem.php index 049243d0..f9125766 100644 --- a/src/ShipmentItem.php +++ b/src/ShipmentItem.php @@ -20,6 +20,10 @@ class ShipmentItem extends WC_Data { protected $product = null; + protected $children = null; + + protected $parent = null; + /** * Order Data array. This is the core order data exposed in APIs since 3.0.0. * @@ -30,6 +34,7 @@ class ShipmentItem extends WC_Data { 'shipment_id' => 0, 'order_item_id' => 0, 'parent_id' => 0, + 'item_parent_id' => 0, 'quantity' => 1, 'product_id' => 0, 'weight' => '', @@ -126,6 +131,10 @@ public function apply_changes() { |-------------------------------------------------------------------------- */ + public function get_general_hook_prefix() { + return 'woocommerce_' . $this->object_type . '_'; + } + public function get_type() { return 'simple'; } @@ -161,7 +170,7 @@ public function get_product_id( $context = 'view' ) { } /** - * Get item parent id. + * Get parent id, e.g. the item linked to a return item. * * @param string $context What the value is for. Valid values are 'view' and 'edit'. * @return int @@ -170,6 +179,16 @@ public function get_parent_id( $context = 'view' ) { return $this->get_prop( 'parent_id', $context ); } + /** + * Get item parent id inside the current shipment. + * + * @param string $context What the value is for. Valid values are 'view' and 'edit'. + * @return int + */ + public function get_item_parent_id( $context = 'view' ) { + return $this->get_prop( 'item_parent_id', $context ); + } + /** * Get order ID this meta belongs to. * @@ -360,6 +379,7 @@ public function sync( $args = array() ) { unset( $default_data['id'] ); unset( $default_data['shipment_id'] ); + unset( $default_data['item_parent_id'] ); $default_data['parent_id'] = $item->get_id(); $args = wp_parse_args( $args, $default_data ); @@ -430,6 +450,7 @@ public function sync( $args = array() ) { 'customs_description' => $product ? $product->get_customs_description() : '', 'manufacture_country' => $product ? $product->get_manufacture_country() : '', 'attributes' => $attributes, + 'item_parent_id' => 0, ) ); } @@ -487,6 +508,7 @@ public function get_product() { public function set_shipment_id( $value ) { $this->order_item = null; $this->shipment = null; + $this->children = null; $this->set_prop( 'shipment_id', absint( $value ) ); } @@ -549,6 +571,92 @@ public function set_parent_id( $value ) { $this->set_prop( 'parent_id', absint( $value ) ); } + /** + * Set item parent id. + * + * @param int $value parent id. + */ + public function set_item_parent_id( $value ) { + $this->set_prop( 'item_parent_id', absint( $value ) ); + } + + /** + * @param ShipmentItem $item + * + * @return void + */ + public function add_child( $item, $key = '' ) { + $key = empty( $key ) ? $item->get_id() : $key; + $children = is_null( $this->children ) ? array() : $this->children; + + $children[ $key ] = $item; + $this->children = $children; + } + + public function remove_child( $key ) { + if ( ! is_null( $this->children ) ) { + if ( isset( $this->children[ $key ] ) ) { + unset( $this->children[ $key ] ); + } + } + } + + /** + * @return ShipmentItem|ShipmentReturnItem|null + */ + public function get_parent() { + if ( ! $this->get_item_parent_id() ) { + return null; + } + + if ( is_null( $this->parent ) ) { + if ( $this->get_shipment() ) { + if ( $item = $this->get_shipment()->get_item( $this->get_item_parent_id() ) ) { + $this->parent = $item; + } + } elseif ( $this->get_id() > 0 ) { + if ( $item = wc_gzd_get_shipment_item( $this->get_item_parent_id() ) ) { + $this->parent = $item; + } + } + } + + return $this->parent; + } + + /** + * @return ShipmentItem[] + */ + public function get_children() { + if ( is_null( $this->children ) ) { + $this->children = array(); + + if ( $this->get_shipment() ) { + $items = $this->get_shipment()->get_items(); + + foreach ( $items as $k => $item ) { + if ( empty( $item->get_item_parent_id() ) ) { + continue; + } + + if ( $item->get_item_parent_id() === $this->get_id() ) { + $this->children[ $k ] = $item; + } + } + } elseif ( $this->get_id() > 0 ) { + $this->children = $this->data_store->read_children( $this ); + } + } + + return $this->children; + } + + public function has_children() { + $children = $this->get_children(); + + return ! empty( $children ) ? true : false; + } + public function set_sku( $sku ) { $this->set_prop( 'sku', $sku ); } @@ -631,4 +739,8 @@ public function set_attributes( $attributes ) { | Other Methods |-------------------------------------------------------------------------- */ + + public function is_readonly() { + return apply_filters( "{$this->get_general_hook_prefix()}is_readonly", $this->get_item_parent_id() > 0 ? true : false, $this ); + } } diff --git a/src/ShipmentReturnItem.php b/src/ShipmentReturnItem.php index 14bba66d..cd12bf85 100644 --- a/src/ShipmentReturnItem.php +++ b/src/ShipmentReturnItem.php @@ -23,7 +23,13 @@ public function get_type() { } public function get_return_reason_code( $context = 'view' ) { - return $this->get_prop( 'return_reason_code', $context ); + $reason_code = $this->get_prop( 'return_reason_code', $context ); + + if ( 'view' === $context && ( $parent = $this->get_parent() ) ) { + $reason_code = $parent->get_return_reason_code(); + } + + return $reason_code; } public function set_return_reason_code( $code ) { diff --git a/src/ShippingProvider/Auto.php b/src/ShippingProvider/Auto.php index c293cc78..3cb91727 100644 --- a/src/ShippingProvider/Auto.php +++ b/src/ShippingProvider/Auto.php @@ -476,6 +476,7 @@ public function get_pickup_locations( $address, $query_args = array() ) { $cache_key = $this->get_pickup_locations_cache_key( $address, $query_args['limit'] ); $pickup_locations = get_transient( $cache_key ); $address = $this->parse_pickup_location_address_args( $address ); + $pickup_locations = false; if ( false === $pickup_locations && ( ! empty( $address['postcode'] ) || ! empty( $address['city'] ) ) ) { $pickup_locations = $this->fetch_pickup_locations( $address, $query_args ); diff --git a/templates/myaccount/view-shipment.php b/templates/myaccount/view-shipment.php index 4c7cf57f..ac1bbdd5 100644 --- a/templates/myaccount/view-shipment.php +++ b/templates/myaccount/view-shipment.php @@ -13,7 +13,7 @@ * the readme will list any important changes. * * @package Vendidero/Germanized/Shipments/Templates - * @version 1.0.0 + * @version 2.0.0 */ defined( 'ABSPATH' ) || exit; ?> @@ -22,7 +22,7 @@ printf( /* translators: 1: order number 2: order date 3: order status */ esc_html_x( 'Shipment #%1$s was created on %2$s and is currently %3$s.', 'shipments', 'woocommerce-germanized-shipments' ), - '' . $shipment->get_shipment_number() . '', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + '' . $shipment->get_shipment_number() . '', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped '' . wc_format_datetime( $shipment->get_date_created() ) . '', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped '' . wc_gzd_get_shipment_status_name( $shipment->get_status() ) . '' // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); diff --git a/templates/shipment/shipment-details-item.php b/templates/shipment/shipment-details-item.php index cee0cfd4..60902b56 100644 --- a/templates/shipment/shipment-details-item.php +++ b/templates/shipment/shipment-details-item.php @@ -28,7 +28,7 @@ * @param boolean $show Whether to show or hide the item. * @param ShipmentItem $item The shipment item instance. * - * @since 3.0.0 + * @since 3.1.0 * @package Vendidero/Germanized/Shipments */ if ( ! apply_filters( 'woocommerce_gzd_shipment_item_visible', true, $item ) ) { @@ -45,10 +45,9 @@ * @since 3.0.1 * @package Vendidero/Germanized/Shipments */ -$item_class = apply_filters( 'woocommerce_gzd_shipment_item_class', 'woocommerce-table__line-item shipment_item', $item, $shipment ); +$item_class = apply_filters( 'woocommerce_gzd_shipment_item_class', 'woocommerce-table__line-item shipment_item ' . ( $item->get_item_parent_id() > 0 ? 'shipment_item-is-child' : '' ) . ( $item->has_children() ? 'shipment_item-is-parent' : '' ), $item, $shipment ); ?>
is_visible(); diff --git a/templates/shipment/shipment-details.php b/templates/shipment/shipment-details.php index eeef73ad..67232813 100644 --- a/templates/shipment/shipment-details.php +++ b/templates/shipment/shipment-details.php @@ -12,7 +12,7 @@ * * @see https://github.com/vendidero/woocommerce-germanized/wiki/Overriding-Germanized-Templates * @package Vendidero/Germanized/Shipments/Templates - * @version 3.2.0 + * @version 3.3.0 */ use Vendidero\Germanized\Shipments\Shipment; @@ -27,7 +27,7 @@ $order = $shipment->get_order(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $show_receiver_details = is_user_logged_in() && $order && $order->get_user_id() === get_current_user_id(); $show_tracking = $show_receiver_details && $shipment->has_tracking(); -$shipment_items = $shipment->get_items(); +$shipment_items = $shipment->get_items( 'customer' ); if ( is_a( $shipment, 'Vendidero\Germanized\Shipments\ReturnShipment' ) ) { if ( $shipment->hide_return_address() ) { @@ -51,14 +51,12 @@

- -