Skip to content

Commit fe81261

Browse files
[Shipping Labels] Apply customs form automatically after pre-fill (#15853)
2 parents 780a18d + 5bc1a76 commit fe81261

File tree

4 files changed

+132
-41
lines changed

4 files changed

+132
-41
lines changed

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

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ final class WooShippingCustomsFormViewModel: ObservableObject {
1717
@Published var returnToSenderIfNotDelivered = false
1818

1919
@Published var requiredInformationIsEntered = false
20-
@Published var itemsRequiredInformationIsEntered = false
20+
@Published private var itemsRequiredInformationIsEntered = false
2121

2222
@Published var contentExplanation = ""
2323
@Published var restrictionDetails = ""
@@ -30,15 +30,22 @@ final class WooShippingCustomsFormViewModel: ObservableObject {
3030
@Published private(set) var destinationCountryCode: String?
3131

3232
private var cancellables = Set<AnyCancellable>()
33-
private let onCompletion: (ShippingLabelCustomsForm) -> ()
33+
34+
/// The callback that passes the `ShippingLabelCustomsForm` to outer environment
35+
/// Called when:
36+
/// - The customs form is closed
37+
/// - The customs form is pre-filled with data and all required fields are completed.
38+
private let onFormReady: (ShippingLabelCustomsForm) -> ()
39+
40+
@Published private(set) var itemsViewModels: [WooShippingCustomsItemViewModel] = []
3441

3542
init(order: Order,
3643
shipment: Shipment,
3744
originCountryCode: AnyPublisher<String?, Never>? = nil,
3845
stores: StoresManager = ServiceLocator.stores,
3946
storageManager: StorageManagerType = ServiceLocator.storageManager,
40-
onCompletion: @escaping (ShippingLabelCustomsForm) -> ()) {
41-
self.onCompletion = onCompletion
47+
onFormReady: @escaping (ShippingLabelCustomsForm) -> ()) {
48+
self.onFormReady = onFormReady
4249

4350
itemsViewModels = shipment.items.map {
4451
WooShippingCustomsItemViewModel(itemName: $0.name,
@@ -55,31 +62,27 @@ final class WooShippingCustomsFormViewModel: ObservableObject {
5562
listenToItemsRequiredInformationValues()
5663
listenForRequiredInformation()
5764
listenForInternationalTransactionNumberIsRequired()
65+
listenForRequiredInformationCompletedUponPreFill()
5866
}
5967

60-
@Published private(set) var itemsViewModels: [WooShippingCustomsItemViewModel] = []
68+
/// WOOMOB-734
69+
/// Solves the issue where a pre-filled form becomes complete without a manual submission
70+
///
71+
/// Listens for the `requiredInformationIsEntered` state
72+
/// As soon as all required info is entered, calls the `emitForm` just once
73+
func listenForRequiredInformationCompletedUponPreFill() {
74+
$requiredInformationIsEntered
75+
.first { $0 == true }
76+
.sink { [weak self] _ in
77+
DispatchQueue.main.async {
78+
self?.emitForm()
79+
}
80+
}
81+
.store(in: &cancellables)
82+
}
6183

6284
func onDismiss() {
63-
/// Ignoring `packageID` and `packageName` as these are not needed in WooShipping plugin, only in WCS&T
64-
let form = ShippingLabelCustomsForm(packageID: "",
65-
packageName: "",
66-
contentsType: contentType.toFormContentsType(),
67-
contentExplanation: contentType == .other ? contentExplanation : "",
68-
restrictionType: restrictionType.toFormRestrictionType(),
69-
restrictionComments: restrictionType == .other ? restrictionDetails : "",
70-
nonDeliveryOption: returnToSenderIfNotDelivered ? .return : .abandon,
71-
itn: internationalTransactionNumber.isValidITN ? internationalTransactionNumber : "",
72-
items: itemsViewModels.map {
73-
ShippingLabelCustomsForm.Item(description: $0.description,
74-
quantity: $0.itemQuantity,
75-
value: Double($0.valuePerUnit) ?? 0,
76-
weight: Double($0.weightPerUnit) ?? 0,
77-
hsTariffNumber: $0.isValidTariffNumber ? $0.hsTariffNumber : "",
78-
originCountry: $0.selectedCountry?.code ?? "",
79-
productID: $0.itemProductID)
80-
}
81-
)
82-
onCompletion(form)
85+
emitForm()
8386
}
8487

8588
func updateDestinationCountry(code: String) {
@@ -192,6 +195,33 @@ private extension WooShippingCustomsFormViewModel {
192195
}
193196
return ServiceLocator.currencySettings.symbol(from: currencyCode)
194197
}
198+
199+
private func emitForm() {
200+
/// Ignoring `packageID` and `packageName` as these are not needed in WooShipping plugin, only in WCS&T
201+
let form = ShippingLabelCustomsForm(
202+
packageID: "",
203+
packageName: "",
204+
contentsType: contentType.toFormContentsType(),
205+
contentExplanation: contentType == .other ? contentExplanation : "",
206+
restrictionType: restrictionType.toFormRestrictionType(),
207+
restrictionComments: restrictionType == .other ? restrictionDetails : "",
208+
nonDeliveryOption: returnToSenderIfNotDelivered ? .return : .abandon,
209+
itn: internationalTransactionNumber.isValidITN ? internationalTransactionNumber : "",
210+
items: itemsViewModels.map {
211+
ShippingLabelCustomsForm.Item(
212+
description: $0.description,
213+
quantity: $0.itemQuantity,
214+
value: Double($0.valuePerUnit) ?? 0,
215+
weight: Double($0.weightPerUnit) ?? 0,
216+
hsTariffNumber: $0.isValidTariffNumber ? $0.hsTariffNumber : "",
217+
originCountry: $0.selectedCountry?.code ?? "",
218+
productID: $0.itemProductID
219+
)
220+
}
221+
)
222+
223+
onFormReady(form)
224+
}
195225
}
196226

197227
private extension WooShippingCustomsFormViewModel {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ private extension WooShippingCustomsItemViewModel {
103103
func combineToPreselectCountry() {
104104
$originCountryCode
105105
.compactMap { $0 }
106+
.filter { !$0.isEmpty }
106107
.first() /// Make sure to only handle the initial value
107108
.combineLatest($countries)
108109
.map { code, countries in

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
1010

1111
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
1212
shipment: sampleShipment,
13-
onCompletion: { _ in })
13+
onFormReady: { _ in })
1414
}
1515

1616
override func tearDown() {
@@ -25,7 +25,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
2525
var passedForm: ShippingLabelCustomsForm?
2626
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
2727
shipment: shipment,
28-
onCompletion: { form in
28+
onFormReady: { form in
2929
passedForm = form
3030
})
3131

@@ -66,7 +66,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
6666
var passedForm: ShippingLabelCustomsForm?
6767
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
6868
shipment: sampleShipment,
69-
onCompletion: { form in
69+
onFormReady: { form in
7070
passedForm = form
7171
})
7272

@@ -84,7 +84,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
8484
var passedForm: ShippingLabelCustomsForm?
8585
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
8686
shipment: sampleShipment,
87-
onCompletion: { form in
87+
onFormReady: { form in
8888
passedForm = form
8989
})
9090

@@ -109,7 +109,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
109109
var passedForm: ShippingLabelCustomsForm?
110110
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
111111
shipment: sampleShipment,
112-
onCompletion: { form in
112+
onFormReady: { form in
113113
passedForm = form
114114
})
115115

@@ -127,7 +127,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
127127
let order = Order.fake().copy(currency: "USD")
128128
viewModel = WooShippingCustomsFormViewModel(order: order,
129129
shipment: sampleShipment,
130-
onCompletion: { _ in })
130+
onFormReady: { _ in })
131131

132132
// Then
133133
XCTAssertEqual(viewModel.itemsViewModels.first?.currencySymbol, "$")
@@ -153,7 +153,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
153153
// Given
154154
viewModel = WooShippingCustomsFormViewModel(order: Order.fake().copy(currency: "USD"),
155155
shipment: sampleShipment,
156-
onCompletion: { _ in })
156+
onFormReady: { _ in })
157157

158158
// When
159159
viewModel.itemsViewModels.forEach { item in
@@ -176,7 +176,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
176176
// Given
177177
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
178178
shipment: sampleShipment,
179-
onCompletion: { _ in })
179+
onFormReady: { _ in })
180180

181181
// When
182182
viewModel.itemsViewModels.first?.hsTariffNumberTotalValue = ("123456", 1000)
@@ -189,7 +189,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
189189
// Given
190190
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
191191
shipment: sampleShipment,
192-
onCompletion: { _ in })
192+
onFormReady: { _ in })
193193

194194
// When
195195
viewModel.itemsViewModels[0].requiredInformationIsEntered = true
@@ -216,7 +216,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
216216
let requiredDestinations = ["IR", "SY", "KP", "CU", "SD"]
217217
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
218218
shipment: sampleShipment,
219-
onCompletion: { _ in })
219+
onFormReady: { _ in })
220220
viewModel.itemsViewModels.forEach { item in
221221
item.hsTariffNumber = ""
222222
item.valuePerUnit = "1000"
@@ -241,7 +241,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
241241
// Given
242242
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
243243
shipment: sampleShipment,
244-
onCompletion: { _ in })
244+
onFormReady: { _ in })
245245

246246
// When
247247
viewModel.itemsViewModels.first?.requiredInformationIsEntered = true
@@ -255,7 +255,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
255255
// Given
256256
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
257257
shipment: sampleShipment,
258-
onCompletion: { _ in })
258+
onFormReady: { _ in })
259259

260260
// When
261261
viewModel.internationalTransactionNumber = "NOEEI 30.37(a)"
@@ -269,7 +269,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
269269
// Given
270270
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
271271
shipment: sampleShipment,
272-
onCompletion: { _ in })
272+
onFormReady: { _ in })
273273

274274
// When
275275
viewModel.itemsViewModels.first?.requiredInformationIsEntered = true
@@ -285,7 +285,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
285285
// Given
286286
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
287287
shipment: sampleShipment,
288-
onCompletion: { _ in })
288+
onFormReady: { _ in })
289289

290290
// When
291291
viewModel.itemsViewModels.first?.requiredInformationIsEntered = true
@@ -301,7 +301,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
301301
// Given
302302
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
303303
shipment: sampleShipment,
304-
onCompletion: { _ in })
304+
onFormReady: { _ in })
305305

306306
// When
307307
viewModel.itemsViewModels.first?.requiredInformationIsEntered = true
@@ -324,7 +324,7 @@ final class WooShippingCustomsFormViewModelTests: XCTestCase {
324324
// When
325325
viewModel = WooShippingCustomsFormViewModel(order: Order.fake(),
326326
shipment: shipment,
327-
onCompletion: { _ in })
327+
onFormReady: { _ in })
328328
let firstItemViewModel = viewModel.itemsViewModels.first
329329

330330
// Then

WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooShippingShipmentDetailsViewModelTests.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,66 @@ final class WooShippingShipmentDetailsViewModelTests: XCTestCase {
817817
// Then
818818
XCTAssertEqual(customsItemViewModel.selectedCountry?.code, expectedCountry)
819819
}
820+
821+
func test_package_contains_complete_customs_form_when_required_data_is_prefilled() throws {
822+
// Setup
823+
let originAddressSubject = PassthroughSubject<WooShippingAddress?, Never>()
824+
let destinationAddressSubject = PassthroughSubject<WooShippingAddress?, Never>()
825+
let stores = MockStoresManager(sessionManager: .testingInstance)
826+
let storageManager = MockStorageManager()
827+
828+
// Given
829+
let originCountry = Country(code: "US", name: "United States", states: [])
830+
let destinationCountry = Country(code: "CA", name: "Canada", states: [])
831+
832+
let countries = [
833+
originCountry,
834+
destinationCountry
835+
]
836+
837+
stores.whenReceivingAction(ofType: DataAction.self) { action in
838+
switch action {
839+
case let .synchronizeCountries(_, onCompletion):
840+
storageManager.insertSampleCountries(readOnlyCountries: countries)
841+
onCompletion(.success(countries))
842+
}
843+
}
844+
845+
let shipment = sampleShipment
846+
847+
let viewModel = WooShippingShipmentDetailsViewModel(
848+
order: Order.fake(),
849+
shipment: shipment,
850+
shippingLabel: nil,
851+
originAddress: originAddressSubject.eraseToAnyPublisher(),
852+
destinationAddress: destinationAddressSubject.eraseToAnyPublisher(),
853+
stores: stores,
854+
storageManager: storageManager
855+
)
856+
857+
// When
858+
destinationAddressSubject.send(sampleDestinationAddress(country: destinationCountry.code, state: ""))
859+
originAddressSubject.send(sampleOriginAddress(country: originCountry.code, state: ""))
860+
861+
viewModel.selectPackage(samplePackageData())
862+
863+
// Then
864+
XCTAssertTrue(viewModel.customsInformationIsCompleted)
865+
866+
waitUntil {
867+
guard let customsForm = viewModel.currentPackage?.customsForm else {
868+
return false
869+
}
870+
871+
let customsFormItem = customsForm.items[0]
872+
let shipmentItem = shipment.items[0]
873+
874+
return customsFormItem.description == shipmentItem.name &&
875+
customsFormItem.value == shipmentItem.value &&
876+
customsFormItem.weight == shipmentItem.weight &&
877+
customsFormItem.originCountry == originCountry.code
878+
}
879+
}
820880
}
821881

822882
private extension WooShippingShipmentDetailsViewModelTests {

0 commit comments

Comments
 (0)