Commit a85d3946e42 for woocommerce

commit a85d3946e4289350bec458202636b5a550108923
Author: Thomas Roberts <5656702+opr@users.noreply.github.com>
Date:   Mon Aug 17 09:48:05 2026 +0100

    fix: accept Latvian postcodes without the LV prefix hyphen (#67684)

    * fix: accept Latvian postcodes without the LV prefix hyphen

    WC_Validation::is_postcode() does not normalise its input, so a Latvian
    postcode can reach it as "LV1050" or "LV 1050" rather than "LV-1050".
    The rule added in #67460 required the literal hyphen, so it rejected
    both. Before that PR they fell through to the fail-open default and were
    accepted.

    POST /wc/store/v1/checkout reaches this on a normal request.
    AbstractAddressSchema::sanitize_callback() formats the postcode with the
    raw country value but validates against the uppercased one, so a client
    sending country "lv" skips the LV case in wc_format_postcode() (which
    would reinsert the hyphen) and then hits the LV case in is_postcode().
    Verified against a local store: rejected on trunk, accepted here.

    The separator is a literal space rather than \s, so a newline or tab
    between the prefix and the digits is still rejected.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    * docs: document the LV postcode pattern and cover its edges

    Explain the optional prefix, the single hyphen or space separator, and
    why the pattern uses a literal space and \z rather than \s and $.

    Add rows for repeated and invalid separators, the top of the four digit
    range, and trailing characters. No behaviour change; every one of these
    already validated this way.

    Raised by CodeRabbit on #67684.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---------

    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/fix-lv-postcode-separator b/plugins/woocommerce/changelog/fix-lv-postcode-separator
new file mode 100644
index 00000000000..263f3e2903b
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-lv-postcode-separator
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Accept Latvian postcodes that carry the LV prefix without a hyphen, restoring values that validated before postcode rules were added for Latvia.
diff --git a/plugins/woocommerce/includes/class-wc-validation.php b/plugins/woocommerce/includes/class-wc-validation.php
index 330dd93acc4..fecdebb7f94 100644
--- a/plugins/woocommerce/includes/class-wc-validation.php
+++ b/plugins/woocommerce/includes/class-wc-validation.php
@@ -144,7 +144,10 @@ class WC_Validation {
 				$valid = (bool) preg_match( '/^(94[8-9][0-9])$/', $postcode );
 				break;
 			case 'LV':
-				$valid = (bool) preg_match( '/^(?:LV-)?[1-9][0-9]{3}\z/i', $postcode );
+				// An optional case-insensitive LV prefix, followed by at most one hyphen or
+				// space, then four digits that do not start with a zero. A literal space
+				// rather than \s, and \z rather than $, so newlines and tabs are rejected.
+				$valid = (bool) preg_match( '/^(?:LV[- ]?)?[1-9][0-9]{3}\z/i', $postcode );
 				break;
 			default:
 				$valid = true;
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-validation-test.php b/plugins/woocommerce/tests/php/includes/class-wc-validation-test.php
index 640ec71c6b3..20e3b975b67 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-validation-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-validation-test.php
@@ -97,6 +97,25 @@ class WC_Validation_Test extends \WC_Unit_Test_Case {
 			array( false, 'ZZ-1050', 'LV' ),
 			array( false, 'LV-ABCD', 'LV' ),
 			array( false, "LV-1050\n", 'LV' ),
+			// The country prefix without a separator, as produced by wc_normalize_postcode().
+			array( true, 'LV1050', 'LV' ),
+			array( true, 'lv1050', 'LV' ),
+			array( false, 'LV0123', 'LV' ),
+			array( false, "LV1050\n", 'LV' ),
+			// A space is accepted as the prefix separator, but only a literal space.
+			array( true, 'LV 1050', 'LV' ),
+			array( true, 'lv 1050', 'LV' ),
+			array( false, "LV\n1050", 'LV' ),
+			array( false, "LV\t1050", 'LV' ),
+			// At most one separator, and only a hyphen or a space.
+			array( false, 'LV--1050', 'LV' ),
+			array( false, 'LV  1050', 'LV' ),
+			array( false, 'LV_1050', 'LV' ),
+			// The bounds of the four digit range, and trailing characters.
+			array( true, '9999', 'LV' ),
+			array( false, '0999', 'LV' ),
+			array( false, 'LV-1050x', 'LV' ),
+			array( false, 'LV-1050 ', 'LV' ),
 		);

 		return array_merge( $cz, $se, $li, $lv );