From 4aa6f99e3cd6bc80564f3b79a0e92153964559d5 Mon Sep 17 00:00:00 2001 From: Yanuar Date: Fri, 24 Jul 2026 14:25:36 +0700 Subject: [PATCH] fix(address): fully sync address both checkout and edit-address (logged in user) --- assets/wp/js/account-address.js | 76 +++++++++++++++- assets/wp/js/form-billing-address.js | 46 ++++++++++ inc/Controllers/AccountAddressController.php | 80 ++++++++++++++++- inc/Controllers/CheckoutController.php | 90 +++++++++++++++++-- inc/Services/CustomerDistrictService.php | 8 ++ lang/kiriminaja-official-id_ID.po | 3 + lang/kiriminaja-official.pot | 4 + .../partials/form-billing-address-config.php | 12 +++ tests/MyAccountDistrictTest.php | 41 +++++++++ ...hopVerseBlockCheckoutCompatibilityTest.php | 21 ++++- 10 files changed, 368 insertions(+), 13 deletions(-) diff --git a/assets/wp/js/account-address.js b/assets/wp/js/account-address.js index e343554..1947ff3 100644 --- a/assets/wp/js/account-address.js +++ b/assets/wp/js/account-address.js @@ -14,15 +14,81 @@ return $("#" + addressType + "_kiriof_destination_area_name"); } + function postcodeField($field) { + var addressType = String($field.attr("id") || "").indexOf("shipping_") === 0 + ? "shipping" + : "billing"; + return $("#" + addressType + "_postcode"); + } + + function extractPostcode(row) { + row = row || {}; + var postcode = row.postcode + || row.zipcode + || row.zip_code + || row.postal_code + || row.kode_pos + || row.kodepos + || ""; + + if (!postcode && row.text) { + var match = String(row.text).match(/\b\d{5}\b/); + postcode = match ? match[0] : ""; + } + + return String(postcode || "").replace(/\s+/g, "").trim(); + } + + function setPostcodeFromDistrict($field, selected) { + var postcode = extractPostcode(selected); + var $postcode = postcodeField($field); + + if (!postcode || !$postcode.length) { + return; + } + + $postcode + .data("kiriofSettingPostcodeFromDistrict", true) + .data("kiriofInitialPostcode", postcode) + .val(postcode) + .trigger("input") + .trigger("change"); + + setTimeout(function () { + $postcode.data("kiriofSettingPostcodeFromDistrict", false); + }, 0); + } + function clearDistrict($field) { $field.val("").trigger("change.select2"); districtNameField($field).val(""); } + function hideBlockMirrorDistrictFields() { + $("input, select, textarea").each(function () { + var fieldName = String(this.name || ""); + var fieldId = String(this.id || ""); + if (fieldName.indexOf("_wc_") === -1 && fieldId.indexOf("_wc_") === -1) { + return; + } + if ( + fieldName.indexOf("kiriminaja-official/kiriof_destination_area") === -1 + && fieldId.indexOf("kiriminaja-official/kiriof_destination_area") === -1 + ) { + return; + } + + $(this).closest(".form-row, p, div").first().hide(); + }); + } + $(function () { var select = $.fn.selectWoo || $.fn.select2; var $fields = districtFields(); + hideBlockMirrorDistrictFields(); + setTimeout(hideBlockMirrorDistrictFields, 300); + if (!$fields.length || !select || typeof kiriofAjax === "undefined") { return; } @@ -56,7 +122,11 @@ : []; return { results: $.map(rows, function (row) { - return { id: row.id, text: row.text }; + return { + id: row.id, + text: row.text, + postcode: extractPostcode(row), + }; }), }; }, @@ -68,6 +138,7 @@ .on("select2:select.kiriofAccountDistrict", function (event) { var selected = event.params && event.params.data ? event.params.data : {}; districtNameField($field).val(selected.text || ""); + setPostcodeFromDistrict($field, selected); }) .on("select2:clear.kiriofAccountDistrict", function () { districtNameField($field).val(""); @@ -79,6 +150,9 @@ }).on( "input.kiriofAccountDistrict change.kiriofAccountDistrict", function () { + if ($(this).data("kiriofSettingPostcodeFromDistrict")) { + return; + } var currentPostcode = String($(this).val() || ""); if (currentPostcode === String($(this).data("kiriofInitialPostcode") || "")) { return; diff --git a/assets/wp/js/form-billing-address.js b/assets/wp/js/form-billing-address.js index 3d87087..cebe59e 100644 --- a/assets/wp/js/form-billing-address.js +++ b/assets/wp/js/form-billing-address.js @@ -36,9 +36,14 @@ } getSearchAreaKelurahan(); + kiriofRestoreClassicDistrictSelections(); changeDistrict(); kiriofScheduleClassicShippingMethodSelectInit(); kiriofInitBlockCheckoutCompatibility(); + if (kiriofBillingAddressConfig.isCheckout) { + setTimeout(kiriofRestoreClassicDistrictSelections, 300); + setTimeout(kiriofRestoreClassicDistrictSelections, 1500); + } if (kiriofBillingAddressConfig.isCart) { @@ -74,6 +79,7 @@ // update_checkout once so WooCommerce can render native fee rows. Calling // it again from updated_checkout creates an endless loading loop. jQuery(document.body).on( 'updated_checkout', function() { + kiriofRestoreClassicDistrictSelections(); kiriofChangeCodPayment(); kiriofChangeDifferentAddress(); }); @@ -1792,6 +1798,45 @@ } } + function kiriofRestoreClassicDistrictSelections() { + kiriofRestoreClassicDistrictSelection( + jQuery('#kiriof_destination_area'), + kiriofBillingAddressConfig.billingDistrict || {}, + jQuery('[name="kiriof_destination_area_name"]') + ); + kiriofRestoreClassicDistrictSelection( + jQuery('#kiriof_shipping_destination_area'), + kiriofBillingAddressConfig.shippingDistrict || {}, + jQuery('[name="kiriof_shipping_destination_area_name"]') + ); + } + + function kiriofRestoreClassicDistrictSelection($select, district, $nameField) { + if (!$select.length || String($select.val() || '')) { + return; + } + + var districtId = String((district && district.id) || ''); + var districtName = String((district && district.name) || $nameField.val() || '').trim(); + if (!districtId || !districtName || kiriofIsPlaceholderDistrictText(districtName)) { + return; + } + + var hasOption = false; + $select.find('option').each(function() { + if (String(jQuery(this).val()) === districtId) { + hasOption = true; + jQuery(this).text(districtName).prop('selected', true); + return false; + } + }); + if (!hasOption) { + $select.append(new Option(districtName, districtId, true, true)); + } + $select.val(districtId).data('kiriofSelectedDistrictText', districtName).trigger('change.select2'); + $nameField.val(districtName); + } + function changeDistrict(){ let kelurahanArea = "select#" + (kiriofBillingAddressConfig.fieldKey || 'kiriof_destination_area') + ",select#kiriof_shipping_destination_area"; @@ -2017,6 +2062,7 @@ $el.trigger('change.select2'); } }); + kiriofRestoreClassicDistrictSelections(); } jQuery(document.body).on('updated_checkout', function() { diff --git a/inc/Controllers/AccountAddressController.php b/inc/Controllers/AccountAddressController.php index 74a5d1f..cfb9601 100644 --- a/inc/Controllers/AccountAddressController.php +++ b/inc/Controllers/AccountAddressController.php @@ -28,6 +28,13 @@ public function addDistrictFields( array $fields, string $address_type ): array $options[ $district['id'] ] = $district['name']; } + $address_2_key = $address_type . '_address_2'; + if ( isset( $fields[ $address_2_key ]['value'] ) && '' !== $district['id'] && (string) $fields[ $address_2_key ]['value'] === $district['id'] ) { + $fields[ $address_2_key ]['value'] = ''; + } + + $fields = $this->hideBlockMirrorDistrictFields( $fields, $address_type, $district ); + $field = array( 'label' => __( 'District', 'kiriminaja-official' ), 'required' => true, @@ -57,6 +64,7 @@ public function validateDistrict( int $user_id, string $address_type, array $add $district_id = $this->postedDistrictId( $address_type ); $district_name = $this->postedDistrictName( $address_type ); + $this->clearPollutedAddress2Post( $address_type, $district_id ); if ( $district_id < 1 || '' === $district_name ) { wc_add_notice( __( 'Please select a District.', 'kiriminaja-official' ), 'error' ); } @@ -67,12 +75,19 @@ public function saveDistrict( int $user_id, string $address_type ): void { return; } + $district_id = $this->postedDistrictId( $address_type ); + $district_name = $this->postedDistrictName( $address_type ); + if ( $district_id > 0 && '' === $district_name ) { + $saved_district = ( new CustomerDistrictService() )->get( $user_id, $address_type ); + $district_name = $saved_district['name']; + } ( new CustomerDistrictService() )->save( $user_id, $address_type, - $this->postedDistrictId( $address_type ), - $this->postedDistrictName( $address_type ) + $district_id, + $district_name ); + $this->syncCheckoutSession( $address_type, $district_id, $district_name ); } private function isEditAddressRequest( string $address_type = '' ): bool { @@ -99,6 +114,67 @@ private function postedDistrictName( string $address_type ): string { return isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : ''; } + private function clearPollutedAddress2Post( string $address_type, int $district_id ): void { + if ( $district_id < 1 ) { + return; + } + + $key = $address_type . '_address_2'; + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- WooCommerce verifies the account address nonce before this hook. + $address_2 = isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : ''; + if ( (string) $district_id === $address_2 ) { + $_POST[ $key ] = ''; + } + } + + private function hideBlockMirrorDistrictFields( array $fields, string $address_type, array $district ): array { + $prefix = '_wc_' . $address_type . '/kiriminaja-official/kiriof_destination_area'; + + foreach ( $fields as $key => $field ) { + if ( ! is_string( $key ) || 0 !== strpos( $key, $prefix ) ) { + continue; + } + + $fields[ $key ]['type'] = 'hidden'; + $fields[ $key ]['required'] = false; + $fields[ $key ]['label'] = ''; + $fields[ $key ]['class'] = array( 'kiriof-hidden-district-mirror' ); + $fields[ $key ]['value'] = false !== strpos( $key, '_name' ) ? $district['name'] : $district['id']; + } + + return $fields; + } + + private function syncCheckoutSession( string $address_type, int $district_id, string $district_name ): void { + if ( ! function_exists( 'WC' ) || ! WC()->session ) { + return; + } + + $id_key = 'shipping' === $address_type ? 'shipping_destination_id' : 'destination_id'; + $name_key = 'shipping' === $address_type ? 'shipping_destination_name' : 'destination_name'; + WC()->session->set( $id_key, $district_id > 0 ? $district_id : '' ); + WC()->session->set( $name_key, $district_name ); + + $postcode_key = $address_type . '_postcode'; + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- WooCommerce verifies the account address nonce before this hook. + $postcode = isset( $_POST[ $postcode_key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $postcode_key ] ) ) : ''; + $postcode = trim( preg_replace( '/\s+/', '', $postcode ) ); + if ( '' === $postcode ) { + return; + } + + $saved_map = (array) WC()->session->get( 'kiriof_destination_postcode_map', array() ); + if ( $district_id > 0 ) { + $saved_map[ $postcode ] = array( + 'destination_id' => (string) $district_id, + 'destination_name' => $district_name, + ); + } else { + unset( $saved_map[ $postcode ] ); + } + WC()->session->set( 'kiriof_destination_postcode_map', $saved_map ); + } + private function insertAfterPostcode( array $fields, string $field_key, array $field ): array { $postcode_key = 0 === strpos( $field_key, 'shipping_' ) ? 'shipping_postcode' : 'billing_postcode'; return $this->insertAfterKey( $fields, $postcode_key, $field_key, $field ); diff --git a/inc/Controllers/CheckoutController.php b/inc/Controllers/CheckoutController.php index bd07def..762922d 100644 --- a/inc/Controllers/CheckoutController.php +++ b/inc/Controllers/CheckoutController.php @@ -125,6 +125,7 @@ public function register() * Remove Billing and shipping Fields */ add_filter('woocommerce_checkout_fields', array($this,'kiriof_billing_fields'), 9999); + add_filter( 'woocommerce_checkout_get_value', array( $this, 'kiriof_checkout_district_value' ), 20, 2 ); add_filter('woocommerce_shipping_chosen_method', array($this,'kiriof_shipping_chosen_method'), 10, 2); add_filter( 'woocommerce_cart_needs_shipping', array($this,'kiriof_filter_cart_needs_shipping')); @@ -137,6 +138,7 @@ public function register() // Phone is required for courier pickup — force it at the locale level so // block checkout also treats it as mandatory (shows "Phone" not "Phone (optional)"). add_filter( 'woocommerce_get_country_locale', array( $this, 'kiriof_require_phone_locale' ), 9999 ); + add_filter( 'woocommerce_default_address_fields', array( $this, 'kiriof_default_address_labels' ), 9999 ); add_action( 'wp_footer', array( $this, 'kiriof_block_checkout_require_phone_label' ) ); /** Control COD availability based on KiriminAja Config tab */ @@ -1518,9 +1520,33 @@ public function kiriof_billing_fields($fields){ $fields = self::kiriof_add_field_subdistrict( $fields ); // Phone is required for courier pickup coordination $fields = self::kiriof_require_phone_fields( $fields ); + $fields = self::kiriof_address_1_label_fields( $fields ); return $fields; } + + public function kiriof_checkout_district_value( $value, string $input ) { + if ( '' !== (string) $value || ! is_user_logged_in() ) { + return $value; + } + + $address_type = ''; + if ( $this->field_destination_key === $input ) { + $address_type = 'billing'; + } elseif ( $this->field_shipping_destination_key === $input ) { + $address_type = 'shipping'; + } + if ( '' === $address_type ) { + return $value; + } + + $district = ( new \KiriminAjaOfficial\Services\CustomerDistrictService() )->get( get_current_user_id(), $address_type ); + if ( '' === $district['id'] && 'shipping' === $address_type ) { + $district = ( new \KiriminAjaOfficial\Services\CustomerDistrictService() )->get( get_current_user_id(), 'billing' ); + } + + return $district['id']; + } private static function kiriof_require_phone_fields( $fields ) { foreach ( array( 'billing', 'shipping' ) as $group ) { $key = $group . '_phone'; @@ -1531,6 +1557,33 @@ private static function kiriof_require_phone_fields( $fields ) { return $fields; } + public function kiriof_default_address_labels( $fields ) { + if ( isset( $fields['address_1'] ) ) { + $fields['address_1']['label'] = self::kiriof_address_1_label(); + } + + return $fields; + } + + private static function kiriof_address_1_label_fields( $fields ) { + foreach ( array( 'billing', 'shipping' ) as $group ) { + $key = $group . '_address_1'; + if ( isset( $fields[ $group ][ $key ] ) ) { + $fields[ $group ][ $key ]['label'] = self::kiriof_address_1_label(); + } + } + + return $fields; + } + + private static function kiriof_address_1_label(): string { + return sprintf( + /* translators: %d: minimum street address length in characters. */ + esc_html__( 'Street address (min. %d char)', 'kiriminaja-official' ), + self::KIRIOF_MIN_ADDRESS_LENGTH + ); + } + /** * Force phone to required in the WooCommerce country locale data. * The block checkout reads locale rules to decide whether to append " (optional)" @@ -1543,11 +1596,17 @@ public function kiriof_require_phone_locale( $locales ) { foreach ( $locales as $country => $fields ) { $locales[ $country ]['phone']['required'] = true; $locales[ $country ]['phone']['hidden'] = false; + if ( isset( $locales[ $country ]['address_1'] ) ) { + $locales[ $country ]['address_1']['label'] = self::kiriof_address_1_label(); + } } // Also set the 'default' locale used as fallback. if ( isset( $locales['default'] ) ) { $locales['default']['phone']['required'] = true; $locales['default']['phone']['hidden'] = false; + if ( isset( $locales['default']['address_1'] ) ) { + $locales['default']['address_1']['label'] = self::kiriof_address_1_label(); + } } return $locales; } @@ -1602,22 +1661,37 @@ private function kiriof_remove_fields_checkout($fields,$fields_selected){ private function kiriof_add_field_subdistrict( $fields ){ $field_key = $this->field_destination_key; $district_service = new \KiriminAjaOfficial\Services\CustomerDistrictService(); - $customer = isset( WC()->customer ) && WC()->customer instanceof \WC_Customer ? WC()->customer : get_current_user_id(); + $customer = get_current_user_id(); + if ( $customer < 1 && isset( WC()->customer ) && WC()->customer instanceof \WC_Customer ) { + $customer = WC()->customer; + } $saved_billing = $district_service->get( $customer, 'billing' ); $saved_shipping = $district_service->get( $customer, 'shipping' ); //billing session - $destination_id = WC()->session->get('destination_id') ?: $saved_billing['id']; - $destination_name = WC()->session->get('destination_name') ?: $saved_billing['name']; + $destination_id = WC()->session->get('destination_id'); + $destination_name = WC()->session->get('destination_name'); + if ( empty( $destination_id ) || empty( $destination_name ) ) { + $destination_id = $saved_billing['id']; + $destination_name = $saved_billing['name']; + } $options = array( '' => esc_html__( 'Select Option', 'kiriminaja-official' ) ); - if ( ! empty( $destination_id ) ) { + if ( ! empty( $destination_id ) && ! empty( $destination_name ) ) { $options[ $destination_id ] = $destination_name; } //shipping session - $shipping_dest_id = WC()->session->get('shipping_destination_id') ?: $saved_shipping['id']; - $shipping_dest_name = WC()->session->get('shipping_destination_name') ?: $saved_shipping['name']; + $shipping_dest_id = WC()->session->get('shipping_destination_id'); + $shipping_dest_name = WC()->session->get('shipping_destination_name'); + if ( empty( $shipping_dest_id ) || empty( $shipping_dest_name ) ) { + $shipping_dest_id = $saved_shipping['id']; + $shipping_dest_name = $saved_shipping['name']; + } + if ( empty( $shipping_dest_id ) || empty( $shipping_dest_name ) ) { + $shipping_dest_id = $destination_id; + $shipping_dest_name = $destination_name; + } $shipping_options = array( '' => esc_html__( 'Select Option', 'kiriminaja-official' ) ); - if ( ! empty( $shipping_dest_id ) ) { + if ( ! empty( $shipping_dest_id ) && ! empty( $shipping_dest_name ) ) { $shipping_options[ $shipping_dest_id ] = $shipping_dest_name; } @@ -1631,6 +1705,7 @@ private function kiriof_add_field_subdistrict( $fields ){ 'priority' => 61, 'options' => $options, 'default' => $destination_id, + 'value' => $destination_id, ); //add field shipping District $fields['shipping'][$this->field_shipping_destination_key] = array( @@ -1642,6 +1717,7 @@ private function kiriof_add_field_subdistrict( $fields ){ 'priority' => 61, 'options' => ! empty( $shipping_dest_id ) ? $shipping_options : $options, 'default' => ! empty( $shipping_dest_id ) ? $shipping_dest_id : $destination_id, + 'value' => ! empty( $shipping_dest_id ) ? $shipping_dest_id : $destination_id, ); return $fields; } diff --git a/inc/Services/CustomerDistrictService.php b/inc/Services/CustomerDistrictService.php index 7e2ba50..bd77fe3 100644 --- a/inc/Services/CustomerDistrictService.php +++ b/inc/Services/CustomerDistrictService.php @@ -25,9 +25,15 @@ public function get( $customer, string $address_type ): array { if ( '' === $id ) { $id = $this->readMeta( $customer, $address_type . '_kiriminaja-official/' . self::FIELD_ID ); } + if ( '' === $id ) { + $id = $this->readMeta( $customer, '_wc_' . $address_type . '/kiriminaja-official/' . self::FIELD_ID ); + } if ( '' === $name ) { $name = $this->readMeta( $customer, $address_type . '_kiriminaja-official/' . self::FIELD_NAME ); } + if ( '' === $name ) { + $name = $this->readMeta( $customer, '_wc_' . $address_type . '/kiriminaja-official/' . self::FIELD_NAME ); + } return array( 'id' => sanitize_text_field( $id ), @@ -52,6 +58,8 @@ public function save( $customer, string $address_type, $district_id, string $dis $address_type . '_' . self::FIELD_NAME => $district_name, $address_type . '_kiriminaja-official/' . self::FIELD_ID => $district_id > 0 ? (string) $district_id : '', $address_type . '_kiriminaja-official/' . self::FIELD_NAME => $district_name, + '_wc_' . $address_type . '/kiriminaja-official/' . self::FIELD_ID => $district_id > 0 ? (string) $district_id : '', + '_wc_' . $address_type . '/kiriminaja-official/' . self::FIELD_NAME => $district_name, ); if ( $customer instanceof \WC_Customer ) { diff --git a/lang/kiriminaja-official-id_ID.po b/lang/kiriminaja-official-id_ID.po index 0499ad8..a403f5c 100644 --- a/lang/kiriminaja-official-id_ID.po +++ b/lang/kiriminaja-official-id_ID.po @@ -2978,3 +2978,6 @@ msgstr "Hanya refresh manual" msgid "Selected courier service does not support Cash on Delivery. Please choose a COD-supported courier service." msgstr "Layanan kurir yang dipilih tidak mendukung Cash on Delivery. Silakan pilih layanan kurir yang mendukung COD." + +msgid "Street address (min. %d char)" +msgstr "Alamat jalan (min. %d karakter)" diff --git a/lang/kiriminaja-official.pot b/lang/kiriminaja-official.pot index c4af28d..024d1f9 100644 --- a/lang/kiriminaja-official.pot +++ b/lang/kiriminaja-official.pot @@ -39,6 +39,10 @@ msgstr "" msgid "https://kiriminaja.com" msgstr "" +#: inc/Controllers/CheckoutController.php +msgid "Street address (min. %d char)" +msgstr "" + #: inc/Base/Helper.php:13 #: templates/transaction-process/view/modal-detail.php:18 msgid "New" diff --git a/templates/front/partials/form-billing-address-config.php b/templates/front/partials/form-billing-address-config.php index c5b59ac..6fa32c3 100644 --- a/templates/front/partials/form-billing-address-config.php +++ b/templates/front/partials/form-billing-address-config.php @@ -8,6 +8,10 @@ * @var bool $kiriof_global_insurance * @var array $kiriof_saved_destination_map * @var string $kiriof_saved_checkout_postcode + * @var string $destination_id + * @var string $destination_name + * @var string $shipping_destination_id + * @var string $shipping_destination_name */ if ( ! defined( 'ABSPATH' ) ) { @@ -17,6 +21,14 @@ return array( 'savedDistrictByPostcode' => is_array( $kiriof_saved_destination_map ) ? $kiriof_saved_destination_map : array(), 'savedCheckoutPostcode' => (string) $kiriof_saved_checkout_postcode, + 'billingDistrict' => array( + 'id' => (string) $destination_id, + 'name' => (string) $destination_name, + ), + 'shippingDistrict' => array( + 'id' => (string) $shipping_destination_id, + 'name' => (string) $shipping_destination_name, + ), 'storeApiNonce' => wp_create_nonce( 'wc_store_api' ), 'storeApiUpdateCustomerUrl' => rest_url( 'wc/store/v1/cart/update-customer' ), 'globalInsurance' => (bool) $kiriof_global_insurance, diff --git a/tests/MyAccountDistrictTest.php b/tests/MyAccountDistrictTest.php index b852d5e..1dc6a56 100644 --- a/tests/MyAccountDistrictTest.php +++ b/tests/MyAccountDistrictTest.php @@ -15,6 +15,15 @@ public function account_address_controller_registers_account_only_district_lifec $this->assertStringContainsString("woocommerce_customer_save_address", $content); $this->assertStringContainsString("is_wc_endpoint_url( 'edit-address' )", $content); $this->assertStringContainsString("\$address_type . '_kiriof_destination_area'", $content); + $this->assertStringContainsString("\$address_type . '_address_2'", $content); + $this->assertStringNotContainsString("\$fields[ \$legacy_key ]['type'] = 'hidden'", $content); + $this->assertStringNotContainsString('isIndonesiaAddress', $content); + $this->assertStringContainsString('syncCheckoutSession', $content); + $this->assertStringContainsString('clearPollutedAddress2Post', $content); + $this->assertStringContainsString('hideBlockMirrorDistrictFields', $content); + $this->assertStringContainsString("\$district_id > 0 && '' === \$district_name", $content); + $this->assertStringContainsString("'shipping_destination_id' : 'destination_id'", $content); + $this->assertStringContainsString("'kiriof_destination_postcode_map'", $content); } #[Test] @@ -24,6 +33,7 @@ public function account_district_service_reads_and_writes_canonical_and_legacy_m $this->assertStringContainsString("\$address_type . '_' . self::FIELD_ID", $content); $this->assertStringContainsString("\$address_type . '_kiriminaja-official/' . self::FIELD_ID", $content); + $this->assertStringContainsString("'_wc_' . \$address_type . '/kiriminaja-official/' . self::FIELD_ID", $content); $this->assertStringContainsString("update_user_meta", $content); $this->assertStringContainsString("update_meta_data", $content); } @@ -39,6 +49,28 @@ public function account_district_script_supports_search_selection_and_postcode_i $this->assertStringContainsString('select2:clear.kiriofAccountDistrict', $content); $this->assertStringContainsString('#billing_postcode, #shipping_postcode', $content); $this->assertStringContainsString('clearDistrict', $content); + $this->assertStringContainsString('extractPostcode', $content); + $this->assertStringContainsString('setPostcodeFromDistrict', $content); + $this->assertStringContainsString('kiriofSettingPostcodeFromDistrict', $content); + $this->assertStringContainsString('postcode: extractPostcode(row)', $content); + $this->assertStringContainsString('hideBlockMirrorDistrictFields', $content); + $this->assertStringNotContainsString('legacyDistrictField', $content); + $this->assertStringNotContainsString('restoreLegacyDistrict', $content); + $this->assertStringNotContainsString('#billing_address_2_field, #shipping_address_2_field', $content); + } + + #[Test] + public function classic_checkout_restores_saved_district_after_select2_refresh(): void + { + $config = file_get_contents(PLUGIN_DIR . '/templates/front/partials/form-billing-address-config.php'); + $script = file_get_contents(PLUGIN_DIR . '/assets/wp/js/form-billing-address.js'); + + $this->assertStringContainsString("'billingDistrict'", $config); + $this->assertStringContainsString("'shippingDistrict'", $config); + $this->assertStringContainsString('kiriofRestoreClassicDistrictSelections', $script); + $this->assertStringContainsString('kiriofRestoreClassicDistrictSelection', $script); + $this->assertStringContainsString("setTimeout(kiriofRestoreClassicDistrictSelections, 1500)", $script); + $this->assertStringContainsString("trigger('change.select2')", $script); } #[Test] @@ -58,6 +90,15 @@ public function checkout_uses_customer_district_as_fallback_and_persists_canonic $content = file_get_contents(PLUGIN_DIR . '/inc/Controllers/CheckoutController.php'); $this->assertStringContainsString("CustomerDistrictService()", $content); + $this->assertStringContainsString("woocommerce_checkout_get_value", $content); + $this->assertStringContainsString("kiriof_checkout_district_value", $content); + $this->assertStringContainsString("\$this->field_destination_key === \$input", $content); + $this->assertStringContainsString("\$this->field_shipping_destination_key === \$input", $content); + $this->assertStringContainsString("'value' => \$destination_id", $content); + $this->assertStringContainsString("'value' => ! empty( \$shipping_dest_id ) ? \$shipping_dest_id : \$destination_id", $content); + $this->assertStringContainsString("\$customer = get_current_user_id()", $content); + $this->assertStringContainsString("! empty( \$destination_id ) && ! empty( \$destination_name )", $content); + $this->assertStringContainsString("! empty( \$shipping_dest_id ) && ! empty( \$shipping_dest_name )", $content); $this->assertStringContainsString("\$district_service->get( \$customer, 'billing' )", $content); $this->assertStringContainsString("\$district_service->get( \$customer, 'shipping' )", $content); $this->assertStringContainsString("\$district_service->save(", $content); diff --git a/tests/ShopVerseBlockCheckoutCompatibilityTest.php b/tests/ShopVerseBlockCheckoutCompatibilityTest.php index 8f41c31..aa8721d 100644 --- a/tests/ShopVerseBlockCheckoutCompatibilityTest.php +++ b/tests/ShopVerseBlockCheckoutCompatibilityTest.php @@ -2251,10 +2251,13 @@ public function classic_checkout_does_not_register_district_twice_through_defaul { $content = file_get_contents(PLUGIN_DIR . '/inc/Controllers/CheckoutController.php'); + $defaultAddressStart = strpos($content, 'public function kiriof_default_address_labels'); + $this->assertNotFalse($defaultAddressStart, 'Default address field filter may only adjust native address labels'); + $defaultAddressBody = substr($content, $defaultAddressStart, 700); $this->assertStringNotContainsString( - "add_filter( 'woocommerce_default_address_fields'", - $content, - 'Classic checkout already injects District via woocommerce_checkout_fields; adding it again through default address fields renders a duplicate prefixed billing_kiriof_destination_area field' + 'kiriof_destination_area', + $defaultAddressBody, + 'Classic checkout already injects District via woocommerce_checkout_fields; default address fields must not add another District field' ); $this->assertStringContainsString( @@ -2390,6 +2393,18 @@ public function classic_checkout_short_address_replaces_generic_missing_shipping ); } + #[Test] + public function street_address_label_shows_minimum_length_hint(): void + { + $controller = file_get_contents(PLUGIN_DIR . '/inc/Controllers/CheckoutController.php'); + + $this->assertStringContainsString('woocommerce_default_address_fields', $controller); + $this->assertStringContainsString('kiriof_default_address_labels', $controller); + $this->assertStringContainsString('kiriof_address_1_label_fields', $controller); + $this->assertStringContainsString('Street address (min. %d char)', $controller); + $this->assertStringContainsString("address_1']['label'] = self::kiriof_address_1_label()", $controller); + } + #[Test] public function classic_checkout_uses_native_wc_fee_rows_instead_of_hidden_placeholder_rows(): void {