Skip to content

Commit 2ad8619

Browse files
[Shipping Labels] Fix purchased labels not presenting destination address (#15866)
2 parents 601c268 + 0290290 commit 2ad8619

13 files changed

+322
-9
lines changed

Modules/Sources/Networking/Mapper/WooShippingConfigMapper.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,5 @@ private struct WooShippingConfigMapperEnvelope: Decodable {
4444
extension WooShippingConfigMapper {
4545
/// Load only the relevant fields from remote
4646
///
47-
static let fieldsToLoad = "config.shipments, config.shippingLabelData.currentOrderLabels"
47+
static let fieldsToLoad = "config.shipments, config.shippingLabelData.currentOrderLabels, config.shippingLabelData.storedData.selected_destination"
4848
}

Modules/Sources/Networking/Model/ShippingLabel/Packages/WooShippingPackagePurchase.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ extension WooShippingPackagePurchase {
7676

7777
/// shipment ID to set for hazmat and customs form
7878
var formattedShipmentID: String {
79-
Values.shipmentIDPrefix + shipmentID
79+
return WooShippingShipmentIDFormatter.formattedShipmentID(shipmentID)
8080
}
8181
}
8282

@@ -219,6 +219,5 @@ extension WooShippingPackagePurchase: Encodable {
219219
static let adult = "adult"
220220
static let signatureRequired = "signatureRequired"
221221
static let adultSignatureRequired = "adultSignatureRequired"
222-
static let shipmentIDPrefix = "shipment_"
223222
}
224223
}

Modules/Sources/Networking/Model/ShippingLabel/Shipments/WooShippingConfigResponse.swift

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,60 @@ public struct WooShippingLabelData: Decodable, Equatable {
6060
/// Labels purchased for the current order
6161
public let currentOrderLabels: [ShippingLabel]
6262

63-
public init(currentOrderLabels: [ShippingLabel]) {
63+
/// Contains destination addresses
64+
public let storedData: StoredData?
65+
66+
public init(
67+
currentOrderLabels: [ShippingLabel],
68+
storedData: StoredData? = nil
69+
) {
6470
self.currentOrderLabels = currentOrderLabels
71+
self.storedData = storedData
6572
}
6673

6774
public init(from decoder: Decoder) throws {
6875
let container = try decoder.container(keyedBy: CodingKeys.self)
69-
let currentOrderLabels = try container.decodeIfPresent([ShippingLabel].self, forKey: .currentOrderLabels) ?? []
70-
self.init(currentOrderLabels: currentOrderLabels)
76+
77+
let storedData = try container.decodeIfPresent(StoredData.self, forKey: .storedData)
78+
let decodedOrderLabels = try container.decodeIfPresent([ShippingLabel].self, forKey: .currentOrderLabels) ?? []
79+
80+
/// Inject destination addresses into labels if present
81+
let orderLabels: [ShippingLabel]
82+
if let destinations = storedData?.selectedDestinations, !destinations.isEmpty {
83+
orderLabels = WooShippingLabelData.mapDestinations(destinations, into: decodedOrderLabels)
84+
} else {
85+
orderLabels = decodedOrderLabels
86+
}
87+
88+
self.init(
89+
currentOrderLabels: orderLabels,
90+
storedData: storedData
91+
)
7192
}
7293

7394
private enum CodingKeys: String, CodingKey {
7495
case currentOrderLabels
96+
case storedData
97+
}
98+
}
99+
100+
public extension WooShippingLabelData {
101+
typealias WooShippingLabelDestinations = [String: WooShippingAddress]
102+
103+
struct StoredData: Decodable, Equatable {
104+
let selectedDestinations: WooShippingLabelDestinations?
105+
106+
public enum CodingKeys: String, CodingKey {
107+
case selectedDestination = "selected_destination"
108+
}
109+
110+
public init(from decoder: any Decoder) throws {
111+
let container = try decoder.container(keyedBy: CodingKeys.self)
112+
self.selectedDestinations = try? container.decodeIfPresent(
113+
WooShippingLabelDestinations.self,
114+
forKey: CodingKeys.selectedDestination
115+
)
116+
}
75117
}
76118
}
77119

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Foundation
2+
3+
extension WooShippingLabelData {
4+
static func mapDestinations(
5+
_ destinations: WooShippingLabelDestinations,
6+
into labels: [ShippingLabel]
7+
) -> [ShippingLabel] {
8+
return labels.map { label in
9+
guard
10+
let shipmentID = label.shipmentID,
11+
let destinationAddress = destinations[
12+
WooShippingShipmentIDFormatter.formattedShipmentID(shipmentID)
13+
] ?? destinations[shipmentID] /// Fallback for ids previously submitted without `shipment_<id>` formatting
14+
else {
15+
return label
16+
}
17+
18+
return label.copy(destinationAddress: destinationAddress.toShippingLabelAddress())
19+
}
20+
}
21+
}

Modules/Sources/Networking/Model/ShippingLabel/WooShippingAddress.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@ extension WooShippingAddress: Codable {
5959
// If no name is sent to validation address request, no name will be received in response.
6060
// So make sure to decode it only if it's present.
6161
let name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
62-
let company = try container.decode(String.self, forKey: .company)
63-
let phone = try container.decode(String.self, forKey: .phone)
62+
let company = try container.decodeIfPresent(String.self, forKey: .company) ?? ""
63+
let phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? ""
6464
let country = try container.decode(String.self, forKey: .country)
6565
let state = try container.decode(String.self, forKey: .state)
6666
let address1 = try container.decodeIfPresent(String.self, forKey: .address1) ?? container.decode(String.self, forKey: .alternateAddress1)
67-
let address2 = try container.decode(String.self, forKey: .address2)
67+
let address2 = try container.decodeIfPresent(String.self, forKey: .address2) ?? ""
6868
let city = try container.decode(String.self, forKey: .city)
6969
let postcode = try container.decode(String.self, forKey: .postcode)
7070

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Foundation
2+
3+
enum WooShippingShipmentIDFormatter {
4+
/// Turns numeric shipment ID into formatted as `shipment_<id>`
5+
/// - Parameter shipmentID: numeric shipment id
6+
/// - Returns: formated id string
7+
static func formattedShipmentID(_ shipmentID: String) -> String {
8+
return isArgumentIDValid(shipmentID) ?
9+
Values.shipmentIDPrefix + shipmentID :
10+
shipmentID
11+
}
12+
}
13+
14+
private extension WooShippingShipmentIDFormatter {
15+
private enum Values {
16+
static let shipmentIDPrefix = "shipment_"
17+
}
18+
19+
/// Make sure we are formatting incoming numeric ID string
20+
private static func isArgumentIDValid(_ argumentID: String) -> Bool {
21+
return Int(argumentID) != nil
22+
}
23+
}

Modules/Sources/NetworkingCore/Model/ShippingLabel/ShippingLabelAddress.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,16 @@ extension ShippingLabelAddress {
117117
init() {
118118
self.init(company: "", name: "", phone: "", country: "", state: "", address1: "", address2: "", city: "", postcode: "")
119119
}
120+
121+
var isEmpty: Bool {
122+
return company.isEmpty &&
123+
name.isEmpty &&
124+
phone.isEmpty &&
125+
country.isEmpty &&
126+
state.isEmpty &&
127+
address1.isEmpty &&
128+
address2.isEmpty &&
129+
city.isEmpty &&
130+
postcode.isEmpty
131+
}
120132
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import Foundation
2+
import XCTest
3+
@testable import Networking
4+
5+
final class WooShippingShipmentIDFormatterTests: XCTestCase {
6+
7+
// MARK: - formattedShipmentID
8+
9+
func test_formatted_shipment_id_when_id_is_numeric_should_return_formatted_id() {
10+
// Given
11+
let sut = WooShippingShipmentIDFormatter.self
12+
let id = "123456"
13+
14+
// When
15+
let formattedID = sut.formattedShipmentID(id)
16+
17+
// Then
18+
XCTAssertEqual(formattedID, "shipment_123456")
19+
}
20+
21+
func test_formatted_shipment_id_when_id_is_already_formatted_should_return_same_id() {
22+
// Given
23+
let sut = WooShippingShipmentIDFormatter.self
24+
let id = "shipment_123456"
25+
26+
// When
27+
let formattedID = sut.formattedShipmentID(id)
28+
29+
// Then
30+
XCTAssertEqual(formattedID, "shipment_123456")
31+
}
32+
33+
func test_formatted_shipment_id_when_non_numeric_should_return_same_id() {
34+
// Given
35+
let sut = WooShippingShipmentIDFormatter.self
36+
let id = "non-numeric-id"
37+
38+
// When
39+
let formattedID = sut.formattedShipmentID(id)
40+
41+
// Then
42+
XCTAssertEqual(formattedID, id)
43+
}
44+
}

Modules/Tests/NetworkingTests/Remote/WooShippingRemoteTests.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,63 @@ final class WooShippingRemoteTests: XCTestCase {
855855
XCTAssertNotNil(result.failure)
856856
}
857857

858+
// MARK: - Load Config With Destinations
859+
860+
func test_loadConfig_withDestinations_parses_success_response() throws {
861+
// Given
862+
let remote = WooShippingRemote(network: network)
863+
network.simulateResponse(requestUrlSuffix: "config/label-purchase/\(sampleOrderID)", filename: "wooshipping-config-with-destinations")
864+
865+
// When
866+
let result: Result<WooShippingConfig, Error> = waitFor { promise in
867+
remote.loadConfig(siteID: self.sampleSiteID, orderID: self.sampleOrderID) { result in
868+
promise(result)
869+
}
870+
}
871+
872+
// Then
873+
let config = try XCTUnwrap(result.get())
874+
let label = try XCTUnwrap(config.shippingLabelData?.currentOrderLabels.first)
875+
XCTAssertNotNil(label.destinationAddress)
876+
XCTAssertEqual(label.destinationAddress.address1, "200 N SPRING ST")
877+
}
878+
879+
func test_loadConfig_withoutDestinations_parses_success_response() throws {
880+
// Given
881+
let remote = WooShippingRemote(network: network)
882+
network.simulateResponse(requestUrlSuffix: "config/label-purchase/\(sampleOrderID)", filename: "wooshipping-config-without-destinations")
883+
884+
// When
885+
let result: Result<WooShippingConfig, Error> = waitFor { promise in
886+
remote.loadConfig(siteID: self.sampleSiteID, orderID: self.sampleOrderID) { result in
887+
promise(result)
888+
}
889+
}
890+
891+
// Then
892+
let config = try XCTUnwrap(result.get())
893+
let label = try XCTUnwrap(config.shippingLabelData?.currentOrderLabels.first)
894+
XCTAssertTrue(label.destinationAddress.isEmpty)
895+
}
896+
897+
func test_loadConfig_withInvalidDestinations_parses_success_response() throws {
898+
// Given
899+
let remote = WooShippingRemote(network: network)
900+
network.simulateResponse(requestUrlSuffix: "config/label-purchase/\(sampleOrderID)", filename: "wooshipping-config-with-invalid-destinations")
901+
902+
// When
903+
let result: Result<WooShippingConfig, Error> = waitFor { promise in
904+
remote.loadConfig(siteID: self.sampleSiteID, orderID: self.sampleOrderID) { result in
905+
promise(result)
906+
}
907+
}
908+
909+
// Then
910+
let config = try XCTUnwrap(result.get())
911+
let label = try XCTUnwrap(config.shippingLabelData?.currentOrderLabels.first)
912+
XCTAssertTrue(label.destinationAddress.isEmpty)
913+
}
914+
858915
// MARK: Update shipment
859916

860917
func test_updateShipment_parses_success_response() throws {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"data": {
3+
"config": {
4+
"shippingLabelData": {
5+
"storedData": {
6+
"selected_destination": {
7+
"shipment_1": {
8+
"address_1": "200 N SPRING ST",
9+
"city": "LOS ANGELES",
10+
"state": "CA",
11+
"postcode": "90012-4801",
12+
"country": "US"
13+
}
14+
}
15+
},
16+
"currentOrderLabels": [
17+
{
18+
"label_id": 4871,
19+
"tracking": null,
20+
"refundable_amount": 0,
21+
"created": 1742292110723,
22+
"carrier_id": "usps",
23+
"service_name": "USPS - Priority Mail",
24+
"status": "PURCHASE_IN_PROGRESS",
25+
"commercial_invoice_url": "",
26+
"is_commercial_invoice_submitted_electronically": false,
27+
"package_name": "Small Flat Rate Box",
28+
"is_letter": false,
29+
"product_names": [
30+
"BG upload"
31+
],
32+
"product_ids": [
33+
522
34+
],
35+
"id": "1",
36+
"receipt_item_id": -1,
37+
"created_date": 1742292110000
38+
}
39+
]
40+
}
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)