Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Revamp Shipping Labels • Customs] Validation & Required #14921

Merged
merged 9 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ extension WooShippingCustomsForm {
value: "Please enter a valid ITN",
comment: "Customs validation warning for the ITN field")
static let itnRequiredWarningMessage = NSLocalizedString("wooShipping.customs.itnValidation",
value: "International Transaction Number is required for shipping items valued over $2,500",
value: "International Transaction Number is required for shipping items " +
"valued over $2,500 per tariff number",
comment: "Customs validation warning for the ITN field")

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,21 @@ private extension WooShippingCustomsFormViewModel {
func listenForInternationalTransactionNumberIsRequired() {
$itemsViewModels
.map { childViewModels in
childViewModels.map { $0.$internationalTransactionNumberIsRequired.eraseToAnyPublisher() }
childViewModels.map { $0.$hsTariffNumberTotalValue.eraseToAnyPublisher() }
}
.flatMap { childPublishers in
childPublishers.combineLatest()
}
.map { childValidityArray in
childValidityArray.contains { $0 }
}.sink { [weak self] value in
self?.internationalTransactionNumberIsRequired = value
.sink { [weak self] value in
// Remove nils
let compactedValues = value.compactMap { $0 }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tip: You can use compacted() when you want to remove nils; it does the same as compactMap { $0 }.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point thanks! Done in 46288e7


var hsTariffNumberTotalValueDictionary: [String: Decimal] = [:]
for (hsTariffNumber, totalValuePerItem) in compactedValues {
hsTariffNumberTotalValueDictionary[hsTariffNumber, default: 0] += totalValuePerItem
}

self?.internationalTransactionNumberIsRequired = hsTariffNumberTotalValueDictionary.values.contains { $0 > 2500 }
}
.store(in: &cancellables)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ final class WooShippingCustomsItemViewModel: ObservableObject {
@Published var valuePerUnit: String = ""
@Published var weightPerUnit: String = ""
@Published var originCountry: WooShippingCustomsCountry
// Useful to determine externally if the shipping requires an ITN
@Published var hsTariffNumberTotalValue: (String, Decimal)?

private let storageManager: StorageManagerType
private let stores: StoresManager
Expand Down Expand Up @@ -53,7 +55,6 @@ final class WooShippingCustomsItemViewModel: ObservableObject {
}

@Published var requiredInformationIsEntered: Bool = false
@Published var internationalTransactionNumberIsRequired: Bool = false

private var cancellables = Set<AnyCancellable>()

Expand All @@ -72,11 +73,11 @@ final class WooShippingCustomsItemViewModel: ObservableObject {

fetchCountries()
combineRequiredInformationIsEntered()
combineInternationalTransactionNumberIsRequired()
combineHSTariffNumberTotalValue()
}
}

extension WooShippingCustomsItemViewModel {
private extension WooShippingCustomsItemViewModel {
func fetchCountries() {
try? resultsController.performFetch()
let action = DataAction.synchronizeCountries(siteID: siteID) { [weak self] (result) in
Expand All @@ -100,16 +101,20 @@ extension WooShippingCustomsItemViewModel {
.store(in: &cancellables)
}

func combineInternationalTransactionNumberIsRequired() {
func combineHSTariffNumberTotalValue() {
Publishers.CombineLatest($valuePerUnit, $hsTariffNumber)
.sink { [weak self] valuePerUnit, hsTariffNumber in
guard let self = self else { return }

// Items valued more than $2500 with a valid HSTariff Number require an International Transaction Number
self.internationalTransactionNumberIsRequired = self.currencySymbol == "$" &&
(Double(valuePerUnit) ?? 0) > 2500 &&
hsTariffNumber.isNotEmpty &&
isValidTariffNumber
guard self.currencySymbol == "$",
let valuePerUnitDecimal = Decimal(string: valuePerUnit),
hsTariffNumber.isNotEmpty,
isValidTariffNumber else {
self.hsTariffNumberTotalValue = nil
return
}

self.hsTariffNumberTotalValue = (hsTariffNumber, valuePerUnitDecimal * orderItem.quantity)
}
.store(in: &cancellables)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,36 +151,40 @@ class WooShippingCustomsFormViewModelTests: XCTestCase {
XCTAssertFalse(viewModel.isValidITN())
}

func test_internationalTransactionNumberIsRequired_when_item_view_models_does_not_require_internationalTransactionNumber_returns_false() {
func test_internationalTransactionNumberIsRequired_when_item_view_models_hsTariffNumberTotalValue_is_nil_then_returns_false() {
// Given
let orderItems = [MockOrderItem.sampleItem(productID: 123, quantity: 2), MockOrderItem.sampleItem()]

viewModel = WooShippingCustomsFormViewModel(order: Order.fake().copy(items: orderItems), onCompletion: { _ in })

viewModel.itemsViewModels.first?.internationalTransactionNumberIsRequired = false
// When
viewModel.itemsViewModels.first?.hsTariffNumberTotalValue = nil

// Then
XCTAssertFalse(viewModel.internationalTransactionNumberIsRequired)
}

func test_requiredInformationIsEntered_when_one_item_view_model_requiredInformationIsEntered_is_false_then_returns_false() {
func test_internationalTransactionNumberIsRequired_when_item_view_models_hsTariffNumberTotalValue_is_less_than_2500_then_returns_false() {
// Given
let orderItems = [MockOrderItem.sampleItem(productID: 123, quantity: 2), MockOrderItem.sampleItem()]

viewModel = WooShippingCustomsFormViewModel(order: Order.fake().copy(items: orderItems), onCompletion: { _ in })

viewModel.itemsViewModels.first?.requiredInformationIsEntered = false
// When
viewModel.itemsViewModels.first?.hsTariffNumberTotalValue = ("123456", 1000)

XCTAssertFalse(viewModel.requiredInformationIsEntered)
}

func test_requiredInformationIsEntered_when_one_item_view_model_requiredInformationIsEntered_is_true_but_requires_itn_then_returns_false() {
func test_internationalTransactionNumberIsRequired_when_item_view_models_hsTariffNumberTotalValue_is_less_than_2500_then_returns_true() {
// Given
let orderItems = [MockOrderItem.sampleItem(productID: 123, quantity: 2), MockOrderItem.sampleItem()]

viewModel = WooShippingCustomsFormViewModel(order: Order.fake().copy(items: orderItems), onCompletion: { _ in })

viewModel.itemsViewModels.first?.requiredInformationIsEntered = true
viewModel.itemsViewModels.first?.internationalTransactionNumberIsRequired = true
viewModel.itemsViewModels.first?.hsTariffNumberTotalValue = ("123456", 1000)
viewModel.itemsViewModels[1].hsTariffNumberTotalValue = ("123456", 2000)
viewModel.internationalTransactionNumber = ""

XCTAssertFalse(viewModel.requiredInformationIsEntered)
Expand All @@ -193,7 +197,6 @@ class WooShippingCustomsFormViewModelTests: XCTestCase {
viewModel = WooShippingCustomsFormViewModel(order: Order.fake().copy(items: orderItems), onCompletion: { _ in })

viewModel.itemsViewModels.first?.requiredInformationIsEntered = true
viewModel.itemsViewModels.first?.internationalTransactionNumberIsRequired = true
viewModel.internationalTransactionNumber = "1234"

XCTAssertFalse(viewModel.requiredInformationIsEntered)
Expand All @@ -209,7 +212,6 @@ class WooShippingCustomsFormViewModelTests: XCTestCase {

viewModel.internationalTransactionNumber = "NOEEI 30.37(a)"
viewModel.itemsViewModels.first?.requiredInformationIsEntered = true
viewModel.itemsViewModels.first?.internationalTransactionNumberIsRequired = true

XCTAssertTrue(viewModel.requiredInformationIsEntered)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,58 +45,53 @@ class WooShippingCustomsItemViewModelTests: XCTestCase {
XCTAssertTrue(viewModel.isValidTariffNumber)
}

func test_internationalTransactionNumberIsRequired_when_currency_symbol_is_not_$_returns_false() {
func test_hsTariffNumberTotalValue_when_currency_symbol_is_not_$_returns_nil() {
// When
let orderItem = MockOrderItem.sampleItem(quantity: 2)
viewModel = WooShippingCustomsItemViewModel(originCountry: WooShippingCustomsCountry(code: "", name: "United States"),
orderItem: MockOrderItem.sampleItem(), currencySymbol: "")
orderItem: orderItem, currencySymbol: "$")

// Then
XCTAssertFalse(viewModel.internationalTransactionNumberIsRequired)
XCTAssertNil(viewModel.hsTariffNumberTotalValue)

}

func test_internationalTransactionNumberIsRequired_when_currency_symbol_is_$_but_value_is_less_than_2500_returns_false() {
func test_hsTariffNumberTotalValue_when_currency_symbol_is_$_but_hsTariffNumber_is_empty_returns_nil() {
// When
let orderItem = MockOrderItem.sampleItem(quantity: 2)
viewModel = WooShippingCustomsItemViewModel(originCountry: WooShippingCustomsCountry(code: "", name: "United States"),
orderItem: MockOrderItem.sampleItem(), currencySymbol: "$")
viewModel.valuePerUnit = "1000"

// Then
XCTAssertFalse(viewModel.internationalTransactionNumberIsRequired)

}

func test_internationalTransactionNumberIsRequired_when_currency_symbol_is_$_and_value_is_more_than_2500_but_no_hs_tariff_number_returns_false() {
// When
viewModel = WooShippingCustomsItemViewModel(originCountry: WooShippingCustomsCountry(code: "", name: "United States"),
orderItem: MockOrderItem.sampleItem(), currencySymbol: "$")
orderItem: orderItem, currencySymbol: "$")
viewModel.valuePerUnit = "1000"
viewModel.hsTariffNumber = ""

// Then
XCTAssertFalse(viewModel.internationalTransactionNumberIsRequired)
XCTAssertNil(viewModel.hsTariffNumberTotalValue)

}

func test_internationalTransactionNumberIsRequired_when_currency_symbol_is_$_and_value_is_more_than_2500_but_invalid_hs_tariff_number_returns_false() {
func test_hsTariffNumberTotalValue_when_currency_symbol_is_$_but_invalid_hs_tariff_number_returns_nil() {
// When
let orderItem = MockOrderItem.sampleItem(quantity: 2)
viewModel = WooShippingCustomsItemViewModel(originCountry: WooShippingCustomsCountry(code: "", name: "United States"),
orderItem: MockOrderItem.sampleItem(), currencySymbol: "$")
viewModel.valuePerUnit = "1000"
orderItem: orderItem, currencySymbol: "$")
viewModel.hsTariffNumber = "123"
viewModel.valuePerUnit = "1000"


// Then
XCTAssertFalse(viewModel.internationalTransactionNumberIsRequired)
XCTAssertNil(viewModel.hsTariffNumberTotalValue)
}

func test_internationalTransactionNumberIsRequired_when_currency_symbol_is_$_and_value_is_more_than_2500_and_valid_hs_tariff_number_returns_true() {
func test_hsTariffNumberTotalValue_when_currency_symbol_is_$_and_value_is_more_than_2500_and_valid_hs_tariff_number_returns_values() {
// When
let orderItem = MockOrderItem.sampleItem(quantity: 2)
viewModel = WooShippingCustomsItemViewModel(originCountry: WooShippingCustomsCountry(code: "", name: "United States"),
orderItem: MockOrderItem.sampleItem(), currencySymbol: "$")
orderItem: orderItem, currencySymbol: "$")
viewModel.valuePerUnit = "3000"
viewModel.hsTariffNumber = "123456"

// Then
XCTAssertTrue(viewModel.internationalTransactionNumberIsRequired)
XCTAssertEqual(viewModel.hsTariffNumberTotalValue?.0, viewModel.hsTariffNumber)
XCTAssertEqual(viewModel.hsTariffNumberTotalValue?.1, Decimal(string: viewModel.valuePerUnit)! * orderItem.quantity)
}
}
Loading