Changeset 3746747
- Timestamp:
- 10/14/2026 03:52:29 PM (11 days ago)
- Location:
- woocommerce-services
- Files:
-
- 18 added
- 18 deleted
- 10 edited
- 1 copied
-
tags/3.7.3 (copied) (copied from woocommerce-services/trunk )
-
tags/3.7.3/classes/class-wc-connect-taxjar-integration.php (modified) (11 diffs)
-
tags/3.7.3/dist/woocommerce-services-3.7.2.css (deleted)
-
tags/3.7.3/dist/woocommerce-services-3.7.2.js (deleted)
-
tags/3.7.3/dist/woocommerce-services-3.7.3.css (added)
-
tags/3.7.3/dist/woocommerce-services-3.7.3.js (added)
-
tags/3.7.3/dist/woocommerce-services-admin-pointers-3.7.2.js (deleted)
-
tags/3.7.3/dist/woocommerce-services-admin-pointers-3.7.3.js (added)
-
tags/3.7.3/dist/woocommerce-services-banner-3.7.2.css (deleted)
-
tags/3.7.3/dist/woocommerce-services-banner-3.7.2.js (deleted)
-
tags/3.7.3/dist/woocommerce-services-banner-3.7.3.css (added)
-
tags/3.7.3/dist/woocommerce-services-banner-3.7.3.js (added)
-
tags/3.7.3/dist/woocommerce-services-new-order-taxjar-3.7.2.js (deleted)
-
tags/3.7.3/dist/woocommerce-services-new-order-taxjar-3.7.3.js (added)
-
tags/3.7.3/dist/woocommerce-services-store-notices-3.7.2.js (deleted)
-
tags/3.7.3/dist/woocommerce-services-store-notices-3.7.3.js (added)
-
tags/3.7.3/dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.2.css (deleted)
-
tags/3.7.3/dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.2.js (deleted)
-
tags/3.7.3/dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.css (added)
-
tags/3.7.3/dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js (added)
-
tags/3.7.3/i18n/languages/woocommerce-services.pot (modified) (18 diffs)
-
tags/3.7.3/readme.txt (modified) (2 diffs)
-
tags/3.7.3/vendor/composer/installed.php (modified) (2 diffs)
-
tags/3.7.3/woocommerce-services.php (modified) (1 diff)
-
trunk/classes/class-wc-connect-taxjar-integration.php (modified) (11 diffs)
-
trunk/dist/woocommerce-services-3.7.2.css (deleted)
-
trunk/dist/woocommerce-services-3.7.2.js (deleted)
-
trunk/dist/woocommerce-services-3.7.3.css (added)
-
trunk/dist/woocommerce-services-3.7.3.js (added)
-
trunk/dist/woocommerce-services-admin-pointers-3.7.2.js (deleted)
-
trunk/dist/woocommerce-services-admin-pointers-3.7.3.js (added)
-
trunk/dist/woocommerce-services-banner-3.7.2.css (deleted)
-
trunk/dist/woocommerce-services-banner-3.7.2.js (deleted)
-
trunk/dist/woocommerce-services-banner-3.7.3.css (added)
-
trunk/dist/woocommerce-services-banner-3.7.3.js (added)
-
trunk/dist/woocommerce-services-new-order-taxjar-3.7.2.js (deleted)
-
trunk/dist/woocommerce-services-new-order-taxjar-3.7.3.js (added)
-
trunk/dist/woocommerce-services-store-notices-3.7.2.js (deleted)
-
trunk/dist/woocommerce-services-store-notices-3.7.3.js (added)
-
trunk/dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.2.css (deleted)
-
trunk/dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.2.js (deleted)
-
trunk/dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.css (added)
-
trunk/dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js (added)
-
trunk/i18n/languages/woocommerce-services.pot (modified) (18 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/vendor/composer/installed.php (modified) (2 diffs)
-
trunk/woocommerce-services.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
woocommerce-services/tags/3.7.3/classes/class-wc-connect-taxjar-integration.php
r3734320 r3746747 72 72 73 73 /** 74 * The TaxJar line id each cart item was sent as, keyed by cart item key. Set with 75 * $response_rate_ids, so each cart and order line can find its own answer. 76 * 77 * @var array<string, string> 78 */ 79 private $response_cart_item_keys = array(); 80 81 /** 82 * The cart item each order item was created from at checkout, keyed by 83 * spl_object_id() of the order item. Only set in the request that creates the 84 * items: an order item loaded from the database carries no cart item key. 85 * 86 * @var array<int, string> 87 */ 88 private $order_item_cart_item_keys = array(); 89 90 /** 74 91 * Rate ids TaxJar returned for an admin recalculation, keyed by order id, then by 75 92 * order item id. Kept apart from $response_rate_ids, which other code reads as … … 77 94 * take_admin_recalculation_rate_ids(). 78 95 * 79 * @var array<int, array<int, int[]>>96 * @var array<int, array<int, array{rate_ids: int[], line: object|null}>> 80 97 */ 81 98 private $admin_recalculation_rate_ids = array(); … … 377 394 add_filter( 'woocommerce_matched_rates', array( $this, 'allow_street_address_for_matched_rates' ), 10, 2 ); 378 395 add_filter( 'woocommerce_cart_totals_get_item_tax_rates', array( $this, 'override_cart_item_tax_rates' ), 10, 3 ); 396 add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'remember_order_item_cart_item_key' ), 10, 2 ); 379 397 add_action( 'woocommerce_order_item_after_calculate_taxes', array( $this, 'override_order_item_taxes' ), 10, 2 ); 380 398 … … 753 771 } 754 772 755 $this->response_rate_ids = $taxes['rate_ids']; 756 $this->response_line_items = $taxes['line_items']; 773 $this->response_rate_ids = $taxes['rate_ids']; 774 $this->response_line_items = $taxes['line_items']; 775 $this->response_cart_item_keys = wp_list_pluck( $line_items, 'id' ); 757 776 758 777 if ( isset( $this->response_line_items ) ) { … … 840 859 $rate_ids = $taxes['rate_ids'][ $line_item['id'] ?? '' ] ?? null; 841 860 if ( is_array( $rate_ids ) && $rate_ids ) { 842 $this->admin_recalculation_rate_ids[ (int) $order_id ][ (int) $item_key ] = $rate_ids; 861 $this->admin_recalculation_rate_ids[ (int) $order_id ][ (int) $item_key ] = array( 862 'rate_ids' => $rate_ids, 863 'line' => $taxes['line_items'][ $line_item['id'] ] ?? null, 864 ); 843 865 } 844 866 } … … 1615 1637 $product_id = $product->get_id(); 1616 1638 1617 // Find the matching line_item_key in response_rate_ids. 1618 // Format is "product_id-fingerprint-occurrence". The trailing "-" delimiter prevents 1619 // false prefix matches (e.g. product ID 1 won't match "10-xyz" because "1-" != "10"). 1620 // First-match-wins is safe: if the same product ID appears multiple times (e.g. 1621 // two bookings), they share the same tax_location and thus the same tax rates. 1622 $matching_rate_ids = null; 1623 foreach ( $this->response_rate_ids as $line_item_key => $rate_ids ) { 1624 if ( strpos( $line_item_key, $product_id . '-' ) === 0 ) { 1625 $matching_rate_ids = $rate_ids; 1626 break; 1627 } 1628 } 1639 // The line this cart item was sent as. Without a record of it, fall back to the 1640 // first line for the product: format is "product_id-fingerprint-occurrence", and 1641 // the trailing "-" keeps product 1 from matching "10-xyz". 1642 $cart_item_key = isset( $item->key ) && is_scalar( $item->key ) ? (string) $item->key : ''; 1643 $line_item_key = $this->response_cart_item_keys[ $cart_item_key ] ?? null; 1644 if ( null === $line_item_key ) { 1645 foreach ( array_keys( $this->response_rate_ids ) as $response_key ) { 1646 if ( strpos( (string) $response_key, $product_id . '-' ) === 0 ) { 1647 $line_item_key = (string) $response_key; 1648 break; 1649 } 1650 } 1651 } 1652 1653 $matching_rate_ids = null === $line_item_key ? null : ( $this->response_rate_ids[ $line_item_key ] ?? null ); 1629 1654 1630 1655 if ( empty( $matching_rate_ids ) || ! is_array( $matching_rate_ids ) ) { … … 1632 1657 } 1633 1658 1634 // Fetch the tax rates from the database using the rate IDs. 1635 $tax_rates = array(); 1636 foreach ( $matching_rate_ids as $rate_id ) { 1637 $rate_id = absint( $rate_id ); 1638 if ( ! $rate_id ) { 1639 continue; 1640 } 1641 1642 // Get rate data from WooCommerce. 1643 $rate_data = WC_Tax::_get_tax_rate( $rate_id ); 1644 if ( $rate_data ) { 1645 $tax_rates[ $rate_id ] = array( 1646 'rate' => (float) $rate_data['tax_rate'], 1647 'label' => $rate_data['tax_rate_name'], 1648 'shipping' => 'yes' === $rate_data['tax_rate_shipping'] ? 'yes' : 'no', 1649 'compound' => 'yes' === $rate_data['tax_rate_compound'] ? 'yes' : 'no', 1650 ); 1651 } 1652 } 1659 // The line's own percentages, as the order uses: rows are shared by every product 1660 // in a tax class and hold whichever line was stored last. 1661 $tax_rates = $this->get_looked_up_rates( $matching_rate_ids, $this->response_line_items[ $line_item_key ] ?? null ); 1653 1662 1654 1663 // Return our rates if we found any, otherwise fall back to WooCommerce's. … … 1669 1678 public function override_order_item_taxes( $item, $calculate_tax_for ) { 1670 1679 // An admin recalculation looked this item up in calculate_backend_totals(). 1671 $admin_ rate_ids= $this->take_admin_recalculation_rate_ids( $item );1672 if ( null !== $admin_ rate_ids) {1673 $this->set_order_item_taxes_from_rate_ids( $item, $admin_ rate_ids);1680 $admin_lookup = $this->take_admin_recalculation_rate_ids( $item ); 1681 if ( null !== $admin_lookup ) { 1682 $this->set_order_item_taxes_from_rate_ids( $item, $admin_lookup['rate_ids'], $admin_lookup['line'] ); 1674 1683 return; 1675 1684 } … … 1685 1694 } 1686 1695 1687 $product_id = $item->get_product_id(); 1696 $line_item_key = $this->find_order_item_line_key( $item ); 1697 1698 // No match means this item wasn't TaxJar-calculated (e.g. cross-state) — leave as-is. 1699 if ( null === $line_item_key ) { 1700 return; 1701 } 1702 1703 $matching_rate_ids = $this->response_rate_ids[ $line_item_key ] ?? null; 1704 $matching_line = $this->response_line_items[ $line_item_key ] ?? null; 1705 1706 if ( empty( $matching_rate_ids ) || ! is_array( $matching_rate_ids ) ) { 1707 // A line sent as exempt writes no rows, so WooCommerce taxed it from rows another 1708 // line wrote. That includes a line checkout sends as exempt on its second pass, 1709 // because TaxJar answered 0% for it on the first. 1710 if ( is_object( $matching_line ) && isset( $matching_line->combined_tax_rate ) && 0.0 === (float) $matching_line->combined_tax_rate ) { 1711 $item->set_taxes( false ); 1712 } 1713 return; 1714 } 1715 1716 $this->set_order_item_taxes_from_rate_ids( $item, $matching_rate_ids, $matching_line ); 1717 } 1718 1719 /** 1720 * Remember the cart item an order item was created from. 1721 * 1722 * @internal Hooked to woocommerce_checkout_create_order_line_item. 1723 * 1724 * @param WC_Order_Item_Product $item The order item being created. 1725 * @param string $cart_item_key The key of the cart item it was copied from. 1726 * 1727 * @since 3.7.3 1728 */ 1729 public function remember_order_item_cart_item_key( $item, $cart_item_key ) { 1730 if ( ! $item instanceof WC_Order_Item_Product || ! is_string( $cart_item_key ) || '' === $cart_item_key ) { 1731 return; 1732 } 1733 1734 $this->order_item_cart_item_keys[ spl_object_id( $item ) ] = $cart_item_key; 1735 } 1736 1737 /** 1738 * Find the TaxJar line an order item was sent as at checkout. 1739 * 1740 * Lines are matched on the product, the variation when there is one. When the cart 1741 * holds that product on more than one line, the order item is matched to the cart 1742 * line it was created from: the same product can be on two lines at different 1743 * prices, and TaxJar can answer them differently. That is the cart item key 1744 * WooCommerce passed when it created the item, or else the cart line with the same 1745 * quantity and amounts. Without either, a line TaxJar taxed is used. 1746 * 1747 * @param WC_Order_Item_Product $item Order item. 1748 * @return string|null The line id, or null when the product was not sent. 1749 */ 1750 private function find_order_item_line_key( $item ) { 1751 $product_id = $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id(); 1688 1752 if ( ! $product_id ) { 1689 return; 1690 } 1691 1692 // Find matching rate_ids by product_id prefix (format: "product_id-fingerprint-occurrence"). 1693 // The trailing "-" delimiter prevents false prefix matches between IDs (e.g. 1 vs 10). 1694 // First-match-wins is safe: same product always shares the same tax_location and rates. 1695 $matching_rate_ids = null; 1696 foreach ( $this->response_rate_ids as $line_item_key => $rate_ids ) { 1753 return null; 1754 } 1755 1756 // An exempt line has a breakdown line but no rate ids, so look in both. 1757 $candidates = array(); 1758 foreach ( array_merge( array_keys( (array) $this->response_line_items ), array_keys( $this->response_rate_ids ) ) as $line_item_key ) { 1759 $line_item_key = (string) $line_item_key; 1697 1760 if ( strpos( $line_item_key, $product_id . '-' ) === 0 ) { 1698 $matching_rate_ids = $rate_ids; 1699 break; 1700 } 1701 } 1702 1703 // No match means this item wasn't TaxJar-calculated (e.g. cross-state) — leave as-is. 1704 if ( empty( $matching_rate_ids ) || ! is_array( $matching_rate_ids ) ) { 1705 return; 1706 } 1707 1708 $this->set_order_item_taxes_from_rate_ids( $item, $matching_rate_ids ); 1761 $candidates[ $line_item_key ] = true; 1762 } 1763 } 1764 $candidates = array_keys( $candidates ); 1765 1766 if ( count( $candidates ) < 2 ) { 1767 return $candidates[0] ?? null; 1768 } 1769 1770 $cart_item_key = $this->order_item_cart_item_keys[ spl_object_id( $item ) ] ?? null; 1771 $line_item_key = null === $cart_item_key ? null : ( $this->response_cart_item_keys[ $cart_item_key ] ?? null ); 1772 if ( null !== $line_item_key && in_array( (string) $line_item_key, $candidates, true ) ) { 1773 return (string) $line_item_key; 1774 } 1775 1776 if ( function_exists( 'WC' ) && WC()->cart instanceof WC_Cart ) { 1777 foreach ( $candidates as $line_item_key ) { 1778 $cart_item_key = array_search( $line_item_key, $this->response_cart_item_keys, true ); 1779 $cart_item = false === $cart_item_key ? array() : WC()->cart->get_cart_item( $cart_item_key ); 1780 1781 if ( 1782 ! empty( $cart_item ) 1783 && (float) $cart_item['quantity'] === (float) $item->get_quantity() 1784 && abs( (float) $cart_item['line_subtotal'] - (float) $item->get_subtotal() ) < 0.01 1785 && abs( (float) $cart_item['line_total'] - (float) $item->get_total() ) < 0.01 1786 ) { 1787 return $line_item_key; 1788 } 1789 } 1790 } 1791 1792 // Still no match: an exempt line would clear the tax of a taxed item, so prefer a 1793 // line TaxJar taxed. That charges what was charged before lines were told apart. 1794 foreach ( $candidates as $line_item_key ) { 1795 if ( ! empty( $this->response_rate_ids[ $line_item_key ] ) ) { 1796 return $line_item_key; 1797 } 1798 } 1799 1800 return $candidates[0]; 1709 1801 } 1710 1802 … … 1716 1808 * 1717 1809 * @param WC_Order_Item $item The order item being taxed. 1718 * @return int[]|null Rate ids, or null when the item was not looked up. 1810 * @return array{rate_ids: int[], line: object|null}|null Rate ids and TaxJar's answer 1811 * line, or null when the item 1812 * was not looked up. 1719 1813 */ 1720 1814 private function take_admin_recalculation_rate_ids( $item ) { … … 1739 1833 * Tax an order item at the given rate rows, replacing what WooCommerce found. 1740 1834 * 1741 * @param WC_Order_Item_Product $item The order item. 1742 * @param array $rate_ids Tax rate ids. 1743 */ 1744 private function set_order_item_taxes_from_rate_ids( $item, array $rate_ids ) { 1745 // Build tax rates array from the stored rate IDs. 1746 $tax_rates = array(); 1747 foreach ( $rate_ids as $rate_id ) { 1748 $rate_id = absint( $rate_id ); 1749 if ( ! $rate_id ) { 1750 continue; 1751 } 1752 1753 $rate_data = \WC_Tax::_get_tax_rate( $rate_id ); 1754 if ( $rate_data ) { 1755 $tax_rates[ $rate_id ] = array( 1756 'rate' => (float) $rate_data['tax_rate'], 1757 'label' => $rate_data['tax_rate_name'], 1758 'shipping' => 'yes' === $rate_data['tax_rate_shipping'] ? 'yes' : 'no', 1759 'compound' => 'yes' === $rate_data['tax_rate_compound'] ? 'yes' : 'no', 1760 ); 1761 } 1762 } 1835 * The percentages come from TaxJar's answer for the item when it is given. Rows are 1836 * shared by every product in a tax class, so they hold whichever line was stored 1837 * last: an item TaxJar answered as exempt would otherwise pay the rate of a taxed 1838 * item beside it. 1839 * 1840 * @param WC_Order_Item_Product $item The order item. 1841 * @param array $rate_ids Tax rate ids. 1842 * @param object|null $response_line TaxJar's breakdown line for the item. 1843 */ 1844 private function set_order_item_taxes_from_rate_ids( $item, array $rate_ids, $response_line = null ) { 1845 // Positions matter: the answer's components were stored in this order. 1846 $tax_rates = $this->get_looked_up_rates( $rate_ids, $response_line ); 1763 1847 1764 1848 if ( empty( $tax_rates ) ) { -
woocommerce-services/tags/3.7.3/i18n/languages/woocommerce-services.pot
r3734320 r3746747 5 5 "Project-Id-Version: _s woocommerce-services\n" 6 6 "Report-Msgid-Bugs-To: \n" 7 "POT-Creation-Date: 2026-10- 08T10:46:46.612Z\n"7 "POT-Creation-Date: 2026-10-14T15:49:53.757Z\n" 8 8 "MIME-Version: 1.0\n" 9 9 "Content-Type: text/plain; charset=UTF-8\n" … … 110 110 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 111 111 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 112 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1112 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 113 113 msgid "Create shipping label" 114 114 msgid_plural "Create shipping labels" … … 117 117 118 118 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 119 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1119 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 120 120 msgid "Track Package" 121 121 msgid_plural "Track Packages" … … 124 124 125 125 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 126 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1126 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 127 127 msgid "Create new label" 128 128 msgstr "" 129 129 130 130 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 131 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1131 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 132 132 msgid "Connection error: unable to create label at this time" 133 133 msgstr "" 134 134 135 135 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 136 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1136 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 137 137 msgid "%(itemCount)d item was fulfilled on {{span}}%(createdDate)s{{/span}}" 138 138 msgid_plural "" … … 142 142 143 143 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 144 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1144 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 145 145 msgid "%(itemCount)d item is ready to be fulfilled" 146 146 msgid_plural "%(itemCount)d items are ready to be fulfilled" … … 810 810 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 811 811 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 812 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1812 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 813 813 msgid "Try WooCommerce Shipping" 814 814 msgstr "" … … 816 816 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 817 817 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 818 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1818 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 819 819 msgid "A new WooCommerce Shipping experience is now available" 820 820 msgstr "" … … 822 822 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 823 823 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 824 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1824 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 825 825 msgid "" 826 826 "We'll ensure a seamless transition by allowing you to migrate all your " … … 830 830 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 831 831 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 832 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1832 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 833 833 msgid "Here's what you can expect from the new shipping experience:" 834 834 msgstr "" … … 836 836 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 837 837 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 838 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1838 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 839 839 msgid "A seamless transition" 840 840 msgstr "" … … 842 842 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 843 843 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 844 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1844 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 845 845 msgid "" 846 846 "Automatically migrate all your compatible settings and shipment history to " … … 850 850 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 851 851 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 852 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1852 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 853 853 msgid "Print and save" 854 854 msgstr "" … … 856 856 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 857 857 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 858 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1858 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 859 859 msgid "" 860 860 "Speed up label creation with a streamlined process to print and save your " … … 864 864 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 865 865 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 866 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1866 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 867 867 msgid "USPS and DHL Express" 868 868 msgstr "" … … 870 870 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 871 871 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 872 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1872 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 873 873 msgid "" 874 874 "Ship your productss using trusted shipping carriers like USPS and DHL " … … 878 878 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 879 879 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 880 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1880 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 881 881 msgid "Enhanced label purchase flow" 882 882 msgstr "" … … 884 884 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 885 885 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 886 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1886 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 887 887 msgid "" 888 888 "Experience a smoother label purchasing process with our updated interface." … … 891 891 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 892 892 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 893 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1893 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 894 894 msgid "Maybe later" 895 895 msgstr "" -
woocommerce-services/tags/3.7.3/readme.txt
r3734320 r3746747 8 8 WC requires at least: 11.0 9 9 WC tested up to: 11.2 10 Stable tag: 3.7. 210 Stable tag: 3.7.3 11 11 License: GPLv2 or later 12 12 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 70 70 71 71 == Changelog == 72 73 = 3.7.3 - 2026-10-14 = 74 * Fix - Stop charging tax on an item the tax service answered as tax-exempt, such as clothing under New York's price threshold, when another item in the same tax class is taxed. This affected orders placed at checkout and orders recalculated in the admin. The cart now also shows the tax the order charges when items in one tax class are taxed differently, including product variations and the same product added twice at different prices. 72 75 73 76 = 3.7.2 - 2026-10-08 = -
woocommerce-services/tags/3.7.3/vendor/composer/installed.php
r3734320 r3746747 4 4 'pretty_version' => 'dev-trunk', 5 5 'version' => 'dev-trunk', 6 'reference' => ' da576c383c6574372e4c4d6c61b88989fc109bc6',6 'reference' => '84d4dd49245d73c4c107a9af4bcd1f143d15ad5c', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-trunk', 15 15 'version' => 'dev-trunk', 16 'reference' => ' da576c383c6574372e4c4d6c61b88989fc109bc6',16 'reference' => '84d4dd49245d73c4c107a9af4bcd1f143d15ad5c', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', -
woocommerce-services/tags/3.7.3/woocommerce-services.php
r3734320 r3746747 11 11 * License: GPLv2 or later 12 12 * License URI: https://www.gnu.org/licenses/gpl-2.0.html 13 * Version: 3.7. 213 * Version: 3.7.3 14 14 * Requires Plugins: woocommerce 15 15 * Requires PHP: 7.4 -
woocommerce-services/trunk/classes/class-wc-connect-taxjar-integration.php
r3734320 r3746747 72 72 73 73 /** 74 * The TaxJar line id each cart item was sent as, keyed by cart item key. Set with 75 * $response_rate_ids, so each cart and order line can find its own answer. 76 * 77 * @var array<string, string> 78 */ 79 private $response_cart_item_keys = array(); 80 81 /** 82 * The cart item each order item was created from at checkout, keyed by 83 * spl_object_id() of the order item. Only set in the request that creates the 84 * items: an order item loaded from the database carries no cart item key. 85 * 86 * @var array<int, string> 87 */ 88 private $order_item_cart_item_keys = array(); 89 90 /** 74 91 * Rate ids TaxJar returned for an admin recalculation, keyed by order id, then by 75 92 * order item id. Kept apart from $response_rate_ids, which other code reads as … … 77 94 * take_admin_recalculation_rate_ids(). 78 95 * 79 * @var array<int, array<int, int[]>>96 * @var array<int, array<int, array{rate_ids: int[], line: object|null}>> 80 97 */ 81 98 private $admin_recalculation_rate_ids = array(); … … 377 394 add_filter( 'woocommerce_matched_rates', array( $this, 'allow_street_address_for_matched_rates' ), 10, 2 ); 378 395 add_filter( 'woocommerce_cart_totals_get_item_tax_rates', array( $this, 'override_cart_item_tax_rates' ), 10, 3 ); 396 add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'remember_order_item_cart_item_key' ), 10, 2 ); 379 397 add_action( 'woocommerce_order_item_after_calculate_taxes', array( $this, 'override_order_item_taxes' ), 10, 2 ); 380 398 … … 753 771 } 754 772 755 $this->response_rate_ids = $taxes['rate_ids']; 756 $this->response_line_items = $taxes['line_items']; 773 $this->response_rate_ids = $taxes['rate_ids']; 774 $this->response_line_items = $taxes['line_items']; 775 $this->response_cart_item_keys = wp_list_pluck( $line_items, 'id' ); 757 776 758 777 if ( isset( $this->response_line_items ) ) { … … 840 859 $rate_ids = $taxes['rate_ids'][ $line_item['id'] ?? '' ] ?? null; 841 860 if ( is_array( $rate_ids ) && $rate_ids ) { 842 $this->admin_recalculation_rate_ids[ (int) $order_id ][ (int) $item_key ] = $rate_ids; 861 $this->admin_recalculation_rate_ids[ (int) $order_id ][ (int) $item_key ] = array( 862 'rate_ids' => $rate_ids, 863 'line' => $taxes['line_items'][ $line_item['id'] ] ?? null, 864 ); 843 865 } 844 866 } … … 1615 1637 $product_id = $product->get_id(); 1616 1638 1617 // Find the matching line_item_key in response_rate_ids. 1618 // Format is "product_id-fingerprint-occurrence". The trailing "-" delimiter prevents 1619 // false prefix matches (e.g. product ID 1 won't match "10-xyz" because "1-" != "10"). 1620 // First-match-wins is safe: if the same product ID appears multiple times (e.g. 1621 // two bookings), they share the same tax_location and thus the same tax rates. 1622 $matching_rate_ids = null; 1623 foreach ( $this->response_rate_ids as $line_item_key => $rate_ids ) { 1624 if ( strpos( $line_item_key, $product_id . '-' ) === 0 ) { 1625 $matching_rate_ids = $rate_ids; 1626 break; 1627 } 1628 } 1639 // The line this cart item was sent as. Without a record of it, fall back to the 1640 // first line for the product: format is "product_id-fingerprint-occurrence", and 1641 // the trailing "-" keeps product 1 from matching "10-xyz". 1642 $cart_item_key = isset( $item->key ) && is_scalar( $item->key ) ? (string) $item->key : ''; 1643 $line_item_key = $this->response_cart_item_keys[ $cart_item_key ] ?? null; 1644 if ( null === $line_item_key ) { 1645 foreach ( array_keys( $this->response_rate_ids ) as $response_key ) { 1646 if ( strpos( (string) $response_key, $product_id . '-' ) === 0 ) { 1647 $line_item_key = (string) $response_key; 1648 break; 1649 } 1650 } 1651 } 1652 1653 $matching_rate_ids = null === $line_item_key ? null : ( $this->response_rate_ids[ $line_item_key ] ?? null ); 1629 1654 1630 1655 if ( empty( $matching_rate_ids ) || ! is_array( $matching_rate_ids ) ) { … … 1632 1657 } 1633 1658 1634 // Fetch the tax rates from the database using the rate IDs. 1635 $tax_rates = array(); 1636 foreach ( $matching_rate_ids as $rate_id ) { 1637 $rate_id = absint( $rate_id ); 1638 if ( ! $rate_id ) { 1639 continue; 1640 } 1641 1642 // Get rate data from WooCommerce. 1643 $rate_data = WC_Tax::_get_tax_rate( $rate_id ); 1644 if ( $rate_data ) { 1645 $tax_rates[ $rate_id ] = array( 1646 'rate' => (float) $rate_data['tax_rate'], 1647 'label' => $rate_data['tax_rate_name'], 1648 'shipping' => 'yes' === $rate_data['tax_rate_shipping'] ? 'yes' : 'no', 1649 'compound' => 'yes' === $rate_data['tax_rate_compound'] ? 'yes' : 'no', 1650 ); 1651 } 1652 } 1659 // The line's own percentages, as the order uses: rows are shared by every product 1660 // in a tax class and hold whichever line was stored last. 1661 $tax_rates = $this->get_looked_up_rates( $matching_rate_ids, $this->response_line_items[ $line_item_key ] ?? null ); 1653 1662 1654 1663 // Return our rates if we found any, otherwise fall back to WooCommerce's. … … 1669 1678 public function override_order_item_taxes( $item, $calculate_tax_for ) { 1670 1679 // An admin recalculation looked this item up in calculate_backend_totals(). 1671 $admin_ rate_ids= $this->take_admin_recalculation_rate_ids( $item );1672 if ( null !== $admin_ rate_ids) {1673 $this->set_order_item_taxes_from_rate_ids( $item, $admin_ rate_ids);1680 $admin_lookup = $this->take_admin_recalculation_rate_ids( $item ); 1681 if ( null !== $admin_lookup ) { 1682 $this->set_order_item_taxes_from_rate_ids( $item, $admin_lookup['rate_ids'], $admin_lookup['line'] ); 1674 1683 return; 1675 1684 } … … 1685 1694 } 1686 1695 1687 $product_id = $item->get_product_id(); 1696 $line_item_key = $this->find_order_item_line_key( $item ); 1697 1698 // No match means this item wasn't TaxJar-calculated (e.g. cross-state) — leave as-is. 1699 if ( null === $line_item_key ) { 1700 return; 1701 } 1702 1703 $matching_rate_ids = $this->response_rate_ids[ $line_item_key ] ?? null; 1704 $matching_line = $this->response_line_items[ $line_item_key ] ?? null; 1705 1706 if ( empty( $matching_rate_ids ) || ! is_array( $matching_rate_ids ) ) { 1707 // A line sent as exempt writes no rows, so WooCommerce taxed it from rows another 1708 // line wrote. That includes a line checkout sends as exempt on its second pass, 1709 // because TaxJar answered 0% for it on the first. 1710 if ( is_object( $matching_line ) && isset( $matching_line->combined_tax_rate ) && 0.0 === (float) $matching_line->combined_tax_rate ) { 1711 $item->set_taxes( false ); 1712 } 1713 return; 1714 } 1715 1716 $this->set_order_item_taxes_from_rate_ids( $item, $matching_rate_ids, $matching_line ); 1717 } 1718 1719 /** 1720 * Remember the cart item an order item was created from. 1721 * 1722 * @internal Hooked to woocommerce_checkout_create_order_line_item. 1723 * 1724 * @param WC_Order_Item_Product $item The order item being created. 1725 * @param string $cart_item_key The key of the cart item it was copied from. 1726 * 1727 * @since 3.7.3 1728 */ 1729 public function remember_order_item_cart_item_key( $item, $cart_item_key ) { 1730 if ( ! $item instanceof WC_Order_Item_Product || ! is_string( $cart_item_key ) || '' === $cart_item_key ) { 1731 return; 1732 } 1733 1734 $this->order_item_cart_item_keys[ spl_object_id( $item ) ] = $cart_item_key; 1735 } 1736 1737 /** 1738 * Find the TaxJar line an order item was sent as at checkout. 1739 * 1740 * Lines are matched on the product, the variation when there is one. When the cart 1741 * holds that product on more than one line, the order item is matched to the cart 1742 * line it was created from: the same product can be on two lines at different 1743 * prices, and TaxJar can answer them differently. That is the cart item key 1744 * WooCommerce passed when it created the item, or else the cart line with the same 1745 * quantity and amounts. Without either, a line TaxJar taxed is used. 1746 * 1747 * @param WC_Order_Item_Product $item Order item. 1748 * @return string|null The line id, or null when the product was not sent. 1749 */ 1750 private function find_order_item_line_key( $item ) { 1751 $product_id = $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id(); 1688 1752 if ( ! $product_id ) { 1689 return; 1690 } 1691 1692 // Find matching rate_ids by product_id prefix (format: "product_id-fingerprint-occurrence"). 1693 // The trailing "-" delimiter prevents false prefix matches between IDs (e.g. 1 vs 10). 1694 // First-match-wins is safe: same product always shares the same tax_location and rates. 1695 $matching_rate_ids = null; 1696 foreach ( $this->response_rate_ids as $line_item_key => $rate_ids ) { 1753 return null; 1754 } 1755 1756 // An exempt line has a breakdown line but no rate ids, so look in both. 1757 $candidates = array(); 1758 foreach ( array_merge( array_keys( (array) $this->response_line_items ), array_keys( $this->response_rate_ids ) ) as $line_item_key ) { 1759 $line_item_key = (string) $line_item_key; 1697 1760 if ( strpos( $line_item_key, $product_id . '-' ) === 0 ) { 1698 $matching_rate_ids = $rate_ids; 1699 break; 1700 } 1701 } 1702 1703 // No match means this item wasn't TaxJar-calculated (e.g. cross-state) — leave as-is. 1704 if ( empty( $matching_rate_ids ) || ! is_array( $matching_rate_ids ) ) { 1705 return; 1706 } 1707 1708 $this->set_order_item_taxes_from_rate_ids( $item, $matching_rate_ids ); 1761 $candidates[ $line_item_key ] = true; 1762 } 1763 } 1764 $candidates = array_keys( $candidates ); 1765 1766 if ( count( $candidates ) < 2 ) { 1767 return $candidates[0] ?? null; 1768 } 1769 1770 $cart_item_key = $this->order_item_cart_item_keys[ spl_object_id( $item ) ] ?? null; 1771 $line_item_key = null === $cart_item_key ? null : ( $this->response_cart_item_keys[ $cart_item_key ] ?? null ); 1772 if ( null !== $line_item_key && in_array( (string) $line_item_key, $candidates, true ) ) { 1773 return (string) $line_item_key; 1774 } 1775 1776 if ( function_exists( 'WC' ) && WC()->cart instanceof WC_Cart ) { 1777 foreach ( $candidates as $line_item_key ) { 1778 $cart_item_key = array_search( $line_item_key, $this->response_cart_item_keys, true ); 1779 $cart_item = false === $cart_item_key ? array() : WC()->cart->get_cart_item( $cart_item_key ); 1780 1781 if ( 1782 ! empty( $cart_item ) 1783 && (float) $cart_item['quantity'] === (float) $item->get_quantity() 1784 && abs( (float) $cart_item['line_subtotal'] - (float) $item->get_subtotal() ) < 0.01 1785 && abs( (float) $cart_item['line_total'] - (float) $item->get_total() ) < 0.01 1786 ) { 1787 return $line_item_key; 1788 } 1789 } 1790 } 1791 1792 // Still no match: an exempt line would clear the tax of a taxed item, so prefer a 1793 // line TaxJar taxed. That charges what was charged before lines were told apart. 1794 foreach ( $candidates as $line_item_key ) { 1795 if ( ! empty( $this->response_rate_ids[ $line_item_key ] ) ) { 1796 return $line_item_key; 1797 } 1798 } 1799 1800 return $candidates[0]; 1709 1801 } 1710 1802 … … 1716 1808 * 1717 1809 * @param WC_Order_Item $item The order item being taxed. 1718 * @return int[]|null Rate ids, or null when the item was not looked up. 1810 * @return array{rate_ids: int[], line: object|null}|null Rate ids and TaxJar's answer 1811 * line, or null when the item 1812 * was not looked up. 1719 1813 */ 1720 1814 private function take_admin_recalculation_rate_ids( $item ) { … … 1739 1833 * Tax an order item at the given rate rows, replacing what WooCommerce found. 1740 1834 * 1741 * @param WC_Order_Item_Product $item The order item. 1742 * @param array $rate_ids Tax rate ids. 1743 */ 1744 private function set_order_item_taxes_from_rate_ids( $item, array $rate_ids ) { 1745 // Build tax rates array from the stored rate IDs. 1746 $tax_rates = array(); 1747 foreach ( $rate_ids as $rate_id ) { 1748 $rate_id = absint( $rate_id ); 1749 if ( ! $rate_id ) { 1750 continue; 1751 } 1752 1753 $rate_data = \WC_Tax::_get_tax_rate( $rate_id ); 1754 if ( $rate_data ) { 1755 $tax_rates[ $rate_id ] = array( 1756 'rate' => (float) $rate_data['tax_rate'], 1757 'label' => $rate_data['tax_rate_name'], 1758 'shipping' => 'yes' === $rate_data['tax_rate_shipping'] ? 'yes' : 'no', 1759 'compound' => 'yes' === $rate_data['tax_rate_compound'] ? 'yes' : 'no', 1760 ); 1761 } 1762 } 1835 * The percentages come from TaxJar's answer for the item when it is given. Rows are 1836 * shared by every product in a tax class, so they hold whichever line was stored 1837 * last: an item TaxJar answered as exempt would otherwise pay the rate of a taxed 1838 * item beside it. 1839 * 1840 * @param WC_Order_Item_Product $item The order item. 1841 * @param array $rate_ids Tax rate ids. 1842 * @param object|null $response_line TaxJar's breakdown line for the item. 1843 */ 1844 private function set_order_item_taxes_from_rate_ids( $item, array $rate_ids, $response_line = null ) { 1845 // Positions matter: the answer's components were stored in this order. 1846 $tax_rates = $this->get_looked_up_rates( $rate_ids, $response_line ); 1763 1847 1764 1848 if ( empty( $tax_rates ) ) { -
woocommerce-services/trunk/i18n/languages/woocommerce-services.pot
r3734320 r3746747 5 5 "Project-Id-Version: _s woocommerce-services\n" 6 6 "Report-Msgid-Bugs-To: \n" 7 "POT-Creation-Date: 2026-10- 08T10:46:46.612Z\n"7 "POT-Creation-Date: 2026-10-14T15:49:53.757Z\n" 8 8 "MIME-Version: 1.0\n" 9 9 "Content-Type: text/plain; charset=UTF-8\n" … … 110 110 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 111 111 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 112 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1112 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 113 113 msgid "Create shipping label" 114 114 msgid_plural "Create shipping labels" … … 117 117 118 118 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 119 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1119 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 120 120 msgid "Track Package" 121 121 msgid_plural "Track Packages" … … 124 124 125 125 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 126 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1126 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 127 127 msgid "Create new label" 128 128 msgstr "" 129 129 130 130 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 131 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1131 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 132 132 msgid "Connection error: unable to create label at this time" 133 133 msgstr "" 134 134 135 135 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 136 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1136 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 137 137 msgid "%(itemCount)d item was fulfilled on {{span}}%(createdDate)s{{/span}}" 138 138 msgid_plural "" … … 142 142 143 143 #: dist/chunks/0f0750e0dfa6ef74edc7.min.js:1 144 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1144 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 145 145 msgid "%(itemCount)d item is ready to be fulfilled" 146 146 msgid_plural "%(itemCount)d items are ready to be fulfilled" … … 810 810 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 811 811 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 812 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1812 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 813 813 msgid "Try WooCommerce Shipping" 814 814 msgstr "" … … 816 816 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 817 817 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 818 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1818 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 819 819 msgid "A new WooCommerce Shipping experience is now available" 820 820 msgstr "" … … 822 822 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 823 823 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 824 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1824 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 825 825 msgid "" 826 826 "We'll ensure a seamless transition by allowing you to migrate all your " … … 830 830 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 831 831 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 832 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1832 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 833 833 msgid "Here's what you can expect from the new shipping experience:" 834 834 msgstr "" … … 836 836 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 837 837 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 838 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1838 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 839 839 msgid "A seamless transition" 840 840 msgstr "" … … 842 842 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 843 843 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 844 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1844 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 845 845 msgid "" 846 846 "Automatically migrate all your compatible settings and shipment history to " … … 850 850 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 851 851 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 852 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1852 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 853 853 msgid "Print and save" 854 854 msgstr "" … … 856 856 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 857 857 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 858 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1858 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 859 859 msgid "" 860 860 "Speed up label creation with a streamlined process to print and save your " … … 864 864 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 865 865 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 866 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1866 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 867 867 msgid "USPS and DHL Express" 868 868 msgstr "" … … 870 870 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 871 871 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 872 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1872 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 873 873 msgid "" 874 874 "Ship your productss using trusted shipping carriers like USPS and DHL " … … 878 878 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 879 879 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 880 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1880 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 881 881 msgid "Enhanced label purchase flow" 882 882 msgstr "" … … 884 884 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 885 885 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 886 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1886 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 887 887 msgid "" 888 888 "Experience a smoother label purchasing process with our updated interface." … … 891 891 #: dist/chunks/1c4cd3dc89733fe0b6a4.min.js:1 892 892 #: dist/chunks/8d185b5886cb12db43a8.min.js:1 893 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7. 2.js:1893 #: dist/woocommerce-services-wcshipping-migration-admin-notice-3.7.3.js:1 894 894 msgid "Maybe later" 895 895 msgstr "" -
woocommerce-services/trunk/readme.txt
r3734320 r3746747 8 8 WC requires at least: 11.0 9 9 WC tested up to: 11.2 10 Stable tag: 3.7. 210 Stable tag: 3.7.3 11 11 License: GPLv2 or later 12 12 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 70 70 71 71 == Changelog == 72 73 = 3.7.3 - 2026-10-14 = 74 * Fix - Stop charging tax on an item the tax service answered as tax-exempt, such as clothing under New York's price threshold, when another item in the same tax class is taxed. This affected orders placed at checkout and orders recalculated in the admin. The cart now also shows the tax the order charges when items in one tax class are taxed differently, including product variations and the same product added twice at different prices. 72 75 73 76 = 3.7.2 - 2026-10-08 = -
woocommerce-services/trunk/vendor/composer/installed.php
r3734320 r3746747 4 4 'pretty_version' => 'dev-trunk', 5 5 'version' => 'dev-trunk', 6 'reference' => ' da576c383c6574372e4c4d6c61b88989fc109bc6',6 'reference' => '84d4dd49245d73c4c107a9af4bcd1f143d15ad5c', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-trunk', 15 15 'version' => 'dev-trunk', 16 'reference' => ' da576c383c6574372e4c4d6c61b88989fc109bc6',16 'reference' => '84d4dd49245d73c4c107a9af4bcd1f143d15ad5c', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', -
woocommerce-services/trunk/woocommerce-services.php
r3734320 r3746747 11 11 * License: GPLv2 or later 12 12 * License URI: https://www.gnu.org/licenses/gpl-2.0.html 13 * Version: 3.7. 213 * Version: 3.7.3 14 14 * Requires Plugins: woocommerce 15 15 * Requires PHP: 7.4
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)