diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 44a9f67d3a9..0cfb17a62db 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -12,6 +12,7 @@ - [*] Shipping Labels: Update mark order completed toggle on purchase form [https://github.com/woocommerce/woocommerce-ios/pull/15917] - [*] Shipping Labels: Optimize data loading on purchase form [https://github.com/woocommerce/woocommerce-ios/pull/15919] - [internal] Optimized assets for app size reduction [https://github.com/woocommerce/woocommerce-ios/pull/15881] +- [*] Shipping Labels: Allow dots in HS tariff number input field for customs settings [https://github.com/woocommerce/woocommerce-ios/pull/15933] 22.8 ----- diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsFormViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsFormViewModel.swift index 0cc5ead0401..a5e598f9535 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsFormViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsFormViewModel.swift @@ -211,7 +211,7 @@ private extension WooShippingCustomsFormViewModel { quantity: $0.itemQuantity, value: Double($0.valuePerUnit) ?? 0, weight: Double($0.weightPerUnit) ?? 0, - hsTariffNumber: $0.isValidTariffNumber ? $0.hsTariffNumber : "", + hsTariffNumber: $0.isValidTariffNumber ? $0.sanitizedHSTariffNumber : "", originCountry: $0.selectedCountry?.code ?? "", productID: $0.itemProductID ) diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModel.swift index f5d6b730aa5..56d31eb114e 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModel.swift @@ -49,17 +49,11 @@ final class WooShippingCustomsItemViewModel: ObservableObject { } var isValidTariffNumber: Bool { - guard hsTariffNumber.isNotEmpty else { - return true - } - - // Check if the string contains only digits - let digitsOnly = CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: hsTariffNumber)) - guard digitsOnly else { return false } + return HSTariffNumberValidator.isNumberValid(hsTariffNumber) + } - // Check the length of the string - let length = hsTariffNumber.count - return length >= 6 && length <= 12 + var sanitizedHSTariffNumber: String { + HSTariffNumberValidator.sanitize(hsTariffNumber) } @Published var requiredInformationIsEntered: Bool = false @@ -150,3 +144,41 @@ private extension WooShippingCustomsItemViewModel { .store(in: &cancellables) } } + +/// Follows validation logic from `woocommerce-shipping/client/utils/customs.ts` +enum HSTariffNumberValidator { + static let pattern = "^(\\d{1,2}\\.?){3,6}$" + + /// Check if the HS Tariff Number is valid. + /// It should be a string of 6 to 12 digits, with optional dots in between every 2 digits. + /// - Parameter tariffNumber: The tariff number string. + /// - Returns: `Bool` if tariff number valid or not. + static func isNumberValid(_ tariffNumber: String) -> Bool { + if tariffNumber.isEmpty { + return true + } + + let patternRange = tariffNumber.range( + of: pattern, + options: .regularExpression + ) + + if patternRange == nil { + return false + } + + let digitsOnly = tariffNumber.components( + separatedBy: CharacterSet.decimalDigits.inverted + ).joined() + let count = digitsOnly.count + return count >= 6 && count <= 12 + } + + /// Sanitize the HS Tariff Number + /// Remove all non-digit characters + /// - Parameter tariffNumber: The tariff number string. + /// - Returns: Tariff string without non-digit characters + static func sanitize(_ tariffNumber: String) -> String { + return tariffNumber.components(separatedBy: CharacterSet.decimalDigits.inverted).joined() + } +} diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModelTests.swift index 5a489499323..5c6092e5074 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModelTests.swift @@ -109,4 +109,41 @@ final class WooShippingCustomsItemViewModelTests: XCTestCase { XCTAssertEqual(viewModel.hsTariffNumberTotalValue?.0, viewModel.hsTariffNumber) XCTAssertEqual(viewModel.hsTariffNumberTotalValue?.1, Decimal(string: viewModel.valuePerUnit)! * 2) } + + func test_isNumberValid_whenGivenValidTariffNumbers_shouldReturnTrue() { + XCTAssertTrue(HSTariffNumberValidator.isNumberValid("123456")) + XCTAssertTrue(HSTariffNumberValidator.isNumberValid("12.34.56")) + XCTAssertTrue(HSTariffNumberValidator.isNumberValid("12.34.56.78")) + XCTAssertTrue(HSTariffNumberValidator.isNumberValid("12.34.56.78.90")) + XCTAssertTrue(HSTariffNumberValidator.isNumberValid("12.34.56.78.90.12")) + XCTAssertTrue(HSTariffNumberValidator.isNumberValid("123456789012")) + XCTAssertTrue(HSTariffNumberValidator.isNumberValid("12.345.678.90")) + XCTAssertTrue(HSTariffNumberValidator.isNumberValid("1.2.3.4.5.6")) + } + + func test_isNumberValid_whenGivenInvalidTariffNumbers_shouldReturnFalse() { + // Less than 6 digits + XCTAssertFalse(HSTariffNumberValidator.isNumberValid("12345")) + XCTAssertFalse(HSTariffNumberValidator.isNumberValid("12.34.5")) + + // More than 12 digits + XCTAssertFalse(HSTariffNumberValidator.isNumberValid("1234567890123")) + XCTAssertFalse(HSTariffNumberValidator.isNumberValid("12.34.56.78.90.123")) + + // Invalid characters + XCTAssertFalse(HSTariffNumberValidator.isNumberValid("12345a")) + XCTAssertFalse(HSTariffNumberValidator.isNumberValid("12.34.5a")) + + // Invalid structure + XCTAssertFalse(HSTariffNumberValidator.isNumberValid("12.34..56")) + XCTAssertFalse(HSTariffNumberValidator.isNumberValid(".123456")) + } + + func test_hsTariffNumber_sanitize_whenGivenStringWithNonDigits_shouldReturnOnlyDigits() { + XCTAssertEqual(HSTariffNumberValidator.sanitize("12.34.56"), "123456") + XCTAssertEqual(HSTariffNumberValidator.sanitize("12a34b56c"), "123456") + XCTAssertEqual(HSTariffNumberValidator.sanitize("...123---456..."), "123456") + XCTAssertEqual(HSTariffNumberValidator.sanitize("123456"), "123456") + XCTAssertEqual(HSTariffNumberValidator.sanitize(""), "") + } }