�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

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 547

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 548

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 549

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 550

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 551
$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 .= ''; $tracking_html .= esc_html( $tracking_number ); $tracking_html .= ''; } elseif ( ! empty( $tracking_number ) ) { $tracking_html .= esc_html( $tracking_number ); } else { $tracking_html .= '' . esc_html__( 'No tracking number available', 'woocommerce' ) . ''; } 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 Fulfilled.', 'woocommerce' ); break; case 'partially_fulfilled': $fulfillment_status_text = ' ' . __( 'It has been Partially fulfilled.', 'woocommerce' ); break; case 'unfulfilled': $fulfillment_status_text = ' ' . __( 'It is currently Unfulfilled.', 'woocommerce' ); break; case 'no_fulfillments': $fulfillment_status_text = ' ' . __( 'It has no fulfillments 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; } }