�PNG  IHDR22?�� pHYs  �� OiCCPPhotoshop ICC profilexڝSgTS�=���BK���KoR RB���&*! J�!��Q�EEȠ�����Q, � ��!���������{�kּ������>���� �H3Q5� �B�������.@� $p�d!s�#�~<<+"��x� �M��0���B�\���t�8K�@z�B�@F���&S�`�cb�P-`'������{[�!�� e�Dh;��V�EX0fK�9�-0IWfH���� � 0Q��){`�##x��F�W<�+��*x��<�$9E�[-qWW.(�I+6aa�@.�y�2�4���������x����6��_-��"bb���ϫp@�t~��,/��;�m��%�h^ �u��f�@����W�p�~<�5�j>{�-�]c�K'Xt����o��(�h���w��?�G�%�fI�q^D$.Tʳ?�D��*�A��,���� �`6�B$��BB d�r`)��B(�Ͱ*`/�@4�Qh��p.�U�=p�a��(�� A�a!ڈb�X#����!�H�$ ɈQ"K�5H1R�T UH�=r9�\F��;�2����G1���Q=� �C��7�F� �dt1�����r�=�6��Ыhڏ>C�0��3�l0.��B�8, �c˱"� ���V����cϱw�E� 6wB aAHXLXN�H� $4� 7 �Q�'"��K�&���b21�XH,#��/{�C�7$�C2'��I��T��F�nR#�,��4H#���dk�9�, +ȅ����3��!�[ �b@q��S�(R�jJ��4�e�2AU��Rݨ�T5�ZB���R�Q��4u�9̓IK�����hh�i��t�ݕN��W���G���w ��Ljg(�gw��L�Ӌ�T071���oUX*�*|�� �J�&�*/T����ު U�U�T��^S}�FU3S� Ԗ�U��P�SSg�;���g�oT?�~Y��Y�L�OC�Q��_�� c�x,!k ��u�5�&���|v*�����=���9C3J3W�R�f?�q��tN �(���~���)�)�4L�1e\k����X�H�Q�G�6������E�Y��A�J'\'Gg����S�Sݧ �M=:��.�k���Dw�n��^��Lo��y���}/�T�m���G X� $� �<�5qo</���QC]�@C�a�a�ᄑ��<��F�F�i�\�$�m�mƣ&&!&KM�M�RM��)�;L;L���͢�֙5�=1�2��כ߷`ZxZ,����eI��Z�Yn�Z9Y�XUZ]�F���%ֻ�����N�N���gð�ɶ�����ۮ�m�}agbg�Ů��}�}��= ���Z~s�r:V:ޚΜ�?}����/gX���3��)�i�S��Ggg�s�󈋉K��.�>.���Ƚ�Jt�q]�z���������ۯ�6�i�ܟ�4�)�Y3s���C�Q��? ��0k߬~OCO�g��#/c/�W�װ��w��a�>�>r��>�<7�2�Y_�7��ȷ�O�o�_��C#�d�z����%g��A�[��z|!��?:�e����A���AA�������!h�쐭!��Α�i�P~���a�a��~ '���W�?�p�X�1�5w��Cs�D�D�Dޛg1O9�-J5*>�.j<�7�4�?�.fY��X�XIlK9.*�6nl������� �{�/�]py�����.,:�@L�N8��A*��%�w%� y��g"/�6ш�C\*N�H*Mz�쑼5y$�3�,幄'���L Lݛ:��v m2=:�1����qB�!M��g�g�fvˬe����n��/��k���Y- �B��TZ(�*�geWf�͉�9���+��̳�ې7�����ᒶ��KW-X潬j9�������(�x��oʿ�ܔ���Ĺd�f�f���-�[����n �ڴ �V����E�/��(ۻ��C���<��e����;?T�T�T�T6��ݵa��n��{��4���[���>ɾ�UUM�f�e�I���?�������m]�Nmq����#�׹���=TR��+�G�����w- 6 U����#pDy��� �� :�v�{���vg/jB��F�S��[b[�O�>����z�G��499�?r����C�d�&����ˮ/~�����јѡ�򗓿m|������������x31^�V���w�w��O�| (�h���SЧ��������c3-� cHRMz%������u0�`:�o�_�F5IDATx��ytUս�?�;���fN$$��@B�!�k��� -*�gE���j�O�kQ�ʠ`U�B_ �AÐ0%�Ȕ�@r3ޛ;߳��$�@��v����^�s����������$��ߢ !�W��5�̱'-٘������4�>�Rƫ$�G��0U�H}�_ _� #��k�MJ/=�9Du *ڲ�k�PQ;0��j|�*�$�^컂脛y���>�z×� B��� ��(�:$�ʔ_�6��C!B����MVx��a��ζyY����j��ƋY2�9�F�{r��r�딟F��Q��hY6��� x��[l��RC��������nt@��;P��3��� F�w��7��:�P���ɋ'���mj���Bƪ�$�Q�L��y��߷g��\���z����v5�p�v$�v櫻�`6~K �Y�X����,�:��"�o�e0II"(��,������l_�C���/4�+f�E�!q��:����u����c��樼.UE�@� �]K�W���$-�h]�JG���Dz)�`��.�A/ ���mᏣc�3jK� �!�>���$�Q�!�_�? \�C2(E �M$Z I{�U���s�ǣ���=��@Բe ޻��W�����@ �w����}�9v,)��.}*|"�s��di���,���G�]W�AC�������|<����[��ߕ������K�*�G nߴ!��‹��J�!��6(L�a)��Am��������;adf��:�xm��nh�;֦I��(���o�C~ډa?�=��s����h��E������xp����)���_���t��W�����´;8���`dz=(��@@�+,�;�`�fhl��XȌ�3,uI�~z(J����� �ZX�A����H'�À�㜦�82|��a�n�z2��I���� ���EY�h�ε<�0�q�{��C��v�˅��?"@��χ�x�WS+��N�~�KQ�J�8ل�� _.W��� !TU{�����,��"�� �;�������HI S� ��))�o�1�H�D� ���� (� ;����"Ҕ��,���� �F=���O��&�Ph>�`���y��߲ &S��P�<��'��1s�1cĂ�N����2)����@�u��tv��<�+�-&�����h��[!FSG���' � z z0��<����8d&�����E0�,��<��"!P�b4��� �{S�����X����.�囤���T��hEF��X�q0"-<����h���]�Py*����F⩍��@v�˂�Y08�� .q��a��s����(!<̫1�3�L�H��7sYW�`��qtu�\7��?�@�Y�� %Ka�x�GZ�*W�P��y�����N0r9��Z�c�%���q,��k�����!!,��>���IB��� �x1�Xu�Y���v-���ֺ��N������JP��a�2`�e5��w��u;�6�i���̃��(K���ťa��(l51��JL��O'%���]���ٖ뿉!��{*�p+f ���i8��D�ȑ���ܩ�M��V��ӟz��x�7Z�1i}�^͜���6e����x��)u�d�ˏ�V�eG�\�X1�����=n�U'�~Y����L�\,�(d!x ,�:�TW8� M`r���@��r+v�Q0��>^;˴�VbF��('����� �*O��i��j�Ӆ����^�S3&� ���{������.ی�� N;��NU%d0�Ce\r"�C�b��&t7N�7��� �ȪD�[%l�۪�$��'m?�A9tI�9>��NVL ��� 6v~M�����ъ�B���0����9$&=ώm��W�0���L���&��E���f�f��'+�9��hv��7,���M��6�D�&�^� �����8 D֮�����o��k^z��O��hP�8q�5d*��YVq3�{��������#LX4�?��jP=3�~�؅c�ݰ�̱�ұ��8̲��2�G�����j��έ�9��w7���U�X�w�85C��?!I&���o&����L���I#�^�eV�,�^t5�k� �xs˛\��U�8��."���{���ǵ�l4��ݼ�;��r"���G�/~���gJV�N糦��ZZ^k+NN�D��I$�=OE�z����|*�*{��%k���G��>�`9��b;�s���8,�qs��c��.b��q�%�>r��n�R�Ƨ�%�,�A��le#�8���}; 21�0�b�&�QX�f���q�00����$>�h�N�'�a3��A���/�r ��L5�8�GԲ'�����w;��c#�q1���h$*��V^�k��QĽ�b:i������� ����H F#�_���,��]g����Ը����`RUU���� ��"�qD���>-��ϤI�~��3�̴�����NZ[[555�eeek7�m����YF#[��Đ��R��i��L�H�r ��@��J-�Pŝ��_|�-��6y��ks�V�U~��6���w.����(��0A� 4��f��� /���C�xŊ�ٳ����z=6�����rg���mKo��o�/����a�g���?'9%9��P��斓��~���L{�� �H��{AW��B��$����v�1b�&Mb�ر��ر#�0`�`xMBp! )Z�Nmo�mZ�g�}�eذaWTVV ���'99��C5��&�&+5�bT>|8��PUUETT����&���t��~TE%��'������b�tEGG;RSS�S�N�v��ѯ��t����u1@�ޚ"f��͈���r'L�Ph��m��ƌ�74o��`�x�q�P���NRSS�X,�����l&&&��MFFA!-��`��ҷ�n��Yuu�����x<I�LB%����KԒ50�!�3�bz�����ʸKL���;---�l6[��`��������Ȏ;�����egg4��!D�T��jWujyZ��������K����%����p�^���X,6��sV=�ܖ����6�3�X ���4ҿ��A2�&����_k�;d�� �VfOIEND�B`�
Warning: session_start(): Session cannot be started after headers have already been sent in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 124

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 130

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 131

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 132

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 133

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 134

Warning: Cannot modify header information - headers already sent by (output started at /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php:1) in /home/mciraet/www/wp-includes/php-ai-client/src/Providers/Enums/Documentsoliders.php on line 135
0x0x
PHP 8.2.31
Preview: FulfillmentUtils.php Size: 22.99 KB
/home/m/c/i/mciraet/www/wp-content/plugins/woocommerce/src/Admin/Features/Fulfillments/FulfillmentUtils.php
<?php declare(strict_types=1);

namespace Automattic\WooCommerce\Admin\Features\Fulfillments;

use Automattic\WooCommerce\Admin\Features\Fulfillments\Providers\AbstractShippingProvider;
use WC_Order;

/**
 * Class FulfillmentUtils
 *
 * Utility class for handling order fulfillments.
 */
class FulfillmentUtils {

	/**
	 * Get pending items for an order.
	 *
	 * @param WC_Order $order The order object.
	 * @param array    $fulfillments An array of fulfillments to check.
	 * @param bool     $without_refunds Whether to exclude refunded items from the pending items.
	 *
	 * @return array An array of pending items.
	 */
	public static function get_pending_items( WC_Order $order, $fulfillments, $without_refunds = true ): array {
		$items_in_fulfillments = self::get_all_items_of_fulfillments( $fulfillments );
		$order_items           = array_map(
			function ( $item ) use ( $order, $without_refunds ) {
				// Refunded item quantities are saved as negative values in the order.
				return array(
					'item_id' => $item->get_id(),
					'item'    => $item,
					'qty'     => $item->get_quantity() + ( $without_refunds ? $order->get_qty_refunded_for_item( $item->get_id() ) : 0 ),
				);
			},
			$order->get_items() ?? array()
		);

		// If there are items in fulfillments, subtract their quantities from the order items.
		if ( ! empty( $items_in_fulfillments ) ) {
			foreach ( $order_items as $item_id => &$item ) {
				if ( isset( $items_in_fulfillments[ $item_id ] ) ) {
					$item['qty'] = $item['qty'] - $items_in_fulfillments[ $item_id ];
				}
			}
		}

		return array_filter(
			$order_items,
			function ( $item ) {
				return $item['qty'] > 0; // Only return items with a positive quantity.
			}
		);
	}

	/**
	 * Get refunded items for an order.
	 *
	 * @param WC_Order $order The order object.
	 *
	 * @return array An array of refunded items with their IDs and quantities.
	 */
	public static function get_refunded_items( WC_Order $order ): array {
		$items_refunded = array();
		foreach ( $order->get_items() as $item ) {
			$items_refunded[ $item->get_id() ] = -1 * $order->get_qty_refunded_for_item( $item->get_id() );
		}
		return array_filter(
			$items_refunded,
			function ( $qty ) {
				return $qty > 0; // Only include items that have been refunded.
			}
		);
	}

	/**
	 * Get order items for a fulfillment.
	 *
	 * @param WC_Order    $order The order object.
	 * @param Fulfillment $fulfillment The fulfillment object.
	 *
	 * @return array An array of order items.
	 */
	public static function get_fulfillment_items( WC_Order $order, Fulfillment $fulfillment ): array {
		$fulfillment_items = array_combine(
			array_column( $fulfillment->get_items(), 'item_id' ),
			array_column( $fulfillment->get_items(), 'qty' )
		);

		$order_items = array_map(
			function ( $item ) use ( $order ) {
				return array(
					'item_id' => $item->get_id(),
					'item'    => $item,
					'qty'     => $item->get_quantity() - $order->get_qty_refunded_for_item( $item ),
				);
			},
			$order->get_items()
		);

		return array_map(
			function ( $item ) use ( $fulfillment_items ) {
				$item['qty'] = $fulfillment_items[ $item['item_id'] ];
				return $item;
			},
			array_filter(
				$order_items,
				function ( $item ) use ( $fulfillment_items ) {
					return isset( $fulfillment_items[ $item['item_id'] ] );
				}
			)
		);
	}

	/**
	 * Check if an order has pending items.
	 *
	 * @param WC_Order $order The order object.
	 * @param array    $fulfillments An array of fulfillments to check.
	 *
	 * @return bool True if there are pending items, false otherwise.
	 */
	public static function has_pending_items( WC_Order $order, array $fulfillments ): bool {
		$pending_items = self::get_pending_items( $order, $fulfillments );
		return ! empty( $pending_items );
	}

	/**
	 * Get the fulfillment status of the entity. This runs like a computed property, where
	 * it checks the fulfillment status of each fulfillment attached to the order,
	 * and computes the overall fulfillment status of the order.
	 *
	 * @param WC_Order $order The order object.
	 * @param array    $fulfillments An array of fulfillments to check.
	 *
	 * @return string The fulfillment status.
	 */
	public static function calculate_order_fulfillment_status( WC_Order $order, $fulfillments = array() ): string {
		$has_fulfillments = ! empty( $fulfillments );
		if ( $has_fulfillments ) {
			$pending_items = self::get_pending_items( $order, $fulfillments );

			$all_fulfilled  = true;
			$some_fulfilled = false;

			foreach ( $fulfillments as $fulfillment ) {
				if ( ! $fulfillment->get_is_fulfilled() ) {
					$all_fulfilled = false;
				} else {
					$some_fulfilled = true;
				}
			}

			if ( $all_fulfilled && empty( $pending_items ) ) {
				$status = 'fulfilled';
			} elseif ( $some_fulfilled ) {
				$status = 'partially_fulfilled';
			} else {
				$status = 'unfulfilled';
			}
		} else {
			$status = 'no_fulfillments';
		}

		/**
		 * This filter allows plugins to modify the fulfillment status of an order.
		 *
		 * @since 10.1.0
		 *
		 * @param string $status The default fulfillment status.
		 * @param WC_Order $order The order object.
		 * @param array $fulfillments An array of fulfillments for the order.
		 */
		return apply_filters(
			'woocommerce_fulfillment_calculate_order_fulfillment_status',
			$status,
			$order,
			$fulfillments
		);
	}

	/**
	 * Get all items from the fulfillments.
	 *
	 * @param array $fulfillments An array of fulfillments.
	 *
	 * @return array An associative array of item IDs and their quantities.
	 */
	public static function get_all_items_of_fulfillments( array $fulfillments ): array {
		$items = array();
		foreach ( $fulfillments as $fulfillment ) {
			$fulfillment_items = $fulfillment->get_items();
			foreach ( $fulfillment_items as $item ) {
				if ( ! isset( $items[ $item['item_id'] ] ) ) {
					$items[ $item['item_id'] ] = 0; // Initialize if not set.
				}
				// Sum the quantities for each item.
				$items[ $item['item_id'] ] += $item['qty'];
			}
		}
		return $items;
	}

	/**
	 * Get the HTML for the fulfillment tracking number.
	 *
	 * @param Fulfillment $fulfillment The fulfillment object.
	 *
	 * @return string The HTML for the tracking number.
	 */
	public static function get_tracking_info_html( Fulfillment $fulfillment ): string {
		$tracking_html   = '';
		$tracking_url    = $fulfillment->get_tracking_url();
		$tracking_number = $fulfillment->get_tracking_number();
		if ( null !== $tracking_url && null !== $tracking_number ) {
			$tracking_html .= '<a href="' . esc_url( $tracking_url ) . '" target="_blank" rel="noopener noreferrer">';
			$tracking_html .= esc_html( $tracking_number );
			$tracking_html .= '</a>';
		} elseif ( ! empty( $tracking_number ) ) {
			$tracking_html .= esc_html( $tracking_number );
		} else {
			$tracking_html .= '<span class="no-tracking">' . esc_html__( 'No tracking number available', 'woocommerce' ) . '</span>';
		}
		return $tracking_html;
	}

	/**
	 * Get the fulfillment status of an order.
	 *
	 * @param WC_Order $order The order object.
	 * @return string The fulfillment status.
	 */
	public static function get_order_fulfillment_status( WC_Order $order ): string {
		if ( ! $order instanceof WC_Order ) {
			return 'no_fulfillments';
		}

		return $order->meta_exists( '_fulfillment_status' ) ? $order->get_meta( '_fulfillment_status', true ) : 'no_fulfillments';
	}

	/**
	 * Get the fulfillment status text for an order.
	 *
	 * @param WC_Order $order The order object.
	 *
	 * @return string The fulfillment status text.
	 */
	public static function get_order_fulfillment_status_text( WC_Order $order ): string {
		// Ensure the order is a valid WC_Order object.
		if ( ! $order instanceof WC_Order ) {
			return '';
		}

		// Check if the order meta exists for fulfillment status.
		$fulfillment_status      = self::get_order_fulfillment_status( $order );
		$fulfillment_status_text = '';
		switch ( $fulfillment_status ) {
			case 'fulfilled':
				$fulfillment_status_text = ' ' . __( 'It has been <mark class="fulfillment-status">Fulfilled</mark>.', 'woocommerce' );
				break;
			case 'partially_fulfilled':
				$fulfillment_status_text = ' ' . __( 'It has been <mark class="fulfillment-status">Partially fulfilled</mark>.', 'woocommerce' );
				break;
			case 'unfulfilled':
				$fulfillment_status_text = ' ' . __( 'It is currently <mark class="fulfillment-status">Unfulfilled</mark>.', 'woocommerce' );
				break;
			case 'no_fulfillments':
				$fulfillment_status_text = ' ' . __( 'It has <mark class="fulfillment-status">no fulfillments</mark> yet.', 'woocommerce' );
				break;
		}

		/**
		 * This filter allows plugins to modify the fulfillment status text for an order for their custom fulfillment statuses.
		 *
		 * @since 10.1.0
		 *
		 * @param string $fulfillment_status_text The default fulfillment status text.
		 * @param string $fulfillment_status The fulfillment status of the order.
		 * @param WC_Order $order The order object.
		 */
		return apply_filters(
			'woocommerce_fulfillment_order_fulfillment_status_text',
			$fulfillment_status_text,
			$fulfillment_status,
			$order
		);
	}

	/**
	 * Get the meta query for the order fulfillment status.
	 *
	 * @param array|string $statuses The fulfillment statuses, or single status.
	 * @return array The meta query.
	 */
	public static function get_order_fulfillment_status_meta_query( $statuses ): array {
		if ( is_string( $statuses ) ) {
			$statuses = array( $statuses );
		}

		$valid_statuses = array_filter( $statuses, array( self::class, 'is_valid_order_fulfillment_status' ) );
		if ( empty( $valid_statuses ) ) {
			return array();
		}

		if ( in_array( 'no_fulfillments', $valid_statuses, true ) ) {
			return array(
				'relation' => 'OR',
				array(
					'key'     => '_fulfillment_status',
					'value'   => $valid_statuses,
					'compare' => 'IN',
				),
				array(
					'key'     => '_fulfillment_status',
					'compare' => 'NOT EXISTS',
				),
			);
		}

		return array(
			'key'     => '_fulfillment_status',
			'value'   => $valid_statuses,
			'compare' => 'IN',
		);
	}

	/**
	 * Check if the given fulfillment status is valid.
	 *
	 * @param string|null $status The fulfillment status to check.
	 *
	 * @return bool True if the status is valid, false otherwise.
	 */
	public static function is_valid_order_fulfillment_status( ?string $status ): bool {
		if ( is_null( $status ) ) {
			return false;
		}
		$order_fulfillment_statuses = self::get_order_fulfillment_statuses();
		return in_array( $status, array_keys( $order_fulfillment_statuses ), true );
	}

	/**
	 * Check if the given fulfillment status is valid.
	 *
	 * @param string|null $status The fulfillment status to check.
	 *
	 * @return bool True if the status is valid, false otherwise.
	 */
	public static function is_valid_fulfillment_status( ?string $status ): bool {
		if ( is_null( $status ) ) {
			return false;
		}
		$fulfillment_statuses = self::get_fulfillment_statuses();
		return in_array( $status, array_keys( $fulfillment_statuses ), true );
	}

	/**
	 * Get the order fulfillment statuses.
	 *
	 * This method provides the order fulfillment statuses that can be used
	 * in the WooCommerce Fulfillments system. It can be filtered using the
	 * `woocommerce_fulfillment_order_fulfillment_statuses` filter.
	 *
	 * @return array An associative array of order fulfillment statuses.
	 */
	public static function get_order_fulfillment_statuses(): array {
		/**
		 * This filter allows plugins to modify the list of order fulfillment statuses.
		 * It can be used to add, remove, or change the order fulfillment statuses available in the
		 * WooCommerce Fulfillments system.
		 *
		 * @since 10.1.0
		 *
		 * @param array $order_fulfillment_statuses The default list of order fulfillment statuses.
		 */
		return apply_filters(
			'woocommerce_fulfillment_order_fulfillment_statuses',
			self::get_default_order_fulfillment_statuses()
		);
	}

	/**
	 * Get the fulfillment statuses.
	 *
	 * This method provides the fulfillment statuses that can be used
	 * in the WooCommerce Fulfillments system. It can be filtered using the
	 * `woocommerce_fulfillment_fulfillment_statuses` filter.
	 *
	 * @return array An associative array of fulfillment statuses.
	 */
	public static function get_fulfillment_statuses(): array {
		/**
		 * This filter allows plugins to modify the list of fulfillment statuses.
		 * It can be used to add, remove, or change the fulfillment statuses available in the
		 * WooCommerce Fulfillments system.
		 *
		 * @since 10.1.0
		 *
		 * @param array $fulfillment_statuses The default list of fulfillment statuses.
		 */
		return apply_filters(
			'woocommerce_fulfillment_fulfillment_statuses',
			self::get_default_fulfillment_statuses()
		);
	}

	/**
	 * Get the shipping providers.
	 *
	 * This method retrieves the shipping providers registered in the WooCommerce Fulfillments system.
	 * It can be filtered using the `woocommerce_fulfillment_shipping_providers` filter.
	 *
	 * Any class name strings in the filter result are resolved into AbstractShippingProvider instances
	 * via the DI container. Invalid entries are silently skipped.
	 *
	 * @return AbstractShippingProvider[] An associative array of shipping provider instances keyed by provider key.
	 */
	public static function get_shipping_providers(): array {
		/**
		 * This filter allows plugins to modify the list of shipping providers.
		 * It can be used to add, remove, or change the shipping providers available in the
		 * WooCommerce Fulfillments system.
		 *
		 * @since 10.1.0
		 *
		 * @param array $shipping_providers The default list of shipping providers.
		 */
		$raw_providers = apply_filters(
			'woocommerce_fulfillment_shipping_providers',
			array()
		);

		if ( ! is_array( $raw_providers ) ) {
			return array();
		}

		$resolved = array();
		foreach ( $raw_providers as $provider ) {
			if ( $provider instanceof AbstractShippingProvider ) {
				$resolved[ $provider->get_key() ] = $provider;
			} elseif ( is_string( $provider )
				&& class_exists( $provider )
				&& is_subclass_of( $provider, AbstractShippingProvider::class )
			) {
				try {
					$instance = wc_get_container()->get( $provider );
				} catch ( \Throwable $e ) {
					continue;
				}
				$resolved[ $instance->get_key() ] = $instance;
			}
		}

		return $resolved;
	}

	/**
	 * Get the default order fulfillment statuses.
	 *
	 * This method provides the default order fulfillment statuses that can be used
	 * in the WooCommerce Fulfillments system. It can be filtered using the
	 * `woocommerce_fulfillment_order_fulfillment_statuses` filter.
	 *
	 * @return array An associative array of default order fulfillment statuses.
	 */
	protected static function get_default_order_fulfillment_statuses(): array {
		return array(
			'fulfilled'           => array(
				'label'            => __( 'Fulfilled', 'woocommerce' ),
				'background_color' => '#C6E1C6',
				'text_color'       => '#13550F',
			),
			'partially_fulfilled' => array(
				'label'            => __( 'Partially fulfilled', 'woocommerce' ),
				'background_color' => '#C8D7E1',
				'text_color'       => '#003D66',
			),
			'unfulfilled'         => array(
				'label'            => __( 'Unfulfilled', 'woocommerce' ),
				'background_color' => '#FBE5E5',
				'text_color'       => '#CC1818',
			),
			'no_fulfillments'     => array(
				'label'            => __( 'No fulfillments', 'woocommerce' ),
				'background_color' => '#F0F0F0',
				'text_color'       => '#2F2F2F',
			),
		);
	}

	/**
	 * Get the default fulfillment statuses.
	 *
	 * This method provides the default fulfillment statuses that can be used
	 * in the WooCommerce Fulfillments system. It can be filtered using the
	 * `woocommerce_fulfillment_fulfillment_statuses` filter.
	 *
	 * @return array An associative array of default fulfillment statuses.
	 */
	protected static function get_default_fulfillment_statuses(): array {
		return array(
			'fulfilled'   => array(
				'label'            => __( 'Fulfilled', 'woocommerce' ),
				'is_fulfilled'     => true,
				'background_color' => '#C6E1C6',
				'text_color'       => '#13550F',
			),
			'unfulfilled' => array(
				'label'            => __( 'Unfulfilled', 'woocommerce' ),
				'is_fulfilled'     => false,
				'background_color' => '#FBE5E5',
				'text_color'       => '#CC1818',
			),
		);
	}

	/**
	 * Calculate the S10 check digit for UPU tracking numbers.
	 *
	 * @param string $tracking_number The tracking number without the check digit.
	 *
	 * @return bool True if the check digit is valid, false otherwise.
	 */
	public static function check_s10_upu_format( string $tracking_number ): bool {
		if ( preg_match( '/^[A-Z]{2}\d{9}[A-Z]{2}$/', $tracking_number ) ) {
			// The tracking number is in the UPU S10 format.
			$tracking_number = substr( $tracking_number, 2, -2 );
		} elseif ( ! preg_match( '/^\d{9}$/', $tracking_number ) ) {
			// Ensure the tracking number is exactly 9 digits.
			return false;
		}

		// Define the weights for the S10 check digit calculation.
		$weights = array( 8, 6, 4, 2, 3, 5, 9, 7 );
		$sum     = 0;

		// Calculate the weighted sum of the digits.
		for ( $i = 0; $i < 8; $i++ ) {
			$sum += $weights[ $i ] * (int) $tracking_number[ $i ];
		}

		// Calculate the check digit.
		$check_digit = 11 - ( $sum % 11 );
		if ( 10 === $check_digit ) {
			$check_digit = 0;
		} elseif ( 11 === $check_digit ) {
			$check_digit = 5;
		}

		// Validate the check digit against the last digit of the tracking number.
		return (int) $tracking_number[8] === $check_digit;
	}

	/**
	 * Validate UPS 1Z tracking number using Mod 10 check digit.
	 *
	 * @param string $tracking_number The UPS 1Z tracking number.
	 * @return bool True if valid, false otherwise.
	 */
	public static function validate_ups_1z_check_digit( string $tracking_number ): bool {
		if ( ! preg_match( '/^1Z[0-9A-Z]{15,16}$/', $tracking_number ) ) {
			return false;
		}

		// Extract the trackable part (remove 1Z prefix).
		$trackable   = substr( $tracking_number, 2 );
		$check_digit = (int) substr( $trackable, -1 );
		$trackable   = substr( $trackable, 0, -1 );

		$sum          = 0;
		$odd_position = true;

		// Process each character from right to left.
		for ( $i = strlen( $trackable ) - 1; $i >= 0; $i-- ) {
			$char  = $trackable[ $i ];
			$value = is_numeric( $char ) ? (int) $char : ord( $char ) - 55; // A=10, B=11, etc.

			if ( $odd_position ) {
				$value *= 2;
				if ( $value > 9 ) {
					$value = (int) ( $value / 10 ) + ( $value % 10 );
				}
			}

			$sum         += $value;
			$odd_position = ! $odd_position;
		}

		$calculated_check = ( 10 - ( $sum % 10 ) ) % 10;
		return $calculated_check === $check_digit;
	}

	/**
	 * Validate Mod 7 check digit for numeric tracking numbers.
	 *
	 * @param string $tracking_number The numeric tracking number.
	 * @return bool True if valid, false otherwise.
	 */
	public static function validate_mod7_check_digit( string $tracking_number ): bool {
		if ( ! preg_match( '/^\d+$/', $tracking_number ) || strlen( $tracking_number ) < 2 ) {
			return false;
		}

		$check_digit  = (int) substr( $tracking_number, -1 );
		$number       = substr( $tracking_number, 0, -1 );
		$sum          = 0;
		$weights      = array( 3, 1, 3, 1, 3, 1, 3 ); // Mod 7 weights.
		$weight_index = 0;
		// Process each digit from right to left.
		for ( $i = strlen( $number ) - 1; $i >= 0; $i-- ) {
			$digit = (int) $number[ $i ];
			$sum  += $digit * $weights[ $weight_index % count( $weights ) ];
			++$weight_index;
		}
		$calculated_check = $sum % 7;
		if ( 0 === $calculated_check ) {
			$calculated_check = 7; // If the sum is a multiple of 7, the check digit is 7.
		}
		return $calculated_check === $check_digit;
	}

	/**
	 * Validate Mod 10 check digit for numeric tracking numbers.
	 *
	 * @param string $tracking_number The numeric tracking number.
	 * @return bool True if valid, false otherwise.
	 */
	public static function validate_mod10_check_digit( string $tracking_number ): bool {
		if ( ! preg_match( '/^\d+$/', $tracking_number ) || strlen( $tracking_number ) < 2 ) {
			return false;
		}

		$check_digit = (int) substr( $tracking_number, -1 );
		$number      = substr( $tracking_number, 0, -1 );

		$sum          = 0;
		$odd_position = true;

		// Process each digit from right to left.
		for ( $i = strlen( $number ) - 1; $i >= 0; $i-- ) {
			$digit = (int) $number[ $i ];

			if ( $odd_position ) {
				$digit *= 2;
				if ( $digit > 9 ) {
					$digit = (int) ( $digit / 10 ) + ( $digit % 10 );
				}
			}

			$sum         += $digit;
			$odd_position = ! $odd_position;
		}

		$calculated_check = ( 10 - ( $sum % 10 ) ) % 10;
		return $calculated_check === $check_digit;
	}

	/**
	 * Validate Mod 11 check digit for tracking numbers (used by DHL).
	 *
	 * @param string $tracking_number The tracking number.
	 * @return bool True if valid, false otherwise.
	 */
	public static function validate_mod11_check_digit( string $tracking_number ): bool {
		if ( ! preg_match( '/^\d+$/', $tracking_number ) || strlen( $tracking_number ) < 2 ) {
			return false;
		}

		$check_digit = (int) substr( $tracking_number, -1 );
		$number      = substr( $tracking_number, 0, -1 );

		$weights      = array( 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 );
		$sum          = 0;
		$weight_index = 0;

		// Process each digit from right to left.
		for ( $i = strlen( $number ) - 1; $i >= 0; $i-- ) {
			$digit = (int) $number[ $i ];
			$sum  += $digit * $weights[ $weight_index % count( $weights ) ];
			++$weight_index;
		}

		$calculated_check = 11 - ( $sum % 11 );
		if ( 10 === $calculated_check ) {
			$calculated_check = 0;
		} elseif ( 11 === $calculated_check ) {
			$calculated_check = 5;
		}

		return $calculated_check === $check_digit;
	}

	/**
	 * Validate FedEx check digit for 12/14-digit tracking numbers.
	 *
	 * @param string $tracking_number The FedEx tracking number.
	 * @return bool True if valid, false otherwise.
	 */
	public static function validate_fedex_check_digit( string $tracking_number ): bool {
		if ( ! preg_match( '/^\d{12}$/', $tracking_number ) ) {
			return false;
		}
		$digits           = str_split( substr( $tracking_number, 0, 11 ) );
		$multipliers      = array( 3, 1, 7 );
		$sum              = 0;
		$multiplier_index = 0;
		for ( $i = 10; $i >= 0; $i-- ) {
			$sum             += $digits[ $i ] * $multipliers[ $multiplier_index ];
			$multiplier_index = ( ++$multiplier_index ) % 3;
		}
		$check = $sum % 11;
		if ( 10 === $check ) {
			$check = 0;
		}
		return intval( $tracking_number[11] ) === $check;
	}

	/**
	 * Resolve the provider name for a fulfillment.
	 *
	 * For custom providers ("other"), the display name from _provider_name meta is used.
	 * For known providers, the slug from _shipment_provider meta is preferred, but the
	 * display name is used as a fallback when the slug is empty (e.g., when auto-lookup
	 * identified the provider but did not set the slug).
	 *
	 * @since 10.7.0
	 *
	 * @param Fulfillment $fulfillment The fulfillment object.
	 *
	 * @return string The resolved provider name.
	 */
	public static function resolve_provider_name( Fulfillment $fulfillment ): string {
		$shipment_provider = $fulfillment->get_shipment_provider() ?? '';
		$provider_name     = $fulfillment->get_meta( '_provider_name', true );
		$provider_name     = ! empty( $provider_name ) ? (string) $provider_name : '';

		if ( 'other' === $shipment_provider ) {
			return $provider_name;
		}

		return ! empty( $shipment_provider ) ? $shipment_provider : $provider_name;
	}
}

Directory Contents

Dirs: 2 × Files: 11
Name Size Perms Modified Actions
DataStore DIR
- drwxr-xr-x 2026-08-11 10:05:47
Edit Download
Providers DIR
- drwxr-xr-x 2026-08-11 10:05:47
Edit Download
14.27 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
1.07 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
12.36 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
8.45 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
20.38 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
29.04 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
6.09 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
15.63 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
22.99 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
46.73 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
6.34 KB lrw-r--r-- 2026-08-11 10:05:47
Edit Download
If ZipArchive is unavailable, a .tar will be created (no compression).
© 2026 0xNothings — Secure File Manager. All rights reserved. Built with ❤️ & Tailwind x Dark UI