Skip to content

Commit 940ec1d

Browse files
[Shipping Labels] Handle zero weight in customs form (#15927)
2 parents 013bd16 + 825bd71 commit 940ec1d

File tree

5 files changed

+106
-5
lines changed

5 files changed

+106
-5
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [*] Watch app: Fixed connection issue upon fresh install [https://github.com/woocommerce/woocommerce-ios/pull/15867]
1717
- [Internal] Shipping Labels: Optimize requests for syncing countries [https://github.com/woocommerce/woocommerce-ios/pull/15875]
1818
- [*] Shipping Labels: Display label size from account settings as default [https://github.com/woocommerce/woocommerce-ios/pull/15873]
19+
- [*] Shipping Labels: Ensured customs form validation enforces non-zero product weight to fix shipping rate loading failure. [https://github.com/woocommerce/woocommerce-ios/pull/15927]
1920

2021
22.7
2122
-----

WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItem.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ struct WooShippingCustomsItem: View {
1111
@State private var isShowingDescriptionInfoDialog = false
1212
@State private var isShowingOriginCountryInfoDialog = false
1313

14-
1514
@Environment(\.shippingWeightUnit) var weightUnit: String
1615

1716
var body: some View {
@@ -167,12 +166,12 @@ struct WooShippingCustomsItem: View {
167166
.padding(.trailing, Layout.unitsHorizontalSpacing)
168167
}
169168
.roundedBorder(cornerRadius: Layout.borderCornerRadius,
170-
lineColor: viewModel.weightPerUnit.isEmpty ? warningRedColor : Color(.separator),
169+
lineColor: viewModel.isValidWeight ? Color(.separator) : warningRedColor,
171170
lineWidth: Layout.borderLineWidth)
172171
Text(Localization.valueRequiredWarningText)
173172
.foregroundColor(warningRedColor)
174173
.footnoteStyle()
175-
.renderedIf(viewModel.weightPerUnit.isEmpty)
174+
.renderedIf(!viewModel.isValidWeight)
176175
}
177176
}
178177
.padding(.bottom, Layout.collapsibleViewVerticalSpacing)

WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModel.swift

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ final class WooShippingCustomsItemViewModel: ObservableObject {
6262
return length >= 6 && length <= 12
6363
}
6464

65+
var isValidWeight: Bool {
66+
return Self.isWeightValid(weightPerUnit)
67+
}
68+
6569
@Published var requiredInformationIsEntered: Bool = false
6670

6771
private var cancellables = Set<AnyCancellable>()
@@ -79,7 +83,12 @@ final class WooShippingCustomsItemViewModel: ObservableObject {
7983
self.itemProductID = itemProductID
8084
self.itemQuantity = itemQuantity
8185
self.valuePerUnit = String(itemValue)
82-
self.weightPerUnit = String(itemWeight)
86+
87+
/// Skip zero weight
88+
if Self.isWeightNonZero(itemWeight) {
89+
self.weightPerUnit = String(itemWeight)
90+
}
91+
8392
self.currencySymbol = currencySymbol
8493
self.storageManager = storageManager
8594

@@ -127,7 +136,11 @@ private extension WooShippingCustomsItemViewModel {
127136
func combineRequiredInformationIsEntered() {
128137
Publishers.CombineLatest4($description, $valuePerUnit, $weightPerUnit, $selectedCountry)
129138
.sink { [weak self] description, valuePerUnit, weightPerUnit, selectedCountry in
130-
self?.requiredInformationIsEntered = description.isNotEmpty && valuePerUnit.isNotEmpty && weightPerUnit.isNotEmpty && selectedCountry != nil
139+
guard let self else { return }
140+
requiredInformationIsEntered = description.isNotEmpty &&
141+
valuePerUnit.isNotEmpty &&
142+
Self.isWeightValid(weightPerUnit) &&
143+
selectedCountry != nil
131144
}
132145
.store(in: &cancellables)
133146
}
@@ -149,4 +162,13 @@ private extension WooShippingCustomsItemViewModel {
149162
}
150163
.store(in: &cancellables)
151164
}
165+
166+
/// Specifically introduced to check for a `0` value
167+
static func isWeightValid(_ weightString: String) -> Bool {
168+
return isWeightNonZero(Double(weightString) ?? 0)
169+
}
170+
171+
static func isWeightNonZero(_ weightValue: Double) -> Bool {
172+
return weightValue > 0
173+
}
152174
}

WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsFormViewModelTests.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import XCTest
22
import Yosemite
3+
import Combine
34
@testable import WooCommerce
45

56
final class WooShippingCustomsFormViewModelTests: XCTestCase {
@@ -332,6 +333,67 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
332333
XCTAssertEqual(firstItemViewModel?.valuePerUnit, String(firstItem?.value ?? 0))
333334
XCTAssertEqual(firstItemViewModel?.weightPerUnit, String(firstItem?.weight ?? 0))
334335
}
336+
337+
func test_requiredInformationIsEntered_when_weight_is_zero_then_returns_false() {
338+
// Given
339+
let storageManager = MockStorageManager()
340+
let originCountryCodeSubject = PassthroughSubject<String?, Never>()
341+
342+
let country = Country(
343+
code: "US",
344+
name: "United States",
345+
states: []
346+
)
347+
storageManager.insertSampleCountries(readOnlyCountries: [country])
348+
349+
let itemWithZeroWeight = ShippingLabelPackageItem(
350+
productOrVariationID: 1,
351+
orderItemID: 123,
352+
name: "Shirt",
353+
weight: 0,
354+
quantity: 1,
355+
value: 10,
356+
dimensions: .fake(),
357+
attributes: [],
358+
imageURL: nil
359+
)
360+
361+
let shipment = Shipment(
362+
contents: [
363+
CollapsibleShipmentItemCardViewModel(
364+
item: itemWithZeroWeight,
365+
currency: "USD"
366+
)
367+
],
368+
currency: "USD",
369+
currencySettings: ServiceLocator.currencySettings,
370+
shippingSettingsService: ServiceLocator.shippingSettingsService
371+
)
372+
373+
viewModel = WooShippingCustomsFormViewModel(
374+
order: .fake(),
375+
shipment: shipment,
376+
originCountryCode: originCountryCodeSubject.eraseToAnyPublisher(),
377+
storageManager: storageManager,
378+
onFormReady: { _ in }
379+
)
380+
381+
let itemViewModel = viewModel.itemsViewModels[0]
382+
itemViewModel.description = "Test Description"
383+
itemViewModel.valuePerUnit = "10.0"
384+
385+
// When
386+
originCountryCodeSubject.send("US")
387+
388+
// Then
389+
XCTAssertFalse(
390+
viewModel.requiredInformationIsEntered,
391+
"requiredInformationIsEntered should be false when an item's weight is zero"
392+
)
393+
XCTAssertTrue(itemViewModel.weightPerUnit.isEmpty)
394+
XCTAssertFalse(itemViewModel.isValidWeight)
395+
XCTAssertFalse(itemViewModel.requiredInformationIsEntered)
396+
}
335397
}
336398

337399
private extension WooShippingCustomsFormViewModelTests {

WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooShipping Customs/WooShippingCustomsItemViewModelTests.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ final class WooShippingCustomsItemViewModelTests: XCTestCase {
4949
XCTAssertTrue(viewModel.isValidTariffNumber)
5050
}
5151

52+
func test_when_item_weight_is_zero_then_it_is_invalid() {
53+
// Given
54+
let viewModel = WooShippingCustomsItemViewModel(
55+
itemName: "Test",
56+
itemProductID: 22,
57+
itemQuantity: 1,
58+
itemValue: 10,
59+
itemWeight: 0,
60+
currencySymbol: "$",
61+
storageManager: MockStorageManager()
62+
)
63+
64+
// Then
65+
XCTAssertTrue(viewModel.weightPerUnit.isEmpty, "Weight should be empty it the initial value is zero")
66+
XCTAssertFalse(viewModel.isValidWeight, "isValidWeight should be false when the weight is zero")
67+
}
68+
5269
func test_hsTariffNumberTotalValue_when_currency_symbol_is_not_$_returns_nil() {
5370
// Given
5471
viewModel = WooShippingCustomsItemViewModel(itemName: "Test",

0 commit comments

Comments
 (0)